Merge tag 'v5.15.57' into rpi-5.15.y
[platform/kernel/linux-rpi.git] / drivers / video / fbdev / rpisense-fb.c
1 /*
2  * Raspberry Pi Sense HAT framebuffer driver
3  * http://raspberrypi.org
4  *
5  * Copyright (C) 2015 Raspberry Pi
6  *
7  * Author: Serge Schneider
8  *
9  *  This program is free software; you can redistribute  it and/or modify it
10  *  under  the terms of  the GNU General  Public License as published by the
11  *  Free Software Foundation;  either version 2 of the  License, or (at your
12  *  option) any later version.
13  *
14  */
15
16 #include <linux/module.h>
17 #include <linux/kernel.h>
18 #include <linux/errno.h>
19 #include <linux/string.h>
20 #include <linux/mm.h>
21 #include <linux/slab.h>
22 #include <linux/uaccess.h>
23 #include <linux/delay.h>
24 #include <linux/fb.h>
25 #include <linux/init.h>
26
27 #include <linux/mfd/rpisense/framebuffer.h>
28 #include <linux/mfd/rpisense/core.h>
29
30 static bool lowlight;
31 module_param(lowlight, bool, 0);
32 MODULE_PARM_DESC(lowlight, "Reduce LED matrix brightness to one third");
33
34 static struct rpisense *rpisense;
35
36 struct rpisense_fb_param {
37         char __iomem *vmem;
38         u8 *vmem_work;
39         u32 vmemsize;
40         u8 *gamma;
41 };
42
43 static u8 gamma_default[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
44                                0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07,
45                                0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0E, 0x0F, 0x11,
46                                0x12, 0x14, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F,};
47
48 static u8 gamma_low[32] = {0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
49                            0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02,
50                            0x03, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06,
51                            0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x0A, 0x0A,};
52
53 static u8 gamma_user[32];
54
55 static u32 pseudo_palette[16];
56
57 static struct rpisense_fb_param rpisense_fb_param = {
58         .vmem = NULL,
59         .vmemsize = 128,
60         .gamma = gamma_default,
61 };
62
63 static struct fb_deferred_io rpisense_fb_defio;
64
65 static struct fb_fix_screeninfo rpisense_fb_fix = {
66         .id =           "RPi-Sense FB",
67         .type =         FB_TYPE_PACKED_PIXELS,
68         .visual =       FB_VISUAL_TRUECOLOR,
69         .xpanstep =     0,
70         .ypanstep =     0,
71         .ywrapstep =    0,
72         .accel =        FB_ACCEL_NONE,
73         .line_length =  16,
74 };
75
76 static struct fb_var_screeninfo rpisense_fb_var = {
77         .xres           = 8,
78         .yres           = 8,
79         .xres_virtual   = 8,
80         .yres_virtual   = 8,
81         .bits_per_pixel = 16,
82         .red            = {11, 5, 0},
83         .green          = {5, 6, 0},
84         .blue           = {0, 5, 0},
85 };
86
87 static ssize_t rpisense_fb_write(struct fb_info *info,
88                                  const char __user *buf, size_t count,
89                                  loff_t *ppos)
90 {
91         ssize_t res = fb_sys_write(info, buf, count, ppos);
92
93         schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
94         return res;
95 }
96
97 static void rpisense_fb_fillrect(struct fb_info *info,
98                                  const struct fb_fillrect *rect)
99 {
100         sys_fillrect(info, rect);
101         schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
102 }
103
104 static void rpisense_fb_copyarea(struct fb_info *info,
105                                  const struct fb_copyarea *area)
106 {
107         sys_copyarea(info, area);
108         schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
109 }
110
111 static void rpisense_fb_imageblit(struct fb_info *info,
112                                   const struct fb_image *image)
113 {
114         sys_imageblit(info, image);
115         schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
116 }
117
118 static void rpisense_fb_deferred_io(struct fb_info *info,
119                                 struct list_head *pagelist)
120 {
121         int i;
122         int j;
123         u8 *vmem_work = rpisense_fb_param.vmem_work;
124         u16 *mem = (u16 *)rpisense_fb_param.vmem;
125         u8 *gamma = rpisense_fb_param.gamma;
126
127         vmem_work[0] = 0;
128         for (j = 0; j < 8; j++) {
129                 for (i = 0; i < 8; i++) {
130                         vmem_work[(j * 24) + i + 1] =
131                                 gamma[(mem[(j * 8) + i] >> 11) & 0x1F];
132                         vmem_work[(j * 24) + (i + 8) + 1] =
133                                 gamma[(mem[(j * 8) + i] >> 6) & 0x1F];
134                         vmem_work[(j * 24) + (i + 16) + 1] =
135                                 gamma[(mem[(j * 8) + i]) & 0x1F];
136                 }
137         }
138         rpisense_block_write(rpisense, vmem_work, 193);
139 }
140
141 static struct fb_deferred_io rpisense_fb_defio = {
142         .delay          = HZ/100,
143         .deferred_io    = rpisense_fb_deferred_io,
144 };
145
146 static int rpisense_fb_ioctl(struct fb_info *info, unsigned int cmd,
147                              unsigned long arg)
148 {
149         switch (cmd) {
150         case SENSEFB_FBIOGET_GAMMA:
151                 if (copy_to_user((void __user *) arg, rpisense_fb_param.gamma,
152                                  sizeof(u8[32])))
153                         return -EFAULT;
154                 return 0;
155         case SENSEFB_FBIOSET_GAMMA:
156                 if (copy_from_user(gamma_user, (void __user *)arg,
157                                    sizeof(u8[32])))
158                         return -EFAULT;
159                 rpisense_fb_param.gamma = gamma_user;
160                 schedule_delayed_work(&info->deferred_work,
161                                       rpisense_fb_defio.delay);
162                 return 0;
163         case SENSEFB_FBIORESET_GAMMA:
164                 switch (arg) {
165                 case 0:
166                         rpisense_fb_param.gamma = gamma_default;
167                         break;
168                 case 1:
169                         rpisense_fb_param.gamma = gamma_low;
170                         break;
171                 case 2:
172                         rpisense_fb_param.gamma = gamma_user;
173                         break;
174                 default:
175                         return -EINVAL;
176                 }
177                 schedule_delayed_work(&info->deferred_work,
178                                       rpisense_fb_defio.delay);
179                 break;
180         default:
181                 return -EINVAL;
182         }
183         return 0;
184 }
185
186 static struct fb_ops rpisense_fb_ops = {
187         .owner          = THIS_MODULE,
188         .fb_read        = fb_sys_read,
189         .fb_write       = rpisense_fb_write,
190         .fb_fillrect    = rpisense_fb_fillrect,
191         .fb_copyarea    = rpisense_fb_copyarea,
192         .fb_imageblit   = rpisense_fb_imageblit,
193         .fb_ioctl       = rpisense_fb_ioctl,
194 };
195
196 static int rpisense_fb_probe(struct platform_device *pdev)
197 {
198         struct fb_info *info;
199         int ret = -ENOMEM;
200         struct rpisense_fb *rpisense_fb;
201
202         rpisense = rpisense_get_dev();
203         rpisense_fb = &rpisense->framebuffer;
204
205         rpisense_fb_param.vmem = vzalloc(rpisense_fb_param.vmemsize);
206         if (!rpisense_fb_param.vmem)
207                 return ret;
208
209         rpisense_fb_param.vmem_work = devm_kmalloc(&pdev->dev, 193, GFP_KERNEL);
210         if (!rpisense_fb_param.vmem_work)
211                 goto err_malloc;
212
213         info = framebuffer_alloc(0, &pdev->dev);
214         if (!info) {
215                 dev_err(&pdev->dev, "Could not allocate framebuffer.\n");
216                 goto err_malloc;
217         }
218         rpisense_fb->info = info;
219
220         rpisense_fb_fix.smem_start = (unsigned long)rpisense_fb_param.vmem;
221         rpisense_fb_fix.smem_len = rpisense_fb_param.vmemsize;
222
223         info->fbops = &rpisense_fb_ops;
224         info->fix = rpisense_fb_fix;
225         info->var = rpisense_fb_var;
226         info->fbdefio = &rpisense_fb_defio;
227         info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
228         info->screen_base = rpisense_fb_param.vmem;
229         info->screen_size = rpisense_fb_param.vmemsize;
230         info->pseudo_palette = pseudo_palette;
231
232         if (lowlight)
233                 rpisense_fb_param.gamma = gamma_low;
234
235         fb_deferred_io_init(info);
236
237         ret = register_framebuffer(info);
238         if (ret < 0) {
239                 dev_err(&pdev->dev, "Could not register framebuffer.\n");
240                 goto err_fballoc;
241         }
242
243         fb_info(info, "%s frame buffer device\n", info->fix.id);
244         schedule_delayed_work(&info->deferred_work, rpisense_fb_defio.delay);
245         return 0;
246 err_fballoc:
247         framebuffer_release(info);
248 err_malloc:
249         vfree(rpisense_fb_param.vmem);
250         return ret;
251 }
252
253 static int rpisense_fb_remove(struct platform_device *pdev)
254 {
255         struct rpisense_fb *rpisense_fb = &rpisense->framebuffer;
256         struct fb_info *info = rpisense_fb->info;
257
258         if (info) {
259                 unregister_framebuffer(info);
260                 fb_deferred_io_cleanup(info);
261                 framebuffer_release(info);
262                 vfree(rpisense_fb_param.vmem);
263         }
264
265         return 0;
266 }
267
268 #ifdef CONFIG_OF
269 static const struct of_device_id rpisense_fb_id[] = {
270         { .compatible = "rpi,rpi-sense-fb" },
271         { },
272 };
273 MODULE_DEVICE_TABLE(of, rpisense_fb_id);
274 #endif
275
276 static struct platform_device_id rpisense_fb_device_id[] = {
277         { .name = "rpi-sense-fb" },
278         { },
279 };
280 MODULE_DEVICE_TABLE(platform, rpisense_fb_device_id);
281
282 static struct platform_driver rpisense_fb_driver = {
283         .probe = rpisense_fb_probe,
284         .remove = rpisense_fb_remove,
285         .driver = {
286                 .name = "rpi-sense-fb",
287                 .owner = THIS_MODULE,
288         },
289 };
290
291 module_platform_driver(rpisense_fb_driver);
292
293 MODULE_DESCRIPTION("Raspberry Pi Sense HAT framebuffer driver");
294 MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>");
295 MODULE_LICENSE("GPL");
296