1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f7018c21STomi Valkeinen /*
3f7018c21STomi Valkeinen * linux/drivers/video/ep93xx-fb.c
4f7018c21STomi Valkeinen *
5f7018c21STomi Valkeinen * Framebuffer support for the EP93xx series.
6f7018c21STomi Valkeinen *
7f7018c21STomi Valkeinen * Copyright (C) 2007 Bluewater Systems Ltd
8f7018c21STomi Valkeinen * Author: Ryan Mallon
9f7018c21STomi Valkeinen *
10f7018c21STomi Valkeinen * Copyright (c) 2009 H Hartley Sweeten <hsweeten@visionengravers.com>
11f7018c21STomi Valkeinen *
12f7018c21STomi Valkeinen * Based on the Cirrus Logic ep93xxfb driver, and various other ep93xxfb
13f7018c21STomi Valkeinen * drivers.
14f7018c21STomi Valkeinen */
15f7018c21STomi Valkeinen
16f7018c21STomi Valkeinen #include <linux/platform_device.h>
17f7018c21STomi Valkeinen #include <linux/module.h>
18f7018c21STomi Valkeinen #include <linux/dma-mapping.h>
19f7018c21STomi Valkeinen #include <linux/slab.h>
20f7018c21STomi Valkeinen #include <linux/clk.h>
21f7018c21STomi Valkeinen #include <linux/fb.h>
22f7018c21STomi Valkeinen #include <linux/io.h>
23f7018c21STomi Valkeinen
24f7018c21STomi Valkeinen #include <linux/platform_data/video-ep93xx.h>
25f7018c21STomi Valkeinen
26f7018c21STomi Valkeinen /* Vertical Frame Timing Registers */
27f7018c21STomi Valkeinen #define EP93XXFB_VLINES_TOTAL 0x0000 /* SW locked */
28f7018c21STomi Valkeinen #define EP93XXFB_VSYNC 0x0004 /* SW locked */
29f7018c21STomi Valkeinen #define EP93XXFB_VACTIVE 0x0008 /* SW locked */
30f7018c21STomi Valkeinen #define EP93XXFB_VBLANK 0x0228 /* SW locked */
31f7018c21STomi Valkeinen #define EP93XXFB_VCLK 0x000c /* SW locked */
32f7018c21STomi Valkeinen
33f7018c21STomi Valkeinen /* Horizontal Frame Timing Registers */
34f7018c21STomi Valkeinen #define EP93XXFB_HCLKS_TOTAL 0x0010 /* SW locked */
35f7018c21STomi Valkeinen #define EP93XXFB_HSYNC 0x0014 /* SW locked */
36f7018c21STomi Valkeinen #define EP93XXFB_HACTIVE 0x0018 /* SW locked */
37f7018c21STomi Valkeinen #define EP93XXFB_HBLANK 0x022c /* SW locked */
38f7018c21STomi Valkeinen #define EP93XXFB_HCLK 0x001c /* SW locked */
39f7018c21STomi Valkeinen
40f7018c21STomi Valkeinen /* Frame Buffer Memory Configuration Registers */
41f7018c21STomi Valkeinen #define EP93XXFB_SCREEN_PAGE 0x0028
42f7018c21STomi Valkeinen #define EP93XXFB_SCREEN_HPAGE 0x002c
43f7018c21STomi Valkeinen #define EP93XXFB_SCREEN_LINES 0x0030
44f7018c21STomi Valkeinen #define EP93XXFB_LINE_LENGTH 0x0034
45f7018c21STomi Valkeinen #define EP93XXFB_VLINE_STEP 0x0038
46f7018c21STomi Valkeinen #define EP93XXFB_LINE_CARRY 0x003c /* SW locked */
47f7018c21STomi Valkeinen #define EP93XXFB_EOL_OFFSET 0x0230
48f7018c21STomi Valkeinen
49f7018c21STomi Valkeinen /* Other Video Registers */
50f7018c21STomi Valkeinen #define EP93XXFB_BRIGHTNESS 0x0020
51f7018c21STomi Valkeinen #define EP93XXFB_ATTRIBS 0x0024 /* SW locked */
52f7018c21STomi Valkeinen #define EP93XXFB_SWLOCK 0x007c /* SW locked */
53f7018c21STomi Valkeinen #define EP93XXFB_AC_RATE 0x0214
54f7018c21STomi Valkeinen #define EP93XXFB_FIFO_LEVEL 0x0234
55f7018c21STomi Valkeinen #define EP93XXFB_PIXELMODE 0x0054
56f7018c21STomi Valkeinen #define EP93XXFB_PIXELMODE_32BPP (0x7 << 0)
57f7018c21STomi Valkeinen #define EP93XXFB_PIXELMODE_24BPP (0x6 << 0)
58f7018c21STomi Valkeinen #define EP93XXFB_PIXELMODE_16BPP (0x4 << 0)
59f7018c21STomi Valkeinen #define EP93XXFB_PIXELMODE_8BPP (0x2 << 0)
60f7018c21STomi Valkeinen #define EP93XXFB_PIXELMODE_SHIFT_1P_24B (0x0 << 3)
61f7018c21STomi Valkeinen #define EP93XXFB_PIXELMODE_SHIFT_1P_18B (0x1 << 3)
62f7018c21STomi Valkeinen #define EP93XXFB_PIXELMODE_COLOR_LUT (0x0 << 10)
63f7018c21STomi Valkeinen #define EP93XXFB_PIXELMODE_COLOR_888 (0x4 << 10)
64f7018c21STomi Valkeinen #define EP93XXFB_PIXELMODE_COLOR_555 (0x5 << 10)
65f7018c21STomi Valkeinen #define EP93XXFB_PARL_IF_OUT 0x0058
66f7018c21STomi Valkeinen #define EP93XXFB_PARL_IF_IN 0x005c
67f7018c21STomi Valkeinen
68f7018c21STomi Valkeinen /* Blink Control Registers */
69f7018c21STomi Valkeinen #define EP93XXFB_BLINK_RATE 0x0040
70f7018c21STomi Valkeinen #define EP93XXFB_BLINK_MASK 0x0044
71f7018c21STomi Valkeinen #define EP93XXFB_BLINK_PATTRN 0x0048
72f7018c21STomi Valkeinen #define EP93XXFB_PATTRN_MASK 0x004c
73f7018c21STomi Valkeinen #define EP93XXFB_BKGRND_OFFSET 0x0050
74f7018c21STomi Valkeinen
75f7018c21STomi Valkeinen /* Hardware Cursor Registers */
76f7018c21STomi Valkeinen #define EP93XXFB_CURSOR_ADR_START 0x0060
77f7018c21STomi Valkeinen #define EP93XXFB_CURSOR_ADR_RESET 0x0064
78f7018c21STomi Valkeinen #define EP93XXFB_CURSOR_SIZE 0x0068
79f7018c21STomi Valkeinen #define EP93XXFB_CURSOR_COLOR1 0x006c
80f7018c21STomi Valkeinen #define EP93XXFB_CURSOR_COLOR2 0x0070
81f7018c21STomi Valkeinen #define EP93XXFB_CURSOR_BLINK_COLOR1 0x021c
82f7018c21STomi Valkeinen #define EP93XXFB_CURSOR_BLINK_COLOR2 0x0220
83f7018c21STomi Valkeinen #define EP93XXFB_CURSOR_XY_LOC 0x0074
84f7018c21STomi Valkeinen #define EP93XXFB_CURSOR_DSCAN_HY_LOC 0x0078
85f7018c21STomi Valkeinen #define EP93XXFB_CURSOR_BLINK_RATE_CTRL 0x0224
86f7018c21STomi Valkeinen
87f7018c21STomi Valkeinen /* LUT Registers */
88f7018c21STomi Valkeinen #define EP93XXFB_GRY_SCL_LUTR 0x0080
89f7018c21STomi Valkeinen #define EP93XXFB_GRY_SCL_LUTG 0x0280
90f7018c21STomi Valkeinen #define EP93XXFB_GRY_SCL_LUTB 0x0300
91f7018c21STomi Valkeinen #define EP93XXFB_LUT_SW_CONTROL 0x0218
92f7018c21STomi Valkeinen #define EP93XXFB_LUT_SW_CONTROL_SWTCH (1 << 0)
93f7018c21STomi Valkeinen #define EP93XXFB_LUT_SW_CONTROL_SSTAT (1 << 1)
94f7018c21STomi Valkeinen #define EP93XXFB_COLOR_LUT 0x0400
95f7018c21STomi Valkeinen
96f7018c21STomi Valkeinen /* Video Signature Registers */
97f7018c21STomi Valkeinen #define EP93XXFB_VID_SIG_RSLT_VAL 0x0200
98f7018c21STomi Valkeinen #define EP93XXFB_VID_SIG_CTRL 0x0204
99f7018c21STomi Valkeinen #define EP93XXFB_VSIG 0x0208
100f7018c21STomi Valkeinen #define EP93XXFB_HSIG 0x020c
101f7018c21STomi Valkeinen #define EP93XXFB_SIG_CLR_STR 0x0210
102f7018c21STomi Valkeinen
103f7018c21STomi Valkeinen /* Minimum / Maximum resolutions supported */
104f7018c21STomi Valkeinen #define EP93XXFB_MIN_XRES 64
105f7018c21STomi Valkeinen #define EP93XXFB_MIN_YRES 64
106f7018c21STomi Valkeinen #define EP93XXFB_MAX_XRES 1024
107f7018c21STomi Valkeinen #define EP93XXFB_MAX_YRES 768
108f7018c21STomi Valkeinen
109f7018c21STomi Valkeinen struct ep93xx_fbi {
110f7018c21STomi Valkeinen struct ep93xxfb_mach_info *mach_info;
111f7018c21STomi Valkeinen struct clk *clk;
112f7018c21STomi Valkeinen struct resource *res;
113f7018c21STomi Valkeinen void __iomem *mmio_base;
114f7018c21STomi Valkeinen unsigned int pseudo_palette[256];
115f7018c21STomi Valkeinen };
116f7018c21STomi Valkeinen
117f7018c21STomi Valkeinen static int check_screenpage_bug = 1;
118f7018c21STomi Valkeinen module_param(check_screenpage_bug, int, 0644);
119f7018c21STomi Valkeinen MODULE_PARM_DESC(check_screenpage_bug,
120f7018c21STomi Valkeinen "Check for bit 27 screen page bug. Default = 1");
121f7018c21STomi Valkeinen
ep93xxfb_readl(struct ep93xx_fbi * fbi,unsigned int off)122f7018c21STomi Valkeinen static inline unsigned int ep93xxfb_readl(struct ep93xx_fbi *fbi,
123f7018c21STomi Valkeinen unsigned int off)
124f7018c21STomi Valkeinen {
125f7018c21STomi Valkeinen return __raw_readl(fbi->mmio_base + off);
126f7018c21STomi Valkeinen }
127f7018c21STomi Valkeinen
ep93xxfb_writel(struct ep93xx_fbi * fbi,unsigned int val,unsigned int off)128f7018c21STomi Valkeinen static inline void ep93xxfb_writel(struct ep93xx_fbi *fbi,
129f7018c21STomi Valkeinen unsigned int val, unsigned int off)
130f7018c21STomi Valkeinen {
131f7018c21STomi Valkeinen __raw_writel(val, fbi->mmio_base + off);
132f7018c21STomi Valkeinen }
133f7018c21STomi Valkeinen
134f7018c21STomi Valkeinen /*
135f7018c21STomi Valkeinen * Write to one of the locked raster registers.
136f7018c21STomi Valkeinen */
ep93xxfb_out_locked(struct ep93xx_fbi * fbi,unsigned int val,unsigned int reg)137f7018c21STomi Valkeinen static inline void ep93xxfb_out_locked(struct ep93xx_fbi *fbi,
138f7018c21STomi Valkeinen unsigned int val, unsigned int reg)
139f7018c21STomi Valkeinen {
140f7018c21STomi Valkeinen /*
141f7018c21STomi Valkeinen * We don't need a lock or delay here since the raster register
142f7018c21STomi Valkeinen * block will remain unlocked until the next access.
143f7018c21STomi Valkeinen */
144f7018c21STomi Valkeinen ep93xxfb_writel(fbi, 0xaa, EP93XXFB_SWLOCK);
145f7018c21STomi Valkeinen ep93xxfb_writel(fbi, val, reg);
146f7018c21STomi Valkeinen }
147f7018c21STomi Valkeinen
ep93xxfb_set_video_attribs(struct fb_info * info)148f7018c21STomi Valkeinen static void ep93xxfb_set_video_attribs(struct fb_info *info)
149f7018c21STomi Valkeinen {
150f7018c21STomi Valkeinen struct ep93xx_fbi *fbi = info->par;
151f7018c21STomi Valkeinen unsigned int attribs;
152f7018c21STomi Valkeinen
153f7018c21STomi Valkeinen attribs = EP93XXFB_ENABLE;
154f7018c21STomi Valkeinen attribs |= fbi->mach_info->flags;
155f7018c21STomi Valkeinen ep93xxfb_out_locked(fbi, attribs, EP93XXFB_ATTRIBS);
156f7018c21STomi Valkeinen }
157f7018c21STomi Valkeinen
ep93xxfb_set_pixelmode(struct fb_info * info)158f7018c21STomi Valkeinen static int ep93xxfb_set_pixelmode(struct fb_info *info)
159f7018c21STomi Valkeinen {
160f7018c21STomi Valkeinen struct ep93xx_fbi *fbi = info->par;
161f7018c21STomi Valkeinen unsigned int val;
162f7018c21STomi Valkeinen
163f7018c21STomi Valkeinen info->var.transp.offset = 0;
164f7018c21STomi Valkeinen info->var.transp.length = 0;
165f7018c21STomi Valkeinen
166f7018c21STomi Valkeinen switch (info->var.bits_per_pixel) {
167f7018c21STomi Valkeinen case 8:
168f7018c21STomi Valkeinen val = EP93XXFB_PIXELMODE_8BPP | EP93XXFB_PIXELMODE_COLOR_LUT |
169f7018c21STomi Valkeinen EP93XXFB_PIXELMODE_SHIFT_1P_18B;
170f7018c21STomi Valkeinen
171f7018c21STomi Valkeinen info->var.red.offset = 0;
172f7018c21STomi Valkeinen info->var.red.length = 8;
173f7018c21STomi Valkeinen info->var.green.offset = 0;
174f7018c21STomi Valkeinen info->var.green.length = 8;
175f7018c21STomi Valkeinen info->var.blue.offset = 0;
176f7018c21STomi Valkeinen info->var.blue.length = 8;
177f7018c21STomi Valkeinen info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
178f7018c21STomi Valkeinen break;
179f7018c21STomi Valkeinen
180f7018c21STomi Valkeinen case 16:
181f7018c21STomi Valkeinen val = EP93XXFB_PIXELMODE_16BPP | EP93XXFB_PIXELMODE_COLOR_555 |
182f7018c21STomi Valkeinen EP93XXFB_PIXELMODE_SHIFT_1P_18B;
183f7018c21STomi Valkeinen
184f7018c21STomi Valkeinen info->var.red.offset = 11;
185f7018c21STomi Valkeinen info->var.red.length = 5;
186f7018c21STomi Valkeinen info->var.green.offset = 5;
187f7018c21STomi Valkeinen info->var.green.length = 6;
188f7018c21STomi Valkeinen info->var.blue.offset = 0;
189f7018c21STomi Valkeinen info->var.blue.length = 5;
190f7018c21STomi Valkeinen info->fix.visual = FB_VISUAL_TRUECOLOR;
191f7018c21STomi Valkeinen break;
192f7018c21STomi Valkeinen
193f7018c21STomi Valkeinen case 24:
194f7018c21STomi Valkeinen val = EP93XXFB_PIXELMODE_24BPP | EP93XXFB_PIXELMODE_COLOR_888 |
195f7018c21STomi Valkeinen EP93XXFB_PIXELMODE_SHIFT_1P_24B;
196f7018c21STomi Valkeinen
197f7018c21STomi Valkeinen info->var.red.offset = 16;
198f7018c21STomi Valkeinen info->var.red.length = 8;
199f7018c21STomi Valkeinen info->var.green.offset = 8;
200f7018c21STomi Valkeinen info->var.green.length = 8;
201f7018c21STomi Valkeinen info->var.blue.offset = 0;
202f7018c21STomi Valkeinen info->var.blue.length = 8;
203f7018c21STomi Valkeinen info->fix.visual = FB_VISUAL_TRUECOLOR;
204f7018c21STomi Valkeinen break;
205f7018c21STomi Valkeinen
206f7018c21STomi Valkeinen case 32:
207f7018c21STomi Valkeinen val = EP93XXFB_PIXELMODE_32BPP | EP93XXFB_PIXELMODE_COLOR_888 |
208f7018c21STomi Valkeinen EP93XXFB_PIXELMODE_SHIFT_1P_24B;
209f7018c21STomi Valkeinen
210f7018c21STomi Valkeinen info->var.red.offset = 16;
211f7018c21STomi Valkeinen info->var.red.length = 8;
212f7018c21STomi Valkeinen info->var.green.offset = 8;
213f7018c21STomi Valkeinen info->var.green.length = 8;
214f7018c21STomi Valkeinen info->var.blue.offset = 0;
215f7018c21STomi Valkeinen info->var.blue.length = 8;
216f7018c21STomi Valkeinen info->fix.visual = FB_VISUAL_TRUECOLOR;
217f7018c21STomi Valkeinen break;
218f7018c21STomi Valkeinen
219f7018c21STomi Valkeinen default:
220f7018c21STomi Valkeinen return -EINVAL;
221f7018c21STomi Valkeinen }
222f7018c21STomi Valkeinen
223f7018c21STomi Valkeinen ep93xxfb_writel(fbi, val, EP93XXFB_PIXELMODE);
224f7018c21STomi Valkeinen return 0;
225f7018c21STomi Valkeinen }
226f7018c21STomi Valkeinen
ep93xxfb_set_timing(struct fb_info * info)227f7018c21STomi Valkeinen static void ep93xxfb_set_timing(struct fb_info *info)
228f7018c21STomi Valkeinen {
229f7018c21STomi Valkeinen struct ep93xx_fbi *fbi = info->par;
230f7018c21STomi Valkeinen unsigned int vlines_total, hclks_total, start, stop;
231f7018c21STomi Valkeinen
232f7018c21STomi Valkeinen vlines_total = info->var.yres + info->var.upper_margin +
233f7018c21STomi Valkeinen info->var.lower_margin + info->var.vsync_len - 1;
234f7018c21STomi Valkeinen
235f7018c21STomi Valkeinen hclks_total = info->var.xres + info->var.left_margin +
236f7018c21STomi Valkeinen info->var.right_margin + info->var.hsync_len - 1;
237f7018c21STomi Valkeinen
238f7018c21STomi Valkeinen ep93xxfb_out_locked(fbi, vlines_total, EP93XXFB_VLINES_TOTAL);
239f7018c21STomi Valkeinen ep93xxfb_out_locked(fbi, hclks_total, EP93XXFB_HCLKS_TOTAL);
240f7018c21STomi Valkeinen
241f7018c21STomi Valkeinen start = vlines_total;
242f7018c21STomi Valkeinen stop = vlines_total - info->var.vsync_len;
243f7018c21STomi Valkeinen ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VSYNC);
244f7018c21STomi Valkeinen
245f7018c21STomi Valkeinen start = vlines_total - info->var.vsync_len - info->var.upper_margin;
246f7018c21STomi Valkeinen stop = info->var.lower_margin - 1;
247f7018c21STomi Valkeinen ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VBLANK);
248f7018c21STomi Valkeinen ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VACTIVE);
249f7018c21STomi Valkeinen
250f7018c21STomi Valkeinen start = vlines_total;
251f7018c21STomi Valkeinen stop = vlines_total + 1;
252f7018c21STomi Valkeinen ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VCLK);
253f7018c21STomi Valkeinen
254f7018c21STomi Valkeinen start = hclks_total;
255f7018c21STomi Valkeinen stop = hclks_total - info->var.hsync_len;
256f7018c21STomi Valkeinen ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HSYNC);
257f7018c21STomi Valkeinen
258f7018c21STomi Valkeinen start = hclks_total - info->var.hsync_len - info->var.left_margin;
259f7018c21STomi Valkeinen stop = info->var.right_margin - 1;
260f7018c21STomi Valkeinen ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HBLANK);
261f7018c21STomi Valkeinen ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HACTIVE);
262f7018c21STomi Valkeinen
263f7018c21STomi Valkeinen start = hclks_total;
264f7018c21STomi Valkeinen stop = hclks_total;
265f7018c21STomi Valkeinen ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HCLK);
266f7018c21STomi Valkeinen
267f7018c21STomi Valkeinen ep93xxfb_out_locked(fbi, 0x0, EP93XXFB_LINE_CARRY);
268f7018c21STomi Valkeinen }
269f7018c21STomi Valkeinen
ep93xxfb_set_par(struct fb_info * info)270f7018c21STomi Valkeinen static int ep93xxfb_set_par(struct fb_info *info)
271f7018c21STomi Valkeinen {
272f7018c21STomi Valkeinen struct ep93xx_fbi *fbi = info->par;
273f7018c21STomi Valkeinen
274f7018c21STomi Valkeinen clk_set_rate(fbi->clk, 1000 * PICOS2KHZ(info->var.pixclock));
275f7018c21STomi Valkeinen
276f7018c21STomi Valkeinen ep93xxfb_set_timing(info);
277f7018c21STomi Valkeinen
278f7018c21STomi Valkeinen info->fix.line_length = info->var.xres_virtual *
279f7018c21STomi Valkeinen info->var.bits_per_pixel / 8;
280f7018c21STomi Valkeinen
281f7018c21STomi Valkeinen ep93xxfb_writel(fbi, info->fix.smem_start, EP93XXFB_SCREEN_PAGE);
282f7018c21STomi Valkeinen ep93xxfb_writel(fbi, info->var.yres - 1, EP93XXFB_SCREEN_LINES);
283f7018c21STomi Valkeinen ep93xxfb_writel(fbi, ((info->var.xres * info->var.bits_per_pixel)
284f7018c21STomi Valkeinen / 32) - 1, EP93XXFB_LINE_LENGTH);
285f7018c21STomi Valkeinen ep93xxfb_writel(fbi, info->fix.line_length / 4, EP93XXFB_VLINE_STEP);
286f7018c21STomi Valkeinen ep93xxfb_set_video_attribs(info);
287f7018c21STomi Valkeinen return 0;
288f7018c21STomi Valkeinen }
289f7018c21STomi Valkeinen
ep93xxfb_check_var(struct fb_var_screeninfo * var,struct fb_info * info)290f7018c21STomi Valkeinen static int ep93xxfb_check_var(struct fb_var_screeninfo *var,
291f7018c21STomi Valkeinen struct fb_info *info)
292f7018c21STomi Valkeinen {
293f7018c21STomi Valkeinen int err;
294f7018c21STomi Valkeinen
295f7018c21STomi Valkeinen err = ep93xxfb_set_pixelmode(info);
296f7018c21STomi Valkeinen if (err)
297f7018c21STomi Valkeinen return err;
298f7018c21STomi Valkeinen
299f7018c21STomi Valkeinen var->xres = max_t(unsigned int, var->xres, EP93XXFB_MIN_XRES);
300f7018c21STomi Valkeinen var->xres = min_t(unsigned int, var->xres, EP93XXFB_MAX_XRES);
301f7018c21STomi Valkeinen var->xres_virtual = max(var->xres_virtual, var->xres);
302f7018c21STomi Valkeinen
303f7018c21STomi Valkeinen var->yres = max_t(unsigned int, var->yres, EP93XXFB_MIN_YRES);
304f7018c21STomi Valkeinen var->yres = min_t(unsigned int, var->yres, EP93XXFB_MAX_YRES);
305f7018c21STomi Valkeinen var->yres_virtual = max(var->yres_virtual, var->yres);
306f7018c21STomi Valkeinen
307f7018c21STomi Valkeinen return 0;
308f7018c21STomi Valkeinen }
309f7018c21STomi Valkeinen
ep93xxfb_mmap(struct fb_info * info,struct vm_area_struct * vma)310f7018c21STomi Valkeinen static int ep93xxfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
311f7018c21STomi Valkeinen {
312f7018c21STomi Valkeinen unsigned int offset = vma->vm_pgoff << PAGE_SHIFT;
313f7018c21STomi Valkeinen
314f7018c21STomi Valkeinen if (offset < info->fix.smem_len) {
3159aee7f04SThomas Zimmermann return dma_mmap_wc(info->device, vma, info->screen_base,
316f6e45661SLuis R. Rodriguez info->fix.smem_start, info->fix.smem_len);
317f7018c21STomi Valkeinen }
318f7018c21STomi Valkeinen
319f7018c21STomi Valkeinen return -EINVAL;
320f7018c21STomi Valkeinen }
321f7018c21STomi Valkeinen
ep93xxfb_blank(int blank_mode,struct fb_info * info)322f7018c21STomi Valkeinen static int ep93xxfb_blank(int blank_mode, struct fb_info *info)
323f7018c21STomi Valkeinen {
324f7018c21STomi Valkeinen struct ep93xx_fbi *fbi = info->par;
325f7018c21STomi Valkeinen unsigned int attribs = ep93xxfb_readl(fbi, EP93XXFB_ATTRIBS);
326f7018c21STomi Valkeinen
327f7018c21STomi Valkeinen if (blank_mode) {
328f7018c21STomi Valkeinen if (fbi->mach_info->blank)
329f7018c21STomi Valkeinen fbi->mach_info->blank(blank_mode, info);
330f7018c21STomi Valkeinen ep93xxfb_out_locked(fbi, attribs & ~EP93XXFB_ENABLE,
331f7018c21STomi Valkeinen EP93XXFB_ATTRIBS);
332f7018c21STomi Valkeinen clk_disable(fbi->clk);
333f7018c21STomi Valkeinen } else {
334f7018c21STomi Valkeinen clk_enable(fbi->clk);
335f7018c21STomi Valkeinen ep93xxfb_out_locked(fbi, attribs | EP93XXFB_ENABLE,
336f7018c21STomi Valkeinen EP93XXFB_ATTRIBS);
337f7018c21STomi Valkeinen if (fbi->mach_info->blank)
338f7018c21STomi Valkeinen fbi->mach_info->blank(blank_mode, info);
339f7018c21STomi Valkeinen }
340f7018c21STomi Valkeinen
341f7018c21STomi Valkeinen return 0;
342f7018c21STomi Valkeinen }
343f7018c21STomi Valkeinen
ep93xxfb_convert_color(int val,int width)344f7018c21STomi Valkeinen static inline int ep93xxfb_convert_color(int val, int width)
345f7018c21STomi Valkeinen {
346f7018c21STomi Valkeinen return ((val << width) + 0x7fff - val) >> 16;
347f7018c21STomi Valkeinen }
348f7018c21STomi Valkeinen
ep93xxfb_setcolreg(unsigned int regno,unsigned int red,unsigned int green,unsigned int blue,unsigned int transp,struct fb_info * info)349f7018c21STomi Valkeinen static int ep93xxfb_setcolreg(unsigned int regno, unsigned int red,
350f7018c21STomi Valkeinen unsigned int green, unsigned int blue,
351f7018c21STomi Valkeinen unsigned int transp, struct fb_info *info)
352f7018c21STomi Valkeinen {
353f7018c21STomi Valkeinen struct ep93xx_fbi *fbi = info->par;
354f7018c21STomi Valkeinen unsigned int *pal = info->pseudo_palette;
355f7018c21STomi Valkeinen unsigned int ctrl, i, rgb, lut_current, lut_stat;
356f7018c21STomi Valkeinen
357f7018c21STomi Valkeinen switch (info->fix.visual) {
358f7018c21STomi Valkeinen case FB_VISUAL_PSEUDOCOLOR:
359f7018c21STomi Valkeinen if (regno > 255)
360f7018c21STomi Valkeinen return 1;
361f7018c21STomi Valkeinen rgb = ((red & 0xff00) << 8) | (green & 0xff00) |
362f7018c21STomi Valkeinen ((blue & 0xff00) >> 8);
363f7018c21STomi Valkeinen
364f7018c21STomi Valkeinen pal[regno] = rgb;
365f7018c21STomi Valkeinen ep93xxfb_writel(fbi, rgb, (EP93XXFB_COLOR_LUT + (regno << 2)));
366f7018c21STomi Valkeinen ctrl = ep93xxfb_readl(fbi, EP93XXFB_LUT_SW_CONTROL);
367f7018c21STomi Valkeinen lut_stat = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SSTAT);
368f7018c21STomi Valkeinen lut_current = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SWTCH);
369f7018c21STomi Valkeinen
370f7018c21STomi Valkeinen if (lut_stat == lut_current) {
371f7018c21STomi Valkeinen for (i = 0; i < 256; i++) {
372f7018c21STomi Valkeinen ep93xxfb_writel(fbi, pal[i],
373f7018c21STomi Valkeinen EP93XXFB_COLOR_LUT + (i << 2));
374f7018c21STomi Valkeinen }
375f7018c21STomi Valkeinen
376f7018c21STomi Valkeinen ep93xxfb_writel(fbi,
377f7018c21STomi Valkeinen ctrl ^ EP93XXFB_LUT_SW_CONTROL_SWTCH,
378f7018c21STomi Valkeinen EP93XXFB_LUT_SW_CONTROL);
379f7018c21STomi Valkeinen }
380f7018c21STomi Valkeinen break;
381f7018c21STomi Valkeinen
382f7018c21STomi Valkeinen case FB_VISUAL_TRUECOLOR:
383f7018c21STomi Valkeinen if (regno > 16)
384f7018c21STomi Valkeinen return 1;
385f7018c21STomi Valkeinen
386f7018c21STomi Valkeinen red = ep93xxfb_convert_color(red, info->var.red.length);
387f7018c21STomi Valkeinen green = ep93xxfb_convert_color(green, info->var.green.length);
388f7018c21STomi Valkeinen blue = ep93xxfb_convert_color(blue, info->var.blue.length);
389f7018c21STomi Valkeinen transp = ep93xxfb_convert_color(transp,
390f7018c21STomi Valkeinen info->var.transp.length);
391f7018c21STomi Valkeinen
392f7018c21STomi Valkeinen pal[regno] = (red << info->var.red.offset) |
393f7018c21STomi Valkeinen (green << info->var.green.offset) |
394f7018c21STomi Valkeinen (blue << info->var.blue.offset) |
395f7018c21STomi Valkeinen (transp << info->var.transp.offset);
396f7018c21STomi Valkeinen break;
397f7018c21STomi Valkeinen
398f7018c21STomi Valkeinen default:
399f7018c21STomi Valkeinen return 1;
400f7018c21STomi Valkeinen }
401f7018c21STomi Valkeinen
402f7018c21STomi Valkeinen return 0;
403f7018c21STomi Valkeinen }
404f7018c21STomi Valkeinen
4058a48ac33SJani Nikula static const struct fb_ops ep93xxfb_ops = {
406f7018c21STomi Valkeinen .owner = THIS_MODULE,
407f7018c21STomi Valkeinen .fb_check_var = ep93xxfb_check_var,
408f7018c21STomi Valkeinen .fb_set_par = ep93xxfb_set_par,
409f7018c21STomi Valkeinen .fb_blank = ep93xxfb_blank,
410f7018c21STomi Valkeinen .fb_fillrect = cfb_fillrect,
411f7018c21STomi Valkeinen .fb_copyarea = cfb_copyarea,
412f7018c21STomi Valkeinen .fb_imageblit = cfb_imageblit,
413f7018c21STomi Valkeinen .fb_setcolreg = ep93xxfb_setcolreg,
414f7018c21STomi Valkeinen .fb_mmap = ep93xxfb_mmap,
415f7018c21STomi Valkeinen };
416f7018c21STomi Valkeinen
ep93xxfb_alloc_videomem(struct fb_info * info)417f7018c21STomi Valkeinen static int ep93xxfb_alloc_videomem(struct fb_info *info)
418f7018c21STomi Valkeinen {
419f7018c21STomi Valkeinen char __iomem *virt_addr;
420f7018c21STomi Valkeinen dma_addr_t phys_addr;
421f7018c21STomi Valkeinen unsigned int fb_size;
422f7018c21STomi Valkeinen
42316478b61SLinus Walleij /* Maximum 16bpp -> used memory is maximum x*y*2 bytes */
42416478b61SLinus Walleij fb_size = EP93XXFB_MAX_XRES * EP93XXFB_MAX_YRES * 2;
42516478b61SLinus Walleij
4269aee7f04SThomas Zimmermann virt_addr = dma_alloc_wc(info->device, fb_size, &phys_addr, GFP_KERNEL);
427f7018c21STomi Valkeinen if (!virt_addr)
428f7018c21STomi Valkeinen return -ENOMEM;
429f7018c21STomi Valkeinen
430f7018c21STomi Valkeinen /*
431f7018c21STomi Valkeinen * There is a bug in the ep93xx framebuffer which causes problems
432f7018c21STomi Valkeinen * if bit 27 of the physical address is set.
4337c7b2a35SAlexander A. Klimov * See: https://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2
434f7018c21STomi Valkeinen * There does not seem to be any official errata for this, but I
435f7018c21STomi Valkeinen * have confirmed the problem exists on my hardware (ep9315) at
436f7018c21STomi Valkeinen * least.
437f7018c21STomi Valkeinen */
438f7018c21STomi Valkeinen if (check_screenpage_bug && phys_addr & (1 << 27)) {
439be05e207SThomas Zimmermann fb_err(info, "ep93xx framebuffer bug. phys addr (0x%x) "
440f7018c21STomi Valkeinen "has bit 27 set: cannot init framebuffer\n",
441f7018c21STomi Valkeinen phys_addr);
442f7018c21STomi Valkeinen
4439aee7f04SThomas Zimmermann dma_free_coherent(info->device, fb_size, virt_addr, phys_addr);
444f7018c21STomi Valkeinen return -ENOMEM;
445f7018c21STomi Valkeinen }
446f7018c21STomi Valkeinen
447f7018c21STomi Valkeinen info->fix.smem_start = phys_addr;
448f7018c21STomi Valkeinen info->fix.smem_len = fb_size;
449f7018c21STomi Valkeinen info->screen_base = virt_addr;
450f7018c21STomi Valkeinen
451f7018c21STomi Valkeinen return 0;
452f7018c21STomi Valkeinen }
453f7018c21STomi Valkeinen
ep93xxfb_dealloc_videomem(struct fb_info * info)454f7018c21STomi Valkeinen static void ep93xxfb_dealloc_videomem(struct fb_info *info)
455f7018c21STomi Valkeinen {
456f7018c21STomi Valkeinen if (info->screen_base)
4579aee7f04SThomas Zimmermann dma_free_coherent(info->device, info->fix.smem_len,
458f7018c21STomi Valkeinen info->screen_base, info->fix.smem_start);
459f7018c21STomi Valkeinen }
460f7018c21STomi Valkeinen
ep93xxfb_probe(struct platform_device * pdev)461f7018c21STomi Valkeinen static int ep93xxfb_probe(struct platform_device *pdev)
462f7018c21STomi Valkeinen {
463f7018c21STomi Valkeinen struct ep93xxfb_mach_info *mach_info = dev_get_platdata(&pdev->dev);
464f7018c21STomi Valkeinen struct fb_info *info;
465f7018c21STomi Valkeinen struct ep93xx_fbi *fbi;
466f7018c21STomi Valkeinen struct resource *res;
467f7018c21STomi Valkeinen char *video_mode;
468f7018c21STomi Valkeinen int err;
469f7018c21STomi Valkeinen
470f7018c21STomi Valkeinen if (!mach_info)
471f7018c21STomi Valkeinen return -EINVAL;
472f7018c21STomi Valkeinen
473f7018c21STomi Valkeinen info = framebuffer_alloc(sizeof(struct ep93xx_fbi), &pdev->dev);
474f7018c21STomi Valkeinen if (!info)
475f7018c21STomi Valkeinen return -ENOMEM;
476f7018c21STomi Valkeinen
477f7018c21STomi Valkeinen platform_set_drvdata(pdev, info);
478f7018c21STomi Valkeinen fbi = info->par;
479f7018c21STomi Valkeinen fbi->mach_info = mach_info;
480f7018c21STomi Valkeinen
481f7018c21STomi Valkeinen err = fb_alloc_cmap(&info->cmap, 256, 0);
482f7018c21STomi Valkeinen if (err)
483f7018c21STomi Valkeinen goto failed_cmap;
484f7018c21STomi Valkeinen
485f7018c21STomi Valkeinen err = ep93xxfb_alloc_videomem(info);
486f7018c21STomi Valkeinen if (err)
487f7018c21STomi Valkeinen goto failed_videomem;
488f7018c21STomi Valkeinen
489f7018c21STomi Valkeinen res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
490f7018c21STomi Valkeinen if (!res) {
491f7018c21STomi Valkeinen err = -ENXIO;
492f7018c21STomi Valkeinen goto failed_resource;
493f7018c21STomi Valkeinen }
494f7018c21STomi Valkeinen
495f7018c21STomi Valkeinen /*
496f7018c21STomi Valkeinen * FIXME - We don't do a request_mem_region here because we are
497f7018c21STomi Valkeinen * sharing the register space with the backlight driver (see
498f7018c21STomi Valkeinen * drivers/video/backlight/ep93xx_bl.c) and doing so will cause
499f7018c21STomi Valkeinen * the second loaded driver to return -EBUSY.
500f7018c21STomi Valkeinen *
501f7018c21STomi Valkeinen * NOTE: No locking is required; the backlight does not touch
502f7018c21STomi Valkeinen * any of the framebuffer registers.
503f7018c21STomi Valkeinen */
504f7018c21STomi Valkeinen fbi->res = res;
505f7018c21STomi Valkeinen fbi->mmio_base = devm_ioremap(&pdev->dev, res->start,
506f7018c21STomi Valkeinen resource_size(res));
507f7018c21STomi Valkeinen if (!fbi->mmio_base) {
508f7018c21STomi Valkeinen err = -ENXIO;
509f7018c21STomi Valkeinen goto failed_resource;
510f7018c21STomi Valkeinen }
511f7018c21STomi Valkeinen
512f7018c21STomi Valkeinen strcpy(info->fix.id, pdev->name);
513f7018c21STomi Valkeinen info->fbops = &ep93xxfb_ops;
514f7018c21STomi Valkeinen info->fix.type = FB_TYPE_PACKED_PIXELS;
515f7018c21STomi Valkeinen info->fix.accel = FB_ACCEL_NONE;
516f7018c21STomi Valkeinen info->var.activate = FB_ACTIVATE_NOW;
517f7018c21STomi Valkeinen info->var.vmode = FB_VMODE_NONINTERLACED;
518f7018c21STomi Valkeinen info->node = -1;
519f7018c21STomi Valkeinen info->state = FBINFO_STATE_RUNNING;
520f7018c21STomi Valkeinen info->pseudo_palette = &fbi->pseudo_palette;
521f7018c21STomi Valkeinen
522f7018c21STomi Valkeinen fb_get_options("ep93xx-fb", &video_mode);
523f7018c21STomi Valkeinen err = fb_find_mode(&info->var, info, video_mode,
52416478b61SLinus Walleij NULL, 0, NULL, 16);
525f7018c21STomi Valkeinen if (err == 0) {
526be05e207SThomas Zimmermann fb_err(info, "No suitable video mode found\n");
527f7018c21STomi Valkeinen err = -EINVAL;
528f7018c21STomi Valkeinen goto failed_resource;
529f7018c21STomi Valkeinen }
530f7018c21STomi Valkeinen
531f7018c21STomi Valkeinen if (mach_info->setup) {
532f7018c21STomi Valkeinen err = mach_info->setup(pdev);
533f7018c21STomi Valkeinen if (err)
534f7018c21STomi Valkeinen goto failed_resource;
535f7018c21STomi Valkeinen }
536f7018c21STomi Valkeinen
537f7018c21STomi Valkeinen err = ep93xxfb_check_var(&info->var, info);
538f7018c21STomi Valkeinen if (err)
539f7018c21STomi Valkeinen goto failed_check;
540f7018c21STomi Valkeinen
541f7018c21STomi Valkeinen fbi->clk = devm_clk_get(&pdev->dev, NULL);
542f7018c21STomi Valkeinen if (IS_ERR(fbi->clk)) {
543f7018c21STomi Valkeinen err = PTR_ERR(fbi->clk);
544f7018c21STomi Valkeinen fbi->clk = NULL;
545f7018c21STomi Valkeinen goto failed_check;
546f7018c21STomi Valkeinen }
547f7018c21STomi Valkeinen
548f7018c21STomi Valkeinen ep93xxfb_set_par(info);
549*9d5651adSYuanjun Gong err = clk_prepare_enable(fbi->clk);
550*9d5651adSYuanjun Gong if (err)
551*9d5651adSYuanjun Gong goto failed_check;
552f7018c21STomi Valkeinen
553f7018c21STomi Valkeinen err = register_framebuffer(info);
554f7018c21STomi Valkeinen if (err)
555c84bf485SGaosheng Cui goto failed_framebuffer;
556f7018c21STomi Valkeinen
557be05e207SThomas Zimmermann fb_info(info, "registered. Mode = %dx%d-%d\n",
558f7018c21STomi Valkeinen info->var.xres, info->var.yres, info->var.bits_per_pixel);
559f7018c21STomi Valkeinen return 0;
560f7018c21STomi Valkeinen
561c84bf485SGaosheng Cui failed_framebuffer:
562c84bf485SGaosheng Cui clk_disable_unprepare(fbi->clk);
563f7018c21STomi Valkeinen failed_check:
564f7018c21STomi Valkeinen if (fbi->mach_info->teardown)
565f7018c21STomi Valkeinen fbi->mach_info->teardown(pdev);
566f7018c21STomi Valkeinen failed_resource:
567f7018c21STomi Valkeinen ep93xxfb_dealloc_videomem(info);
568f7018c21STomi Valkeinen failed_videomem:
569f7018c21STomi Valkeinen fb_dealloc_cmap(&info->cmap);
570f7018c21STomi Valkeinen failed_cmap:
571f7018c21STomi Valkeinen kfree(info);
572f7018c21STomi Valkeinen
573f7018c21STomi Valkeinen return err;
574f7018c21STomi Valkeinen }
575f7018c21STomi Valkeinen
ep93xxfb_remove(struct platform_device * pdev)57681431a9eSUwe Kleine-König static void ep93xxfb_remove(struct platform_device *pdev)
577f7018c21STomi Valkeinen {
578f7018c21STomi Valkeinen struct fb_info *info = platform_get_drvdata(pdev);
579f7018c21STomi Valkeinen struct ep93xx_fbi *fbi = info->par;
580f7018c21STomi Valkeinen
581f7018c21STomi Valkeinen unregister_framebuffer(info);
5820937a7b3SAlexander Sverdlin clk_disable_unprepare(fbi->clk);
583f7018c21STomi Valkeinen ep93xxfb_dealloc_videomem(info);
584f7018c21STomi Valkeinen fb_dealloc_cmap(&info->cmap);
585f7018c21STomi Valkeinen
586f7018c21STomi Valkeinen if (fbi->mach_info->teardown)
587f7018c21STomi Valkeinen fbi->mach_info->teardown(pdev);
588f7018c21STomi Valkeinen
589f7018c21STomi Valkeinen kfree(info);
590f7018c21STomi Valkeinen }
591f7018c21STomi Valkeinen
592f7018c21STomi Valkeinen static struct platform_driver ep93xxfb_driver = {
593f7018c21STomi Valkeinen .probe = ep93xxfb_probe,
59481431a9eSUwe Kleine-König .remove_new = ep93xxfb_remove,
595f7018c21STomi Valkeinen .driver = {
596f7018c21STomi Valkeinen .name = "ep93xx-fb",
597f7018c21STomi Valkeinen },
598f7018c21STomi Valkeinen };
599f7018c21STomi Valkeinen module_platform_driver(ep93xxfb_driver);
600f7018c21STomi Valkeinen
601f7018c21STomi Valkeinen MODULE_DESCRIPTION("EP93XX Framebuffer Driver");
602f7018c21STomi Valkeinen MODULE_ALIAS("platform:ep93xx-fb");
603f7018c21STomi Valkeinen MODULE_AUTHOR("Ryan Mallon, "
604f7018c21STomi Valkeinen "H Hartley Sweeten <hsweeten@visionengravers.com");
605f7018c21STomi Valkeinen MODULE_LICENSE("GPL");
606