1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2007 Google, Inc.
4 * Copyright (C) 2012 Intel, Inc.
7 #include <linux/module.h>
8 #include <linux/kernel.h>
9 #include <linux/dma-mapping.h>
10 #include <linux/errno.h>
11 #include <linux/string.h>
12 #include <linux/slab.h>
13 #include <linux/delay.h>
16 #include <linux/init.h>
17 #include <linux/interrupt.h>
18 #include <linux/ioport.h>
19 #include <linux/platform_device.h>
20 #include <linux/acpi.h>
28 FB_SET_ROTATION
= 0x14,
30 FB_GET_PHYS_WIDTH
= 0x1c,
31 FB_GET_PHYS_HEIGHT
= 0x20,
33 FB_INT_VSYNC
= 1U << 0,
34 FB_INT_BASE_UPDATE_DONE
= 1U << 1
38 void __iomem
*reg_base
;
41 wait_queue_head_t wait
;
42 int base_update_count
;
48 static irqreturn_t
goldfish_fb_interrupt(int irq
, void *dev_id
)
50 unsigned long irq_flags
;
51 struct goldfish_fb
*fb
= dev_id
;
54 spin_lock_irqsave(&fb
->lock
, irq_flags
);
55 status
= readl(fb
->reg_base
+ FB_INT_STATUS
);
56 if (status
& FB_INT_BASE_UPDATE_DONE
) {
57 fb
->base_update_count
++;
60 spin_unlock_irqrestore(&fb
->lock
, irq_flags
);
61 return status
? IRQ_HANDLED
: IRQ_NONE
;
64 static inline u32
convert_bitfield(int val
, struct fb_bitfield
*bf
)
66 unsigned int mask
= (1 << bf
->length
) - 1;
68 return (val
>> (16 - bf
->length
) & mask
) << bf
->offset
;
72 goldfish_fb_setcolreg(unsigned int regno
, unsigned int red
, unsigned int green
,
73 unsigned int blue
, unsigned int transp
, struct fb_info
*info
)
75 struct goldfish_fb
*fb
= container_of(info
, struct goldfish_fb
, fb
);
78 fb
->cmap
[regno
] = convert_bitfield(transp
, &fb
->fb
.var
.transp
) |
79 convert_bitfield(blue
, &fb
->fb
.var
.blue
) |
80 convert_bitfield(green
, &fb
->fb
.var
.green
) |
81 convert_bitfield(red
, &fb
->fb
.var
.red
);
88 static int goldfish_fb_check_var(struct fb_var_screeninfo
*var
,
91 if ((var
->rotate
& 1) != (info
->var
.rotate
& 1)) {
92 if ((var
->xres
!= info
->var
.yres
) ||
93 (var
->yres
!= info
->var
.xres
) ||
94 (var
->xres_virtual
!= info
->var
.yres
) ||
95 (var
->yres_virtual
> info
->var
.xres
* 2) ||
96 (var
->yres_virtual
< info
->var
.xres
)) {
100 if ((var
->xres
!= info
->var
.xres
) ||
101 (var
->yres
!= info
->var
.yres
) ||
102 (var
->xres_virtual
!= info
->var
.xres
) ||
103 (var
->yres_virtual
> info
->var
.yres
* 2) ||
104 (var
->yres_virtual
< info
->var
.yres
)) {
108 if ((var
->xoffset
!= info
->var
.xoffset
) ||
109 (var
->bits_per_pixel
!= info
->var
.bits_per_pixel
) ||
110 (var
->grayscale
!= info
->var
.grayscale
)) {
116 static int goldfish_fb_set_par(struct fb_info
*info
)
118 struct goldfish_fb
*fb
= container_of(info
, struct goldfish_fb
, fb
);
120 if (fb
->rotation
!= fb
->fb
.var
.rotate
) {
121 info
->fix
.line_length
= info
->var
.xres
* 2;
122 fb
->rotation
= fb
->fb
.var
.rotate
;
123 writel(fb
->rotation
, fb
->reg_base
+ FB_SET_ROTATION
);
129 static int goldfish_fb_pan_display(struct fb_var_screeninfo
*var
,
130 struct fb_info
*info
)
132 unsigned long irq_flags
;
133 int base_update_count
;
134 struct goldfish_fb
*fb
= container_of(info
, struct goldfish_fb
, fb
);
136 spin_lock_irqsave(&fb
->lock
, irq_flags
);
137 base_update_count
= fb
->base_update_count
;
138 writel(fb
->fb
.fix
.smem_start
+ fb
->fb
.var
.xres
* 2 * var
->yoffset
,
139 fb
->reg_base
+ FB_SET_BASE
);
140 spin_unlock_irqrestore(&fb
->lock
, irq_flags
);
141 wait_event_timeout(fb
->wait
,
142 fb
->base_update_count
!= base_update_count
, HZ
/ 15);
143 if (fb
->base_update_count
== base_update_count
)
144 pr_err("%s: timeout waiting for base update\n", __func__
);
148 static int goldfish_fb_blank(int blank
, struct fb_info
*info
)
150 struct goldfish_fb
*fb
= container_of(info
, struct goldfish_fb
, fb
);
153 case FB_BLANK_NORMAL
:
154 writel(1, fb
->reg_base
+ FB_SET_BLANK
);
156 case FB_BLANK_UNBLANK
:
157 writel(0, fb
->reg_base
+ FB_SET_BLANK
);
163 static const struct fb_ops goldfish_fb_ops
= {
164 .owner
= THIS_MODULE
,
165 .fb_check_var
= goldfish_fb_check_var
,
166 .fb_set_par
= goldfish_fb_set_par
,
167 .fb_setcolreg
= goldfish_fb_setcolreg
,
168 .fb_pan_display
= goldfish_fb_pan_display
,
169 .fb_blank
= goldfish_fb_blank
,
170 .fb_fillrect
= cfb_fillrect
,
171 .fb_copyarea
= cfb_copyarea
,
172 .fb_imageblit
= cfb_imageblit
,
176 static int goldfish_fb_probe(struct platform_device
*pdev
)
180 struct goldfish_fb
*fb
;
185 fb
= kzalloc(sizeof(*fb
), GFP_KERNEL
);
188 goto err_fb_alloc_failed
;
190 spin_lock_init(&fb
->lock
);
191 init_waitqueue_head(&fb
->wait
);
192 platform_set_drvdata(pdev
, fb
);
194 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
199 fb
->reg_base
= ioremap(r
->start
, PAGE_SIZE
);
200 if (fb
->reg_base
== NULL
) {
205 fb
->irq
= platform_get_irq(pdev
, 0);
211 width
= readl(fb
->reg_base
+ FB_GET_WIDTH
);
212 height
= readl(fb
->reg_base
+ FB_GET_HEIGHT
);
214 fb
->fb
.fbops
= &goldfish_fb_ops
;
215 fb
->fb
.flags
= FBINFO_FLAG_DEFAULT
;
216 fb
->fb
.pseudo_palette
= fb
->cmap
;
217 fb
->fb
.fix
.type
= FB_TYPE_PACKED_PIXELS
;
218 fb
->fb
.fix
.visual
= FB_VISUAL_TRUECOLOR
;
219 fb
->fb
.fix
.line_length
= width
* 2;
220 fb
->fb
.fix
.accel
= FB_ACCEL_NONE
;
221 fb
->fb
.fix
.ypanstep
= 1;
223 fb
->fb
.var
.xres
= width
;
224 fb
->fb
.var
.yres
= height
;
225 fb
->fb
.var
.xres_virtual
= width
;
226 fb
->fb
.var
.yres_virtual
= height
* 2;
227 fb
->fb
.var
.bits_per_pixel
= 16;
228 fb
->fb
.var
.activate
= FB_ACTIVATE_NOW
;
229 fb
->fb
.var
.height
= readl(fb
->reg_base
+ FB_GET_PHYS_HEIGHT
);
230 fb
->fb
.var
.width
= readl(fb
->reg_base
+ FB_GET_PHYS_WIDTH
);
231 fb
->fb
.var
.pixclock
= 0;
233 fb
->fb
.var
.red
.offset
= 11;
234 fb
->fb
.var
.red
.length
= 5;
235 fb
->fb
.var
.green
.offset
= 5;
236 fb
->fb
.var
.green
.length
= 6;
237 fb
->fb
.var
.blue
.offset
= 0;
238 fb
->fb
.var
.blue
.length
= 5;
240 framesize
= width
* height
* 2 * 2;
241 fb
->fb
.screen_base
= (char __force __iomem
*)dma_alloc_coherent(
242 &pdev
->dev
, framesize
,
243 &fbpaddr
, GFP_KERNEL
);
244 pr_debug("allocating frame buffer %d * %d, got %p\n",
245 width
, height
, fb
->fb
.screen_base
);
246 if (fb
->fb
.screen_base
== NULL
) {
248 goto err_alloc_screen_base_failed
;
250 fb
->fb
.fix
.smem_start
= fbpaddr
;
251 fb
->fb
.fix
.smem_len
= framesize
;
253 ret
= fb_set_var(&fb
->fb
, &fb
->fb
.var
);
255 goto err_fb_set_var_failed
;
257 ret
= request_irq(fb
->irq
, goldfish_fb_interrupt
, IRQF_SHARED
,
260 goto err_request_irq_failed
;
262 writel(FB_INT_BASE_UPDATE_DONE
, fb
->reg_base
+ FB_INT_ENABLE
);
263 goldfish_fb_pan_display(&fb
->fb
.var
, &fb
->fb
); /* updates base */
265 ret
= register_framebuffer(&fb
->fb
);
267 goto err_register_framebuffer_failed
;
270 err_register_framebuffer_failed
:
271 free_irq(fb
->irq
, fb
);
272 err_request_irq_failed
:
273 err_fb_set_var_failed
:
274 dma_free_coherent(&pdev
->dev
, framesize
,
275 (void *)fb
->fb
.screen_base
,
276 fb
->fb
.fix
.smem_start
);
277 err_alloc_screen_base_failed
:
279 iounmap(fb
->reg_base
);
286 static int goldfish_fb_remove(struct platform_device
*pdev
)
289 struct goldfish_fb
*fb
= platform_get_drvdata(pdev
);
291 framesize
= fb
->fb
.var
.xres_virtual
* fb
->fb
.var
.yres_virtual
* 2;
292 unregister_framebuffer(&fb
->fb
);
293 free_irq(fb
->irq
, fb
);
295 dma_free_coherent(&pdev
->dev
, framesize
, (void *)fb
->fb
.screen_base
,
296 fb
->fb
.fix
.smem_start
);
297 iounmap(fb
->reg_base
);
302 static const struct of_device_id goldfish_fb_of_match
[] = {
303 { .compatible
= "google,goldfish-fb", },
306 MODULE_DEVICE_TABLE(of
, goldfish_fb_of_match
);
308 static const struct acpi_device_id goldfish_fb_acpi_match
[] = {
312 MODULE_DEVICE_TABLE(acpi
, goldfish_fb_acpi_match
);
314 static struct platform_driver goldfish_fb_driver
= {
315 .probe
= goldfish_fb_probe
,
316 .remove
= goldfish_fb_remove
,
318 .name
= "goldfish_fb",
319 .of_match_table
= goldfish_fb_of_match
,
320 .acpi_match_table
= ACPI_PTR(goldfish_fb_acpi_match
),
324 module_platform_driver(goldfish_fb_driver
);
326 MODULE_LICENSE("GPL v2");