Linux 6.9-rc1
[linux-2.6-microblaze.git] / drivers / video / fbdev / hecubafb.c
1 /*
2  * linux/drivers/video/hecubafb.c -- FB driver for Hecuba/Apollo controller
3  *
4  * Copyright (C) 2006, Jaya Kumar
5  * This work was sponsored by CIS(M) Sdn Bhd
6  *
7  * This file is subject to the terms and conditions of the GNU General Public
8  * License. See the file COPYING in the main directory of this archive for
9  * more details.
10  *
11  * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
12  * This work was possible because of apollo display code from E-Ink's website
13  * http://support.eink.com/community
14  * All information used to write this code is from public material made
15  * available by E-Ink on its support site. Some commands such as 0xA4
16  * were found by looping through cmd=0x00 thru 0xFF and supplying random
17  * values. There are other commands that the display is capable of,
18  * beyond the 5 used here but they are more complex.
19  *
20  * This driver is written to be used with the Hecuba display architecture.
21  * The actual display chip is called Apollo and the interface electronics
22  * it needs is called Hecuba.
23  *
24  * It is intended to be architecture independent. A board specific driver
25  * must be used to perform all the physical IO interactions. An example
26  * is provided as n411.c
27  *
28  */
29
30 #include <linux/module.h>
31 #include <linux/kernel.h>
32 #include <linux/errno.h>
33 #include <linux/string.h>
34 #include <linux/mm.h>
35 #include <linux/vmalloc.h>
36 #include <linux/delay.h>
37 #include <linux/interrupt.h>
38 #include <linux/fb.h>
39 #include <linux/init.h>
40 #include <linux/platform_device.h>
41 #include <linux/list.h>
42 #include <linux/uaccess.h>
43
44 #include <video/hecubafb.h>
45
46 /* Display specific information */
47 #define DPY_W 600
48 #define DPY_H 800
49
50 static const struct fb_fix_screeninfo hecubafb_fix = {
51         .id =           "hecubafb",
52         .type =         FB_TYPE_PACKED_PIXELS,
53         .visual =       FB_VISUAL_MONO01,
54         .xpanstep =     0,
55         .ypanstep =     0,
56         .ywrapstep =    0,
57         .line_length =  DPY_W,
58         .accel =        FB_ACCEL_NONE,
59 };
60
61 static const struct fb_var_screeninfo hecubafb_var = {
62         .xres           = DPY_W,
63         .yres           = DPY_H,
64         .xres_virtual   = DPY_W,
65         .yres_virtual   = DPY_H,
66         .bits_per_pixel = 1,
67         .nonstd         = 1,
68 };
69
70 /* main hecubafb functions */
71
72 static void apollo_send_data(struct hecubafb_par *par, unsigned char data)
73 {
74         /* set data */
75         par->board->set_data(par, data);
76
77         /* set DS low */
78         par->board->set_ctl(par, HCB_DS_BIT, 0);
79
80         /* wait for ack */
81         par->board->wait_for_ack(par, 0);
82
83         /* set DS hi */
84         par->board->set_ctl(par, HCB_DS_BIT, 1);
85
86         /* wait for ack to clear */
87         par->board->wait_for_ack(par, 1);
88 }
89
90 static void apollo_send_command(struct hecubafb_par *par, unsigned char data)
91 {
92         /* command so set CD to high */
93         par->board->set_ctl(par, HCB_CD_BIT, 1);
94
95         /* actually strobe with command */
96         apollo_send_data(par, data);
97
98         /* clear CD back to low */
99         par->board->set_ctl(par, HCB_CD_BIT, 0);
100 }
101
102 static void hecubafb_dpy_update(struct hecubafb_par *par)
103 {
104         int i;
105         unsigned char *buf = par->info->screen_buffer;
106
107         apollo_send_command(par, APOLLO_START_NEW_IMG);
108
109         for (i=0; i < (DPY_W*DPY_H/8); i++) {
110                 apollo_send_data(par, *(buf++));
111         }
112
113         apollo_send_command(par, APOLLO_STOP_IMG_DATA);
114         apollo_send_command(par, APOLLO_DISPLAY_IMG);
115 }
116
117 /* this is called back from the deferred io workqueue */
118 static void hecubafb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
119 {
120         hecubafb_dpy_update(info->par);
121 }
122
123 static void hecubafb_defio_damage_range(struct fb_info *info, off_t off, size_t len)
124 {
125         struct hecubafb_par *par = info->par;
126
127         hecubafb_dpy_update(par);
128 }
129
130 static void hecubafb_defio_damage_area(struct fb_info *info, u32 x, u32 y,
131                                        u32 width, u32 height)
132 {
133         struct hecubafb_par *par = info->par;
134
135         hecubafb_dpy_update(par);
136 }
137
138 FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(hecubafb,
139                                    hecubafb_defio_damage_range,
140                                    hecubafb_defio_damage_area)
141
142 static const struct fb_ops hecubafb_ops = {
143         .owner  = THIS_MODULE,
144         FB_DEFAULT_DEFERRED_OPS(hecubafb),
145 };
146
147 static struct fb_deferred_io hecubafb_defio = {
148         .delay          = HZ,
149         .deferred_io    = hecubafb_dpy_deferred_io,
150 };
151
152 static int hecubafb_probe(struct platform_device *dev)
153 {
154         struct fb_info *info;
155         struct hecuba_board *board;
156         int retval = -ENOMEM;
157         int videomemorysize;
158         unsigned char *videomemory;
159         struct hecubafb_par *par;
160
161         /* pick up board specific routines */
162         board = dev->dev.platform_data;
163         if (!board)
164                 return -EINVAL;
165
166         /* try to count device specific driver, if can't, platform recalls */
167         if (!try_module_get(board->owner))
168                 return -ENODEV;
169
170         videomemorysize = (DPY_W*DPY_H)/8;
171
172         videomemory = vzalloc(videomemorysize);
173         if (!videomemory)
174                 goto err_videomem_alloc;
175
176         info = framebuffer_alloc(sizeof(struct hecubafb_par), &dev->dev);
177         if (!info)
178                 goto err_fballoc;
179
180         info->screen_buffer = videomemory;
181         info->fbops = &hecubafb_ops;
182
183         info->var = hecubafb_var;
184         info->fix = hecubafb_fix;
185         info->fix.smem_len = videomemorysize;
186         par = info->par;
187         par->info = info;
188         par->board = board;
189         par->send_command = apollo_send_command;
190         par->send_data = apollo_send_data;
191
192         info->flags = FBINFO_VIRTFB;
193
194         info->fbdefio = &hecubafb_defio;
195         fb_deferred_io_init(info);
196
197         retval = register_framebuffer(info);
198         if (retval < 0)
199                 goto err_fbreg;
200         platform_set_drvdata(dev, info);
201
202         fb_info(info, "Hecuba frame buffer device, using %dK of video memory\n",
203                 videomemorysize >> 10);
204
205         /* this inits the dpy */
206         retval = par->board->init(par);
207         if (retval < 0)
208                 goto err_fbreg;
209
210         return 0;
211 err_fbreg:
212         framebuffer_release(info);
213 err_fballoc:
214         vfree(videomemory);
215 err_videomem_alloc:
216         module_put(board->owner);
217         return retval;
218 }
219
220 static void hecubafb_remove(struct platform_device *dev)
221 {
222         struct fb_info *info = platform_get_drvdata(dev);
223
224         if (info) {
225                 struct hecubafb_par *par = info->par;
226                 fb_deferred_io_cleanup(info);
227                 unregister_framebuffer(info);
228                 vfree(info->screen_buffer);
229                 if (par->board->remove)
230                         par->board->remove(par);
231                 module_put(par->board->owner);
232                 framebuffer_release(info);
233         }
234 }
235
236 static struct platform_driver hecubafb_driver = {
237         .probe  = hecubafb_probe,
238         .remove_new = hecubafb_remove,
239         .driver = {
240                 .name   = "hecubafb",
241         },
242 };
243 module_platform_driver(hecubafb_driver);
244
245 MODULE_DESCRIPTION("fbdev driver for Hecuba/Apollo controller");
246 MODULE_AUTHOR("Jaya Kumar");
247 MODULE_LICENSE("GPL");