hid-picolcd_fb.c revision 033d9959ed2dc1029217d4165f80a71702dc578e
1fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/***************************************************************************
2fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
3fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *                                                                         *
4fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *   Based on Logitech G13 driver (v0.4)                                   *
5fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
6fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *                                                                         *
7fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *   This program is free software: you can redistribute it and/or modify  *
8fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *   it under the terms of the GNU General Public License as published by  *
9fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *   the Free Software Foundation, version 2 of the License.               *
10fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *                                                                         *
11fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *   This driver is distributed in the hope that it will be useful, but    *
12fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
13fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
14fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *   General Public License for more details.                              *
15fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *                                                                         *
16fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *   You should have received a copy of the GNU General Public License     *
17fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *   along with this software. If not see <http://www.gnu.org/licenses/>.  *
18fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont ***************************************************************************/
19fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
20fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/hid.h>
21d1c60a0350a1840281d4de579af6c7665d7ed877Stephen Rothwell#include <linux/vmalloc.h>
22fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include "usbhid/usbhid.h"
23fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/usb.h>
24fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
25fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/fb.h>
26fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/module.h>
27fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
28fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include "hid-picolcd.h"
29fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
30fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Framebuffer
31fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *
32fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * The PicoLCD use a Topway LCD module of 256x64 pixel
33fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * This display area is tiled over 4 controllers with 8 tiles
34fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * each. Each tile has 8x64 pixel, each data byte representing
35fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * a 1-bit wide vertical line of the tile.
36fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *
37fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * The display can be updated at a tile granularity.
38fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *
39fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *       Chip 1           Chip 2           Chip 3           Chip 4
40fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * +----------------+----------------+----------------+----------------+
41fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * |     Tile 1     |     Tile 1     |     Tile 1     |     Tile 1     |
42fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * +----------------+----------------+----------------+----------------+
43fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * |     Tile 2     |     Tile 2     |     Tile 2     |     Tile 2     |
44fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * +----------------+----------------+----------------+----------------+
45fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *                                  ...
46fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * +----------------+----------------+----------------+----------------+
47fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * |     Tile 8     |     Tile 8     |     Tile 8     |     Tile 8     |
48fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * +----------------+----------------+----------------+----------------+
49fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont */
50fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#define PICOLCDFB_NAME "picolcdfb"
51fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#define PICOLCDFB_WIDTH (256)
52fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#define PICOLCDFB_HEIGHT (64)
53fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8)
54fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
55fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#define PICOLCDFB_UPDATE_RATE_LIMIT   10
56fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#define PICOLCDFB_UPDATE_RATE_DEFAULT  2
57fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
58fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Framebuffer visual structures */
59fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic const struct fb_fix_screeninfo picolcdfb_fix = {
60fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.id          = PICOLCDFB_NAME,
61fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.type        = FB_TYPE_PACKED_PIXELS,
62fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.visual      = FB_VISUAL_MONO01,
63fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.xpanstep    = 0,
64fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.ypanstep    = 0,
65fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.ywrapstep   = 0,
66fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.line_length = PICOLCDFB_WIDTH / 8,
67fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.accel       = FB_ACCEL_NONE,
68fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont};
69fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
70fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic const struct fb_var_screeninfo picolcdfb_var = {
71fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.xres           = PICOLCDFB_WIDTH,
72fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.yres           = PICOLCDFB_HEIGHT,
73fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.xres_virtual   = PICOLCDFB_WIDTH,
74fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.yres_virtual   = PICOLCDFB_HEIGHT,
75fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.width          = 103,
76fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.height         = 26,
77fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.bits_per_pixel = 1,
78fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.grayscale      = 1,
79fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.red            = {
80fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.offset = 0,
81fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.length = 1,
82fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.msb_right = 0,
83fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	},
84fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.green          = {
85fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.offset = 0,
86fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.length = 1,
87fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.msb_right = 0,
88fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	},
89fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.blue           = {
90fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.offset = 0,
91fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.length = 1,
92fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.msb_right = 0,
93fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	},
94fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.transp         = {
95fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.offset = 0,
96fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.length = 0,
97fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.msb_right = 0,
98fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	},
99fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont};
100fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
101fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Send a given tile to PicoLCD */
10216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémontstatic int picolcd_fb_send_tile(struct picolcd_data *data, u8 *vbitmap,
10316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		int chip, int tile)
104fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
10516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct hid_report *report1, *report2;
106fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	unsigned long flags;
107fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	u8 *tdata;
108fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	int i;
109fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
11016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, data->hdev);
11116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if (!report1 || report1->maxfield != 1)
11216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		return -ENODEV;
11316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	report2 = picolcd_out_report(REPORT_LCD_DATA, data->hdev);
11416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if (!report2 || report2->maxfield != 1)
115fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return -ENODEV;
116fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
117fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	spin_lock_irqsave(&data->lock, flags);
11816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if ((data->status & PICOLCD_FAILED)) {
11916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		spin_unlock_irqrestore(&data->lock, flags);
12016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		return -ENODEV;
12116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	}
122fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  0, chip << 2);
123fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  1, 0x02);
124fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  2, 0x00);
125fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  3, 0x00);
126fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  4, 0xb8 | tile);
127fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  5, 0x00);
128fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  6, 0x00);
129fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  7, 0x40);
130fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  8, 0x00);
131fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  9, 0x00);
132fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0], 10,   32);
133fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
134fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report2->field[0],  0, (chip << 2) | 0x01);
135fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report2->field[0],  1, 0x00);
136fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report2->field[0],  2, 0x00);
137fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report2->field[0],  3,   32);
138fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
13916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	tdata = vbitmap + (tile * 4 + chip) * 64;
140fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	for (i = 0; i < 64; i++)
141fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		if (i < 32)
142fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			hid_set_field(report1->field[0], 11 + i, tdata[i]);
143fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		else
144fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			hid_set_field(report2->field[0], 4 + i - 32, tdata[i]);
145fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
146fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	usbhid_submit_report(data->hdev, report1, USB_DIR_OUT);
147fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	usbhid_submit_report(data->hdev, report2, USB_DIR_OUT);
148fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	spin_unlock_irqrestore(&data->lock, flags);
149fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return 0;
150fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
151fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
152fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Translate a single tile*/
153fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp,
154fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		int chip, int tile)
155fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
156fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	int i, b, changed = 0;
157fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	u8 tdata[64];
158fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	u8 *vdata = vbitmap + (tile * 4 + chip) * 64;
159fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
160fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (bpp == 1) {
161fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		for (b = 7; b >= 0; b--) {
162fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32;
163fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			for (i = 0; i < 64; i++) {
164fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				tdata[i] <<= 1;
165fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01;
166fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			}
167fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		}
168fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	} else if (bpp == 8) {
169fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		for (b = 7; b >= 0; b--) {
170fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8;
171fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			for (i = 0; i < 64; i++) {
172fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				tdata[i] <<= 1;
173fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00;
174fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			}
175fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		}
176fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	} else {
177fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		/* Oops, we should never get here! */
178fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		WARN_ON(1);
179fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return 0;
180fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
181fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
182fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	for (i = 0; i < 64; i++)
183fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		if (tdata[i] != vdata[i]) {
184fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			changed = 1;
185fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			vdata[i] = tdata[i];
186fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		}
187fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return changed;
188fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
189fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
190fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontvoid picolcd_fb_refresh(struct picolcd_data *data)
191fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
192fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (data->fb_info)
193fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		schedule_delayed_work(&data->fb_info->deferred_work, 0);
194fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
195fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
196fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Reconfigure LCD display */
197fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontint picolcd_fb_reset(struct picolcd_data *data, int clear)
198fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
199fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev);
20016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct picolcd_fb_data *fbdata = data->fb_info->par;
201fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	int i, j;
202fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	unsigned long flags;
203fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 };
204fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
205fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (!report || report->maxfield != 1)
206fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return -ENODEV;
207fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
208fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	spin_lock_irqsave(&data->lock, flags);
209fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	for (i = 0; i < 4; i++) {
210fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		for (j = 0; j < report->field[0]->maxusage; j++)
211fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			if (j == 0)
212fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				hid_set_field(report->field[0], j, i << 2);
213fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			else if (j < sizeof(mapcmd))
214fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				hid_set_field(report->field[0], j, mapcmd[j]);
215fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			else
216fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				hid_set_field(report->field[0], j, 0);
217fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
218fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
219fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	spin_unlock_irqrestore(&data->lock, flags);
220fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
22116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if (clear) {
22216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		memset(fbdata->vbitmap, 0, PICOLCDFB_SIZE);
22316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		memset(fbdata->bitmap, 0, PICOLCDFB_SIZE*fbdata->bpp);
224fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
22516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->force = 1;
226fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
227fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	/* schedule first output of framebuffer */
22816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if (fbdata->ready)
22916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		schedule_delayed_work(&data->fb_info->deferred_work, 0);
23016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	else
23116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		fbdata->ready = 1;
232fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
233fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return 0;
234fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
235fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
236fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Update fb_vbitmap from the screen_base and send changed tiles to device */
237a93ab8494873a88622bf74be861a93f875643524Bruno Prémontstatic void picolcd_fb_update(struct fb_info *info)
238fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
239fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	int chip, tile, n;
240fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	unsigned long flags;
24116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct picolcd_fb_data *fbdata = info->par;
242a93ab8494873a88622bf74be861a93f875643524Bruno Prémont	struct picolcd_data *data;
243fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
244a93ab8494873a88622bf74be861a93f875643524Bruno Prémont	mutex_lock(&info->lock);
245fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
24616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	spin_lock_irqsave(&fbdata->lock, flags);
24716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if (!fbdata->ready && fbdata->picolcd)
24816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		picolcd_fb_reset(fbdata->picolcd, 0);
24916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	spin_unlock_irqrestore(&fbdata->lock, flags);
250fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
251fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	/*
252fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	 * Translate the framebuffer into the format needed by the PicoLCD.
253fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	 * See display layout above.
254fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	 * Do this one tile after the other and push those tiles that changed.
255fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	 *
256fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	 * Wait for our IO to complete as otherwise we might flood the queue!
257fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	 */
258fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	n = 0;
259fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	for (chip = 0; chip < 4; chip++)
26016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		for (tile = 0; tile < 8; tile++) {
26116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			if (!fbdata->force && !picolcd_fb_update_tile(
26216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont					fbdata->vbitmap, fbdata->bitmap,
26316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont					fbdata->bpp, chip, tile))
26416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				continue;
26516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			n += 2;
26616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
26716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				spin_lock_irqsave(&fbdata->lock, flags);
26816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				data = fbdata->picolcd;
26916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				spin_unlock_irqrestore(&fbdata->lock, flags);
27016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				mutex_unlock(&info->lock);
27116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				if (!data)
27216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont					return;
27316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				usbhid_wait_io(data->hdev);
27416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				mutex_lock(&info->lock);
27516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				n = 0;
276fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			}
27716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			spin_lock_irqsave(&fbdata->lock, flags);
27816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			data = fbdata->picolcd;
27916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			spin_unlock_irqrestore(&fbdata->lock, flags);
28016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			if (!data || picolcd_fb_send_tile(data,
28116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont					fbdata->vbitmap, chip, tile))
28216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				goto out;
28316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		}
28416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->force = false;
285a93ab8494873a88622bf74be861a93f875643524Bruno Prémont	if (n) {
28616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		spin_lock_irqsave(&fbdata->lock, flags);
28716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		data = fbdata->picolcd;
28816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		spin_unlock_irqrestore(&fbdata->lock, flags);
289a93ab8494873a88622bf74be861a93f875643524Bruno Prémont		mutex_unlock(&info->lock);
29016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		if (data)
29116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			usbhid_wait_io(data->hdev);
292a93ab8494873a88622bf74be861a93f875643524Bruno Prémont		return;
293a93ab8494873a88622bf74be861a93f875643524Bruno Prémont	}
294a93ab8494873a88622bf74be861a93f875643524Bruno Prémontout:
295a93ab8494873a88622bf74be861a93f875643524Bruno Prémont	mutex_unlock(&info->lock);
296fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
297fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
298fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Stub to call the system default and update the image on the picoLCD */
299fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic void picolcd_fb_fillrect(struct fb_info *info,
300fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		const struct fb_fillrect *rect)
301fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
302fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (!info->par)
303fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return;
304fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	sys_fillrect(info, rect);
305fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
306fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	schedule_delayed_work(&info->deferred_work, 0);
307fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
308fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
309fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Stub to call the system default and update the image on the picoLCD */
310fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic void picolcd_fb_copyarea(struct fb_info *info,
311fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		const struct fb_copyarea *area)
312fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
313fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (!info->par)
314fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return;
315fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	sys_copyarea(info, area);
316fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
317fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	schedule_delayed_work(&info->deferred_work, 0);
318fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
319fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
320fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Stub to call the system default and update the image on the picoLCD */
321fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image)
322fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
323fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (!info->par)
324fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return;
325fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	sys_imageblit(info, image);
326fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
327fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	schedule_delayed_work(&info->deferred_work, 0);
328fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
329fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
330fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/*
331fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * this is the slow path from userspace. they can seek and write to
332fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * the fb. it's inefficient to do anything less than a full screen draw
333fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont */
334fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf,
335fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		size_t count, loff_t *ppos)
336fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
337fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	ssize_t ret;
338fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (!info->par)
339fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return -ENODEV;
340fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	ret = fb_sys_write(info, buf, count, ppos);
341fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (ret >= 0)
342fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		schedule_delayed_work(&info->deferred_work, 0);
343fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return ret;
344fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
345fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
346fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic int picolcd_fb_blank(int blank, struct fb_info *info)
347fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
348fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	/* We let fb notification do this for us via lcd/backlight device */
349fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return 0;
350fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
351fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
352fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic void picolcd_fb_destroy(struct fb_info *info)
353fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
35416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct picolcd_fb_data *fbdata = info->par;
35516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont
3569966c37c467167f29850fd0a856fce7031353bf3Bruno Prémont	/* make sure no work is deferred */
357a93ab8494873a88622bf74be861a93f875643524Bruno Prémont	fb_deferred_io_cleanup(info);
358fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
35916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	/* No thridparty should ever unregister our framebuffer! */
36016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	WARN_ON(fbdata->picolcd != NULL);
36116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont
3629966c37c467167f29850fd0a856fce7031353bf3Bruno Prémont	vfree((u8 *)info->fix.smem_start);
3639966c37c467167f29850fd0a856fce7031353bf3Bruno Prémont	framebuffer_release(info);
364fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
365fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
366fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
367fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
368fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	__u32 bpp      = var->bits_per_pixel;
369fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	__u32 activate = var->activate;
370fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
371fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	/* only allow 1/8 bit depth (8-bit is grayscale) */
372fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	*var = picolcdfb_var;
373fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	var->activate = activate;
374fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (bpp >= 8) {
375fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		var->bits_per_pixel = 8;
376fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		var->red.length     = 8;
377fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		var->green.length   = 8;
378fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		var->blue.length    = 8;
379fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	} else {
380fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		var->bits_per_pixel = 1;
381fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		var->red.length     = 1;
382fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		var->green.length   = 1;
383fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		var->blue.length    = 1;
384fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
385fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return 0;
386fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
387fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
388fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic int picolcd_set_par(struct fb_info *info)
389fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
39016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct picolcd_fb_data *fbdata = info->par;
391fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	u8 *tmp_fb, *o_fb;
39216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if (info->var.bits_per_pixel == fbdata->bpp)
393fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return 0;
394fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	/* switch between 1/8 bit depths */
395fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8)
396fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return -EINVAL;
397fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
39816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	o_fb   = fbdata->bitmap;
399fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL);
400fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (!tmp_fb)
401fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return -ENOMEM;
402fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
403fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	/* translate FB content to new bits-per-pixel */
404fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (info->var.bits_per_pixel == 1) {
405fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		int i, b;
406fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		for (i = 0; i < PICOLCDFB_SIZE; i++) {
407fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			u8 p = 0;
408fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			for (b = 0; b < 8; b++) {
409fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				p <<= 1;
410fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				p |= o_fb[i*8+b] ? 0x01 : 0x00;
411fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			}
412fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			tmp_fb[i] = p;
413fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		}
414fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		memcpy(o_fb, tmp_fb, PICOLCDFB_SIZE);
415fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		info->fix.visual = FB_VISUAL_MONO01;
416fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		info->fix.line_length = PICOLCDFB_WIDTH / 8;
417fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	} else {
418fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		int i;
419fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE);
420fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		for (i = 0; i < PICOLCDFB_SIZE * 8; i++)
421fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00;
422fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		info->fix.visual = FB_VISUAL_DIRECTCOLOR;
423fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		info->fix.line_length = PICOLCDFB_WIDTH;
424fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
425fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
426fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	kfree(tmp_fb);
42716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->bpp = info->var.bits_per_pixel;
428fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return 0;
429fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
430fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
431fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Note this can't be const because of struct fb_info definition */
432fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic struct fb_ops picolcdfb_ops = {
433fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.owner        = THIS_MODULE,
434fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_destroy   = picolcd_fb_destroy,
435fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_read      = fb_sys_read,
436fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_write     = picolcd_fb_write,
437fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_blank     = picolcd_fb_blank,
438fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_fillrect  = picolcd_fb_fillrect,
439fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_copyarea  = picolcd_fb_copyarea,
440fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_imageblit = picolcd_fb_imageblit,
441fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_check_var = picolcd_fb_check_var,
442fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_set_par   = picolcd_set_par,
443fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont};
444fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
445fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
446fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Callback from deferred IO workqueue */
447fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)
448fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
449a93ab8494873a88622bf74be861a93f875643524Bruno Prémont	picolcd_fb_update(info);
450fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
451fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
452fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic const struct fb_deferred_io picolcd_fb_defio = {
453fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT,
454fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.deferred_io = picolcd_fb_deferred_io,
455fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont};
456fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
457fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
458fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/*
459fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * The "fb_update_rate" sysfs attribute
460fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont */
461fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic ssize_t picolcd_fb_update_rate_show(struct device *dev,
462fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		struct device_attribute *attr, char *buf)
463fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
464fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	struct picolcd_data *data = dev_get_drvdata(dev);
46516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct picolcd_fb_data *fbdata = data->fb_info->par;
46616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	unsigned i, fb_update_rate = fbdata->update_rate;
467fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	size_t ret = 0;
468fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
469fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++)
470fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		if (ret >= PAGE_SIZE)
471fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			break;
472fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		else if (i == fb_update_rate)
473fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i);
474fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		else
475fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i);
476fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (ret > 0)
477fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n';
478fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return ret;
479fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
480fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
481fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic ssize_t picolcd_fb_update_rate_store(struct device *dev,
482fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		struct device_attribute *attr, const char *buf, size_t count)
483fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
484fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	struct picolcd_data *data = dev_get_drvdata(dev);
48516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct picolcd_fb_data *fbdata = data->fb_info->par;
486fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	int i;
487fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	unsigned u;
488fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
489fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (count < 1 || count > 10)
490fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return -EINVAL;
491fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
492fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	i = sscanf(buf, "%u", &u);
493fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (i != 1)
494fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return -EINVAL;
495fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
496fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (u > PICOLCDFB_UPDATE_RATE_LIMIT)
497fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return -ERANGE;
498fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	else if (u == 0)
499fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		u = PICOLCDFB_UPDATE_RATE_DEFAULT;
500fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
50116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->update_rate = u;
50216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	data->fb_info->fbdefio->delay = HZ / fbdata->update_rate;
503fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return count;
504fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
505fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
506fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show,
507fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		picolcd_fb_update_rate_store);
508fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
509fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* initialize Framebuffer device */
510fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontint picolcd_init_framebuffer(struct picolcd_data *data)
511fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
512fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	struct device *dev = &data->hdev->dev;
513fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	struct fb_info *info = NULL;
51416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct picolcd_fb_data *fbdata = NULL;
515fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	int i, error = -ENOMEM;
516fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	u32 *palette;
517fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
518fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	/* The extra memory is:
519fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	 * - 256*u32 for pseudo_palette
5209966c37c467167f29850fd0a856fce7031353bf3Bruno Prémont	 * - struct fb_deferred_io
521fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	 */
5229966c37c467167f29850fd0a856fce7031353bf3Bruno Prémont	info = framebuffer_alloc(256 * sizeof(u32) +
52316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			sizeof(struct fb_deferred_io) +
52416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			sizeof(struct picolcd_fb_data) +
52516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			PICOLCDFB_SIZE, dev);
526fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (info == NULL) {
527fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		dev_err(dev, "failed to allocate a framebuffer\n");
528fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		goto err_nomem;
529fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
530fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
5319966c37c467167f29850fd0a856fce7031353bf3Bruno Prémont	info->fbdefio = info->par;
5329966c37c467167f29850fd0a856fce7031353bf3Bruno Prémont	*info->fbdefio = picolcd_fb_defio;
53316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	info->par += sizeof(struct fb_deferred_io);
53416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	palette = info->par;
53516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	info->par += 256 * sizeof(u32);
536fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	for (i = 0; i < 256; i++)
537fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		palette[i] = i > 0 && i < 16 ? 0xff : 0;
538fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	info->pseudo_palette = palette;
539fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	info->fbops = &picolcdfb_ops;
540fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	info->var = picolcdfb_var;
541fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	info->fix = picolcdfb_fix;
542fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	info->fix.smem_len   = PICOLCDFB_SIZE*8;
543fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	info->flags = FBINFO_FLAG_DEFAULT;
544fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
54516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata = info->par;
54616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	spin_lock_init(&fbdata->lock);
54716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->picolcd = data;
54816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;
54916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->bpp     = picolcdfb_var.bits_per_pixel;
55016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->force   = 1;
55116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->vbitmap = info->par + sizeof(struct picolcd_fb_data);
55216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->bitmap  = vmalloc(PICOLCDFB_SIZE*8);
55316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if (fbdata->bitmap == NULL) {
55416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		dev_err(dev, "can't get a free page for framebuffer\n");
55516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		goto err_nomem;
55616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	}
55716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	info->screen_base = (char __force __iomem *)fbdata->bitmap;
55816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	info->fix.smem_start = (unsigned long)fbdata->bitmap;
55916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	memset(fbdata->vbitmap, 0xff, PICOLCDFB_SIZE);
56016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	data->fb_info = info;
56116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont
562fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	error = picolcd_fb_reset(data, 1);
563fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (error) {
564fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		dev_err(dev, "failed to configure display\n");
565fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		goto err_cleanup;
566fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
56716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont
568fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	error = device_create_file(dev, &dev_attr_fb_update_rate);
569fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (error) {
570fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		dev_err(dev, "failed to create sysfs attributes\n");
571fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		goto err_cleanup;
572fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
57316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont
574fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	fb_deferred_io_init(info);
575fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	error = register_framebuffer(info);
576fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (error) {
577fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		dev_err(dev, "failed to register framebuffer\n");
578fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		goto err_sysfs;
579fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
580fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return 0;
581fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
582fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémonterr_sysfs:
583fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	device_remove_file(dev, &dev_attr_fb_update_rate);
58416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fb_deferred_io_cleanup(info);
585fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémonterr_cleanup:
586fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	data->fb_info    = NULL;
587fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
588fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémonterr_nomem:
58916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if (fbdata)
59016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		vfree(fbdata->bitmap);
591fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	framebuffer_release(info);
592fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return error;
593fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
594fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
595fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontvoid picolcd_exit_framebuffer(struct picolcd_data *data)
596fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
597fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	struct fb_info *info = data->fb_info;
59816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct picolcd_fb_data *fbdata = info->par;
59916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	unsigned long flags;
600fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
601fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
60216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont
60316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	/* disconnect framebuffer from HID dev */
60416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	spin_lock_irqsave(&fbdata->lock, flags);
60516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->picolcd = NULL;
60616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	spin_unlock_irqrestore(&fbdata->lock, flags);
60716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont
60816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	/* make sure there is no running update - thus that fbdata->picolcd
60916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	 * once obtained under lock is guaranteed not to get free() under
61016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	 * the feet of the deferred work */
611033d9959ed2dc1029217d4165f80a71702dc578eLinus Torvalds	flush_delayed_work(&info->deferred_work);
61216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont
61316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	data->fb_info = NULL;
614fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	unregister_framebuffer(info);
615fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
616