blob: 92dc9bd5b667bb8eb94e3d4b327c69b72b47d0eb [file] [log] [blame]
Damian7caa4342011-05-18 11:10:07 +00001/*
2 * SuperH Mobile MERAM Driver for SuperH Mobile LCDC Driver
3 *
4 * Copyright (c) 2011 Damian Hobson-Garcia <dhobsong@igel.co.jp>
5 * Takanari Hayama <taki@igel.co.jp>
6 *
7 * This file is subject to the terms and conditions of the GNU General Public
8 * License. See the file "COPYING" in the main directory of this archive
9 * for more details.
10 */
11
Laurent Pinchart75543402011-09-19 11:40:31 +020012#include <linux/device.h>
Laurent Pinchart974d250b2011-09-19 11:40:31 +020013#include <linux/genalloc.h>
Laurent Pinchart75543402011-09-19 11:40:31 +020014#include <linux/io.h>
Damian7caa4342011-05-18 11:10:07 +000015#include <linux/kernel.h>
16#include <linux/module.h>
Damian7caa4342011-05-18 11:10:07 +000017#include <linux/platform_device.h>
Laurent Pinchart75543402011-09-19 11:40:31 +020018#include <linux/pm_runtime.h>
19#include <linux/slab.h>
20
Laurent Pinchart8a209742011-07-13 12:13:47 +020021#include <video/sh_mobile_meram.h>
Damian7caa4342011-05-18 11:10:07 +000022
Laurent Pinchart75543402011-09-19 11:40:31 +020023/* -----------------------------------------------------------------------------
24 * MERAM registers
25 */
26
Laurent Pinchartf0a260f2011-07-13 12:13:47 +020027#define MEVCR1 0x4
28#define MEVCR1_RST (1 << 31)
29#define MEVCR1_WD (1 << 30)
30#define MEVCR1_AMD1 (1 << 29)
31#define MEVCR1_AMD0 (1 << 28)
32#define MEQSEL1 0x40
33#define MEQSEL2 0x44
Damian7caa4342011-05-18 11:10:07 +000034
Laurent Pinchartf0a260f2011-07-13 12:13:47 +020035#define MExxCTL 0x400
36#define MExxCTL_BV (1 << 31)
37#define MExxCTL_BSZ_SHIFT 28
38#define MExxCTL_MSAR_MASK (0x7ff << MExxCTL_MSAR_SHIFT)
39#define MExxCTL_MSAR_SHIFT 16
40#define MExxCTL_NXT_MASK (0x1f << MExxCTL_NXT_SHIFT)
41#define MExxCTL_NXT_SHIFT 11
42#define MExxCTL_WD1 (1 << 10)
43#define MExxCTL_WD0 (1 << 9)
44#define MExxCTL_WS (1 << 8)
45#define MExxCTL_CB (1 << 7)
46#define MExxCTL_WBF (1 << 6)
47#define MExxCTL_WF (1 << 5)
48#define MExxCTL_RF (1 << 4)
49#define MExxCTL_CM (1 << 3)
50#define MExxCTL_MD_READ (1 << 0)
51#define MExxCTL_MD_WRITE (2 << 0)
52#define MExxCTL_MD_ICB_WB (3 << 0)
53#define MExxCTL_MD_ICB (4 << 0)
54#define MExxCTL_MD_FB (7 << 0)
55#define MExxCTL_MD_MASK (7 << 0)
56#define MExxBSIZE 0x404
57#define MExxBSIZE_RCNT_SHIFT 28
58#define MExxBSIZE_YSZM1_SHIFT 16
59#define MExxBSIZE_XSZM1_SHIFT 0
60#define MExxMNCF 0x408
61#define MExxMNCF_KWBNM_SHIFT 28
62#define MExxMNCF_KRBNM_SHIFT 24
63#define MExxMNCF_BNM_SHIFT 16
64#define MExxMNCF_XBV (1 << 15)
65#define MExxMNCF_CPL_YCBCR444 (1 << 12)
66#define MExxMNCF_CPL_YCBCR420 (2 << 12)
67#define MExxMNCF_CPL_YCBCR422 (3 << 12)
68#define MExxMNCF_CPL_MSK (3 << 12)
69#define MExxMNCF_BL (1 << 2)
70#define MExxMNCF_LNM_SHIFT 0
71#define MExxSARA 0x410
72#define MExxSARB 0x414
73#define MExxSBSIZE 0x418
74#define MExxSBSIZE_HDV (1 << 31)
75#define MExxSBSIZE_HSZ16 (0 << 28)
76#define MExxSBSIZE_HSZ32 (1 << 28)
77#define MExxSBSIZE_HSZ64 (2 << 28)
78#define MExxSBSIZE_HSZ128 (3 << 28)
79#define MExxSBSIZE_SBSIZZ_SHIFT 0
Damian7caa4342011-05-18 11:10:07 +000080
Laurent Pinchartf0a260f2011-07-13 12:13:47 +020081#define MERAM_MExxCTL_VAL(next, addr) \
82 ((((next) << MExxCTL_NXT_SHIFT) & MExxCTL_NXT_MASK) | \
83 (((addr) << MExxCTL_MSAR_SHIFT) & MExxCTL_MSAR_MASK))
84#define MERAM_MExxBSIZE_VAL(rcnt, yszm1, xszm1) \
85 (((rcnt) << MExxBSIZE_RCNT_SHIFT) | \
86 ((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \
87 ((xszm1) << MExxBSIZE_XSZM1_SHIFT))
Damian7caa4342011-05-18 11:10:07 +000088
Laurent Pinchart762f7cc2011-09-19 11:40:31 +020089static const unsigned long common_regs[] = {
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +020090 MEVCR1,
91 MEQSEL1,
92 MEQSEL2,
93};
Laurent Pinchart75543402011-09-19 11:40:31 +020094#define MERAM_REGS_SIZE ARRAY_SIZE(common_regs)
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +020095
Laurent Pinchart762f7cc2011-09-19 11:40:31 +020096static const unsigned long icb_regs[] = {
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +020097 MExxCTL,
98 MExxBSIZE,
99 MExxMNCF,
100 MExxSARA,
101 MExxSARB,
102 MExxSBSIZE,
103};
104#define ICB_REGS_SIZE ARRAY_SIZE(icb_regs)
105
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200106/*
107 * sh_mobile_meram_icb - MERAM ICB information
108 * @regs: Registers cache
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200109 * @offset: MERAM block offset
110 * @size: MERAM block size in bytes
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200111 * @cache_unit: Bytes to cache per ICB
112 * @pixelformat: Video pixel format of the data stored in the ICB
113 * @current_reg: Which of Start Address Register A (0) or B (1) is in use
114 */
115struct sh_mobile_meram_icb {
116 unsigned long regs[ICB_REGS_SIZE];
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200117 unsigned long offset;
118 unsigned int size;
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200119
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200120 unsigned int cache_unit;
121 unsigned int pixelformat;
122 unsigned int current_reg;
123};
124
Laurent Pinchart75543402011-09-19 11:40:31 +0200125#define MERAM_ICB_NUM 32
126
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200127/*
128 * sh_mobile_meram_priv - MERAM device
129 * @base: Registers base address
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200130 * @meram: MERAM physical address
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200131 * @regs: Registers cache
132 * @lock: Protects used_icb and icbs
133 * @used_icb: Bitmask of used ICBs
134 * @icbs: ICBs
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200135 * @pool: Allocation pool to manage the MERAM
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200136 */
Damian Hobson-Garcia0aa492b2011-06-22 07:49:50 +0200137struct sh_mobile_meram_priv {
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200138 void __iomem *base;
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200139 unsigned long meram;
Laurent Pinchart75543402011-09-19 11:40:31 +0200140 unsigned long regs[MERAM_REGS_SIZE];
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200141
142 struct mutex lock;
143 unsigned long used_icb;
Laurent Pinchart75543402011-09-19 11:40:31 +0200144 struct sh_mobile_meram_icb icbs[MERAM_ICB_NUM];
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200145
146 struct gen_pool *pool;
Damian Hobson-Garcia0aa492b2011-06-22 07:49:50 +0200147};
148
Damian7caa4342011-05-18 11:10:07 +0000149/* settings */
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200150#define MERAM_GRANULARITY 1024
Laurent Pinchart75543402011-09-19 11:40:31 +0200151#define MERAM_SEC_LINE 15
152#define MERAM_LINE_WIDTH 2048
Damian7caa4342011-05-18 11:10:07 +0000153
Laurent Pinchart75543402011-09-19 11:40:31 +0200154/* -----------------------------------------------------------------------------
155 * Registers access
Damian7caa4342011-05-18 11:10:07 +0000156 */
157
Laurent Pinchartf0a260f2011-07-13 12:13:47 +0200158#define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20)
Damian7caa4342011-05-18 11:10:07 +0000159
Laurent Pinchart05432832011-09-19 11:40:31 +0200160static inline void meram_write_icb(void __iomem *base, unsigned int idx,
161 unsigned int off, unsigned long val)
Damian7caa4342011-05-18 11:10:07 +0000162{
163 iowrite32(val, MERAM_ICB_OFFSET(base, idx, off));
164}
165
Laurent Pinchart05432832011-09-19 11:40:31 +0200166static inline unsigned long meram_read_icb(void __iomem *base, unsigned int idx,
167 unsigned int off)
Damian7caa4342011-05-18 11:10:07 +0000168{
169 return ioread32(MERAM_ICB_OFFSET(base, idx, off));
170}
171
Laurent Pinchart05432832011-09-19 11:40:31 +0200172static inline void meram_write_reg(void __iomem *base, unsigned int off,
173 unsigned long val)
Damian7caa4342011-05-18 11:10:07 +0000174{
175 iowrite32(val, base + off);
176}
177
Laurent Pinchart05432832011-09-19 11:40:31 +0200178static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
Damian7caa4342011-05-18 11:10:07 +0000179{
180 return ioread32(base + off);
181}
182
Laurent Pinchart75543402011-09-19 11:40:31 +0200183/* -----------------------------------------------------------------------------
184 * Allocation
Damian7caa4342011-05-18 11:10:07 +0000185 */
186
Laurent Pinchart75543402011-09-19 11:40:31 +0200187/* Check if there's no overlaps in MERAM allocation. */
Laurent Pinchart762f7cc2011-09-19 11:40:31 +0200188static int meram_check_overlap(struct sh_mobile_meram_priv *priv,
189 const struct sh_mobile_meram_icb_cfg *new)
Damian7caa4342011-05-18 11:10:07 +0000190{
Damian7caa4342011-05-18 11:10:07 +0000191 /* valid ICB? */
192 if (new->marker_icb & ~0x1f || new->cache_icb & ~0x1f)
193 return 1;
194
195 if (test_bit(new->marker_icb, &priv->used_icb) ||
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200196 test_bit(new->cache_icb, &priv->used_icb))
Damian7caa4342011-05-18 11:10:07 +0000197 return 1;
198
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200199 return 0;
200}
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200201
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200202/* Allocate memory for the ICBs and mark them as used. */
203static int meram_alloc(struct sh_mobile_meram_priv *priv,
204 const struct sh_mobile_meram_icb_cfg *new,
205 int pixelformat)
206{
207 struct sh_mobile_meram_icb *marker = &priv->icbs[new->marker_icb];
208 unsigned long mem;
Damian7caa4342011-05-18 11:10:07 +0000209
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200210 mem = gen_pool_alloc(priv->pool, new->meram_size * 1024);
211 if (mem == 0)
212 return -ENOMEM;
213
214 __set_bit(new->marker_icb, &priv->used_icb);
215 __set_bit(new->cache_icb, &priv->used_icb);
216
217 marker->offset = mem - priv->meram;
218 marker->size = new->meram_size * 1024;
219 marker->current_reg = 1;
220 marker->pixelformat = pixelformat;
Damian7caa4342011-05-18 11:10:07 +0000221
222 return 0;
223}
224
Laurent Pinchart75543402011-09-19 11:40:31 +0200225/* Unmark the specified ICB as used. */
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200226static void meram_free(struct sh_mobile_meram_priv *priv,
227 const struct sh_mobile_meram_icb_cfg *icb)
Damian7caa4342011-05-18 11:10:07 +0000228{
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200229 struct sh_mobile_meram_icb *marker = &priv->icbs[icb->marker_icb];
230
231 gen_pool_free(priv->pool, priv->meram + marker->offset, marker->size);
232
Damian7caa4342011-05-18 11:10:07 +0000233 __clear_bit(icb->marker_icb, &priv->used_icb);
234 __clear_bit(icb->cache_icb, &priv->used_icb);
Damian7caa4342011-05-18 11:10:07 +0000235}
236
Laurent Pinchart75543402011-09-19 11:40:31 +0200237/* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */
Laurent Pinchart762f7cc2011-09-19 11:40:31 +0200238static int is_nvcolor(int cspace)
Damian3fedd2a2011-05-18 11:10:08 +0000239{
240 if (cspace == SH_MOBILE_MERAM_PF_NV ||
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200241 cspace == SH_MOBILE_MERAM_PF_NV24)
Damian3fedd2a2011-05-18 11:10:08 +0000242 return 1;
243 return 0;
244}
Damian7caa4342011-05-18 11:10:07 +0000245
Laurent Pinchart75543402011-09-19 11:40:31 +0200246/* Set the next address to fetch. */
Laurent Pinchart762f7cc2011-09-19 11:40:31 +0200247static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
248 const struct sh_mobile_meram_cfg *cfg,
249 unsigned long base_addr_y,
250 unsigned long base_addr_c)
Damian7caa4342011-05-18 11:10:07 +0000251{
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200252 struct sh_mobile_meram_icb *icb = &priv->icbs[cfg->icb[0].marker_icb];
Damian7caa4342011-05-18 11:10:07 +0000253 unsigned long target;
254
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200255 icb->current_reg ^= 1;
256 target = icb->current_reg ? MExxSARB : MExxSARA;
Damian7caa4342011-05-18 11:10:07 +0000257
258 /* set the next address to fetch */
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200259 meram_write_icb(priv->base, cfg->icb[0].cache_icb, target,
Damian7caa4342011-05-18 11:10:07 +0000260 base_addr_y);
261 meram_write_icb(priv->base, cfg->icb[0].marker_icb, target,
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200262 base_addr_y +
263 priv->icbs[cfg->icb[0].marker_icb].cache_unit);
Damian7caa4342011-05-18 11:10:07 +0000264
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200265 if (is_nvcolor(icb->pixelformat)) {
Damian7caa4342011-05-18 11:10:07 +0000266 meram_write_icb(priv->base, cfg->icb[1].cache_icb, target,
267 base_addr_c);
268 meram_write_icb(priv->base, cfg->icb[1].marker_icb, target,
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200269 base_addr_c +
270 priv->icbs[cfg->icb[1].marker_icb].cache_unit);
Damian7caa4342011-05-18 11:10:07 +0000271 }
272}
273
Laurent Pinchart75543402011-09-19 11:40:31 +0200274/* Get the next ICB address. */
Laurent Pinchart762f7cc2011-09-19 11:40:31 +0200275static void
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200276meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
277 const struct sh_mobile_meram_cfg *cfg,
278 unsigned long *icb_addr_y, unsigned long *icb_addr_c)
Damian7caa4342011-05-18 11:10:07 +0000279{
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200280 struct sh_mobile_meram_priv *priv = pdata->priv;
281 struct sh_mobile_meram_icb *icb = &priv->icbs[cfg->icb[0].marker_icb];
Damian7caa4342011-05-18 11:10:07 +0000282 unsigned long icb_offset;
283
284 if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0)
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200285 icb_offset = 0x80000000 | (icb->current_reg << 29);
Damian7caa4342011-05-18 11:10:07 +0000286 else
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200287 icb_offset = 0xc0000000 | (icb->current_reg << 23);
Damian7caa4342011-05-18 11:10:07 +0000288
289 *icb_addr_y = icb_offset | (cfg->icb[0].marker_icb << 24);
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200290 if (is_nvcolor(icb->pixelformat))
Damian7caa4342011-05-18 11:10:07 +0000291 *icb_addr_c = icb_offset | (cfg->icb[1].marker_icb << 24);
292}
293
294#define MERAM_CALC_BYTECOUNT(x, y) \
295 (((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1))
296
Laurent Pinchart75543402011-09-19 11:40:31 +0200297/* Initialize MERAM. */
Damian7caa4342011-05-18 11:10:07 +0000298static int meram_init(struct sh_mobile_meram_priv *priv,
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200299 const struct sh_mobile_meram_icb_cfg *icb,
Laurent Pinchart05432832011-09-19 11:40:31 +0200300 unsigned int xres, unsigned int yres,
301 unsigned int *out_pitch)
Damian7caa4342011-05-18 11:10:07 +0000302{
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200303 struct sh_mobile_meram_icb *marker = &priv->icbs[icb->marker_icb];
Damian7caa4342011-05-18 11:10:07 +0000304 unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
305 unsigned long bnm;
Laurent Pinchart05432832011-09-19 11:40:31 +0200306 unsigned int lcdc_pitch;
307 unsigned int xpitch;
308 unsigned int line_cnt;
309 unsigned int save_lines;
Damian7caa4342011-05-18 11:10:07 +0000310
311 /* adjust pitch to 1024, 2048, 4096 or 8192 */
312 lcdc_pitch = (xres - 1) | 1023;
313 lcdc_pitch = lcdc_pitch | (lcdc_pitch >> 1);
314 lcdc_pitch = lcdc_pitch | (lcdc_pitch >> 2);
315 lcdc_pitch += 1;
316
317 /* derive settings */
318 if (lcdc_pitch == 8192 && yres >= 1024) {
319 lcdc_pitch = xpitch = MERAM_LINE_WIDTH;
320 line_cnt = total_byte_count >> 11;
321 *out_pitch = xres;
322 save_lines = (icb->meram_size / 16 / MERAM_SEC_LINE);
323 save_lines *= MERAM_SEC_LINE;
324 } else {
325 xpitch = xres;
326 line_cnt = yres;
327 *out_pitch = lcdc_pitch;
328 save_lines = icb->meram_size / (lcdc_pitch >> 10) / 2;
329 save_lines &= 0xff;
330 }
331 bnm = (save_lines - 1) << 16;
332
333 /* TODO: we better to check if we have enough MERAM buffer size */
334
335 /* set up ICB */
336 meram_write_icb(priv->base, icb->cache_icb, MExxBSIZE,
337 MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1));
338 meram_write_icb(priv->base, icb->marker_icb, MExxBSIZE,
339 MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1));
340
341 meram_write_icb(priv->base, icb->cache_icb, MExxMNCF, bnm);
342 meram_write_icb(priv->base, icb->marker_icb, MExxMNCF, bnm);
343
344 meram_write_icb(priv->base, icb->cache_icb, MExxSBSIZE, xpitch);
345 meram_write_icb(priv->base, icb->marker_icb, MExxSBSIZE, xpitch);
346
347 /* save a cache unit size */
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200348 priv->icbs[icb->cache_icb].cache_unit = xres * save_lines;
349 priv->icbs[icb->marker_icb].cache_unit = xres * save_lines;
Damian7caa4342011-05-18 11:10:07 +0000350
351 /*
352 * Set MERAM for framebuffer
353 *
Damian7caa4342011-05-18 11:10:07 +0000354 * we also chain the cache_icb and the marker_icb.
355 * we also split the allocated MERAM buffer between two ICBs.
356 */
357 meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200358 MERAM_MExxCTL_VAL(icb->marker_icb, marker->offset) |
Laurent Pinchartf0a260f2011-07-13 12:13:47 +0200359 MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
360 MExxCTL_MD_FB);
Damian7caa4342011-05-18 11:10:07 +0000361 meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200362 MERAM_MExxCTL_VAL(icb->cache_icb, marker->offset +
Laurent Pinchartf0a260f2011-07-13 12:13:47 +0200363 icb->meram_size / 2) |
364 MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
365 MExxCTL_MD_FB);
Damian7caa4342011-05-18 11:10:07 +0000366
367 return 0;
368}
369
370static void meram_deinit(struct sh_mobile_meram_priv *priv,
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200371 const struct sh_mobile_meram_icb_cfg *icb)
Damian7caa4342011-05-18 11:10:07 +0000372{
373 /* disable ICB */
Laurent Pinchartda6cf512011-08-31 13:00:52 +0200374 meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
375 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
376 meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
377 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200378
379 priv->icbs[icb->cache_icb].cache_unit = 0;
380 priv->icbs[icb->marker_icb].cache_unit = 0;
Damian7caa4342011-05-18 11:10:07 +0000381}
382
Laurent Pinchart75543402011-09-19 11:40:31 +0200383/* -----------------------------------------------------------------------------
384 * Registration/unregistration
Damian7caa4342011-05-18 11:10:07 +0000385 */
386
387static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200388 const struct sh_mobile_meram_cfg *cfg,
Laurent Pinchart05432832011-09-19 11:40:31 +0200389 unsigned int xres, unsigned int yres,
390 unsigned int pixelformat,
Damian7caa4342011-05-18 11:10:07 +0000391 unsigned long base_addr_y,
392 unsigned long base_addr_c,
393 unsigned long *icb_addr_y,
394 unsigned long *icb_addr_c,
Laurent Pinchart05432832011-09-19 11:40:31 +0200395 unsigned int *pitch)
Damian7caa4342011-05-18 11:10:07 +0000396{
397 struct platform_device *pdev;
398 struct sh_mobile_meram_priv *priv;
Laurent Pinchart05432832011-09-19 11:40:31 +0200399 unsigned int out_pitch;
400 unsigned int n;
Damian7caa4342011-05-18 11:10:07 +0000401 int error = 0;
402
403 if (!pdata || !pdata->priv || !pdata->pdev || !cfg)
404 return -EINVAL;
405
406 if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
Damian3fedd2a2011-05-18 11:10:08 +0000407 pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
Damian7caa4342011-05-18 11:10:07 +0000408 pixelformat != SH_MOBILE_MERAM_PF_RGB)
409 return -EINVAL;
410
411 priv = pdata->priv;
412 pdev = pdata->pdev;
413
414 dev_dbg(&pdev->dev, "registering %dx%d (%s) (y=%08lx, c=%08lx)",
415 xres, yres, (!pixelformat) ? "yuv" : "rgb",
416 base_addr_y, base_addr_c);
417
Damian7caa4342011-05-18 11:10:07 +0000418 /* we can't handle wider than 8192px */
419 if (xres > 8192) {
420 dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
Laurent Pinchart7963e212011-07-13 12:13:47 +0200421 return -EINVAL;
Damian7caa4342011-05-18 11:10:07 +0000422 }
423
424 /* do we have at least one ICB config? */
425 if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) {
426 dev_err(&pdev->dev, "at least one ICB is required.");
Laurent Pinchart7963e212011-07-13 12:13:47 +0200427 return -EINVAL;
428 }
429
430 mutex_lock(&priv->lock);
431
Damian7caa4342011-05-18 11:10:07 +0000432 /* make sure that there's no overlaps */
433 if (meram_check_overlap(priv, &cfg->icb[0])) {
434 dev_err(&pdev->dev, "conflicting config detected.");
435 error = -EINVAL;
436 goto err;
437 }
438 n = 1;
439
440 /* do the same if we have the second ICB set */
441 if (cfg->icb[1].marker_icb >= 0 && cfg->icb[1].cache_icb >= 0) {
442 if (meram_check_overlap(priv, &cfg->icb[1])) {
443 dev_err(&pdev->dev, "conflicting config detected.");
444 error = -EINVAL;
445 goto err;
446 }
447 n = 2;
448 }
449
Damian3fedd2a2011-05-18 11:10:08 +0000450 if (is_nvcolor(pixelformat) && n != 2) {
Damian7caa4342011-05-18 11:10:07 +0000451 dev_err(&pdev->dev, "requires two ICB sets for planar Y/C.");
452 error = -EINVAL;
453 goto err;
454 }
455
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200456 /* We now register the ICBs and allocate the MERAM regions. */
457 error = meram_alloc(priv, &cfg->icb[0], pixelformat);
458 if (error < 0)
459 goto err;
460
461 if (is_nvcolor(pixelformat)) {
462 error = meram_alloc(priv, &cfg->icb[1], pixelformat);
463 if (error < 0) {
464 meram_free(priv, &cfg->icb[0]);
465 goto err;
466 }
467 }
Damian7caa4342011-05-18 11:10:07 +0000468
469 /* initialize MERAM */
470 meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch);
471 *pitch = out_pitch;
472 if (pixelformat == SH_MOBILE_MERAM_PF_NV)
473 meram_init(priv, &cfg->icb[1], xres, (yres + 1) / 2,
474 &out_pitch);
Damian3fedd2a2011-05-18 11:10:08 +0000475 else if (pixelformat == SH_MOBILE_MERAM_PF_NV24)
476 meram_init(priv, &cfg->icb[1], 2 * xres, (yres + 1) / 2,
477 &out_pitch);
Damian7caa4342011-05-18 11:10:07 +0000478
Damian7caa4342011-05-18 11:10:07 +0000479 meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c);
480 meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c);
481
482 dev_dbg(&pdev->dev, "registered - can access via y=%08lx, c=%08lx",
483 *icb_addr_y, *icb_addr_c);
484
485err:
486 mutex_unlock(&priv->lock);
487 return error;
488}
489
490static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata,
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200491 const struct sh_mobile_meram_cfg *cfg)
Damian7caa4342011-05-18 11:10:07 +0000492{
493 struct sh_mobile_meram_priv *priv;
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200494 struct sh_mobile_meram_icb *icb;
Damian7caa4342011-05-18 11:10:07 +0000495
496 if (!pdata || !pdata->priv || !cfg)
497 return -EINVAL;
498
499 priv = pdata->priv;
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200500 icb = &priv->icbs[cfg->icb[0].marker_icb];
Damian7caa4342011-05-18 11:10:07 +0000501
502 mutex_lock(&priv->lock);
503
504 /* deinit & unmark */
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200505 if (is_nvcolor(icb->pixelformat)) {
Damian7caa4342011-05-18 11:10:07 +0000506 meram_deinit(priv, &cfg->icb[1]);
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200507 meram_free(priv, &cfg->icb[1]);
Damian7caa4342011-05-18 11:10:07 +0000508 }
509 meram_deinit(priv, &cfg->icb[0]);
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200510 meram_free(priv, &cfg->icb[0]);
Damian7caa4342011-05-18 11:10:07 +0000511
512 mutex_unlock(&priv->lock);
513
514 return 0;
515}
516
517static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata,
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200518 const struct sh_mobile_meram_cfg *cfg,
Damian7caa4342011-05-18 11:10:07 +0000519 unsigned long base_addr_y,
520 unsigned long base_addr_c,
521 unsigned long *icb_addr_y,
522 unsigned long *icb_addr_c)
523{
524 struct sh_mobile_meram_priv *priv;
525
526 if (!pdata || !pdata->priv || !cfg)
527 return -EINVAL;
528
529 priv = pdata->priv;
530
531 mutex_lock(&priv->lock);
532
533 meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c);
534 meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c);
535
536 mutex_unlock(&priv->lock);
537
538 return 0;
539}
540
Laurent Pinchart75543402011-09-19 11:40:31 +0200541static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
542 .module = THIS_MODULE,
543 .meram_register = sh_mobile_meram_register,
544 .meram_unregister = sh_mobile_meram_unregister,
545 .meram_update = sh_mobile_meram_update,
546};
547
548/* -----------------------------------------------------------------------------
549 * Power management
550 */
551
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200552static int sh_mobile_meram_runtime_suspend(struct device *dev)
553{
554 struct platform_device *pdev = to_platform_device(dev);
555 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
Laurent Pinchart05432832011-09-19 11:40:31 +0200556 unsigned int i, j;
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200557
Laurent Pinchart75543402011-09-19 11:40:31 +0200558 for (i = 0; i < MERAM_REGS_SIZE; i++)
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200559 priv->regs[i] = meram_read_reg(priv->base, common_regs[i]);
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200560
Laurent Pinchart05432832011-09-19 11:40:31 +0200561 for (i = 0; i < 32; i++) {
562 if (!test_bit(i, &priv->used_icb))
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200563 continue;
Laurent Pinchart05432832011-09-19 11:40:31 +0200564 for (j = 0; j < ICB_REGS_SIZE; j++) {
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200565 priv->icbs[i].regs[j] =
Laurent Pinchart05432832011-09-19 11:40:31 +0200566 meram_read_icb(priv->base, i, icb_regs[j]);
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200567 /* Reset ICB on resume */
Laurent Pinchart05432832011-09-19 11:40:31 +0200568 if (icb_regs[j] == MExxCTL)
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200569 priv->icbs[i].regs[j] |=
Laurent Pinchartf0a260f2011-07-13 12:13:47 +0200570 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF;
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200571 }
572 }
573 return 0;
574}
575
576static int sh_mobile_meram_runtime_resume(struct device *dev)
577{
578 struct platform_device *pdev = to_platform_device(dev);
579 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
Laurent Pinchart05432832011-09-19 11:40:31 +0200580 unsigned int i, j;
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200581
Laurent Pinchart05432832011-09-19 11:40:31 +0200582 for (i = 0; i < 32; i++) {
583 if (!test_bit(i, &priv->used_icb))
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200584 continue;
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200585 for (j = 0; j < ICB_REGS_SIZE; j++)
Laurent Pinchart05432832011-09-19 11:40:31 +0200586 meram_write_icb(priv->base, i, icb_regs[j],
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200587 priv->icbs[i].regs[j]);
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200588 }
589
Laurent Pinchart75543402011-09-19 11:40:31 +0200590 for (i = 0; i < MERAM_REGS_SIZE; i++)
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200591 meram_write_reg(priv->base, common_regs[i], priv->regs[i]);
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200592 return 0;
593}
594
595static const struct dev_pm_ops sh_mobile_meram_dev_pm_ops = {
596 .runtime_suspend = sh_mobile_meram_runtime_suspend,
597 .runtime_resume = sh_mobile_meram_runtime_resume,
598};
599
Laurent Pinchart75543402011-09-19 11:40:31 +0200600/* -----------------------------------------------------------------------------
601 * Probe/remove and driver init/exit
Damian7caa4342011-05-18 11:10:07 +0000602 */
603
Damian7caa4342011-05-18 11:10:07 +0000604static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
605{
606 struct sh_mobile_meram_priv *priv;
607 struct sh_mobile_meram_info *pdata = pdev->dev.platform_data;
Laurent Pincharte1d11442011-09-19 11:40:31 +0200608 struct resource *regs;
609 struct resource *meram;
Damian7caa4342011-05-18 11:10:07 +0000610 int error;
611
612 if (!pdata) {
613 dev_err(&pdev->dev, "no platform data defined\n");
614 return -EINVAL;
615 }
616
Laurent Pincharte1d11442011-09-19 11:40:31 +0200617 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
618 meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
619 if (regs == NULL || meram == NULL) {
Damian7caa4342011-05-18 11:10:07 +0000620 dev_err(&pdev->dev, "cannot get platform resources\n");
621 return -ENOENT;
622 }
623
624 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
625 if (!priv) {
626 dev_err(&pdev->dev, "cannot allocate device data\n");
627 return -ENOMEM;
628 }
629
Damian7caa4342011-05-18 11:10:07 +0000630 /* initialize private data */
631 mutex_init(&priv->lock);
Damian7caa4342011-05-18 11:10:07 +0000632 pdata->ops = &sh_mobile_meram_ops;
633 pdata->priv = priv;
634 pdata->pdev = pdev;
635
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200636 /* Request memory regions and remap the registers. */
Laurent Pincharte1d11442011-09-19 11:40:31 +0200637 if (!request_mem_region(regs->start, resource_size(regs), pdev->name)) {
638 dev_err(&pdev->dev, "MERAM registers region already claimed\n");
639 error = -EBUSY;
640 goto err_req_regs;
641 }
642
643 if (!request_mem_region(meram->start, resource_size(meram),
644 pdev->name)) {
645 dev_err(&pdev->dev, "MERAM memory region already claimed\n");
646 error = -EBUSY;
647 goto err_req_meram;
648 }
649
650 priv->base = ioremap_nocache(regs->start, resource_size(regs));
651 if (!priv->base) {
652 dev_err(&pdev->dev, "ioremap failed\n");
653 error = -EFAULT;
654 goto err_ioremap;
655 }
656
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200657 priv->meram = meram->start;
658
659 /* Create and initialize the MERAM memory pool. */
660 priv->pool = gen_pool_create(ilog2(MERAM_GRANULARITY), -1);
661 if (priv->pool == NULL) {
662 error = -ENOMEM;
663 goto err_genpool;
664 }
665
666 error = gen_pool_add(priv->pool, meram->start, resource_size(meram),
667 -1);
668 if (error < 0)
669 goto err_genpool;
670
Damian7caa4342011-05-18 11:10:07 +0000671 /* initialize ICB addressing mode */
672 if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1)
Laurent Pinchartf0a260f2011-07-13 12:13:47 +0200673 meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1);
Damian7caa4342011-05-18 11:10:07 +0000674
Laurent Pincharte1d11442011-09-19 11:40:31 +0200675 platform_set_drvdata(pdev, priv);
Damian Hobson-Garcia17673772011-07-04 08:06:11 +0200676 pm_runtime_enable(&pdev->dev);
677
Damian7caa4342011-05-18 11:10:07 +0000678 dev_info(&pdev->dev, "sh_mobile_meram initialized.");
679
680 return 0;
681
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200682err_genpool:
683 if (priv->pool)
684 gen_pool_destroy(priv->pool);
685 iounmap(priv->base);
Laurent Pincharte1d11442011-09-19 11:40:31 +0200686err_ioremap:
687 release_mem_region(meram->start, resource_size(meram));
688err_req_meram:
689 release_mem_region(regs->start, resource_size(regs));
690err_req_regs:
691 mutex_destroy(&priv->lock);
692 kfree(priv);
Damian7caa4342011-05-18 11:10:07 +0000693
694 return error;
695}
696
697
698static int sh_mobile_meram_remove(struct platform_device *pdev)
699{
700 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
Laurent Pincharte1d11442011-09-19 11:40:31 +0200701 struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
702 struct resource *meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
Damian7caa4342011-05-18 11:10:07 +0000703
Damian Hobson-Garcia17673772011-07-04 08:06:11 +0200704 pm_runtime_disable(&pdev->dev);
705
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200706 gen_pool_destroy(priv->pool);
707
Laurent Pincharte1d11442011-09-19 11:40:31 +0200708 iounmap(priv->base);
709 release_mem_region(meram->start, resource_size(meram));
710 release_mem_region(regs->start, resource_size(regs));
Damian7caa4342011-05-18 11:10:07 +0000711
712 mutex_destroy(&priv->lock);
713
714 kfree(priv);
715
716 return 0;
717}
718
719static struct platform_driver sh_mobile_meram_driver = {
720 .driver = {
721 .name = "sh_mobile_meram",
722 .owner = THIS_MODULE,
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200723 .pm = &sh_mobile_meram_dev_pm_ops,
Damian7caa4342011-05-18 11:10:07 +0000724 },
725 .probe = sh_mobile_meram_probe,
726 .remove = sh_mobile_meram_remove,
727};
728
Axel Lin4277f2c2011-11-26 10:25:54 +0800729module_platform_driver(sh_mobile_meram_driver);
Damian7caa4342011-05-18 11:10:07 +0000730
731MODULE_DESCRIPTION("SuperH Mobile MERAM driver");
732MODULE_AUTHOR("Damian Hobson-Garcia / Takanari Hayama");
733MODULE_LICENSE("GPL v2");