blob: 4aa3fcb87f17891df7d4b04f46b3685f229907b9 [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 Pinchart48110052011-12-12 16:36:13 +010013#include <linux/err.h>
Laurent Pinchart974d250b2011-09-19 11:40:31 +020014#include <linux/genalloc.h>
Laurent Pinchart75543402011-09-19 11:40:31 +020015#include <linux/io.h>
Damian7caa4342011-05-18 11:10:07 +000016#include <linux/kernel.h>
17#include <linux/module.h>
Damian7caa4342011-05-18 11:10:07 +000018#include <linux/platform_device.h>
Laurent Pinchart75543402011-09-19 11:40:31 +020019#include <linux/pm_runtime.h>
20#include <linux/slab.h>
21
Laurent Pinchart8a209742011-07-13 12:13:47 +020022#include <video/sh_mobile_meram.h>
Damian7caa4342011-05-18 11:10:07 +000023
Laurent Pinchart75543402011-09-19 11:40:31 +020024/* -----------------------------------------------------------------------------
25 * MERAM registers
26 */
27
Laurent Pinchartf0a260f2011-07-13 12:13:47 +020028#define MEVCR1 0x4
29#define MEVCR1_RST (1 << 31)
30#define MEVCR1_WD (1 << 30)
31#define MEVCR1_AMD1 (1 << 29)
32#define MEVCR1_AMD0 (1 << 28)
33#define MEQSEL1 0x40
34#define MEQSEL2 0x44
Damian7caa4342011-05-18 11:10:07 +000035
Laurent Pinchartf0a260f2011-07-13 12:13:47 +020036#define MExxCTL 0x400
37#define MExxCTL_BV (1 << 31)
38#define MExxCTL_BSZ_SHIFT 28
39#define MExxCTL_MSAR_MASK (0x7ff << MExxCTL_MSAR_SHIFT)
40#define MExxCTL_MSAR_SHIFT 16
41#define MExxCTL_NXT_MASK (0x1f << MExxCTL_NXT_SHIFT)
42#define MExxCTL_NXT_SHIFT 11
43#define MExxCTL_WD1 (1 << 10)
44#define MExxCTL_WD0 (1 << 9)
45#define MExxCTL_WS (1 << 8)
46#define MExxCTL_CB (1 << 7)
47#define MExxCTL_WBF (1 << 6)
48#define MExxCTL_WF (1 << 5)
49#define MExxCTL_RF (1 << 4)
50#define MExxCTL_CM (1 << 3)
51#define MExxCTL_MD_READ (1 << 0)
52#define MExxCTL_MD_WRITE (2 << 0)
53#define MExxCTL_MD_ICB_WB (3 << 0)
54#define MExxCTL_MD_ICB (4 << 0)
55#define MExxCTL_MD_FB (7 << 0)
56#define MExxCTL_MD_MASK (7 << 0)
57#define MExxBSIZE 0x404
58#define MExxBSIZE_RCNT_SHIFT 28
59#define MExxBSIZE_YSZM1_SHIFT 16
60#define MExxBSIZE_XSZM1_SHIFT 0
61#define MExxMNCF 0x408
62#define MExxMNCF_KWBNM_SHIFT 28
63#define MExxMNCF_KRBNM_SHIFT 24
64#define MExxMNCF_BNM_SHIFT 16
65#define MExxMNCF_XBV (1 << 15)
66#define MExxMNCF_CPL_YCBCR444 (1 << 12)
67#define MExxMNCF_CPL_YCBCR420 (2 << 12)
68#define MExxMNCF_CPL_YCBCR422 (3 << 12)
69#define MExxMNCF_CPL_MSK (3 << 12)
70#define MExxMNCF_BL (1 << 2)
71#define MExxMNCF_LNM_SHIFT 0
72#define MExxSARA 0x410
73#define MExxSARB 0x414
74#define MExxSBSIZE 0x418
75#define MExxSBSIZE_HDV (1 << 31)
76#define MExxSBSIZE_HSZ16 (0 << 28)
77#define MExxSBSIZE_HSZ32 (1 << 28)
78#define MExxSBSIZE_HSZ64 (2 << 28)
79#define MExxSBSIZE_HSZ128 (3 << 28)
80#define MExxSBSIZE_SBSIZZ_SHIFT 0
Damian7caa4342011-05-18 11:10:07 +000081
Laurent Pinchartf0a260f2011-07-13 12:13:47 +020082#define MERAM_MExxCTL_VAL(next, addr) \
83 ((((next) << MExxCTL_NXT_SHIFT) & MExxCTL_NXT_MASK) | \
84 (((addr) << MExxCTL_MSAR_SHIFT) & MExxCTL_MSAR_MASK))
85#define MERAM_MExxBSIZE_VAL(rcnt, yszm1, xszm1) \
86 (((rcnt) << MExxBSIZE_RCNT_SHIFT) | \
87 ((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \
88 ((xszm1) << MExxBSIZE_XSZM1_SHIFT))
Damian7caa4342011-05-18 11:10:07 +000089
Laurent Pinchart762f7cc2011-09-19 11:40:31 +020090static const unsigned long common_regs[] = {
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +020091 MEVCR1,
92 MEQSEL1,
93 MEQSEL2,
94};
Laurent Pinchart75543402011-09-19 11:40:31 +020095#define MERAM_REGS_SIZE ARRAY_SIZE(common_regs)
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +020096
Laurent Pinchart762f7cc2011-09-19 11:40:31 +020097static const unsigned long icb_regs[] = {
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +020098 MExxCTL,
99 MExxBSIZE,
100 MExxMNCF,
101 MExxSARA,
102 MExxSARB,
103 MExxSBSIZE,
104};
105#define ICB_REGS_SIZE ARRAY_SIZE(icb_regs)
106
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200107/*
108 * sh_mobile_meram_icb - MERAM ICB information
109 * @regs: Registers cache
Laurent Pinchart48110052011-12-12 16:36:13 +0100110 * @index: ICB index
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200111 * @offset: MERAM block offset
Laurent Pinchart48110052011-12-12 16:36:13 +0100112 * @size: MERAM block size in KiB
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200113 * @cache_unit: Bytes to cache per ICB
114 * @pixelformat: Video pixel format of the data stored in the ICB
115 * @current_reg: Which of Start Address Register A (0) or B (1) is in use
116 */
117struct sh_mobile_meram_icb {
118 unsigned long regs[ICB_REGS_SIZE];
Laurent Pinchart48110052011-12-12 16:36:13 +0100119 unsigned int index;
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200120 unsigned long offset;
121 unsigned int size;
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200122
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200123 unsigned int cache_unit;
124 unsigned int pixelformat;
125 unsigned int current_reg;
126};
127
Laurent Pinchart75543402011-09-19 11:40:31 +0200128#define MERAM_ICB_NUM 32
129
Laurent Pinchart48110052011-12-12 16:36:13 +0100130struct sh_mobile_meram_fb_plane {
131 struct sh_mobile_meram_icb *marker;
132 struct sh_mobile_meram_icb *cache;
133};
134
135struct sh_mobile_meram_fb_cache {
136 unsigned int nplanes;
137 struct sh_mobile_meram_fb_plane planes[2];
138};
139
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200140/*
141 * sh_mobile_meram_priv - MERAM device
142 * @base: Registers base address
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200143 * @meram: MERAM physical address
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200144 * @regs: Registers cache
145 * @lock: Protects used_icb and icbs
146 * @used_icb: Bitmask of used ICBs
147 * @icbs: ICBs
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200148 * @pool: Allocation pool to manage the MERAM
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200149 */
Damian Hobson-Garcia0aa492b2011-06-22 07:49:50 +0200150struct sh_mobile_meram_priv {
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200151 void __iomem *base;
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200152 unsigned long meram;
Laurent Pinchart75543402011-09-19 11:40:31 +0200153 unsigned long regs[MERAM_REGS_SIZE];
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200154
155 struct mutex lock;
156 unsigned long used_icb;
Laurent Pinchart75543402011-09-19 11:40:31 +0200157 struct sh_mobile_meram_icb icbs[MERAM_ICB_NUM];
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200158
159 struct gen_pool *pool;
Damian Hobson-Garcia0aa492b2011-06-22 07:49:50 +0200160};
161
Damian7caa4342011-05-18 11:10:07 +0000162/* settings */
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200163#define MERAM_GRANULARITY 1024
Laurent Pinchart75543402011-09-19 11:40:31 +0200164#define MERAM_SEC_LINE 15
165#define MERAM_LINE_WIDTH 2048
Damian7caa4342011-05-18 11:10:07 +0000166
Laurent Pinchart75543402011-09-19 11:40:31 +0200167/* -----------------------------------------------------------------------------
168 * Registers access
Damian7caa4342011-05-18 11:10:07 +0000169 */
170
Laurent Pinchartf0a260f2011-07-13 12:13:47 +0200171#define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20)
Damian7caa4342011-05-18 11:10:07 +0000172
Laurent Pinchart05432832011-09-19 11:40:31 +0200173static inline void meram_write_icb(void __iomem *base, unsigned int idx,
174 unsigned int off, unsigned long val)
Damian7caa4342011-05-18 11:10:07 +0000175{
176 iowrite32(val, MERAM_ICB_OFFSET(base, idx, off));
177}
178
Laurent Pinchart05432832011-09-19 11:40:31 +0200179static inline unsigned long meram_read_icb(void __iomem *base, unsigned int idx,
180 unsigned int off)
Damian7caa4342011-05-18 11:10:07 +0000181{
182 return ioread32(MERAM_ICB_OFFSET(base, idx, off));
183}
184
Laurent Pinchart05432832011-09-19 11:40:31 +0200185static inline void meram_write_reg(void __iomem *base, unsigned int off,
186 unsigned long val)
Damian7caa4342011-05-18 11:10:07 +0000187{
188 iowrite32(val, base + off);
189}
190
Laurent Pinchart05432832011-09-19 11:40:31 +0200191static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
Damian7caa4342011-05-18 11:10:07 +0000192{
193 return ioread32(base + off);
194}
195
Laurent Pinchart75543402011-09-19 11:40:31 +0200196/* -----------------------------------------------------------------------------
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100197 * LCDC cache planes allocation, init, cleanup and free
Damian7caa4342011-05-18 11:10:07 +0000198 */
199
Laurent Pinchart48110052011-12-12 16:36:13 +0100200/* Allocate ICBs and MERAM for a plane. */
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100201static int meram_plane_alloc(struct sh_mobile_meram_priv *priv,
202 struct sh_mobile_meram_fb_plane *plane,
203 size_t size)
Damian7caa4342011-05-18 11:10:07 +0000204{
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200205 unsigned long mem;
Laurent Pinchart48110052011-12-12 16:36:13 +0100206 unsigned long idx;
Damian7caa4342011-05-18 11:10:07 +0000207
Laurent Pinchart48110052011-12-12 16:36:13 +0100208 idx = find_first_zero_bit(&priv->used_icb, 28);
209 if (idx == 28)
210 return -ENOMEM;
211 plane->cache = &priv->icbs[idx];
212
213 idx = find_next_zero_bit(&priv->used_icb, 32, 28);
214 if (idx == 32)
215 return -ENOMEM;
216 plane->marker = &priv->icbs[idx];
217
218 mem = gen_pool_alloc(priv->pool, size * 1024);
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200219 if (mem == 0)
220 return -ENOMEM;
221
Laurent Pinchart48110052011-12-12 16:36:13 +0100222 __set_bit(plane->marker->index, &priv->used_icb);
223 __set_bit(plane->cache->index, &priv->used_icb);
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200224
Laurent Pinchart48110052011-12-12 16:36:13 +0100225 plane->marker->offset = mem - priv->meram;
226 plane->marker->size = size;
Damian7caa4342011-05-18 11:10:07 +0000227
228 return 0;
229}
230
Laurent Pinchart48110052011-12-12 16:36:13 +0100231/* Free ICBs and MERAM for a plane. */
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100232static void meram_plane_free(struct sh_mobile_meram_priv *priv,
233 struct sh_mobile_meram_fb_plane *plane)
Damian7caa4342011-05-18 11:10:07 +0000234{
Laurent Pinchart48110052011-12-12 16:36:13 +0100235 gen_pool_free(priv->pool, priv->meram + plane->marker->offset,
236 plane->marker->size * 1024);
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200237
Laurent Pinchart48110052011-12-12 16:36:13 +0100238 __clear_bit(plane->marker->index, &priv->used_icb);
239 __clear_bit(plane->cache->index, &priv->used_icb);
Damian7caa4342011-05-18 11:10:07 +0000240}
241
Laurent Pinchart75543402011-09-19 11:40:31 +0200242/* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */
Laurent Pinchart762f7cc2011-09-19 11:40:31 +0200243static int is_nvcolor(int cspace)
Damian3fedd2a2011-05-18 11:10:08 +0000244{
245 if (cspace == SH_MOBILE_MERAM_PF_NV ||
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200246 cspace == SH_MOBILE_MERAM_PF_NV24)
Damian3fedd2a2011-05-18 11:10:08 +0000247 return 1;
248 return 0;
249}
Damian7caa4342011-05-18 11:10:07 +0000250
Laurent Pinchart75543402011-09-19 11:40:31 +0200251/* Set the next address to fetch. */
Laurent Pinchart762f7cc2011-09-19 11:40:31 +0200252static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
Laurent Pinchart48110052011-12-12 16:36:13 +0100253 struct sh_mobile_meram_fb_cache *cache,
Laurent Pinchart762f7cc2011-09-19 11:40:31 +0200254 unsigned long base_addr_y,
255 unsigned long base_addr_c)
Damian7caa4342011-05-18 11:10:07 +0000256{
Laurent Pinchart48110052011-12-12 16:36:13 +0100257 struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
Damian7caa4342011-05-18 11:10:07 +0000258 unsigned long target;
259
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200260 icb->current_reg ^= 1;
261 target = icb->current_reg ? MExxSARB : MExxSARA;
Damian7caa4342011-05-18 11:10:07 +0000262
263 /* set the next address to fetch */
Laurent Pinchart48110052011-12-12 16:36:13 +0100264 meram_write_icb(priv->base, cache->planes[0].cache->index, target,
Damian7caa4342011-05-18 11:10:07 +0000265 base_addr_y);
Laurent Pinchart48110052011-12-12 16:36:13 +0100266 meram_write_icb(priv->base, cache->planes[0].marker->index, target,
267 base_addr_y + cache->planes[0].marker->cache_unit);
Damian7caa4342011-05-18 11:10:07 +0000268
Laurent Pinchart48110052011-12-12 16:36:13 +0100269 if (cache->nplanes == 2) {
270 meram_write_icb(priv->base, cache->planes[1].cache->index,
271 target, base_addr_c);
272 meram_write_icb(priv->base, cache->planes[1].marker->index,
273 target, base_addr_c +
274 cache->planes[1].marker->cache_unit);
Damian7caa4342011-05-18 11:10:07 +0000275 }
276}
277
Laurent Pinchart75543402011-09-19 11:40:31 +0200278/* Get the next ICB address. */
Laurent Pinchart762f7cc2011-09-19 11:40:31 +0200279static void
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200280meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
Laurent Pinchart48110052011-12-12 16:36:13 +0100281 struct sh_mobile_meram_fb_cache *cache,
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200282 unsigned long *icb_addr_y, unsigned long *icb_addr_c)
Damian7caa4342011-05-18 11:10:07 +0000283{
Laurent Pinchart48110052011-12-12 16:36:13 +0100284 struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
Damian7caa4342011-05-18 11:10:07 +0000285 unsigned long icb_offset;
286
287 if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0)
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200288 icb_offset = 0x80000000 | (icb->current_reg << 29);
Damian7caa4342011-05-18 11:10:07 +0000289 else
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200290 icb_offset = 0xc0000000 | (icb->current_reg << 23);
Damian7caa4342011-05-18 11:10:07 +0000291
Laurent Pinchart48110052011-12-12 16:36:13 +0100292 *icb_addr_y = icb_offset | (cache->planes[0].marker->index << 24);
293 if (cache->nplanes == 2)
294 *icb_addr_c = icb_offset
295 | (cache->planes[1].marker->index << 24);
Damian7caa4342011-05-18 11:10:07 +0000296}
297
298#define MERAM_CALC_BYTECOUNT(x, y) \
299 (((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1))
300
Laurent Pinchart75543402011-09-19 11:40:31 +0200301/* Initialize MERAM. */
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100302static int meram_plane_init(struct sh_mobile_meram_priv *priv,
303 struct sh_mobile_meram_fb_plane *plane,
304 unsigned int xres, unsigned int yres,
305 unsigned int *out_pitch)
Damian7caa4342011-05-18 11:10:07 +0000306{
Laurent Pinchart48110052011-12-12 16:36:13 +0100307 struct sh_mobile_meram_icb *marker = plane->marker;
Damian7caa4342011-05-18 11:10:07 +0000308 unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
309 unsigned long bnm;
Laurent Pinchart05432832011-09-19 11:40:31 +0200310 unsigned int lcdc_pitch;
311 unsigned int xpitch;
312 unsigned int line_cnt;
313 unsigned int save_lines;
Damian7caa4342011-05-18 11:10:07 +0000314
315 /* adjust pitch to 1024, 2048, 4096 or 8192 */
316 lcdc_pitch = (xres - 1) | 1023;
317 lcdc_pitch = lcdc_pitch | (lcdc_pitch >> 1);
318 lcdc_pitch = lcdc_pitch | (lcdc_pitch >> 2);
319 lcdc_pitch += 1;
320
321 /* derive settings */
322 if (lcdc_pitch == 8192 && yres >= 1024) {
323 lcdc_pitch = xpitch = MERAM_LINE_WIDTH;
324 line_cnt = total_byte_count >> 11;
325 *out_pitch = xres;
Laurent Pinchart48110052011-12-12 16:36:13 +0100326 save_lines = plane->marker->size / 16 / MERAM_SEC_LINE;
Damian7caa4342011-05-18 11:10:07 +0000327 save_lines *= MERAM_SEC_LINE;
328 } else {
329 xpitch = xres;
330 line_cnt = yres;
331 *out_pitch = lcdc_pitch;
Laurent Pinchart48110052011-12-12 16:36:13 +0100332 save_lines = plane->marker->size / (lcdc_pitch >> 10) / 2;
Damian7caa4342011-05-18 11:10:07 +0000333 save_lines &= 0xff;
334 }
335 bnm = (save_lines - 1) << 16;
336
337 /* TODO: we better to check if we have enough MERAM buffer size */
338
339 /* set up ICB */
Laurent Pinchart48110052011-12-12 16:36:13 +0100340 meram_write_icb(priv->base, plane->cache->index, MExxBSIZE,
Damian7caa4342011-05-18 11:10:07 +0000341 MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1));
Laurent Pinchart48110052011-12-12 16:36:13 +0100342 meram_write_icb(priv->base, plane->marker->index, MExxBSIZE,
Damian7caa4342011-05-18 11:10:07 +0000343 MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1));
344
Laurent Pinchart48110052011-12-12 16:36:13 +0100345 meram_write_icb(priv->base, plane->cache->index, MExxMNCF, bnm);
346 meram_write_icb(priv->base, plane->marker->index, MExxMNCF, bnm);
Damian7caa4342011-05-18 11:10:07 +0000347
Laurent Pinchart48110052011-12-12 16:36:13 +0100348 meram_write_icb(priv->base, plane->cache->index, MExxSBSIZE, xpitch);
349 meram_write_icb(priv->base, plane->marker->index, MExxSBSIZE, xpitch);
Damian7caa4342011-05-18 11:10:07 +0000350
351 /* save a cache unit size */
Laurent Pinchart48110052011-12-12 16:36:13 +0100352 plane->cache->cache_unit = xres * save_lines;
353 plane->marker->cache_unit = xres * save_lines;
Damian7caa4342011-05-18 11:10:07 +0000354
355 /*
356 * Set MERAM for framebuffer
357 *
Damian7caa4342011-05-18 11:10:07 +0000358 * we also chain the cache_icb and the marker_icb.
359 * we also split the allocated MERAM buffer between two ICBs.
360 */
Laurent Pinchart48110052011-12-12 16:36:13 +0100361 meram_write_icb(priv->base, plane->cache->index, MExxCTL,
362 MERAM_MExxCTL_VAL(plane->marker->index, marker->offset)
363 | MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
Laurent Pinchartf0a260f2011-07-13 12:13:47 +0200364 MExxCTL_MD_FB);
Laurent Pinchart48110052011-12-12 16:36:13 +0100365 meram_write_icb(priv->base, plane->marker->index, MExxCTL,
366 MERAM_MExxCTL_VAL(plane->cache->index, marker->offset +
367 plane->marker->size / 2) |
Laurent Pinchartf0a260f2011-07-13 12:13:47 +0200368 MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
369 MExxCTL_MD_FB);
Damian7caa4342011-05-18 11:10:07 +0000370
371 return 0;
372}
373
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100374static void meram_plane_cleanup(struct sh_mobile_meram_priv *priv,
375 struct sh_mobile_meram_fb_plane *plane)
Damian7caa4342011-05-18 11:10:07 +0000376{
377 /* disable ICB */
Laurent Pinchart48110052011-12-12 16:36:13 +0100378 meram_write_icb(priv->base, plane->cache->index, MExxCTL,
Laurent Pinchartda6cf512011-08-31 13:00:52 +0200379 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
Laurent Pinchart48110052011-12-12 16:36:13 +0100380 meram_write_icb(priv->base, plane->marker->index, MExxCTL,
Laurent Pinchartda6cf512011-08-31 13:00:52 +0200381 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200382
Laurent Pinchart48110052011-12-12 16:36:13 +0100383 plane->cache->cache_unit = 0;
384 plane->marker->cache_unit = 0;
Damian7caa4342011-05-18 11:10:07 +0000385}
386
Laurent Pinchart75543402011-09-19 11:40:31 +0200387/* -----------------------------------------------------------------------------
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100388 * LCDC cache operations
Damian7caa4342011-05-18 11:10:07 +0000389 */
390
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100391/* Allocate memory for the ICBs and mark them as used. */
392static struct sh_mobile_meram_fb_cache *
393meram_cache_alloc(struct sh_mobile_meram_priv *priv,
394 const struct sh_mobile_meram_cfg *cfg,
395 int pixelformat)
396{
397 unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
398 struct sh_mobile_meram_fb_cache *cache;
399 int ret;
400
401 cache = kzalloc(sizeof(*cache), GFP_KERNEL);
402 if (cache == NULL)
403 return ERR_PTR(-ENOMEM);
404
405 cache->nplanes = nplanes;
406
407 ret = meram_plane_alloc(priv, &cache->planes[0],
408 cfg->icb[0].meram_size);
409 if (ret < 0)
410 goto error;
411
412 cache->planes[0].marker->current_reg = 1;
413 cache->planes[0].marker->pixelformat = pixelformat;
414
415 if (cache->nplanes == 1)
416 return cache;
417
418 ret = meram_plane_alloc(priv, &cache->planes[1],
419 cfg->icb[1].meram_size);
420 if (ret < 0) {
421 meram_plane_free(priv, &cache->planes[0]);
422 goto error;
423 }
424
425 return cache;
426
427error:
428 kfree(cache);
429 return ERR_PTR(-ENOMEM);
430}
431
432static void *sh_mobile_cache_alloc(struct sh_mobile_meram_info *pdata,
433 const struct sh_mobile_meram_cfg *cfg,
434 unsigned int xres, unsigned int yres,
435 unsigned int pixelformat,
436 unsigned int *pitch)
Damian7caa4342011-05-18 11:10:07 +0000437{
Laurent Pinchart48110052011-12-12 16:36:13 +0100438 struct sh_mobile_meram_fb_cache *cache;
Laurent Pinchartcdf88b92011-11-22 00:56:58 +0100439 struct sh_mobile_meram_priv *priv = pdata->priv;
440 struct platform_device *pdev = pdata->pdev;
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100441 unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
Laurent Pinchart05432832011-09-19 11:40:31 +0200442 unsigned int out_pitch;
Damian7caa4342011-05-18 11:10:07 +0000443
Damian7caa4342011-05-18 11:10:07 +0000444 if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
Damian3fedd2a2011-05-18 11:10:08 +0000445 pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
Damian7caa4342011-05-18 11:10:07 +0000446 pixelformat != SH_MOBILE_MERAM_PF_RGB)
Laurent Pinchart48110052011-12-12 16:36:13 +0100447 return ERR_PTR(-EINVAL);
Damian7caa4342011-05-18 11:10:07 +0000448
Laurent Pinchart97d16fe2011-11-22 00:56:58 +0100449 dev_dbg(&pdev->dev, "registering %dx%d (%s)", xres, yres,
450 !pixelformat ? "yuv" : "rgb");
Damian7caa4342011-05-18 11:10:07 +0000451
Damian7caa4342011-05-18 11:10:07 +0000452 /* we can't handle wider than 8192px */
453 if (xres > 8192) {
454 dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
Laurent Pinchart48110052011-12-12 16:36:13 +0100455 return ERR_PTR(-EINVAL);
Laurent Pinchart7963e212011-07-13 12:13:47 +0200456 }
457
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100458 if (cfg->icb[0].meram_size == 0)
459 return ERR_PTR(-EINVAL);
460
461 if (nplanes == 2 && cfg->icb[1].meram_size == 0)
462 return ERR_PTR(-EINVAL);
463
Laurent Pinchart7963e212011-07-13 12:13:47 +0200464 mutex_lock(&priv->lock);
465
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200466 /* We now register the ICBs and allocate the MERAM regions. */
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100467 cache = meram_cache_alloc(priv, cfg, pixelformat);
Laurent Pinchart48110052011-12-12 16:36:13 +0100468 if (IS_ERR(cache)) {
469 dev_err(&pdev->dev, "MERAM allocation failed (%ld).",
470 PTR_ERR(cache));
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200471 goto err;
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200472 }
Damian7caa4342011-05-18 11:10:07 +0000473
474 /* initialize MERAM */
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100475 meram_plane_init(priv, &cache->planes[0], xres, yres, &out_pitch);
Damian7caa4342011-05-18 11:10:07 +0000476 *pitch = out_pitch;
477 if (pixelformat == SH_MOBILE_MERAM_PF_NV)
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100478 meram_plane_init(priv, &cache->planes[1],
479 xres, (yres + 1) / 2, &out_pitch);
Damian3fedd2a2011-05-18 11:10:08 +0000480 else if (pixelformat == SH_MOBILE_MERAM_PF_NV24)
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100481 meram_plane_init(priv, &cache->planes[1],
482 2 * xres, (yres + 1) / 2, &out_pitch);
Damian7caa4342011-05-18 11:10:07 +0000483
Damian7caa4342011-05-18 11:10:07 +0000484err:
485 mutex_unlock(&priv->lock);
Laurent Pinchart48110052011-12-12 16:36:13 +0100486 return cache;
Damian7caa4342011-05-18 11:10:07 +0000487}
488
Laurent Pinchartcdf88b92011-11-22 00:56:58 +0100489static void
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100490sh_mobile_cache_free(struct sh_mobile_meram_info *pdata, void *data)
Damian7caa4342011-05-18 11:10:07 +0000491{
Laurent Pinchart48110052011-12-12 16:36:13 +0100492 struct sh_mobile_meram_fb_cache *cache = data;
Laurent Pinchartcdf88b92011-11-22 00:56:58 +0100493 struct sh_mobile_meram_priv *priv = pdata->priv;
Damian7caa4342011-05-18 11:10:07 +0000494
495 mutex_lock(&priv->lock);
496
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100497 /* Cleanup and free. */
498 meram_plane_cleanup(priv, &cache->planes[0]);
499 meram_plane_free(priv, &cache->planes[0]);
Laurent Pinchart48110052011-12-12 16:36:13 +0100500
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100501 if (cache->nplanes == 2) {
502 meram_plane_cleanup(priv, &cache->planes[1]);
503 meram_plane_free(priv, &cache->planes[1]);
504 }
505
506 kfree(cache);
Damian7caa4342011-05-18 11:10:07 +0000507
508 mutex_unlock(&priv->lock);
Damian7caa4342011-05-18 11:10:07 +0000509}
510
Laurent Pinchartcdf88b92011-11-22 00:56:58 +0100511static void
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100512sh_mobile_cache_update(struct sh_mobile_meram_info *pdata, void *data,
Laurent Pinchart48110052011-12-12 16:36:13 +0100513 unsigned long base_addr_y, unsigned long base_addr_c,
514 unsigned long *icb_addr_y, unsigned long *icb_addr_c)
Damian7caa4342011-05-18 11:10:07 +0000515{
Laurent Pinchart48110052011-12-12 16:36:13 +0100516 struct sh_mobile_meram_fb_cache *cache = data;
Laurent Pinchartcdf88b92011-11-22 00:56:58 +0100517 struct sh_mobile_meram_priv *priv = pdata->priv;
Damian7caa4342011-05-18 11:10:07 +0000518
519 mutex_lock(&priv->lock);
520
Laurent Pinchart48110052011-12-12 16:36:13 +0100521 meram_set_next_addr(priv, cache, base_addr_y, base_addr_c);
522 meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
Damian7caa4342011-05-18 11:10:07 +0000523
524 mutex_unlock(&priv->lock);
Damian7caa4342011-05-18 11:10:07 +0000525}
526
Laurent Pinchart75543402011-09-19 11:40:31 +0200527static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
528 .module = THIS_MODULE,
Laurent Pinchart4a2371772012-03-15 12:40:47 +0100529 .cache_alloc = sh_mobile_cache_alloc,
530 .cache_free = sh_mobile_cache_free,
531 .cache_update = sh_mobile_cache_update,
Laurent Pinchart75543402011-09-19 11:40:31 +0200532};
533
534/* -----------------------------------------------------------------------------
535 * Power management
536 */
537
Laurent Pinchartaf899562011-11-22 00:56:58 +0100538static int sh_mobile_meram_suspend(struct device *dev)
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200539{
540 struct platform_device *pdev = to_platform_device(dev);
541 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
Laurent Pinchart05432832011-09-19 11:40:31 +0200542 unsigned int i, j;
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200543
Laurent Pinchart75543402011-09-19 11:40:31 +0200544 for (i = 0; i < MERAM_REGS_SIZE; i++)
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200545 priv->regs[i] = meram_read_reg(priv->base, common_regs[i]);
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200546
Laurent Pinchart05432832011-09-19 11:40:31 +0200547 for (i = 0; i < 32; i++) {
548 if (!test_bit(i, &priv->used_icb))
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200549 continue;
Laurent Pinchart05432832011-09-19 11:40:31 +0200550 for (j = 0; j < ICB_REGS_SIZE; j++) {
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200551 priv->icbs[i].regs[j] =
Laurent Pinchart05432832011-09-19 11:40:31 +0200552 meram_read_icb(priv->base, i, icb_regs[j]);
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200553 /* Reset ICB on resume */
Laurent Pinchart05432832011-09-19 11:40:31 +0200554 if (icb_regs[j] == MExxCTL)
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200555 priv->icbs[i].regs[j] |=
Laurent Pinchartf0a260f2011-07-13 12:13:47 +0200556 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF;
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200557 }
558 }
559 return 0;
560}
561
Laurent Pinchartaf899562011-11-22 00:56:58 +0100562static int sh_mobile_meram_resume(struct device *dev)
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200563{
564 struct platform_device *pdev = to_platform_device(dev);
565 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
Laurent Pinchart05432832011-09-19 11:40:31 +0200566 unsigned int i, j;
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200567
Laurent Pinchart05432832011-09-19 11:40:31 +0200568 for (i = 0; i < 32; i++) {
569 if (!test_bit(i, &priv->used_icb))
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200570 continue;
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200571 for (j = 0; j < ICB_REGS_SIZE; j++)
Laurent Pinchart05432832011-09-19 11:40:31 +0200572 meram_write_icb(priv->base, i, icb_regs[j],
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200573 priv->icbs[i].regs[j]);
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200574 }
575
Laurent Pinchart75543402011-09-19 11:40:31 +0200576 for (i = 0; i < MERAM_REGS_SIZE; i++)
Laurent Pinchart2a618e02011-09-19 11:40:31 +0200577 meram_write_reg(priv->base, common_regs[i], priv->regs[i]);
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200578 return 0;
579}
580
Laurent Pinchartaf899562011-11-22 00:56:58 +0100581static UNIVERSAL_DEV_PM_OPS(sh_mobile_meram_dev_pm_ops,
582 sh_mobile_meram_suspend,
583 sh_mobile_meram_resume, NULL);
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200584
Laurent Pinchart75543402011-09-19 11:40:31 +0200585/* -----------------------------------------------------------------------------
586 * Probe/remove and driver init/exit
Damian7caa4342011-05-18 11:10:07 +0000587 */
588
Damian7caa4342011-05-18 11:10:07 +0000589static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
590{
591 struct sh_mobile_meram_priv *priv;
592 struct sh_mobile_meram_info *pdata = pdev->dev.platform_data;
Laurent Pincharte1d11442011-09-19 11:40:31 +0200593 struct resource *regs;
594 struct resource *meram;
Laurent Pinchart48110052011-12-12 16:36:13 +0100595 unsigned int i;
Damian7caa4342011-05-18 11:10:07 +0000596 int error;
597
598 if (!pdata) {
599 dev_err(&pdev->dev, "no platform data defined\n");
600 return -EINVAL;
601 }
602
Laurent Pincharte1d11442011-09-19 11:40:31 +0200603 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
604 meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
605 if (regs == NULL || meram == NULL) {
Damian7caa4342011-05-18 11:10:07 +0000606 dev_err(&pdev->dev, "cannot get platform resources\n");
607 return -ENOENT;
608 }
609
610 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
611 if (!priv) {
612 dev_err(&pdev->dev, "cannot allocate device data\n");
613 return -ENOMEM;
614 }
615
Laurent Pinchart48110052011-12-12 16:36:13 +0100616 /* Initialize private data. */
Damian7caa4342011-05-18 11:10:07 +0000617 mutex_init(&priv->lock);
Laurent Pinchart48110052011-12-12 16:36:13 +0100618 priv->used_icb = pdata->reserved_icbs;
619
620 for (i = 0; i < MERAM_ICB_NUM; ++i)
621 priv->icbs[i].index = i;
622
Damian7caa4342011-05-18 11:10:07 +0000623 pdata->ops = &sh_mobile_meram_ops;
624 pdata->priv = priv;
625 pdata->pdev = pdev;
626
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200627 /* Request memory regions and remap the registers. */
Laurent Pincharte1d11442011-09-19 11:40:31 +0200628 if (!request_mem_region(regs->start, resource_size(regs), pdev->name)) {
629 dev_err(&pdev->dev, "MERAM registers region already claimed\n");
630 error = -EBUSY;
631 goto err_req_regs;
632 }
633
634 if (!request_mem_region(meram->start, resource_size(meram),
635 pdev->name)) {
636 dev_err(&pdev->dev, "MERAM memory region already claimed\n");
637 error = -EBUSY;
638 goto err_req_meram;
639 }
640
641 priv->base = ioremap_nocache(regs->start, resource_size(regs));
642 if (!priv->base) {
643 dev_err(&pdev->dev, "ioremap failed\n");
644 error = -EFAULT;
645 goto err_ioremap;
646 }
647
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200648 priv->meram = meram->start;
649
650 /* Create and initialize the MERAM memory pool. */
651 priv->pool = gen_pool_create(ilog2(MERAM_GRANULARITY), -1);
652 if (priv->pool == NULL) {
653 error = -ENOMEM;
654 goto err_genpool;
655 }
656
657 error = gen_pool_add(priv->pool, meram->start, resource_size(meram),
658 -1);
659 if (error < 0)
660 goto err_genpool;
661
Damian7caa4342011-05-18 11:10:07 +0000662 /* initialize ICB addressing mode */
663 if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1)
Laurent Pinchartf0a260f2011-07-13 12:13:47 +0200664 meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1);
Damian7caa4342011-05-18 11:10:07 +0000665
Laurent Pincharte1d11442011-09-19 11:40:31 +0200666 platform_set_drvdata(pdev, priv);
Damian Hobson-Garcia17673772011-07-04 08:06:11 +0200667 pm_runtime_enable(&pdev->dev);
668
Damian7caa4342011-05-18 11:10:07 +0000669 dev_info(&pdev->dev, "sh_mobile_meram initialized.");
670
671 return 0;
672
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200673err_genpool:
674 if (priv->pool)
675 gen_pool_destroy(priv->pool);
676 iounmap(priv->base);
Laurent Pincharte1d11442011-09-19 11:40:31 +0200677err_ioremap:
678 release_mem_region(meram->start, resource_size(meram));
679err_req_meram:
680 release_mem_region(regs->start, resource_size(regs));
681err_req_regs:
682 mutex_destroy(&priv->lock);
683 kfree(priv);
Damian7caa4342011-05-18 11:10:07 +0000684
685 return error;
686}
687
688
689static int sh_mobile_meram_remove(struct platform_device *pdev)
690{
691 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
Laurent Pincharte1d11442011-09-19 11:40:31 +0200692 struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
693 struct resource *meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
Damian7caa4342011-05-18 11:10:07 +0000694
Damian Hobson-Garcia17673772011-07-04 08:06:11 +0200695 pm_runtime_disable(&pdev->dev);
696
Laurent Pinchart974d250b2011-09-19 11:40:31 +0200697 gen_pool_destroy(priv->pool);
698
Laurent Pincharte1d11442011-09-19 11:40:31 +0200699 iounmap(priv->base);
700 release_mem_region(meram->start, resource_size(meram));
701 release_mem_region(regs->start, resource_size(regs));
Damian7caa4342011-05-18 11:10:07 +0000702
703 mutex_destroy(&priv->lock);
704
705 kfree(priv);
706
707 return 0;
708}
709
710static struct platform_driver sh_mobile_meram_driver = {
711 .driver = {
712 .name = "sh_mobile_meram",
713 .owner = THIS_MODULE,
Damian Hobson-Garcia0b3bb772011-06-22 07:49:51 +0200714 .pm = &sh_mobile_meram_dev_pm_ops,
Damian7caa4342011-05-18 11:10:07 +0000715 },
716 .probe = sh_mobile_meram_probe,
717 .remove = sh_mobile_meram_remove,
718};
719
Axel Lin4277f2c2011-11-26 10:25:54 +0800720module_platform_driver(sh_mobile_meram_driver);
Damian7caa4342011-05-18 11:10:07 +0000721
722MODULE_DESCRIPTION("SuperH Mobile MERAM driver");
723MODULE_AUTHOR("Damian Hobson-Garcia / Takanari Hayama");
724MODULE_LICENSE("GPL v2");