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