2 * Copyright (C) 2007 Google, Inc.
3 * Copyright (C) 2012 Intel, Inc.
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
16 #include <linux/module.h>
17 #include <linux/kernel.h>
18 #include <linux/dma-mapping.h>
19 #include <linux/errno.h>
20 #include <linux/string.h>
21 #include <linux/slab.h>
22 #include <linux/delay.h>
25 #include <linux/init.h>
26 #include <linux/interrupt.h>
27 #include <linux/ioport.h>
28 #include <linux/platform_device.h>
29 #include <linux/acpi.h>
37 FB_SET_ROTATION
= 0x14,
39 FB_GET_PHYS_WIDTH
= 0x1c,
40 FB_GET_PHYS_HEIGHT
= 0x20,
42 FB_INT_VSYNC
= 1U << 0,
43 FB_INT_BASE_UPDATE_DONE
= 1U << 1
47 void __iomem
*reg_base
;
50 wait_queue_head_t wait
;
51 int base_update_count
;
57 static irqreturn_t
goldfish_fb_interrupt(int irq
, void *dev_id
)
59 unsigned long irq_flags
;
60 struct goldfish_fb
*fb
= dev_id
;
63 spin_lock_irqsave(&fb
->lock
, irq_flags
);
64 status
= readl(fb
->reg_base
+ FB_INT_STATUS
);
65 if (status
& FB_INT_BASE_UPDATE_DONE
) {
66 fb
->base_update_count
++;
69 spin_unlock_irqrestore(&fb
->lock
, irq_flags
);
70 return status
? IRQ_HANDLED
: IRQ_NONE
;
73 static inline u32
convert_bitfield(int val
, struct fb_bitfield
*bf
)
75 unsigned int mask
= (1 << bf
->length
) - 1;
77 return (val
>> (16 - bf
->length
) & mask
) << bf
->offset
;
81 goldfish_fb_setcolreg(unsigned int regno
, unsigned int red
, unsigned int green
,
82 unsigned int blue
, unsigned int transp
, struct fb_info
*info
)
84 struct goldfish_fb
*fb
= container_of(info
, struct goldfish_fb
, fb
);
87 fb
->cmap
[regno
] = convert_bitfield(transp
, &fb
->fb
.var
.transp
) |
88 convert_bitfield(blue
, &fb
->fb
.var
.blue
) |
89 convert_bitfield(green
, &fb
->fb
.var
.green
) |
90 convert_bitfield(red
, &fb
->fb
.var
.red
);
97 static int goldfish_fb_check_var(struct fb_var_screeninfo
*var
,
100 if ((var
->rotate
& 1) != (info
->var
.rotate
& 1)) {
101 if ((var
->xres
!= info
->var
.yres
) ||
102 (var
->yres
!= info
->var
.xres
) ||
103 (var
->xres_virtual
!= info
->var
.yres
) ||
104 (var
->yres_virtual
> info
->var
.xres
* 2) ||
105 (var
->yres_virtual
< info
->var
.xres
)) {
109 if ((var
->xres
!= info
->var
.xres
) ||
110 (var
->yres
!= info
->var
.yres
) ||
111 (var
->xres_virtual
!= info
->var
.xres
) ||
112 (var
->yres_virtual
> info
->var
.yres
* 2) ||
113 (var
->yres_virtual
< info
->var
.yres
)) {
117 if ((var
->xoffset
!= info
->var
.xoffset
) ||
118 (var
->bits_per_pixel
!= info
->var
.bits_per_pixel
) ||
119 (var
->grayscale
!= info
->var
.grayscale
)) {
125 static int goldfish_fb_set_par(struct fb_info
*info
)
127 struct goldfish_fb
*fb
= container_of(info
, struct goldfish_fb
, fb
);
129 if (fb
->rotation
!= fb
->fb
.var
.rotate
) {
130 info
->fix
.line_length
= info
->var
.xres
* 2;
131 fb
->rotation
= fb
->fb
.var
.rotate
;
132 writel(fb
->rotation
, fb
->reg_base
+ FB_SET_ROTATION
);
138 static int goldfish_fb_pan_display(struct fb_var_screeninfo
*var
,
139 struct fb_info
*info
)
141 unsigned long irq_flags
;
142 int base_update_count
;
143 struct goldfish_fb
*fb
= container_of(info
, struct goldfish_fb
, fb
);
145 spin_lock_irqsave(&fb
->lock
, irq_flags
);
146 base_update_count
= fb
->base_update_count
;
147 writel(fb
->fb
.fix
.smem_start
+ fb
->fb
.var
.xres
* 2 * var
->yoffset
,
148 fb
->reg_base
+ FB_SET_BASE
);
149 spin_unlock_irqrestore(&fb
->lock
, irq_flags
);
150 wait_event_timeout(fb
->wait
,
151 fb
->base_update_count
!= base_update_count
, HZ
/ 15);
152 if (fb
->base_update_count
== base_update_count
)
153 pr_err("%s: timeout waiting for base update\n", __func__
);
157 static int goldfish_fb_blank(int blank
, struct fb_info
*info
)
159 struct goldfish_fb
*fb
= container_of(info
, struct goldfish_fb
, fb
);
162 case FB_BLANK_NORMAL
:
163 writel(1, fb
->reg_base
+ FB_SET_BLANK
);
165 case FB_BLANK_UNBLANK
:
166 writel(0, fb
->reg_base
+ FB_SET_BLANK
);
172 static struct fb_ops goldfish_fb_ops
= {
173 .owner
= THIS_MODULE
,
174 .fb_check_var
= goldfish_fb_check_var
,
175 .fb_set_par
= goldfish_fb_set_par
,
176 .fb_setcolreg
= goldfish_fb_setcolreg
,
177 .fb_pan_display
= goldfish_fb_pan_display
,
178 .fb_blank
= goldfish_fb_blank
,
179 .fb_fillrect
= cfb_fillrect
,
180 .fb_copyarea
= cfb_copyarea
,
181 .fb_imageblit
= cfb_imageblit
,
185 static int goldfish_fb_probe(struct platform_device
*pdev
)
189 struct goldfish_fb
*fb
;
194 fb
= kzalloc(sizeof(*fb
), GFP_KERNEL
);
197 goto err_fb_alloc_failed
;
199 spin_lock_init(&fb
->lock
);
200 init_waitqueue_head(&fb
->wait
);
201 platform_set_drvdata(pdev
, fb
);
203 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
208 fb
->reg_base
= ioremap(r
->start
, PAGE_SIZE
);
209 if (fb
->reg_base
== NULL
) {
214 fb
->irq
= platform_get_irq(pdev
, 0);
220 width
= readl(fb
->reg_base
+ FB_GET_WIDTH
);
221 height
= readl(fb
->reg_base
+ FB_GET_HEIGHT
);
223 fb
->fb
.fbops
= &goldfish_fb_ops
;
224 fb
->fb
.flags
= FBINFO_FLAG_DEFAULT
;
225 fb
->fb
.pseudo_palette
= fb
->cmap
;
226 fb
->fb
.fix
.type
= FB_TYPE_PACKED_PIXELS
;
227 fb
->fb
.fix
.visual
= FB_VISUAL_TRUECOLOR
;
228 fb
->fb
.fix
.line_length
= width
* 2;
229 fb
->fb
.fix
.accel
= FB_ACCEL_NONE
;
230 fb
->fb
.fix
.ypanstep
= 1;
232 fb
->fb
.var
.xres
= width
;
233 fb
->fb
.var
.yres
= height
;
234 fb
->fb
.var
.xres_virtual
= width
;
235 fb
->fb
.var
.yres_virtual
= height
* 2;
236 fb
->fb
.var
.bits_per_pixel
= 16;
237 fb
->fb
.var
.activate
= FB_ACTIVATE_NOW
;
238 fb
->fb
.var
.height
= readl(fb
->reg_base
+ FB_GET_PHYS_HEIGHT
);
239 fb
->fb
.var
.width
= readl(fb
->reg_base
+ FB_GET_PHYS_WIDTH
);
240 fb
->fb
.var
.pixclock
= 0;
242 fb
->fb
.var
.red
.offset
= 11;
243 fb
->fb
.var
.red
.length
= 5;
244 fb
->fb
.var
.green
.offset
= 5;
245 fb
->fb
.var
.green
.length
= 6;
246 fb
->fb
.var
.blue
.offset
= 0;
247 fb
->fb
.var
.blue
.length
= 5;
249 framesize
= width
* height
* 2 * 2;
250 fb
->fb
.screen_base
= (char __force __iomem
*)dma_alloc_coherent(
251 &pdev
->dev
, framesize
,
252 &fbpaddr
, GFP_KERNEL
);
253 pr_debug("allocating frame buffer %d * %d, got %p\n",
254 width
, height
, fb
->fb
.screen_base
);
255 if (fb
->fb
.screen_base
== NULL
) {
257 goto err_alloc_screen_base_failed
;
259 fb
->fb
.fix
.smem_start
= fbpaddr
;
260 fb
->fb
.fix
.smem_len
= framesize
;
262 ret
= fb_set_var(&fb
->fb
, &fb
->fb
.var
);
264 goto err_fb_set_var_failed
;
266 ret
= request_irq(fb
->irq
, goldfish_fb_interrupt
, IRQF_SHARED
,
269 goto err_request_irq_failed
;
271 writel(FB_INT_BASE_UPDATE_DONE
, fb
->reg_base
+ FB_INT_ENABLE
);
272 goldfish_fb_pan_display(&fb
->fb
.var
, &fb
->fb
); /* updates base */
274 ret
= register_framebuffer(&fb
->fb
);
276 goto err_register_framebuffer_failed
;
279 err_register_framebuffer_failed
:
280 free_irq(fb
->irq
, fb
);
281 err_request_irq_failed
:
282 err_fb_set_var_failed
:
283 dma_free_coherent(&pdev
->dev
, framesize
,
284 (void *)fb
->fb
.screen_base
,
285 fb
->fb
.fix
.smem_start
);
286 err_alloc_screen_base_failed
:
288 iounmap(fb
->reg_base
);
295 static int goldfish_fb_remove(struct platform_device
*pdev
)
298 struct goldfish_fb
*fb
= platform_get_drvdata(pdev
);
300 framesize
= fb
->fb
.var
.xres_virtual
* fb
->fb
.var
.yres_virtual
* 2;
301 unregister_framebuffer(&fb
->fb
);
302 free_irq(fb
->irq
, fb
);
304 dma_free_coherent(&pdev
->dev
, framesize
, (void *)fb
->fb
.screen_base
,
305 fb
->fb
.fix
.smem_start
);
306 iounmap(fb
->reg_base
);
311 static const struct of_device_id goldfish_fb_of_match
[] = {
312 { .compatible
= "google,goldfish-fb", },
315 MODULE_DEVICE_TABLE(of
, goldfish_fb_of_match
);
317 static const struct acpi_device_id goldfish_fb_acpi_match
[] = {
321 MODULE_DEVICE_TABLE(acpi
, goldfish_fb_acpi_match
);
323 static struct platform_driver goldfish_fb_driver
= {
324 .probe
= goldfish_fb_probe
,
325 .remove
= goldfish_fb_remove
,
327 .name
= "goldfish_fb",
328 .of_match_table
= goldfish_fb_of_match
,
329 .acpi_match_table
= ACPI_PTR(goldfish_fb_acpi_match
),
333 module_platform_driver(goldfish_fb_driver
);
335 MODULE_LICENSE("GPL v2");