1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Frame buffer driver for the Carmine GPU.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * The driver configures the GPU as follows
6*4882a593Smuzhiyun * - FB0 is display 0 with unique memory area
7*4882a593Smuzhiyun * - FB1 is display 1 with unique memory area
8*4882a593Smuzhiyun * - both display use 32 bit colors
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun #include <linux/delay.h>
11*4882a593Smuzhiyun #include <linux/errno.h>
12*4882a593Smuzhiyun #include <linux/fb.h>
13*4882a593Smuzhiyun #include <linux/interrupt.h>
14*4882a593Smuzhiyun #include <linux/pci.h>
15*4882a593Smuzhiyun #include <linux/slab.h>
16*4882a593Smuzhiyun #include <linux/module.h>
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun #include "carminefb.h"
19*4882a593Smuzhiyun #include "carminefb_regs.h"
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun #if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)
22*4882a593Smuzhiyun #error "The endianness of the target host has not been defined."
23*4882a593Smuzhiyun #endif
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun /*
26*4882a593Smuzhiyun * The initial video mode can be supplied via two different ways:
27*4882a593Smuzhiyun * - as a string that is passed to fb_find_mode() (module option fb_mode_str)
28*4882a593Smuzhiyun * - as an integer that picks the video mode from carmine_modedb[] (module
29*4882a593Smuzhiyun * option fb_mode)
30*4882a593Smuzhiyun *
31*4882a593Smuzhiyun * If nothing is used than the initial video mode will be the
32*4882a593Smuzhiyun * CARMINEFB_DEFAULT_VIDEO_MODE member of the carmine_modedb[].
33*4882a593Smuzhiyun */
34*4882a593Smuzhiyun #define CARMINEFB_DEFAULT_VIDEO_MODE 1
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun static unsigned int fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE;
37*4882a593Smuzhiyun module_param(fb_mode, uint, 0444);
38*4882a593Smuzhiyun MODULE_PARM_DESC(fb_mode, "Initial video mode as integer.");
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun static char *fb_mode_str;
41*4882a593Smuzhiyun module_param(fb_mode_str, charp, 0444);
42*4882a593Smuzhiyun MODULE_PARM_DESC(fb_mode_str, "Initial video mode in characters.");
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun /*
45*4882a593Smuzhiyun * Carminefb displays:
46*4882a593Smuzhiyun * 0b000 None
47*4882a593Smuzhiyun * 0b001 Display 0
48*4882a593Smuzhiyun * 0b010 Display 1
49*4882a593Smuzhiyun */
50*4882a593Smuzhiyun static int fb_displays = CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1;
51*4882a593Smuzhiyun module_param(fb_displays, int, 0444);
52*4882a593Smuzhiyun MODULE_PARM_DESC(fb_displays, "Bit mode, which displays are used");
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun struct carmine_hw {
55*4882a593Smuzhiyun void __iomem *v_regs;
56*4882a593Smuzhiyun void __iomem *screen_mem;
57*4882a593Smuzhiyun struct fb_info *fb[MAX_DISPLAY];
58*4882a593Smuzhiyun };
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun struct carmine_resolution {
61*4882a593Smuzhiyun u32 htp;
62*4882a593Smuzhiyun u32 hsp;
63*4882a593Smuzhiyun u32 hsw;
64*4882a593Smuzhiyun u32 hdp;
65*4882a593Smuzhiyun u32 vtr;
66*4882a593Smuzhiyun u32 vsp;
67*4882a593Smuzhiyun u32 vsw;
68*4882a593Smuzhiyun u32 vdp;
69*4882a593Smuzhiyun u32 disp_mode;
70*4882a593Smuzhiyun };
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun struct carmine_fb {
73*4882a593Smuzhiyun void __iomem *display_reg;
74*4882a593Smuzhiyun void __iomem *screen_base;
75*4882a593Smuzhiyun u32 smem_offset;
76*4882a593Smuzhiyun u32 cur_mode;
77*4882a593Smuzhiyun u32 new_mode;
78*4882a593Smuzhiyun struct carmine_resolution *res;
79*4882a593Smuzhiyun u32 pseudo_palette[16];
80*4882a593Smuzhiyun };
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun static struct fb_fix_screeninfo carminefb_fix = {
83*4882a593Smuzhiyun .id = "Carmine",
84*4882a593Smuzhiyun .type = FB_TYPE_PACKED_PIXELS,
85*4882a593Smuzhiyun .visual = FB_VISUAL_TRUECOLOR,
86*4882a593Smuzhiyun .accel = FB_ACCEL_NONE,
87*4882a593Smuzhiyun };
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun static const struct fb_videomode carmine_modedb[] = {
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun .name = "640x480",
92*4882a593Smuzhiyun .xres = 640,
93*4882a593Smuzhiyun .yres = 480,
94*4882a593Smuzhiyun }, {
95*4882a593Smuzhiyun .name = "800x600",
96*4882a593Smuzhiyun .xres = 800,
97*4882a593Smuzhiyun .yres = 600,
98*4882a593Smuzhiyun },
99*4882a593Smuzhiyun };
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun static struct carmine_resolution car_modes[] = {
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun /* 640x480 */
104*4882a593Smuzhiyun .htp = 800,
105*4882a593Smuzhiyun .hsp = 672,
106*4882a593Smuzhiyun .hsw = 96,
107*4882a593Smuzhiyun .hdp = 640,
108*4882a593Smuzhiyun .vtr = 525,
109*4882a593Smuzhiyun .vsp = 490,
110*4882a593Smuzhiyun .vsw = 2,
111*4882a593Smuzhiyun .vdp = 480,
112*4882a593Smuzhiyun .disp_mode = 0x1400,
113*4882a593Smuzhiyun },
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun /* 800x600 */
116*4882a593Smuzhiyun .htp = 1060,
117*4882a593Smuzhiyun .hsp = 864,
118*4882a593Smuzhiyun .hsw = 72,
119*4882a593Smuzhiyun .hdp = 800,
120*4882a593Smuzhiyun .vtr = 628,
121*4882a593Smuzhiyun .vsp = 601,
122*4882a593Smuzhiyun .vsw = 2,
123*4882a593Smuzhiyun .vdp = 600,
124*4882a593Smuzhiyun .disp_mode = 0x0d00,
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun };
127*4882a593Smuzhiyun
carmine_find_mode(const struct fb_var_screeninfo * var)128*4882a593Smuzhiyun static int carmine_find_mode(const struct fb_var_screeninfo *var)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun int i;
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(car_modes); i++)
133*4882a593Smuzhiyun if (car_modes[i].hdp == var->xres &&
134*4882a593Smuzhiyun car_modes[i].vdp == var->yres)
135*4882a593Smuzhiyun return i;
136*4882a593Smuzhiyun return -EINVAL;
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun
c_set_disp_reg(const struct carmine_fb * par,u32 offset,u32 val)139*4882a593Smuzhiyun static void c_set_disp_reg(const struct carmine_fb *par,
140*4882a593Smuzhiyun u32 offset, u32 val)
141*4882a593Smuzhiyun {
142*4882a593Smuzhiyun writel(val, par->display_reg + offset);
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun
c_get_disp_reg(const struct carmine_fb * par,u32 offset)145*4882a593Smuzhiyun static u32 c_get_disp_reg(const struct carmine_fb *par,
146*4882a593Smuzhiyun u32 offset)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun return readl(par->display_reg + offset);
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
c_set_hw_reg(const struct carmine_hw * hw,u32 offset,u32 val)151*4882a593Smuzhiyun static void c_set_hw_reg(const struct carmine_hw *hw,
152*4882a593Smuzhiyun u32 offset, u32 val)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun writel(val, hw->v_regs + offset);
155*4882a593Smuzhiyun }
156*4882a593Smuzhiyun
c_get_hw_reg(const struct carmine_hw * hw,u32 offset)157*4882a593Smuzhiyun static u32 c_get_hw_reg(const struct carmine_hw *hw,
158*4882a593Smuzhiyun u32 offset)
159*4882a593Smuzhiyun {
160*4882a593Smuzhiyun return readl(hw->v_regs + offset);
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
carmine_setcolreg(unsigned regno,unsigned red,unsigned green,unsigned blue,unsigned transp,struct fb_info * info)163*4882a593Smuzhiyun static int carmine_setcolreg(unsigned regno, unsigned red, unsigned green,
164*4882a593Smuzhiyun unsigned blue, unsigned transp, struct fb_info *info)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun if (regno >= 16)
167*4882a593Smuzhiyun return 1;
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun red >>= 8;
170*4882a593Smuzhiyun green >>= 8;
171*4882a593Smuzhiyun blue >>= 8;
172*4882a593Smuzhiyun transp >>= 8;
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun ((__be32 *)info->pseudo_palette)[regno] = cpu_to_be32(transp << 24 |
175*4882a593Smuzhiyun red << 0 | green << 8 | blue << 16);
176*4882a593Smuzhiyun return 0;
177*4882a593Smuzhiyun }
178*4882a593Smuzhiyun
carmine_check_var(struct fb_var_screeninfo * var,struct fb_info * info)179*4882a593Smuzhiyun static int carmine_check_var(struct fb_var_screeninfo *var,
180*4882a593Smuzhiyun struct fb_info *info)
181*4882a593Smuzhiyun {
182*4882a593Smuzhiyun int ret;
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun ret = carmine_find_mode(var);
185*4882a593Smuzhiyun if (ret < 0)
186*4882a593Smuzhiyun return ret;
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun if (var->grayscale || var->rotate || var->nonstd)
189*4882a593Smuzhiyun return -EINVAL;
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun var->xres_virtual = var->xres;
192*4882a593Smuzhiyun var->yres_virtual = var->yres;
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun var->bits_per_pixel = 32;
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun #ifdef __BIG_ENDIAN
197*4882a593Smuzhiyun var->transp.offset = 24;
198*4882a593Smuzhiyun var->red.offset = 0;
199*4882a593Smuzhiyun var->green.offset = 8;
200*4882a593Smuzhiyun var->blue.offset = 16;
201*4882a593Smuzhiyun #else
202*4882a593Smuzhiyun var->transp.offset = 24;
203*4882a593Smuzhiyun var->red.offset = 16;
204*4882a593Smuzhiyun var->green.offset = 8;
205*4882a593Smuzhiyun var->blue.offset = 0;
206*4882a593Smuzhiyun #endif
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun var->red.length = 8;
209*4882a593Smuzhiyun var->green.length = 8;
210*4882a593Smuzhiyun var->blue.length = 8;
211*4882a593Smuzhiyun var->transp.length = 8;
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun var->red.msb_right = 0;
214*4882a593Smuzhiyun var->green.msb_right = 0;
215*4882a593Smuzhiyun var->blue.msb_right = 0;
216*4882a593Smuzhiyun var->transp.msb_right = 0;
217*4882a593Smuzhiyun return 0;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun
carmine_init_display_param(struct carmine_fb * par)220*4882a593Smuzhiyun static void carmine_init_display_param(struct carmine_fb *par)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun u32 width;
223*4882a593Smuzhiyun u32 height;
224*4882a593Smuzhiyun u32 param;
225*4882a593Smuzhiyun u32 window_size;
226*4882a593Smuzhiyun u32 soffset = par->smem_offset;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_C_TRANS, 0);
229*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_MLMR_TRANS, 0);
230*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_CURSOR_MODE,
231*4882a593Smuzhiyun CARMINE_CURSOR0_PRIORITY_MASK |
232*4882a593Smuzhiyun CARMINE_CURSOR1_PRIORITY_MASK |
233*4882a593Smuzhiyun CARMINE_CURSOR_CUTZ_MASK);
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun /* Set default cursor position */
236*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_CUR1_POS, 0 << 16 | 0);
237*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_CUR2_POS, 0 << 16 | 0);
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun /* Set default display mode */
240*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L0_EXT_MODE, CARMINE_WINDOW_MODE |
241*4882a593Smuzhiyun CARMINE_EXT_CMODE_DIRECT24_RGBA);
242*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L1_EXT_MODE,
243*4882a593Smuzhiyun CARMINE_EXT_CMODE_DIRECT24_RGBA);
244*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L2_EXT_MODE, CARMINE_EXTEND_MODE |
245*4882a593Smuzhiyun CARMINE_EXT_CMODE_DIRECT24_RGBA);
246*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L3_EXT_MODE, CARMINE_EXTEND_MODE |
247*4882a593Smuzhiyun CARMINE_EXT_CMODE_DIRECT24_RGBA);
248*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L4_EXT_MODE, CARMINE_EXTEND_MODE |
249*4882a593Smuzhiyun CARMINE_EXT_CMODE_DIRECT24_RGBA);
250*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L5_EXT_MODE, CARMINE_EXTEND_MODE |
251*4882a593Smuzhiyun CARMINE_EXT_CMODE_DIRECT24_RGBA);
252*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L6_EXT_MODE, CARMINE_EXTEND_MODE |
253*4882a593Smuzhiyun CARMINE_EXT_CMODE_DIRECT24_RGBA);
254*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L7_EXT_MODE, CARMINE_EXTEND_MODE |
255*4882a593Smuzhiyun CARMINE_EXT_CMODE_DIRECT24_RGBA);
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun /* Set default frame size to layer mode register */
258*4882a593Smuzhiyun width = par->res->hdp * 4 / CARMINE_DISP_WIDTH_UNIT;
259*4882a593Smuzhiyun width = width << CARMINE_DISP_WIDTH_SHIFT;
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun height = par->res->vdp - 1;
262*4882a593Smuzhiyun param = width | height;
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L0_MODE_W_H, param);
265*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIDTH, width);
266*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L2_MODE_W_H, param);
267*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L3_MODE_W_H, param);
268*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L4_MODE_W_H, param);
269*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L5_MODE_W_H, param);
270*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L6_MODE_W_H, param);
271*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L7_MODE_W_H, param);
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun /* Set default pos and size */
274*4882a593Smuzhiyun window_size = (par->res->vdp - 1) << CARMINE_DISP_WIN_H_SHIFT;
275*4882a593Smuzhiyun window_size |= par->res->hdp;
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_POS, 0);
278*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_SIZE, window_size);
279*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_POS, 0);
280*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_SIZE, window_size);
281*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_POS, 0);
282*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_SIZE, window_size);
283*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_POS, 0);
284*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_SIZE, window_size);
285*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_POS, 0);
286*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_SIZE, window_size);
287*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_POS, 0);
288*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_SIZE, window_size);
289*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_POS, 0);
290*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_SIZE, window_size);
291*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_POS, 0);
292*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_SIZE, window_size);
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun /* Set default origin address */
295*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L0_ORG_ADR, soffset);
296*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L1_ORG_ADR, soffset);
297*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L2_ORG_ADR1, soffset);
298*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L3_ORG_ADR1, soffset);
299*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L4_ORG_ADR1, soffset);
300*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L5_ORG_ADR1, soffset);
301*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L6_ORG_ADR1, soffset);
302*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L7_ORG_ADR1, soffset);
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun /* Set default display address */
305*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_ADR, soffset);
306*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_ADR1, soffset);
307*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_ADR1, soffset);
308*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_ADR1, soffset);
309*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_ADR1, soffset);
310*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_ADR0, soffset);
311*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_ADR0, soffset);
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun /* Set default display position */
314*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_POS, 0);
315*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_POS, 0);
316*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_POS, 0);
317*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_POS, 0);
318*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_POS, 0);
319*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_POS, 0);
320*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_POS, 0);
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun /* Set default blend mode */
323*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L0, 0);
324*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L1, 0);
325*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L2, 0);
326*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L3, 0);
327*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L4, 0);
328*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L5, 0);
329*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L6, 0);
330*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L7, 0);
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun /* default transparency mode */
333*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L0_TRANS, 0);
334*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L1_TRANS, 0);
335*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L2_TRANS, 0);
336*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L3_TRANS, 0);
337*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L4_TRANS, 0);
338*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L5_TRANS, 0);
339*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L6_TRANS, 0);
340*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L7_TRANS, 0);
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun /* Set default read skip parameter */
343*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L0RM, 0);
344*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L2RM, 0);
345*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L3RM, 0);
346*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L4RM, 0);
347*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L5RM, 0);
348*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L6RM, 0);
349*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L7RM, 0);
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L0PX, 0);
352*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L2PX, 0);
353*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L3PX, 0);
354*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L4PX, 0);
355*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L5PX, 0);
356*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L6PX, 0);
357*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L7PX, 0);
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L0PY, 0);
360*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L2PY, 0);
361*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L3PY, 0);
362*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L4PY, 0);
363*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L5PY, 0);
364*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L6PY, 0);
365*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_L7PY, 0);
366*4882a593Smuzhiyun }
367*4882a593Smuzhiyun
set_display_parameters(struct carmine_fb * par)368*4882a593Smuzhiyun static void set_display_parameters(struct carmine_fb *par)
369*4882a593Smuzhiyun {
370*4882a593Smuzhiyun u32 mode;
371*4882a593Smuzhiyun u32 hdp, vdp, htp, hsp, hsw, vtr, vsp, vsw;
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun /*
374*4882a593Smuzhiyun * display timing. Parameters are decreased by one because hardware
375*4882a593Smuzhiyun * spec is 0 to (n - 1)
376*4882a593Smuzhiyun * */
377*4882a593Smuzhiyun hdp = par->res->hdp - 1;
378*4882a593Smuzhiyun vdp = par->res->vdp - 1;
379*4882a593Smuzhiyun htp = par->res->htp - 1;
380*4882a593Smuzhiyun hsp = par->res->hsp - 1;
381*4882a593Smuzhiyun hsw = par->res->hsw - 1;
382*4882a593Smuzhiyun vtr = par->res->vtr - 1;
383*4882a593Smuzhiyun vsp = par->res->vsp - 1;
384*4882a593Smuzhiyun vsw = par->res->vsw - 1;
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_H_TOTAL,
387*4882a593Smuzhiyun htp << CARMINE_DISP_HTP_SHIFT);
388*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_H_PERIOD,
389*4882a593Smuzhiyun (hdp << CARMINE_DISP_HDB_SHIFT) | hdp);
390*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_V_H_W_H_POS,
391*4882a593Smuzhiyun (vsw << CARMINE_DISP_VSW_SHIFT) |
392*4882a593Smuzhiyun (hsw << CARMINE_DISP_HSW_SHIFT) |
393*4882a593Smuzhiyun (hsp));
394*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_V_TOTAL,
395*4882a593Smuzhiyun vtr << CARMINE_DISP_VTR_SHIFT);
396*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_V_PERIOD_POS,
397*4882a593Smuzhiyun (vdp << CARMINE_DISP_VDP_SHIFT) | vsp);
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun /* clock */
400*4882a593Smuzhiyun mode = c_get_disp_reg(par, CARMINE_DISP_REG_DCM1);
401*4882a593Smuzhiyun mode = (mode & ~CARMINE_DISP_DCM_MASK) |
402*4882a593Smuzhiyun (par->res->disp_mode & CARMINE_DISP_DCM_MASK);
403*4882a593Smuzhiyun /* enable video output and layer 0 */
404*4882a593Smuzhiyun mode |= CARMINE_DEN | CARMINE_L0E;
405*4882a593Smuzhiyun c_set_disp_reg(par, CARMINE_DISP_REG_DCM1, mode);
406*4882a593Smuzhiyun }
407*4882a593Smuzhiyun
carmine_set_par(struct fb_info * info)408*4882a593Smuzhiyun static int carmine_set_par(struct fb_info *info)
409*4882a593Smuzhiyun {
410*4882a593Smuzhiyun struct carmine_fb *par = info->par;
411*4882a593Smuzhiyun int ret;
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun ret = carmine_find_mode(&info->var);
414*4882a593Smuzhiyun if (ret < 0)
415*4882a593Smuzhiyun return ret;
416*4882a593Smuzhiyun
417*4882a593Smuzhiyun par->new_mode = ret;
418*4882a593Smuzhiyun if (par->cur_mode != par->new_mode) {
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun par->cur_mode = par->new_mode;
421*4882a593Smuzhiyun par->res = &car_modes[par->new_mode];
422*4882a593Smuzhiyun
423*4882a593Smuzhiyun carmine_init_display_param(par);
424*4882a593Smuzhiyun set_display_parameters(par);
425*4882a593Smuzhiyun }
426*4882a593Smuzhiyun
427*4882a593Smuzhiyun info->fix.line_length = info->var.xres * info->var.bits_per_pixel / 8;
428*4882a593Smuzhiyun return 0;
429*4882a593Smuzhiyun }
430*4882a593Smuzhiyun
init_hardware(struct carmine_hw * hw)431*4882a593Smuzhiyun static int init_hardware(struct carmine_hw *hw)
432*4882a593Smuzhiyun {
433*4882a593Smuzhiyun u32 flags;
434*4882a593Smuzhiyun u32 loops;
435*4882a593Smuzhiyun u32 ret;
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun /* Initialize Carmine */
438*4882a593Smuzhiyun /* Sets internal clock */
439*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE,
440*4882a593Smuzhiyun CARMINE_DFLT_IP_CLOCK_ENABLE);
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun /* Video signal output is turned off */
443*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0);
444*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0);
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun /* Software reset */
447*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 1);
448*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 0);
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun /* I/O mode settings */
451*4882a593Smuzhiyun flags = CARMINE_DFLT_IP_DCTL_IO_CONT1 << 16 |
452*4882a593Smuzhiyun CARMINE_DFLT_IP_DCTL_IO_CONT0;
453*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_IOCONT1_IOCONT0,
454*4882a593Smuzhiyun flags);
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun /* DRAM initial sequence */
457*4882a593Smuzhiyun flags = CARMINE_DFLT_IP_DCTL_MODE << 16 | CARMINE_DFLT_IP_DCTL_ADD;
458*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD,
459*4882a593Smuzhiyun flags);
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun flags = CARMINE_DFLT_IP_DCTL_SET_TIME1 << 16 |
462*4882a593Smuzhiyun CARMINE_DFLT_IP_DCTL_EMODE;
463*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_SETTIME1_EMODE,
464*4882a593Smuzhiyun flags);
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun flags = CARMINE_DFLT_IP_DCTL_REFRESH << 16 |
467*4882a593Smuzhiyun CARMINE_DFLT_IP_DCTL_SET_TIME2;
468*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_REFRESH_SETTIME2,
469*4882a593Smuzhiyun flags);
470*4882a593Smuzhiyun
471*4882a593Smuzhiyun flags = CARMINE_DFLT_IP_DCTL_RESERVE2 << 16 |
472*4882a593Smuzhiyun CARMINE_DFLT_IP_DCTL_FIFO_DEPTH;
473*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV2_RSV1, flags);
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun flags = CARMINE_DFLT_IP_DCTL_DDRIF2 << 16 | CARMINE_DFLT_IP_DCTL_DDRIF1;
476*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_DDRIF2_DDRIF1,
477*4882a593Smuzhiyun flags);
478*4882a593Smuzhiyun
479*4882a593Smuzhiyun flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 |
480*4882a593Smuzhiyun CARMINE_DFLT_IP_DCTL_STATES;
481*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES,
482*4882a593Smuzhiyun flags);
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun /* Executes DLL reset */
485*4882a593Smuzhiyun if (CARMINE_DCTL_DLL_RESET) {
486*4882a593Smuzhiyun for (loops = 0; loops < CARMINE_DCTL_INIT_WAIT_LIMIT; loops++) {
487*4882a593Smuzhiyun
488*4882a593Smuzhiyun ret = c_get_hw_reg(hw, CARMINE_DCTL_REG +
489*4882a593Smuzhiyun CARMINE_DCTL_REG_RSV0_STATES);
490*4882a593Smuzhiyun ret &= CARMINE_DCTL_REG_STATES_MASK;
491*4882a593Smuzhiyun if (!ret)
492*4882a593Smuzhiyun break;
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun mdelay(CARMINE_DCTL_INIT_WAIT_INTERVAL);
495*4882a593Smuzhiyun }
496*4882a593Smuzhiyun
497*4882a593Smuzhiyun if (loops >= CARMINE_DCTL_INIT_WAIT_LIMIT) {
498*4882a593Smuzhiyun printk(KERN_ERR "DRAM init failed\n");
499*4882a593Smuzhiyun return -EIO;
500*4882a593Smuzhiyun }
501*4882a593Smuzhiyun }
502*4882a593Smuzhiyun
503*4882a593Smuzhiyun flags = CARMINE_DFLT_IP_DCTL_MODE_AFT_RST << 16 |
504*4882a593Smuzhiyun CARMINE_DFLT_IP_DCTL_ADD;
505*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD, flags);
506*4882a593Smuzhiyun
507*4882a593Smuzhiyun flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 |
508*4882a593Smuzhiyun CARMINE_DFLT_IP_DCTL_STATES_AFT_RST;
509*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES,
510*4882a593Smuzhiyun flags);
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun /* Initialize the write back register */
513*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_WB_REG + CARMINE_WB_REG_WBM,
514*4882a593Smuzhiyun CARMINE_WB_REG_WBM_DEFAULT);
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun /* Initialize the Kottos registers */
517*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRINTM, 0);
518*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRERRM, 0);
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun /* Set DC offsets */
521*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PX, 0);
522*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PY, 0);
523*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LX, 0);
524*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LY, 0);
525*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TX, 0);
526*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TY, 0);
527*4882a593Smuzhiyun return 0;
528*4882a593Smuzhiyun }
529*4882a593Smuzhiyun
530*4882a593Smuzhiyun static const struct fb_ops carminefb_ops = {
531*4882a593Smuzhiyun .owner = THIS_MODULE,
532*4882a593Smuzhiyun .fb_fillrect = cfb_fillrect,
533*4882a593Smuzhiyun .fb_copyarea = cfb_copyarea,
534*4882a593Smuzhiyun .fb_imageblit = cfb_imageblit,
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun .fb_check_var = carmine_check_var,
537*4882a593Smuzhiyun .fb_set_par = carmine_set_par,
538*4882a593Smuzhiyun .fb_setcolreg = carmine_setcolreg,
539*4882a593Smuzhiyun };
540*4882a593Smuzhiyun
alloc_carmine_fb(void __iomem * regs,void __iomem * smem_base,int smem_offset,struct device * device,struct fb_info ** rinfo)541*4882a593Smuzhiyun static int alloc_carmine_fb(void __iomem *regs, void __iomem *smem_base,
542*4882a593Smuzhiyun int smem_offset, struct device *device,
543*4882a593Smuzhiyun struct fb_info **rinfo)
544*4882a593Smuzhiyun {
545*4882a593Smuzhiyun int ret;
546*4882a593Smuzhiyun struct fb_info *info;
547*4882a593Smuzhiyun struct carmine_fb *par;
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun info = framebuffer_alloc(sizeof *par, device);
550*4882a593Smuzhiyun if (!info)
551*4882a593Smuzhiyun return -ENOMEM;
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun par = info->par;
554*4882a593Smuzhiyun par->display_reg = regs;
555*4882a593Smuzhiyun par->smem_offset = smem_offset;
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun info->screen_base = smem_base + smem_offset;
558*4882a593Smuzhiyun info->screen_size = CARMINE_DISPLAY_MEM;
559*4882a593Smuzhiyun info->fbops = &carminefb_ops;
560*4882a593Smuzhiyun
561*4882a593Smuzhiyun info->fix = carminefb_fix;
562*4882a593Smuzhiyun info->pseudo_palette = par->pseudo_palette;
563*4882a593Smuzhiyun info->flags = FBINFO_DEFAULT;
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun ret = fb_alloc_cmap(&info->cmap, 256, 1);
566*4882a593Smuzhiyun if (ret < 0)
567*4882a593Smuzhiyun goto err_free_fb;
568*4882a593Smuzhiyun
569*4882a593Smuzhiyun if (fb_mode >= ARRAY_SIZE(carmine_modedb))
570*4882a593Smuzhiyun fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE;
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun par->cur_mode = par->new_mode = ~0;
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun ret = fb_find_mode(&info->var, info, fb_mode_str, carmine_modedb,
575*4882a593Smuzhiyun ARRAY_SIZE(carmine_modedb),
576*4882a593Smuzhiyun &carmine_modedb[fb_mode], 32);
577*4882a593Smuzhiyun if (!ret || ret == 4) {
578*4882a593Smuzhiyun ret = -EINVAL;
579*4882a593Smuzhiyun goto err_dealloc_cmap;
580*4882a593Smuzhiyun }
581*4882a593Smuzhiyun
582*4882a593Smuzhiyun fb_videomode_to_modelist(carmine_modedb, ARRAY_SIZE(carmine_modedb),
583*4882a593Smuzhiyun &info->modelist);
584*4882a593Smuzhiyun
585*4882a593Smuzhiyun ret = register_framebuffer(info);
586*4882a593Smuzhiyun if (ret < 0)
587*4882a593Smuzhiyun goto err_dealloc_cmap;
588*4882a593Smuzhiyun
589*4882a593Smuzhiyun fb_info(info, "%s frame buffer device\n", info->fix.id);
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun *rinfo = info;
592*4882a593Smuzhiyun return 0;
593*4882a593Smuzhiyun
594*4882a593Smuzhiyun err_dealloc_cmap:
595*4882a593Smuzhiyun fb_dealloc_cmap(&info->cmap);
596*4882a593Smuzhiyun err_free_fb:
597*4882a593Smuzhiyun framebuffer_release(info);
598*4882a593Smuzhiyun return ret;
599*4882a593Smuzhiyun }
600*4882a593Smuzhiyun
cleanup_fb_device(struct fb_info * info)601*4882a593Smuzhiyun static void cleanup_fb_device(struct fb_info *info)
602*4882a593Smuzhiyun {
603*4882a593Smuzhiyun if (info) {
604*4882a593Smuzhiyun unregister_framebuffer(info);
605*4882a593Smuzhiyun fb_dealloc_cmap(&info->cmap);
606*4882a593Smuzhiyun framebuffer_release(info);
607*4882a593Smuzhiyun }
608*4882a593Smuzhiyun }
609*4882a593Smuzhiyun
carminefb_probe(struct pci_dev * dev,const struct pci_device_id * ent)610*4882a593Smuzhiyun static int carminefb_probe(struct pci_dev *dev, const struct pci_device_id *ent)
611*4882a593Smuzhiyun {
612*4882a593Smuzhiyun struct carmine_hw *hw;
613*4882a593Smuzhiyun struct device *device = &dev->dev;
614*4882a593Smuzhiyun struct fb_info *info;
615*4882a593Smuzhiyun int ret;
616*4882a593Smuzhiyun
617*4882a593Smuzhiyun ret = pci_enable_device(dev);
618*4882a593Smuzhiyun if (ret)
619*4882a593Smuzhiyun return ret;
620*4882a593Smuzhiyun
621*4882a593Smuzhiyun ret = -ENOMEM;
622*4882a593Smuzhiyun hw = kzalloc(sizeof *hw, GFP_KERNEL);
623*4882a593Smuzhiyun if (!hw)
624*4882a593Smuzhiyun goto err_enable_pci;
625*4882a593Smuzhiyun
626*4882a593Smuzhiyun carminefb_fix.mmio_start = pci_resource_start(dev, CARMINE_CONFIG_BAR);
627*4882a593Smuzhiyun carminefb_fix.mmio_len = pci_resource_len(dev, CARMINE_CONFIG_BAR);
628*4882a593Smuzhiyun
629*4882a593Smuzhiyun if (!request_mem_region(carminefb_fix.mmio_start,
630*4882a593Smuzhiyun carminefb_fix.mmio_len,
631*4882a593Smuzhiyun "carminefb regbase")) {
632*4882a593Smuzhiyun printk(KERN_ERR "carminefb: Can't reserve regbase.\n");
633*4882a593Smuzhiyun ret = -EBUSY;
634*4882a593Smuzhiyun goto err_free_hw;
635*4882a593Smuzhiyun }
636*4882a593Smuzhiyun hw->v_regs = ioremap(carminefb_fix.mmio_start,
637*4882a593Smuzhiyun carminefb_fix.mmio_len);
638*4882a593Smuzhiyun if (!hw->v_regs) {
639*4882a593Smuzhiyun printk(KERN_ERR "carminefb: Can't remap %s register.\n",
640*4882a593Smuzhiyun carminefb_fix.id);
641*4882a593Smuzhiyun goto err_free_reg_mmio;
642*4882a593Smuzhiyun }
643*4882a593Smuzhiyun
644*4882a593Smuzhiyun carminefb_fix.smem_start = pci_resource_start(dev, CARMINE_MEMORY_BAR);
645*4882a593Smuzhiyun carminefb_fix.smem_len = pci_resource_len(dev, CARMINE_MEMORY_BAR);
646*4882a593Smuzhiyun
647*4882a593Smuzhiyun /* The memory area tends to be very large (256 MiB). Remap only what
648*4882a593Smuzhiyun * is required for that largest resolution to avoid remaps at run
649*4882a593Smuzhiyun * time
650*4882a593Smuzhiyun */
651*4882a593Smuzhiyun if (carminefb_fix.smem_len > CARMINE_TOTAL_DIPLAY_MEM)
652*4882a593Smuzhiyun carminefb_fix.smem_len = CARMINE_TOTAL_DIPLAY_MEM;
653*4882a593Smuzhiyun
654*4882a593Smuzhiyun else if (carminefb_fix.smem_len < CARMINE_TOTAL_DIPLAY_MEM) {
655*4882a593Smuzhiyun printk(KERN_ERR "carminefb: Memory bar is only %d bytes, %d "
656*4882a593Smuzhiyun "are required.", carminefb_fix.smem_len,
657*4882a593Smuzhiyun CARMINE_TOTAL_DIPLAY_MEM);
658*4882a593Smuzhiyun goto err_unmap_vregs;
659*4882a593Smuzhiyun }
660*4882a593Smuzhiyun
661*4882a593Smuzhiyun if (!request_mem_region(carminefb_fix.smem_start,
662*4882a593Smuzhiyun carminefb_fix.smem_len, "carminefb smem")) {
663*4882a593Smuzhiyun printk(KERN_ERR "carminefb: Can't reserve smem.\n");
664*4882a593Smuzhiyun goto err_unmap_vregs;
665*4882a593Smuzhiyun }
666*4882a593Smuzhiyun
667*4882a593Smuzhiyun hw->screen_mem = ioremap(carminefb_fix.smem_start,
668*4882a593Smuzhiyun carminefb_fix.smem_len);
669*4882a593Smuzhiyun if (!hw->screen_mem) {
670*4882a593Smuzhiyun printk(KERN_ERR "carmine: Can't ioremap smem area.\n");
671*4882a593Smuzhiyun goto err_reg_smem;
672*4882a593Smuzhiyun }
673*4882a593Smuzhiyun
674*4882a593Smuzhiyun ret = init_hardware(hw);
675*4882a593Smuzhiyun if (ret)
676*4882a593Smuzhiyun goto err_unmap_screen;
677*4882a593Smuzhiyun
678*4882a593Smuzhiyun info = NULL;
679*4882a593Smuzhiyun if (fb_displays & CARMINE_USE_DISPLAY0) {
680*4882a593Smuzhiyun ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP0_REG,
681*4882a593Smuzhiyun hw->screen_mem, CARMINE_DISPLAY_MEM * 0,
682*4882a593Smuzhiyun device, &info);
683*4882a593Smuzhiyun if (ret)
684*4882a593Smuzhiyun goto err_deinit_hw;
685*4882a593Smuzhiyun }
686*4882a593Smuzhiyun
687*4882a593Smuzhiyun hw->fb[0] = info;
688*4882a593Smuzhiyun
689*4882a593Smuzhiyun info = NULL;
690*4882a593Smuzhiyun if (fb_displays & CARMINE_USE_DISPLAY1) {
691*4882a593Smuzhiyun ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP1_REG,
692*4882a593Smuzhiyun hw->screen_mem, CARMINE_DISPLAY_MEM * 1,
693*4882a593Smuzhiyun device, &info);
694*4882a593Smuzhiyun if (ret)
695*4882a593Smuzhiyun goto err_cleanup_fb0;
696*4882a593Smuzhiyun }
697*4882a593Smuzhiyun
698*4882a593Smuzhiyun hw->fb[1] = info;
699*4882a593Smuzhiyun info = NULL;
700*4882a593Smuzhiyun
701*4882a593Smuzhiyun pci_set_drvdata(dev, hw);
702*4882a593Smuzhiyun return 0;
703*4882a593Smuzhiyun
704*4882a593Smuzhiyun err_cleanup_fb0:
705*4882a593Smuzhiyun cleanup_fb_device(hw->fb[0]);
706*4882a593Smuzhiyun err_deinit_hw:
707*4882a593Smuzhiyun /* disable clock, etc */
708*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0);
709*4882a593Smuzhiyun err_unmap_screen:
710*4882a593Smuzhiyun iounmap(hw->screen_mem);
711*4882a593Smuzhiyun err_reg_smem:
712*4882a593Smuzhiyun release_mem_region(carminefb_fix.smem_start, carminefb_fix.smem_len);
713*4882a593Smuzhiyun err_unmap_vregs:
714*4882a593Smuzhiyun iounmap(hw->v_regs);
715*4882a593Smuzhiyun err_free_reg_mmio:
716*4882a593Smuzhiyun release_mem_region(carminefb_fix.mmio_start, carminefb_fix.mmio_len);
717*4882a593Smuzhiyun err_free_hw:
718*4882a593Smuzhiyun kfree(hw);
719*4882a593Smuzhiyun err_enable_pci:
720*4882a593Smuzhiyun pci_disable_device(dev);
721*4882a593Smuzhiyun return ret;
722*4882a593Smuzhiyun }
723*4882a593Smuzhiyun
carminefb_remove(struct pci_dev * dev)724*4882a593Smuzhiyun static void carminefb_remove(struct pci_dev *dev)
725*4882a593Smuzhiyun {
726*4882a593Smuzhiyun struct carmine_hw *hw = pci_get_drvdata(dev);
727*4882a593Smuzhiyun struct fb_fix_screeninfo fix;
728*4882a593Smuzhiyun int i;
729*4882a593Smuzhiyun
730*4882a593Smuzhiyun /* in case we use only fb1 and not fb1 */
731*4882a593Smuzhiyun if (hw->fb[0])
732*4882a593Smuzhiyun fix = hw->fb[0]->fix;
733*4882a593Smuzhiyun else
734*4882a593Smuzhiyun fix = hw->fb[1]->fix;
735*4882a593Smuzhiyun
736*4882a593Smuzhiyun /* deactivate display(s) and switch clocks */
737*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0);
738*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0);
739*4882a593Smuzhiyun c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0);
740*4882a593Smuzhiyun
741*4882a593Smuzhiyun for (i = 0; i < MAX_DISPLAY; i++)
742*4882a593Smuzhiyun cleanup_fb_device(hw->fb[i]);
743*4882a593Smuzhiyun
744*4882a593Smuzhiyun iounmap(hw->screen_mem);
745*4882a593Smuzhiyun release_mem_region(fix.smem_start, fix.smem_len);
746*4882a593Smuzhiyun iounmap(hw->v_regs);
747*4882a593Smuzhiyun release_mem_region(fix.mmio_start, fix.mmio_len);
748*4882a593Smuzhiyun
749*4882a593Smuzhiyun pci_disable_device(dev);
750*4882a593Smuzhiyun kfree(hw);
751*4882a593Smuzhiyun }
752*4882a593Smuzhiyun
753*4882a593Smuzhiyun #define PCI_VENDOR_ID_FUJITU_LIMITED 0x10cf
754*4882a593Smuzhiyun static struct pci_device_id carmine_devices[] = {
755*4882a593Smuzhiyun {
756*4882a593Smuzhiyun PCI_DEVICE(PCI_VENDOR_ID_FUJITU_LIMITED, 0x202b)},
757*4882a593Smuzhiyun {0, 0, 0, 0, 0, 0, 0}
758*4882a593Smuzhiyun };
759*4882a593Smuzhiyun
760*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, carmine_devices);
761*4882a593Smuzhiyun
762*4882a593Smuzhiyun static struct pci_driver carmine_pci_driver = {
763*4882a593Smuzhiyun .name = "carminefb",
764*4882a593Smuzhiyun .id_table = carmine_devices,
765*4882a593Smuzhiyun .probe = carminefb_probe,
766*4882a593Smuzhiyun .remove = carminefb_remove,
767*4882a593Smuzhiyun };
768*4882a593Smuzhiyun
carminefb_init(void)769*4882a593Smuzhiyun static int __init carminefb_init(void)
770*4882a593Smuzhiyun {
771*4882a593Smuzhiyun if (!(fb_displays &
772*4882a593Smuzhiyun (CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1))) {
773*4882a593Smuzhiyun printk(KERN_ERR "If you disable both displays than you don't "
774*4882a593Smuzhiyun "need the driver at all\n");
775*4882a593Smuzhiyun return -EINVAL;
776*4882a593Smuzhiyun }
777*4882a593Smuzhiyun return pci_register_driver(&carmine_pci_driver);
778*4882a593Smuzhiyun }
779*4882a593Smuzhiyun module_init(carminefb_init);
780*4882a593Smuzhiyun
carminefb_cleanup(void)781*4882a593Smuzhiyun static void __exit carminefb_cleanup(void)
782*4882a593Smuzhiyun {
783*4882a593Smuzhiyun pci_unregister_driver(&carmine_pci_driver);
784*4882a593Smuzhiyun }
785*4882a593Smuzhiyun module_exit(carminefb_cleanup);
786*4882a593Smuzhiyun
787*4882a593Smuzhiyun MODULE_AUTHOR("Sebastian Siewior <bigeasy@linutronix.de>");
788*4882a593Smuzhiyun MODULE_DESCRIPTION("Framebuffer driver for Fujitsu Carmine based devices");
789*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
790