11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2f7018c21STomi Valkeinen /*
3f7018c21STomi Valkeinen * linux/drivers/video/mmp/fb/mmpfb.c
4f7018c21STomi Valkeinen * Framebuffer driver for Marvell Display controller.
5f7018c21STomi Valkeinen *
6f7018c21STomi Valkeinen * Copyright (C) 2012 Marvell Technology Group Ltd.
7f7018c21STomi Valkeinen * Authors: Zhou Zhu <zzhu3@marvell.com>
8f7018c21STomi Valkeinen */
9f7018c21STomi Valkeinen #include <linux/module.h>
10f7018c21STomi Valkeinen #include <linux/dma-mapping.h>
11f7018c21STomi Valkeinen #include <linux/platform_device.h>
12f7018c21STomi Valkeinen #include "mmpfb.h"
13f7018c21STomi Valkeinen
var_to_pixfmt(struct fb_var_screeninfo * var)14f7018c21STomi Valkeinen static int var_to_pixfmt(struct fb_var_screeninfo *var)
15f7018c21STomi Valkeinen {
16f7018c21STomi Valkeinen /*
17f7018c21STomi Valkeinen * Pseudocolor mode?
18f7018c21STomi Valkeinen */
19f7018c21STomi Valkeinen if (var->bits_per_pixel == 8)
20f7018c21STomi Valkeinen return PIXFMT_PSEUDOCOLOR;
21f7018c21STomi Valkeinen
22f7018c21STomi Valkeinen /*
23f7018c21STomi Valkeinen * Check for YUV422PLANAR.
24f7018c21STomi Valkeinen */
25f7018c21STomi Valkeinen if (var->bits_per_pixel == 16 && var->red.length == 8 &&
26f7018c21STomi Valkeinen var->green.length == 4 && var->blue.length == 4) {
27f7018c21STomi Valkeinen if (var->green.offset >= var->blue.offset)
28f7018c21STomi Valkeinen return PIXFMT_YUV422P;
29f7018c21STomi Valkeinen else
30f7018c21STomi Valkeinen return PIXFMT_YVU422P;
31f7018c21STomi Valkeinen }
32f7018c21STomi Valkeinen
33f7018c21STomi Valkeinen /*
34f7018c21STomi Valkeinen * Check for YUV420PLANAR.
35f7018c21STomi Valkeinen */
36f7018c21STomi Valkeinen if (var->bits_per_pixel == 12 && var->red.length == 8 &&
37f7018c21STomi Valkeinen var->green.length == 2 && var->blue.length == 2) {
38f7018c21STomi Valkeinen if (var->green.offset >= var->blue.offset)
39f7018c21STomi Valkeinen return PIXFMT_YUV420P;
40f7018c21STomi Valkeinen else
41f7018c21STomi Valkeinen return PIXFMT_YVU420P;
42f7018c21STomi Valkeinen }
43f7018c21STomi Valkeinen
44f7018c21STomi Valkeinen /*
45f7018c21STomi Valkeinen * Check for YUV422PACK.
46f7018c21STomi Valkeinen */
47f7018c21STomi Valkeinen if (var->bits_per_pixel == 16 && var->red.length == 16 &&
48f7018c21STomi Valkeinen var->green.length == 16 && var->blue.length == 16) {
49f7018c21STomi Valkeinen if (var->red.offset == 0)
50f7018c21STomi Valkeinen return PIXFMT_YUYV;
51f7018c21STomi Valkeinen else if (var->green.offset >= var->blue.offset)
52f7018c21STomi Valkeinen return PIXFMT_UYVY;
53f7018c21STomi Valkeinen else
54f7018c21STomi Valkeinen return PIXFMT_VYUY;
55f7018c21STomi Valkeinen }
56f7018c21STomi Valkeinen
57f7018c21STomi Valkeinen /*
58f7018c21STomi Valkeinen * Check for 565/1555.
59f7018c21STomi Valkeinen */
60f7018c21STomi Valkeinen if (var->bits_per_pixel == 16 && var->red.length <= 5 &&
61f7018c21STomi Valkeinen var->green.length <= 6 && var->blue.length <= 5) {
62f7018c21STomi Valkeinen if (var->transp.length == 0) {
63f7018c21STomi Valkeinen if (var->red.offset >= var->blue.offset)
64f7018c21STomi Valkeinen return PIXFMT_RGB565;
65f7018c21STomi Valkeinen else
66f7018c21STomi Valkeinen return PIXFMT_BGR565;
67f7018c21STomi Valkeinen }
68f7018c21STomi Valkeinen }
69f7018c21STomi Valkeinen
70f7018c21STomi Valkeinen /*
71f7018c21STomi Valkeinen * Check for 888/A888.
72f7018c21STomi Valkeinen */
73f7018c21STomi Valkeinen if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&
74f7018c21STomi Valkeinen var->green.length <= 8 && var->blue.length <= 8) {
75f7018c21STomi Valkeinen if (var->bits_per_pixel == 24 && var->transp.length == 0) {
76f7018c21STomi Valkeinen if (var->red.offset >= var->blue.offset)
77f7018c21STomi Valkeinen return PIXFMT_RGB888PACK;
78f7018c21STomi Valkeinen else
79f7018c21STomi Valkeinen return PIXFMT_BGR888PACK;
80f7018c21STomi Valkeinen }
81f7018c21STomi Valkeinen
82f7018c21STomi Valkeinen if (var->bits_per_pixel == 32 && var->transp.offset == 24) {
83f7018c21STomi Valkeinen if (var->red.offset >= var->blue.offset)
84f7018c21STomi Valkeinen return PIXFMT_RGBA888;
85f7018c21STomi Valkeinen else
86f7018c21STomi Valkeinen return PIXFMT_BGRA888;
87f7018c21STomi Valkeinen } else {
88f7018c21STomi Valkeinen if (var->red.offset >= var->blue.offset)
89f7018c21STomi Valkeinen return PIXFMT_RGB888UNPACK;
90f7018c21STomi Valkeinen else
91f7018c21STomi Valkeinen return PIXFMT_BGR888UNPACK;
92f7018c21STomi Valkeinen }
93f7018c21STomi Valkeinen }
94f7018c21STomi Valkeinen
95f7018c21STomi Valkeinen return -EINVAL;
96f7018c21STomi Valkeinen }
97f7018c21STomi Valkeinen
pixfmt_to_var(struct fb_var_screeninfo * var,int pix_fmt)98f7018c21STomi Valkeinen static void pixfmt_to_var(struct fb_var_screeninfo *var, int pix_fmt)
99f7018c21STomi Valkeinen {
100f7018c21STomi Valkeinen switch (pix_fmt) {
101f7018c21STomi Valkeinen case PIXFMT_RGB565:
102f7018c21STomi Valkeinen var->bits_per_pixel = 16;
103f7018c21STomi Valkeinen var->red.offset = 11; var->red.length = 5;
104f7018c21STomi Valkeinen var->green.offset = 5; var->green.length = 6;
105f7018c21STomi Valkeinen var->blue.offset = 0; var->blue.length = 5;
106f7018c21STomi Valkeinen var->transp.offset = 0; var->transp.length = 0;
107f7018c21STomi Valkeinen break;
108f7018c21STomi Valkeinen case PIXFMT_BGR565:
109f7018c21STomi Valkeinen var->bits_per_pixel = 16;
110f7018c21STomi Valkeinen var->red.offset = 0; var->red.length = 5;
111f7018c21STomi Valkeinen var->green.offset = 5; var->green.length = 6;
112f7018c21STomi Valkeinen var->blue.offset = 11; var->blue.length = 5;
113f7018c21STomi Valkeinen var->transp.offset = 0; var->transp.length = 0;
114f7018c21STomi Valkeinen break;
115f7018c21STomi Valkeinen case PIXFMT_RGB888UNPACK:
116f7018c21STomi Valkeinen var->bits_per_pixel = 32;
117f7018c21STomi Valkeinen var->red.offset = 16; var->red.length = 8;
118f7018c21STomi Valkeinen var->green.offset = 8; var->green.length = 8;
119f7018c21STomi Valkeinen var->blue.offset = 0; var->blue.length = 8;
120f7018c21STomi Valkeinen var->transp.offset = 0; var->transp.length = 0;
121f7018c21STomi Valkeinen break;
122f7018c21STomi Valkeinen case PIXFMT_BGR888UNPACK:
123f7018c21STomi Valkeinen var->bits_per_pixel = 32;
124f7018c21STomi Valkeinen var->red.offset = 0; var->red.length = 8;
125f7018c21STomi Valkeinen var->green.offset = 8; var->green.length = 8;
126f7018c21STomi Valkeinen var->blue.offset = 16; var->blue.length = 8;
127f7018c21STomi Valkeinen var->transp.offset = 0; var->transp.length = 0;
128f7018c21STomi Valkeinen break;
129f7018c21STomi Valkeinen case PIXFMT_RGBA888:
130f7018c21STomi Valkeinen var->bits_per_pixel = 32;
131f7018c21STomi Valkeinen var->red.offset = 16; var->red.length = 8;
132f7018c21STomi Valkeinen var->green.offset = 8; var->green.length = 8;
133f7018c21STomi Valkeinen var->blue.offset = 0; var->blue.length = 8;
134f7018c21STomi Valkeinen var->transp.offset = 24; var->transp.length = 8;
135f7018c21STomi Valkeinen break;
136f7018c21STomi Valkeinen case PIXFMT_BGRA888:
137f7018c21STomi Valkeinen var->bits_per_pixel = 32;
138f7018c21STomi Valkeinen var->red.offset = 0; var->red.length = 8;
139f7018c21STomi Valkeinen var->green.offset = 8; var->green.length = 8;
140f7018c21STomi Valkeinen var->blue.offset = 16; var->blue.length = 8;
141f7018c21STomi Valkeinen var->transp.offset = 24; var->transp.length = 8;
142f7018c21STomi Valkeinen break;
143f7018c21STomi Valkeinen case PIXFMT_RGB888PACK:
144f7018c21STomi Valkeinen var->bits_per_pixel = 24;
145f7018c21STomi Valkeinen var->red.offset = 16; var->red.length = 8;
146f7018c21STomi Valkeinen var->green.offset = 8; var->green.length = 8;
147f7018c21STomi Valkeinen var->blue.offset = 0; var->blue.length = 8;
148f7018c21STomi Valkeinen var->transp.offset = 0; var->transp.length = 0;
149f7018c21STomi Valkeinen break;
150f7018c21STomi Valkeinen case PIXFMT_BGR888PACK:
151f7018c21STomi Valkeinen var->bits_per_pixel = 24;
152f7018c21STomi Valkeinen var->red.offset = 0; var->red.length = 8;
153f7018c21STomi Valkeinen var->green.offset = 8; var->green.length = 8;
154f7018c21STomi Valkeinen var->blue.offset = 16; var->blue.length = 8;
155f7018c21STomi Valkeinen var->transp.offset = 0; var->transp.length = 0;
156f7018c21STomi Valkeinen break;
157f7018c21STomi Valkeinen case PIXFMT_YUV420P:
158f7018c21STomi Valkeinen var->bits_per_pixel = 12;
159f7018c21STomi Valkeinen var->red.offset = 4; var->red.length = 8;
160f7018c21STomi Valkeinen var->green.offset = 2; var->green.length = 2;
161f7018c21STomi Valkeinen var->blue.offset = 0; var->blue.length = 2;
162f7018c21STomi Valkeinen var->transp.offset = 0; var->transp.length = 0;
163f7018c21STomi Valkeinen break;
164f7018c21STomi Valkeinen case PIXFMT_YVU420P:
165f7018c21STomi Valkeinen var->bits_per_pixel = 12;
166f7018c21STomi Valkeinen var->red.offset = 4; var->red.length = 8;
167f7018c21STomi Valkeinen var->green.offset = 0; var->green.length = 2;
168f7018c21STomi Valkeinen var->blue.offset = 2; var->blue.length = 2;
169f7018c21STomi Valkeinen var->transp.offset = 0; var->transp.length = 0;
170f7018c21STomi Valkeinen break;
171f7018c21STomi Valkeinen case PIXFMT_YUV422P:
172f7018c21STomi Valkeinen var->bits_per_pixel = 16;
173f7018c21STomi Valkeinen var->red.offset = 8; var->red.length = 8;
174f7018c21STomi Valkeinen var->green.offset = 4; var->green.length = 4;
175f7018c21STomi Valkeinen var->blue.offset = 0; var->blue.length = 4;
176f7018c21STomi Valkeinen var->transp.offset = 0; var->transp.length = 0;
177f7018c21STomi Valkeinen break;
178f7018c21STomi Valkeinen case PIXFMT_YVU422P:
179f7018c21STomi Valkeinen var->bits_per_pixel = 16;
180f7018c21STomi Valkeinen var->red.offset = 8; var->red.length = 8;
181f7018c21STomi Valkeinen var->green.offset = 0; var->green.length = 4;
182f7018c21STomi Valkeinen var->blue.offset = 4; var->blue.length = 4;
183f7018c21STomi Valkeinen var->transp.offset = 0; var->transp.length = 0;
184f7018c21STomi Valkeinen break;
185f7018c21STomi Valkeinen case PIXFMT_UYVY:
186f7018c21STomi Valkeinen var->bits_per_pixel = 16;
187f7018c21STomi Valkeinen var->red.offset = 8; var->red.length = 16;
188f7018c21STomi Valkeinen var->green.offset = 4; var->green.length = 16;
189f7018c21STomi Valkeinen var->blue.offset = 0; var->blue.length = 16;
190f7018c21STomi Valkeinen var->transp.offset = 0; var->transp.length = 0;
191f7018c21STomi Valkeinen break;
192f7018c21STomi Valkeinen case PIXFMT_VYUY:
193f7018c21STomi Valkeinen var->bits_per_pixel = 16;
194f7018c21STomi Valkeinen var->red.offset = 8; var->red.length = 16;
195f7018c21STomi Valkeinen var->green.offset = 0; var->green.length = 16;
196f7018c21STomi Valkeinen var->blue.offset = 4; var->blue.length = 16;
197f7018c21STomi Valkeinen var->transp.offset = 0; var->transp.length = 0;
198f7018c21STomi Valkeinen break;
199f7018c21STomi Valkeinen case PIXFMT_YUYV:
200f7018c21STomi Valkeinen var->bits_per_pixel = 16;
201f7018c21STomi Valkeinen var->red.offset = 0; var->red.length = 16;
202f7018c21STomi Valkeinen var->green.offset = 4; var->green.length = 16;
203f7018c21STomi Valkeinen var->blue.offset = 8; var->blue.length = 16;
204f7018c21STomi Valkeinen var->transp.offset = 0; var->transp.length = 0;
205f7018c21STomi Valkeinen break;
206f7018c21STomi Valkeinen case PIXFMT_PSEUDOCOLOR:
207f7018c21STomi Valkeinen var->bits_per_pixel = 8;
208f7018c21STomi Valkeinen var->red.offset = 0; var->red.length = 8;
209f7018c21STomi Valkeinen var->green.offset = 0; var->green.length = 8;
210f7018c21STomi Valkeinen var->blue.offset = 0; var->blue.length = 8;
211f7018c21STomi Valkeinen var->transp.offset = 0; var->transp.length = 0;
212f7018c21STomi Valkeinen break;
213f7018c21STomi Valkeinen }
214f7018c21STomi Valkeinen }
215f7018c21STomi Valkeinen
216f7018c21STomi Valkeinen /*
217f7018c21STomi Valkeinen * fb framework has its limitation:
218f7018c21STomi Valkeinen * 1. input color/output color is not seprated
219f7018c21STomi Valkeinen * 2. fb_videomode not include output color
220f7018c21STomi Valkeinen * so for fb usage, we keep a output format which is not changed
221f7018c21STomi Valkeinen * then it's added for mmpmode
222f7018c21STomi Valkeinen */
fbmode_to_mmpmode(struct mmp_mode * mode,struct fb_videomode * videomode,int output_fmt)223f7018c21STomi Valkeinen static void fbmode_to_mmpmode(struct mmp_mode *mode,
224f7018c21STomi Valkeinen struct fb_videomode *videomode, int output_fmt)
225f7018c21STomi Valkeinen {
226f7018c21STomi Valkeinen u64 div_result = 1000000000000ll;
227f7018c21STomi Valkeinen mode->name = videomode->name;
228f7018c21STomi Valkeinen mode->refresh = videomode->refresh;
229f7018c21STomi Valkeinen mode->xres = videomode->xres;
230f7018c21STomi Valkeinen mode->yres = videomode->yres;
231f7018c21STomi Valkeinen
232f7018c21STomi Valkeinen do_div(div_result, videomode->pixclock);
233f7018c21STomi Valkeinen mode->pixclock_freq = (u32)div_result;
234f7018c21STomi Valkeinen
235f7018c21STomi Valkeinen mode->left_margin = videomode->left_margin;
236f7018c21STomi Valkeinen mode->right_margin = videomode->right_margin;
237f7018c21STomi Valkeinen mode->upper_margin = videomode->upper_margin;
238f7018c21STomi Valkeinen mode->lower_margin = videomode->lower_margin;
239f7018c21STomi Valkeinen mode->hsync_len = videomode->hsync_len;
240f7018c21STomi Valkeinen mode->vsync_len = videomode->vsync_len;
241f7018c21STomi Valkeinen mode->hsync_invert = !!(videomode->sync & FB_SYNC_HOR_HIGH_ACT);
242f7018c21STomi Valkeinen mode->vsync_invert = !!(videomode->sync & FB_SYNC_VERT_HIGH_ACT);
243f7018c21STomi Valkeinen /* no defined flag in fb, use vmode>>3*/
244f7018c21STomi Valkeinen mode->invert_pixclock = !!(videomode->vmode & 8);
245f7018c21STomi Valkeinen mode->pix_fmt_out = output_fmt;
246f7018c21STomi Valkeinen }
247f7018c21STomi Valkeinen
mmpmode_to_fbmode(struct fb_videomode * videomode,struct mmp_mode * mode)248f7018c21STomi Valkeinen static void mmpmode_to_fbmode(struct fb_videomode *videomode,
249f7018c21STomi Valkeinen struct mmp_mode *mode)
250f7018c21STomi Valkeinen {
251f7018c21STomi Valkeinen u64 div_result = 1000000000000ll;
252f7018c21STomi Valkeinen
253f7018c21STomi Valkeinen videomode->name = mode->name;
254f7018c21STomi Valkeinen videomode->refresh = mode->refresh;
255f7018c21STomi Valkeinen videomode->xres = mode->xres;
256f7018c21STomi Valkeinen videomode->yres = mode->yres;
257f7018c21STomi Valkeinen
258f7018c21STomi Valkeinen do_div(div_result, mode->pixclock_freq);
259f7018c21STomi Valkeinen videomode->pixclock = (u32)div_result;
260f7018c21STomi Valkeinen
261f7018c21STomi Valkeinen videomode->left_margin = mode->left_margin;
262f7018c21STomi Valkeinen videomode->right_margin = mode->right_margin;
263f7018c21STomi Valkeinen videomode->upper_margin = mode->upper_margin;
264f7018c21STomi Valkeinen videomode->lower_margin = mode->lower_margin;
265f7018c21STomi Valkeinen videomode->hsync_len = mode->hsync_len;
266f7018c21STomi Valkeinen videomode->vsync_len = mode->vsync_len;
267f7018c21STomi Valkeinen videomode->sync = (mode->hsync_invert ? FB_SYNC_HOR_HIGH_ACT : 0)
268f7018c21STomi Valkeinen | (mode->vsync_invert ? FB_SYNC_VERT_HIGH_ACT : 0);
269f7018c21STomi Valkeinen videomode->vmode = mode->invert_pixclock ? 8 : 0;
270f7018c21STomi Valkeinen }
271f7018c21STomi Valkeinen
mmpfb_check_var(struct fb_var_screeninfo * var,struct fb_info * info)272f7018c21STomi Valkeinen static int mmpfb_check_var(struct fb_var_screeninfo *var,
273f7018c21STomi Valkeinen struct fb_info *info)
274f7018c21STomi Valkeinen {
275f7018c21STomi Valkeinen struct mmpfb_info *fbi = info->par;
276f7018c21STomi Valkeinen
277f7018c21STomi Valkeinen if (var->bits_per_pixel == 8)
278f7018c21STomi Valkeinen return -EINVAL;
279f7018c21STomi Valkeinen /*
280f7018c21STomi Valkeinen * Basic geometry sanity checks.
281f7018c21STomi Valkeinen */
282f7018c21STomi Valkeinen if (var->xoffset + var->xres > var->xres_virtual)
283f7018c21STomi Valkeinen return -EINVAL;
284f7018c21STomi Valkeinen if (var->yoffset + var->yres > var->yres_virtual)
285f7018c21STomi Valkeinen return -EINVAL;
286f7018c21STomi Valkeinen
287f7018c21STomi Valkeinen /*
288f7018c21STomi Valkeinen * Check size of framebuffer.
289f7018c21STomi Valkeinen */
290f7018c21STomi Valkeinen if (var->xres_virtual * var->yres_virtual *
291f7018c21STomi Valkeinen (var->bits_per_pixel >> 3) > fbi->fb_size)
292f7018c21STomi Valkeinen return -EINVAL;
293f7018c21STomi Valkeinen
294f7018c21STomi Valkeinen return 0;
295f7018c21STomi Valkeinen }
296f7018c21STomi Valkeinen
chan_to_field(unsigned int chan,struct fb_bitfield * bf)297f7018c21STomi Valkeinen static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
298f7018c21STomi Valkeinen {
299f7018c21STomi Valkeinen return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
300f7018c21STomi Valkeinen }
301f7018c21STomi Valkeinen
to_rgb(u16 red,u16 green,u16 blue)302f7018c21STomi Valkeinen static u32 to_rgb(u16 red, u16 green, u16 blue)
303f7018c21STomi Valkeinen {
304f7018c21STomi Valkeinen red >>= 8;
305f7018c21STomi Valkeinen green >>= 8;
306f7018c21STomi Valkeinen blue >>= 8;
307f7018c21STomi Valkeinen
308f7018c21STomi Valkeinen return (red << 16) | (green << 8) | blue;
309f7018c21STomi Valkeinen }
310f7018c21STomi Valkeinen
mmpfb_setcolreg(unsigned int regno,unsigned int red,unsigned int green,unsigned int blue,unsigned int trans,struct fb_info * info)311f7018c21STomi Valkeinen static int mmpfb_setcolreg(unsigned int regno, unsigned int red,
312f7018c21STomi Valkeinen unsigned int green, unsigned int blue,
313f7018c21STomi Valkeinen unsigned int trans, struct fb_info *info)
314f7018c21STomi Valkeinen {
315f7018c21STomi Valkeinen struct mmpfb_info *fbi = info->par;
316f7018c21STomi Valkeinen u32 val;
317f7018c21STomi Valkeinen
318f7018c21STomi Valkeinen if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) {
319f7018c21STomi Valkeinen val = chan_to_field(red, &info->var.red);
320f7018c21STomi Valkeinen val |= chan_to_field(green, &info->var.green);
321f7018c21STomi Valkeinen val |= chan_to_field(blue , &info->var.blue);
322f7018c21STomi Valkeinen fbi->pseudo_palette[regno] = val;
323f7018c21STomi Valkeinen }
324f7018c21STomi Valkeinen
325f7018c21STomi Valkeinen if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
326f7018c21STomi Valkeinen val = to_rgb(red, green, blue);
327f7018c21STomi Valkeinen /* TODO */
328f7018c21STomi Valkeinen }
329f7018c21STomi Valkeinen
330f7018c21STomi Valkeinen return 0;
331f7018c21STomi Valkeinen }
332f7018c21STomi Valkeinen
mmpfb_pan_display(struct fb_var_screeninfo * var,struct fb_info * info)333f7018c21STomi Valkeinen static int mmpfb_pan_display(struct fb_var_screeninfo *var,
334f7018c21STomi Valkeinen struct fb_info *info)
335f7018c21STomi Valkeinen {
336f7018c21STomi Valkeinen struct mmpfb_info *fbi = info->par;
337f7018c21STomi Valkeinen struct mmp_addr addr;
338f7018c21STomi Valkeinen
339f7018c21STomi Valkeinen memset(&addr, 0, sizeof(addr));
340f7018c21STomi Valkeinen addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
341f7018c21STomi Valkeinen * var->bits_per_pixel / 8 + fbi->fb_start_dma;
342f7018c21STomi Valkeinen mmp_overlay_set_addr(fbi->overlay, &addr);
343f7018c21STomi Valkeinen
344f7018c21STomi Valkeinen return 0;
345f7018c21STomi Valkeinen }
346f7018c21STomi Valkeinen
var_update(struct fb_info * info)347f7018c21STomi Valkeinen static int var_update(struct fb_info *info)
348f7018c21STomi Valkeinen {
349f7018c21STomi Valkeinen struct mmpfb_info *fbi = info->par;
350f7018c21STomi Valkeinen struct fb_var_screeninfo *var = &info->var;
351f7018c21STomi Valkeinen struct fb_videomode *m;
352f7018c21STomi Valkeinen int pix_fmt;
353f7018c21STomi Valkeinen
354f7018c21STomi Valkeinen /* set pix_fmt */
355f7018c21STomi Valkeinen pix_fmt = var_to_pixfmt(var);
356f7018c21STomi Valkeinen if (pix_fmt < 0)
357f7018c21STomi Valkeinen return -EINVAL;
358f7018c21STomi Valkeinen pixfmt_to_var(var, pix_fmt);
359f7018c21STomi Valkeinen fbi->pix_fmt = pix_fmt;
360f7018c21STomi Valkeinen
361f7018c21STomi Valkeinen /* set var according to best video mode*/
362f7018c21STomi Valkeinen m = (struct fb_videomode *)fb_match_mode(var, &info->modelist);
363f7018c21STomi Valkeinen if (!m) {
364f7018c21STomi Valkeinen dev_err(fbi->dev, "set par: no match mode, use best mode\n");
365f7018c21STomi Valkeinen m = (struct fb_videomode *)fb_find_best_mode(var,
366f7018c21STomi Valkeinen &info->modelist);
367f7018c21STomi Valkeinen fb_videomode_to_var(var, m);
368f7018c21STomi Valkeinen }
369f7018c21STomi Valkeinen memcpy(&fbi->mode, m, sizeof(struct fb_videomode));
370f7018c21STomi Valkeinen
371f7018c21STomi Valkeinen /* fix to 2* yres */
372f7018c21STomi Valkeinen var->yres_virtual = var->yres * 2;
373f7018c21STomi Valkeinen info->fix.visual = (pix_fmt == PIXFMT_PSEUDOCOLOR) ?
374f7018c21STomi Valkeinen FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
375f7018c21STomi Valkeinen info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
376f7018c21STomi Valkeinen info->fix.ypanstep = var->yres;
377f7018c21STomi Valkeinen return 0;
378f7018c21STomi Valkeinen }
379f7018c21STomi Valkeinen
mmpfb_set_win(struct fb_info * info)380f7018c21STomi Valkeinen static void mmpfb_set_win(struct fb_info *info)
381f7018c21STomi Valkeinen {
382f7018c21STomi Valkeinen struct mmpfb_info *fbi = info->par;
383f7018c21STomi Valkeinen struct fb_var_screeninfo *var = &info->var;
384f7018c21STomi Valkeinen struct mmp_win win;
385f7018c21STomi Valkeinen u32 stride;
386f7018c21STomi Valkeinen
387f7018c21STomi Valkeinen memset(&win, 0, sizeof(win));
388f7018c21STomi Valkeinen win.xsrc = win.xdst = fbi->mode.xres;
389f7018c21STomi Valkeinen win.ysrc = win.ydst = fbi->mode.yres;
390f7018c21STomi Valkeinen win.pix_fmt = fbi->pix_fmt;
391f7018c21STomi Valkeinen stride = pixfmt_to_stride(win.pix_fmt);
392f7018c21STomi Valkeinen win.pitch[0] = var->xres_virtual * stride;
393f7018c21STomi Valkeinen win.pitch[1] = win.pitch[2] =
394f7018c21STomi Valkeinen (stride == 1) ? (var->xres_virtual >> 1) : 0;
395f7018c21STomi Valkeinen mmp_overlay_set_win(fbi->overlay, &win);
396f7018c21STomi Valkeinen }
397f7018c21STomi Valkeinen
mmpfb_set_par(struct fb_info * info)398f7018c21STomi Valkeinen static int mmpfb_set_par(struct fb_info *info)
399f7018c21STomi Valkeinen {
400f7018c21STomi Valkeinen struct mmpfb_info *fbi = info->par;
401f7018c21STomi Valkeinen struct fb_var_screeninfo *var = &info->var;
402f7018c21STomi Valkeinen struct mmp_addr addr;
403f7018c21STomi Valkeinen struct mmp_mode mode;
404f7018c21STomi Valkeinen int ret;
405f7018c21STomi Valkeinen
406f7018c21STomi Valkeinen ret = var_update(info);
407f7018c21STomi Valkeinen if (ret != 0)
408f7018c21STomi Valkeinen return ret;
409f7018c21STomi Valkeinen
410f7018c21STomi Valkeinen /* set window/path according to new videomode */
411f7018c21STomi Valkeinen fbmode_to_mmpmode(&mode, &fbi->mode, fbi->output_fmt);
412f7018c21STomi Valkeinen mmp_path_set_mode(fbi->path, &mode);
413f7018c21STomi Valkeinen
414f7018c21STomi Valkeinen /* set window related info */
415f7018c21STomi Valkeinen mmpfb_set_win(info);
416f7018c21STomi Valkeinen
417f7018c21STomi Valkeinen /* set address always */
418f7018c21STomi Valkeinen memset(&addr, 0, sizeof(addr));
419f7018c21STomi Valkeinen addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
420f7018c21STomi Valkeinen * var->bits_per_pixel / 8 + fbi->fb_start_dma;
421f7018c21STomi Valkeinen mmp_overlay_set_addr(fbi->overlay, &addr);
422f7018c21STomi Valkeinen
423f7018c21STomi Valkeinen return 0;
424f7018c21STomi Valkeinen }
425f7018c21STomi Valkeinen
mmpfb_power(struct mmpfb_info * fbi,int power)426f7018c21STomi Valkeinen static void mmpfb_power(struct mmpfb_info *fbi, int power)
427f7018c21STomi Valkeinen {
428f7018c21STomi Valkeinen struct mmp_addr addr;
429f7018c21STomi Valkeinen struct fb_var_screeninfo *var = &fbi->fb_info->var;
430f7018c21STomi Valkeinen
431f7018c21STomi Valkeinen /* for power on, always set address/window again */
432f7018c21STomi Valkeinen if (power) {
433f7018c21STomi Valkeinen /* set window related info */
434f7018c21STomi Valkeinen mmpfb_set_win(fbi->fb_info);
435f7018c21STomi Valkeinen
436f7018c21STomi Valkeinen /* set address always */
437f7018c21STomi Valkeinen memset(&addr, 0, sizeof(addr));
438f7018c21STomi Valkeinen addr.phys[0] = fbi->fb_start_dma +
439f7018c21STomi Valkeinen (var->yoffset * var->xres_virtual + var->xoffset)
440f7018c21STomi Valkeinen * var->bits_per_pixel / 8;
441f7018c21STomi Valkeinen mmp_overlay_set_addr(fbi->overlay, &addr);
442f7018c21STomi Valkeinen }
443f7018c21STomi Valkeinen mmp_overlay_set_onoff(fbi->overlay, power);
444f7018c21STomi Valkeinen }
445f7018c21STomi Valkeinen
mmpfb_blank(int blank,struct fb_info * info)446f7018c21STomi Valkeinen static int mmpfb_blank(int blank, struct fb_info *info)
447f7018c21STomi Valkeinen {
448f7018c21STomi Valkeinen struct mmpfb_info *fbi = info->par;
449f7018c21STomi Valkeinen
450f7018c21STomi Valkeinen mmpfb_power(fbi, (blank == FB_BLANK_UNBLANK));
451f7018c21STomi Valkeinen
452f7018c21STomi Valkeinen return 0;
453f7018c21STomi Valkeinen }
454f7018c21STomi Valkeinen
4558a48ac33SJani Nikula static const struct fb_ops mmpfb_ops = {
456f7018c21STomi Valkeinen .owner = THIS_MODULE,
457*42f02c5bSThomas Zimmermann FB_DEFAULT_IOMEM_OPS,
458f7018c21STomi Valkeinen .fb_blank = mmpfb_blank,
459f7018c21STomi Valkeinen .fb_check_var = mmpfb_check_var,
460f7018c21STomi Valkeinen .fb_set_par = mmpfb_set_par,
461f7018c21STomi Valkeinen .fb_setcolreg = mmpfb_setcolreg,
462f7018c21STomi Valkeinen .fb_pan_display = mmpfb_pan_display,
463f7018c21STomi Valkeinen };
464f7018c21STomi Valkeinen
modes_setup(struct mmpfb_info * fbi)465f7018c21STomi Valkeinen static int modes_setup(struct mmpfb_info *fbi)
466f7018c21STomi Valkeinen {
467f7018c21STomi Valkeinen struct fb_videomode *videomodes;
468f7018c21STomi Valkeinen struct mmp_mode *mmp_modes;
469f7018c21STomi Valkeinen struct fb_info *info = fbi->fb_info;
470f7018c21STomi Valkeinen int videomode_num, i;
471f7018c21STomi Valkeinen
472f7018c21STomi Valkeinen /* get videomodes from path */
473f7018c21STomi Valkeinen videomode_num = mmp_path_get_modelist(fbi->path, &mmp_modes);
474f7018c21STomi Valkeinen if (!videomode_num) {
475f7018c21STomi Valkeinen dev_warn(fbi->dev, "can't get videomode num\n");
476f7018c21STomi Valkeinen return 0;
477f7018c21STomi Valkeinen }
478f7018c21STomi Valkeinen /* put videomode list to info structure */
4796396bb22SKees Cook videomodes = kcalloc(videomode_num, sizeof(struct fb_videomode),
480f7018c21STomi Valkeinen GFP_KERNEL);
481e0e894f5SMarkus Elfring if (!videomodes)
482f7018c21STomi Valkeinen return -ENOMEM;
483e0e894f5SMarkus Elfring
484f7018c21STomi Valkeinen for (i = 0; i < videomode_num; i++)
485f7018c21STomi Valkeinen mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]);
486f7018c21STomi Valkeinen fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist);
487f7018c21STomi Valkeinen
488f7018c21STomi Valkeinen /* set videomode[0] as default mode */
489f7018c21STomi Valkeinen memcpy(&fbi->mode, &videomodes[0], sizeof(struct fb_videomode));
490f7018c21STomi Valkeinen fbi->output_fmt = mmp_modes[0].pix_fmt_out;
491f7018c21STomi Valkeinen fb_videomode_to_var(&info->var, &fbi->mode);
492f7018c21STomi Valkeinen mmp_path_set_mode(fbi->path, &mmp_modes[0]);
493f7018c21STomi Valkeinen
494f7018c21STomi Valkeinen kfree(videomodes);
495f7018c21STomi Valkeinen return videomode_num;
496f7018c21STomi Valkeinen }
497f7018c21STomi Valkeinen
fb_info_setup(struct fb_info * info,struct mmpfb_info * fbi)498f7018c21STomi Valkeinen static int fb_info_setup(struct fb_info *info,
499f7018c21STomi Valkeinen struct mmpfb_info *fbi)
500f7018c21STomi Valkeinen {
501f7018c21STomi Valkeinen int ret = 0;
502f7018c21STomi Valkeinen /* Initialise static fb parameters.*/
503b3e148d7SThomas Zimmermann info->flags = FBINFO_PARTIAL_PAN_OK |
504f7018c21STomi Valkeinen FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
505f7018c21STomi Valkeinen info->node = -1;
506f7018c21STomi Valkeinen strcpy(info->fix.id, fbi->name);
507f7018c21STomi Valkeinen info->fix.type = FB_TYPE_PACKED_PIXELS;
508f7018c21STomi Valkeinen info->fix.type_aux = 0;
509f7018c21STomi Valkeinen info->fix.xpanstep = 0;
510f7018c21STomi Valkeinen info->fix.ypanstep = info->var.yres;
511f7018c21STomi Valkeinen info->fix.ywrapstep = 0;
512f7018c21STomi Valkeinen info->fix.accel = FB_ACCEL_NONE;
513f7018c21STomi Valkeinen info->fix.smem_start = fbi->fb_start_dma;
514f7018c21STomi Valkeinen info->fix.smem_len = fbi->fb_size;
515f7018c21STomi Valkeinen info->fix.visual = (fbi->pix_fmt == PIXFMT_PSEUDOCOLOR) ?
516f7018c21STomi Valkeinen FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
517f7018c21STomi Valkeinen info->fix.line_length = info->var.xres_virtual *
518f7018c21STomi Valkeinen info->var.bits_per_pixel / 8;
519f7018c21STomi Valkeinen info->fbops = &mmpfb_ops;
520f7018c21STomi Valkeinen info->pseudo_palette = fbi->pseudo_palette;
521e41f6b17SBartlomiej Zolnierkiewicz info->screen_buffer = fbi->fb_start;
522f7018c21STomi Valkeinen info->screen_size = fbi->fb_size;
523f7018c21STomi Valkeinen
524f7018c21STomi Valkeinen /* For FB framework: Allocate color map and Register framebuffer*/
525f7018c21STomi Valkeinen if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
526f7018c21STomi Valkeinen ret = -ENOMEM;
527f7018c21STomi Valkeinen
528f7018c21STomi Valkeinen return ret;
529f7018c21STomi Valkeinen }
530f7018c21STomi Valkeinen
fb_info_clear(struct fb_info * info)531f7018c21STomi Valkeinen static void fb_info_clear(struct fb_info *info)
532f7018c21STomi Valkeinen {
533f7018c21STomi Valkeinen fb_dealloc_cmap(&info->cmap);
534f7018c21STomi Valkeinen }
535f7018c21STomi Valkeinen
mmpfb_probe(struct platform_device * pdev)536f7018c21STomi Valkeinen static int mmpfb_probe(struct platform_device *pdev)
537f7018c21STomi Valkeinen {
538f7018c21STomi Valkeinen struct mmp_buffer_driver_mach_info *mi;
539a513ad2dSDan Carpenter struct fb_info *info;
540a513ad2dSDan Carpenter struct mmpfb_info *fbi;
541f7018c21STomi Valkeinen int ret, modes_num;
542f7018c21STomi Valkeinen
543f7018c21STomi Valkeinen mi = pdev->dev.platform_data;
544f7018c21STomi Valkeinen if (mi == NULL) {
545f7018c21STomi Valkeinen dev_err(&pdev->dev, "no platform data defined\n");
546f7018c21STomi Valkeinen return -EINVAL;
547f7018c21STomi Valkeinen }
548f7018c21STomi Valkeinen
549f7018c21STomi Valkeinen /* initialize fb */
550f7018c21STomi Valkeinen info = framebuffer_alloc(sizeof(struct mmpfb_info), &pdev->dev);
551f7018c21STomi Valkeinen if (info == NULL)
552f7018c21STomi Valkeinen return -ENOMEM;
553f7018c21STomi Valkeinen fbi = info->par;
554f7018c21STomi Valkeinen
555f7018c21STomi Valkeinen /* init fb */
556f7018c21STomi Valkeinen fbi->fb_info = info;
557f7018c21STomi Valkeinen platform_set_drvdata(pdev, fbi);
558f7018c21STomi Valkeinen fbi->dev = &pdev->dev;
559f7018c21STomi Valkeinen fbi->name = mi->name;
560f7018c21STomi Valkeinen fbi->pix_fmt = mi->default_pixfmt;
561f7018c21STomi Valkeinen pixfmt_to_var(&info->var, fbi->pix_fmt);
562f7018c21STomi Valkeinen mutex_init(&fbi->access_ok);
563f7018c21STomi Valkeinen
564f7018c21STomi Valkeinen /* get display path by name */
565f7018c21STomi Valkeinen fbi->path = mmp_get_path(mi->path_name);
566f7018c21STomi Valkeinen if (!fbi->path) {
567f7018c21STomi Valkeinen dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name);
568f7018c21STomi Valkeinen ret = -EINVAL;
569f7018c21STomi Valkeinen goto failed_destroy_mutex;
570f7018c21STomi Valkeinen }
571f7018c21STomi Valkeinen
572f7018c21STomi Valkeinen dev_info(fbi->dev, "path %s get\n", fbi->path->name);
573f7018c21STomi Valkeinen
574f7018c21STomi Valkeinen /* get overlay */
575f7018c21STomi Valkeinen fbi->overlay = mmp_path_get_overlay(fbi->path, mi->overlay_id);
576f7018c21STomi Valkeinen if (!fbi->overlay) {
577f7018c21STomi Valkeinen ret = -EINVAL;
578f7018c21STomi Valkeinen goto failed_destroy_mutex;
579f7018c21STomi Valkeinen }
580f7018c21STomi Valkeinen /* set fetch used */
581f7018c21STomi Valkeinen mmp_overlay_set_fetch(fbi->overlay, mi->dmafetch_id);
582f7018c21STomi Valkeinen
583f7018c21STomi Valkeinen modes_num = modes_setup(fbi);
584f7018c21STomi Valkeinen if (modes_num < 0) {
585f7018c21STomi Valkeinen ret = modes_num;
586f7018c21STomi Valkeinen goto failed_destroy_mutex;
587f7018c21STomi Valkeinen }
588f7018c21STomi Valkeinen
589f7018c21STomi Valkeinen /*
590f7018c21STomi Valkeinen * if get modes success, means not hotplug panels, use caculated buffer
591f7018c21STomi Valkeinen * or use default size
592f7018c21STomi Valkeinen */
593f7018c21STomi Valkeinen if (modes_num > 0) {
594f7018c21STomi Valkeinen /* fix to 2* yres */
595f7018c21STomi Valkeinen info->var.yres_virtual = info->var.yres * 2;
596f7018c21STomi Valkeinen
597f7018c21STomi Valkeinen /* Allocate framebuffer memory: size = modes xy *4 */
598f7018c21STomi Valkeinen fbi->fb_size = info->var.xres_virtual * info->var.yres_virtual
599f7018c21STomi Valkeinen * info->var.bits_per_pixel / 8;
600f7018c21STomi Valkeinen } else {
601f7018c21STomi Valkeinen fbi->fb_size = MMPFB_DEFAULT_SIZE;
602f7018c21STomi Valkeinen }
603f7018c21STomi Valkeinen
604f7018c21STomi Valkeinen fbi->fb_start = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size),
605f7018c21STomi Valkeinen &fbi->fb_start_dma, GFP_KERNEL);
606f7018c21STomi Valkeinen if (fbi->fb_start == NULL) {
607f7018c21STomi Valkeinen dev_err(&pdev->dev, "can't alloc framebuffer\n");
608f7018c21STomi Valkeinen ret = -ENOMEM;
609f7018c21STomi Valkeinen goto failed_destroy_mutex;
610f7018c21STomi Valkeinen }
611f7018c21STomi Valkeinen dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024);
612f7018c21STomi Valkeinen
613f7018c21STomi Valkeinen /* fb power on */
614f7018c21STomi Valkeinen if (modes_num > 0)
615f7018c21STomi Valkeinen mmpfb_power(fbi, 1);
616f7018c21STomi Valkeinen
617f7018c21STomi Valkeinen ret = fb_info_setup(info, fbi);
618f7018c21STomi Valkeinen if (ret < 0)
619f7018c21STomi Valkeinen goto failed_free_buff;
620f7018c21STomi Valkeinen
621f7018c21STomi Valkeinen ret = register_framebuffer(info);
622f7018c21STomi Valkeinen if (ret < 0) {
623f7018c21STomi Valkeinen dev_err(&pdev->dev, "Failed to register fb: %d\n", ret);
624f7018c21STomi Valkeinen ret = -ENXIO;
625f7018c21STomi Valkeinen goto failed_clear_info;
626f7018c21STomi Valkeinen }
627f7018c21STomi Valkeinen
628f7018c21STomi Valkeinen dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n",
629f7018c21STomi Valkeinen info->node, info->fix.id);
630f7018c21STomi Valkeinen
631f7018c21STomi Valkeinen #ifdef CONFIG_LOGO
632f7018c21STomi Valkeinen if (fbi->fb_start) {
633f7018c21STomi Valkeinen fb_prepare_logo(info, 0);
634f7018c21STomi Valkeinen fb_show_logo(info, 0);
635f7018c21STomi Valkeinen }
636f7018c21STomi Valkeinen #endif
637f7018c21STomi Valkeinen
638f7018c21STomi Valkeinen return 0;
639f7018c21STomi Valkeinen
640f7018c21STomi Valkeinen failed_clear_info:
641f7018c21STomi Valkeinen fb_info_clear(info);
642f7018c21STomi Valkeinen failed_free_buff:
643f7018c21STomi Valkeinen dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), fbi->fb_start,
644f7018c21STomi Valkeinen fbi->fb_start_dma);
645f7018c21STomi Valkeinen failed_destroy_mutex:
646f7018c21STomi Valkeinen mutex_destroy(&fbi->access_ok);
647f7018c21STomi Valkeinen dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n");
648f7018c21STomi Valkeinen
649f7018c21STomi Valkeinen framebuffer_release(info);
650f7018c21STomi Valkeinen
651f7018c21STomi Valkeinen return ret;
652f7018c21STomi Valkeinen }
653f7018c21STomi Valkeinen
654f7018c21STomi Valkeinen static struct platform_driver mmpfb_driver = {
655f7018c21STomi Valkeinen .driver = {
656f7018c21STomi Valkeinen .name = "mmp-fb",
657f7018c21STomi Valkeinen },
658f7018c21STomi Valkeinen .probe = mmpfb_probe,
659f7018c21STomi Valkeinen };
660f7018c21STomi Valkeinen
mmpfb_init(void)661f7018c21STomi Valkeinen static int mmpfb_init(void)
662f7018c21STomi Valkeinen {
663f7018c21STomi Valkeinen return platform_driver_register(&mmpfb_driver);
664f7018c21STomi Valkeinen }
665f7018c21STomi Valkeinen module_init(mmpfb_init);
666f7018c21STomi Valkeinen
667f7018c21STomi Valkeinen MODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>");
668f7018c21STomi Valkeinen MODULE_DESCRIPTION("Framebuffer driver for Marvell displays");
669f7018c21STomi Valkeinen MODULE_LICENSE("GPL");
670