1// SPDX-License-Identifier: GPL-2.0-only
2/***************************************************************************
3 *   Copyright (C) 2010-2012 by Bruno Pr��mont <bonbons@linux-vserver.org>  *
4 *                                                                         *
5 *   Based on Logitech G13 driver (v0.4)                                   *
6 *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
7 *                                                                         *
8 ***************************************************************************/
9
10#include <linux/hid.h>
11#include <linux/vmalloc.h>
12
13#include <linux/fb.h>
14#include <linux/module.h>
15
16#include "hid-picolcd.h"
17
18/* Framebuffer
19 *
20 * The PicoLCD use a Topway LCD module of 256x64 pixel
21 * This display area is tiled over 4 controllers with 8 tiles
22 * each. Each tile has 8x64 pixel, each data byte representing
23 * a 1-bit wide vertical line of the tile.
24 *
25 * The display can be updated at a tile granularity.
26 *
27 *       Chip 1           Chip 2           Chip 3           Chip 4
28 * +----------------+----------------+----------------+----------------+
29 * |     Tile 1     |     Tile 1     |     Tile 1     |     Tile 1     |
30 * +----------------+----------------+----------------+----------------+
31 * |     Tile 2     |     Tile 2     |     Tile 2     |     Tile 2     |
32 * +----------------+----------------+----------------+----------------+
33 *                                  ...
34 * +----------------+----------------+----------------+----------------+
35 * |     Tile 8     |     Tile 8     |     Tile 8     |     Tile 8     |
36 * +----------------+----------------+----------------+----------------+
37 */
38#define PICOLCDFB_NAME "picolcdfb"
39#define PICOLCDFB_WIDTH (256)
40#define PICOLCDFB_HEIGHT (64)
41#define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8)
42
43#define PICOLCDFB_UPDATE_RATE_LIMIT   10
44#define PICOLCDFB_UPDATE_RATE_DEFAULT  2
45
46/* Framebuffer visual structures */
47static const struct fb_fix_screeninfo picolcdfb_fix = {
48	.id          = PICOLCDFB_NAME,
49	.type        = FB_TYPE_PACKED_PIXELS,
50	.visual      = FB_VISUAL_MONO01,
51	.xpanstep    = 0,
52	.ypanstep    = 0,
53	.ywrapstep   = 0,
54	.line_length = PICOLCDFB_WIDTH / 8,
55	.accel       = FB_ACCEL_NONE,
56};
57
58static const struct fb_var_screeninfo picolcdfb_var = {
59	.xres           = PICOLCDFB_WIDTH,
60	.yres           = PICOLCDFB_HEIGHT,
61	.xres_virtual   = PICOLCDFB_WIDTH,
62	.yres_virtual   = PICOLCDFB_HEIGHT,
63	.width          = 103,
64	.height         = 26,
65	.bits_per_pixel = 1,
66	.grayscale      = 1,
67	.red            = {
68		.offset = 0,
69		.length = 1,
70		.msb_right = 0,
71	},
72	.green          = {
73		.offset = 0,
74		.length = 1,
75		.msb_right = 0,
76	},
77	.blue           = {
78		.offset = 0,
79		.length = 1,
80		.msb_right = 0,
81	},
82	.transp         = {
83		.offset = 0,
84		.length = 0,
85		.msb_right = 0,
86	},
87};
88
89/* Send a given tile to PicoLCD */
90static int picolcd_fb_send_tile(struct picolcd_data *data, u8 *vbitmap,
91		int chip, int tile)
92{
93	struct hid_report *report1, *report2;
94	unsigned long flags;
95	u8 *tdata;
96	int i;
97
98	report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, data->hdev);
99	if (!report1 || report1->maxfield != 1)
100		return -ENODEV;
101	report2 = picolcd_out_report(REPORT_LCD_DATA, data->hdev);
102	if (!report2 || report2->maxfield != 1)
103		return -ENODEV;
104
105	spin_lock_irqsave(&data->lock, flags);
106	if ((data->status & PICOLCD_FAILED)) {
107		spin_unlock_irqrestore(&data->lock, flags);
108		return -ENODEV;
109	}
110	hid_set_field(report1->field[0],  0, chip << 2);
111	hid_set_field(report1->field[0],  1, 0x02);
112	hid_set_field(report1->field[0],  2, 0x00);
113	hid_set_field(report1->field[0],  3, 0x00);
114	hid_set_field(report1->field[0],  4, 0xb8 | tile);
115	hid_set_field(report1->field[0],  5, 0x00);
116	hid_set_field(report1->field[0],  6, 0x00);
117	hid_set_field(report1->field[0],  7, 0x40);
118	hid_set_field(report1->field[0],  8, 0x00);
119	hid_set_field(report1->field[0],  9, 0x00);
120	hid_set_field(report1->field[0], 10,   32);
121
122	hid_set_field(report2->field[0],  0, (chip << 2) | 0x01);
123	hid_set_field(report2->field[0],  1, 0x00);
124	hid_set_field(report2->field[0],  2, 0x00);
125	hid_set_field(report2->field[0],  3,   32);
126
127	tdata = vbitmap + (tile * 4 + chip) * 64;
128	for (i = 0; i < 64; i++)
129		if (i < 32)
130			hid_set_field(report1->field[0], 11 + i, tdata[i]);
131		else
132			hid_set_field(report2->field[0], 4 + i - 32, tdata[i]);
133
134	hid_hw_request(data->hdev, report1, HID_REQ_SET_REPORT);
135	hid_hw_request(data->hdev, report2, HID_REQ_SET_REPORT);
136	spin_unlock_irqrestore(&data->lock, flags);
137	return 0;
138}
139
140/* Translate a single tile*/
141static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp,
142		int chip, int tile)
143{
144	int i, b, changed = 0;
145	u8 tdata[64];
146	u8 *vdata = vbitmap + (tile * 4 + chip) * 64;
147
148	if (bpp == 1) {
149		for (b = 7; b >= 0; b--) {
150			const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32;
151			for (i = 0; i < 64; i++) {
152				tdata[i] <<= 1;
153				tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01;
154			}
155		}
156	} else if (bpp == 8) {
157		for (b = 7; b >= 0; b--) {
158			const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8;
159			for (i = 0; i < 64; i++) {
160				tdata[i] <<= 1;
161				tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00;
162			}
163		}
164	} else {
165		/* Oops, we should never get here! */
166		WARN_ON(1);
167		return 0;
168	}
169
170	for (i = 0; i < 64; i++)
171		if (tdata[i] != vdata[i]) {
172			changed = 1;
173			vdata[i] = tdata[i];
174		}
175	return changed;
176}
177
178void picolcd_fb_refresh(struct picolcd_data *data)
179{
180	if (data->fb_info)
181		schedule_delayed_work(&data->fb_info->deferred_work, 0);
182}
183
184/* Reconfigure LCD display */
185int picolcd_fb_reset(struct picolcd_data *data, int clear)
186{
187	struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev);
188	struct picolcd_fb_data *fbdata = data->fb_info->par;
189	int i, j;
190	unsigned long flags;
191	static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 };
192
193	if (!report || report->maxfield != 1)
194		return -ENODEV;
195
196	spin_lock_irqsave(&data->lock, flags);
197	for (i = 0; i < 4; i++) {
198		for (j = 0; j < report->field[0]->maxusage; j++)
199			if (j == 0)
200				hid_set_field(report->field[0], j, i << 2);
201			else if (j < sizeof(mapcmd))
202				hid_set_field(report->field[0], j, mapcmd[j]);
203			else
204				hid_set_field(report->field[0], j, 0);
205		hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT);
206	}
207	spin_unlock_irqrestore(&data->lock, flags);
208
209	if (clear) {
210		memset(fbdata->vbitmap, 0, PICOLCDFB_SIZE);
211		memset(fbdata->bitmap, 0, PICOLCDFB_SIZE*fbdata->bpp);
212	}
213	fbdata->force = 1;
214
215	/* schedule first output of framebuffer */
216	if (fbdata->ready)
217		schedule_delayed_work(&data->fb_info->deferred_work, 0);
218	else
219		fbdata->ready = 1;
220
221	return 0;
222}
223
224/* Update fb_vbitmap from the screen_buffer and send changed tiles to device */
225static void picolcd_fb_update(struct fb_info *info)
226{
227	int chip, tile, n;
228	unsigned long flags;
229	struct picolcd_fb_data *fbdata = info->par;
230	struct picolcd_data *data;
231
232	mutex_lock(&info->lock);
233
234	spin_lock_irqsave(&fbdata->lock, flags);
235	if (!fbdata->ready && fbdata->picolcd)
236		picolcd_fb_reset(fbdata->picolcd, 0);
237	spin_unlock_irqrestore(&fbdata->lock, flags);
238
239	/*
240	 * Translate the framebuffer into the format needed by the PicoLCD.
241	 * See display layout above.
242	 * Do this one tile after the other and push those tiles that changed.
243	 *
244	 * Wait for our IO to complete as otherwise we might flood the queue!
245	 */
246	n = 0;
247	for (chip = 0; chip < 4; chip++)
248		for (tile = 0; tile < 8; tile++) {
249			if (!fbdata->force && !picolcd_fb_update_tile(
250					fbdata->vbitmap, fbdata->bitmap,
251					fbdata->bpp, chip, tile))
252				continue;
253			n += 2;
254			if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
255				spin_lock_irqsave(&fbdata->lock, flags);
256				data = fbdata->picolcd;
257				spin_unlock_irqrestore(&fbdata->lock, flags);
258				mutex_unlock(&info->lock);
259				if (!data)
260					return;
261				hid_hw_wait(data->hdev);
262				mutex_lock(&info->lock);
263				n = 0;
264			}
265			spin_lock_irqsave(&fbdata->lock, flags);
266			data = fbdata->picolcd;
267			spin_unlock_irqrestore(&fbdata->lock, flags);
268			if (!data || picolcd_fb_send_tile(data,
269					fbdata->vbitmap, chip, tile))
270				goto out;
271		}
272	fbdata->force = false;
273	if (n) {
274		spin_lock_irqsave(&fbdata->lock, flags);
275		data = fbdata->picolcd;
276		spin_unlock_irqrestore(&fbdata->lock, flags);
277		mutex_unlock(&info->lock);
278		if (data)
279			hid_hw_wait(data->hdev);
280		return;
281	}
282out:
283	mutex_unlock(&info->lock);
284}
285
286static int picolcd_fb_blank(int blank, struct fb_info *info)
287{
288	/* We let fb notification do this for us via lcd/backlight device */
289	return 0;
290}
291
292static void picolcd_fb_destroy(struct fb_info *info)
293{
294	struct picolcd_fb_data *fbdata = info->par;
295
296	/* make sure no work is deferred */
297	fb_deferred_io_cleanup(info);
298
299	/* No thridparty should ever unregister our framebuffer! */
300	WARN_ON(fbdata->picolcd != NULL);
301
302	vfree((u8 *)info->fix.smem_start);
303	framebuffer_release(info);
304}
305
306static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
307{
308	__u32 bpp      = var->bits_per_pixel;
309	__u32 activate = var->activate;
310
311	/* only allow 1/8 bit depth (8-bit is grayscale) */
312	*var = picolcdfb_var;
313	var->activate = activate;
314	if (bpp >= 8) {
315		var->bits_per_pixel = 8;
316		var->red.length     = 8;
317		var->green.length   = 8;
318		var->blue.length    = 8;
319	} else {
320		var->bits_per_pixel = 1;
321		var->red.length     = 1;
322		var->green.length   = 1;
323		var->blue.length    = 1;
324	}
325	return 0;
326}
327
328static int picolcd_set_par(struct fb_info *info)
329{
330	struct picolcd_fb_data *fbdata = info->par;
331	u8 *tmp_fb, *o_fb;
332	if (info->var.bits_per_pixel == fbdata->bpp)
333		return 0;
334	/* switch between 1/8 bit depths */
335	if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8)
336		return -EINVAL;
337
338	o_fb   = fbdata->bitmap;
339	tmp_fb = kmalloc_array(PICOLCDFB_SIZE, info->var.bits_per_pixel,
340			       GFP_KERNEL);
341	if (!tmp_fb)
342		return -ENOMEM;
343
344	/* translate FB content to new bits-per-pixel */
345	if (info->var.bits_per_pixel == 1) {
346		int i, b;
347		for (i = 0; i < PICOLCDFB_SIZE; i++) {
348			u8 p = 0;
349			for (b = 0; b < 8; b++) {
350				p <<= 1;
351				p |= o_fb[i*8+b] ? 0x01 : 0x00;
352			}
353			tmp_fb[i] = p;
354		}
355		memcpy(o_fb, tmp_fb, PICOLCDFB_SIZE);
356		info->fix.visual = FB_VISUAL_MONO01;
357		info->fix.line_length = PICOLCDFB_WIDTH / 8;
358	} else {
359		int i;
360		memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE);
361		for (i = 0; i < PICOLCDFB_SIZE * 8; i++)
362			o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00;
363		info->fix.visual = FB_VISUAL_DIRECTCOLOR;
364		info->fix.line_length = PICOLCDFB_WIDTH;
365	}
366
367	kfree(tmp_fb);
368	fbdata->bpp = info->var.bits_per_pixel;
369	return 0;
370}
371
372static void picolcdfb_ops_damage_range(struct fb_info *info, off_t off, size_t len)
373{
374	if (!info->par)
375		return;
376	schedule_delayed_work(&info->deferred_work, 0);
377}
378
379static void picolcdfb_ops_damage_area(struct fb_info *info, u32 x, u32 y, u32 width, u32 height)
380{
381	if (!info->par)
382		return;
383	schedule_delayed_work(&info->deferred_work, 0);
384}
385
386FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(picolcdfb_ops,
387				   picolcdfb_ops_damage_range,
388				   picolcdfb_ops_damage_area)
389
390static const struct fb_ops picolcdfb_ops = {
391	.owner        = THIS_MODULE,
392	FB_DEFAULT_DEFERRED_OPS(picolcdfb_ops),
393	.fb_destroy   = picolcd_fb_destroy,
394	.fb_blank     = picolcd_fb_blank,
395	.fb_check_var = picolcd_fb_check_var,
396	.fb_set_par   = picolcd_set_par,
397};
398
399
400/* Callback from deferred IO workqueue */
401static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagereflist)
402{
403	picolcd_fb_update(info);
404}
405
406static const struct fb_deferred_io picolcd_fb_defio = {
407	.delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT,
408	.deferred_io = picolcd_fb_deferred_io,
409};
410
411
412/*
413 * The "fb_update_rate" sysfs attribute
414 */
415static ssize_t picolcd_fb_update_rate_show(struct device *dev,
416		struct device_attribute *attr, char *buf)
417{
418	struct picolcd_data *data = dev_get_drvdata(dev);
419	struct picolcd_fb_data *fbdata = data->fb_info->par;
420	unsigned i, fb_update_rate = fbdata->update_rate;
421	size_t ret = 0;
422
423	for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++)
424		if (ret >= PAGE_SIZE)
425			break;
426		else if (i == fb_update_rate)
427			ret += scnprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i);
428		else
429			ret += scnprintf(buf+ret, PAGE_SIZE-ret, "%u ", i);
430	if (ret > 0)
431		buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n';
432	return ret;
433}
434
435static ssize_t picolcd_fb_update_rate_store(struct device *dev,
436		struct device_attribute *attr, const char *buf, size_t count)
437{
438	struct picolcd_data *data = dev_get_drvdata(dev);
439	struct picolcd_fb_data *fbdata = data->fb_info->par;
440	int i;
441	unsigned u;
442
443	if (count < 1 || count > 10)
444		return -EINVAL;
445
446	i = sscanf(buf, "%u", &u);
447	if (i != 1)
448		return -EINVAL;
449
450	if (u > PICOLCDFB_UPDATE_RATE_LIMIT)
451		return -ERANGE;
452	else if (u == 0)
453		u = PICOLCDFB_UPDATE_RATE_DEFAULT;
454
455	fbdata->update_rate = u;
456	data->fb_info->fbdefio->delay = HZ / fbdata->update_rate;
457	return count;
458}
459
460static DEVICE_ATTR(fb_update_rate, 0664, picolcd_fb_update_rate_show,
461		picolcd_fb_update_rate_store);
462
463/* initialize Framebuffer device */
464int picolcd_init_framebuffer(struct picolcd_data *data)
465{
466	struct device *dev = &data->hdev->dev;
467	struct fb_info *info = NULL;
468	struct picolcd_fb_data *fbdata = NULL;
469	int i, error = -ENOMEM;
470	u32 *palette;
471
472	/* The extra memory is:
473	 * - 256*u32 for pseudo_palette
474	 * - struct fb_deferred_io
475	 */
476	info = framebuffer_alloc(256 * sizeof(u32) +
477			sizeof(struct fb_deferred_io) +
478			sizeof(struct picolcd_fb_data) +
479			PICOLCDFB_SIZE, dev);
480	if (!info)
481		goto err_nomem;
482
483	info->fbdefio = info->par;
484	*info->fbdefio = picolcd_fb_defio;
485	info->par += sizeof(struct fb_deferred_io);
486	palette = info->par;
487	info->par += 256 * sizeof(u32);
488	for (i = 0; i < 256; i++)
489		palette[i] = i > 0 && i < 16 ? 0xff : 0;
490	info->pseudo_palette = palette;
491	info->fbops = &picolcdfb_ops;
492	info->var = picolcdfb_var;
493	info->fix = picolcdfb_fix;
494	info->fix.smem_len   = PICOLCDFB_SIZE*8;
495
496	fbdata = info->par;
497	spin_lock_init(&fbdata->lock);
498	fbdata->picolcd = data;
499	fbdata->update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;
500	fbdata->bpp     = picolcdfb_var.bits_per_pixel;
501	fbdata->force   = 1;
502	fbdata->vbitmap = info->par + sizeof(struct picolcd_fb_data);
503	fbdata->bitmap  = vmalloc(PICOLCDFB_SIZE*8);
504	if (fbdata->bitmap == NULL) {
505		dev_err(dev, "can't get a free page for framebuffer\n");
506		goto err_nomem;
507	}
508	info->flags |= FBINFO_VIRTFB;
509	info->screen_buffer = fbdata->bitmap;
510	info->fix.smem_start = (unsigned long)fbdata->bitmap;
511	memset(fbdata->vbitmap, 0xff, PICOLCDFB_SIZE);
512	data->fb_info = info;
513
514	error = picolcd_fb_reset(data, 1);
515	if (error) {
516		dev_err(dev, "failed to configure display\n");
517		goto err_cleanup;
518	}
519
520	error = device_create_file(dev, &dev_attr_fb_update_rate);
521	if (error) {
522		dev_err(dev, "failed to create sysfs attributes\n");
523		goto err_cleanup;
524	}
525
526	fb_deferred_io_init(info);
527	error = register_framebuffer(info);
528	if (error) {
529		dev_err(dev, "failed to register framebuffer\n");
530		goto err_sysfs;
531	}
532	return 0;
533
534err_sysfs:
535	device_remove_file(dev, &dev_attr_fb_update_rate);
536	fb_deferred_io_cleanup(info);
537err_cleanup:
538	data->fb_info    = NULL;
539
540err_nomem:
541	if (fbdata)
542		vfree(fbdata->bitmap);
543	framebuffer_release(info);
544	return error;
545}
546
547void picolcd_exit_framebuffer(struct picolcd_data *data)
548{
549	struct fb_info *info = data->fb_info;
550	struct picolcd_fb_data *fbdata;
551	unsigned long flags;
552
553	if (!info)
554		return;
555
556	device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
557	fbdata = info->par;
558
559	/* disconnect framebuffer from HID dev */
560	spin_lock_irqsave(&fbdata->lock, flags);
561	fbdata->picolcd = NULL;
562	spin_unlock_irqrestore(&fbdata->lock, flags);
563
564	/* make sure there is no running update - thus that fbdata->picolcd
565	 * once obtained under lock is guaranteed not to get free() under
566	 * the feet of the deferred work */
567	flush_delayed_work(&info->deferred_work);
568
569	data->fb_info = NULL;
570	unregister_framebuffer(info);
571}
572