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
23fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/fb.h>
24fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/module.h>
25fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
26fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include "hid-picolcd.h"
27fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
28fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Framebuffer
29fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *
30fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * The PicoLCD use a Topway LCD module of 256x64 pixel
31fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * This display area is tiled over 4 controllers with 8 tiles
32fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * each. Each tile has 8x64 pixel, each data byte representing
33fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * a 1-bit wide vertical line of the tile.
34fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *
35fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * The display can be updated at a tile granularity.
36fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *
37fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *       Chip 1           Chip 2           Chip 3           Chip 4
38fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * +----------------+----------------+----------------+----------------+
39fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * |     Tile 1     |     Tile 1     |     Tile 1     |     Tile 1     |
40fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * +----------------+----------------+----------------+----------------+
41fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * |     Tile 2     |     Tile 2     |     Tile 2     |     Tile 2     |
42fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * +----------------+----------------+----------------+----------------+
43fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont *                                  ...
44fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * +----------------+----------------+----------------+----------------+
45fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * |     Tile 8     |     Tile 8     |     Tile 8     |     Tile 8     |
46fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * +----------------+----------------+----------------+----------------+
47fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont */
48fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#define PICOLCDFB_NAME "picolcdfb"
49fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#define PICOLCDFB_WIDTH (256)
50fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#define PICOLCDFB_HEIGHT (64)
51fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8)
52fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
53fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#define PICOLCDFB_UPDATE_RATE_LIMIT   10
54fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#define PICOLCDFB_UPDATE_RATE_DEFAULT  2
55fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
56fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Framebuffer visual structures */
57fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic const struct fb_fix_screeninfo picolcdfb_fix = {
58fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.id          = PICOLCDFB_NAME,
59fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.type        = FB_TYPE_PACKED_PIXELS,
60fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.visual      = FB_VISUAL_MONO01,
61fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.xpanstep    = 0,
62fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.ypanstep    = 0,
63fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.ywrapstep   = 0,
64fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.line_length = PICOLCDFB_WIDTH / 8,
65fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.accel       = FB_ACCEL_NONE,
66fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont};
67fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
68fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic const struct fb_var_screeninfo picolcdfb_var = {
69fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.xres           = PICOLCDFB_WIDTH,
70fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.yres           = PICOLCDFB_HEIGHT,
71fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.xres_virtual   = PICOLCDFB_WIDTH,
72fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.yres_virtual   = PICOLCDFB_HEIGHT,
73fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.width          = 103,
74fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.height         = 26,
75fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.bits_per_pixel = 1,
76fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.grayscale      = 1,
77fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.red            = {
78fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.offset = 0,
79fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.length = 1,
80fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.msb_right = 0,
81fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	},
82fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.green          = {
83fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.offset = 0,
84fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.length = 1,
85fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.msb_right = 0,
86fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	},
87fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.blue           = {
88fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.offset = 0,
89fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.length = 1,
90fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.msb_right = 0,
91fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	},
92fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.transp         = {
93fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.offset = 0,
94fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.length = 0,
95fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		.msb_right = 0,
96fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	},
97fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont};
98fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
99fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Send a given tile to PicoLCD */
10016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémontstatic int picolcd_fb_send_tile(struct picolcd_data *data, u8 *vbitmap,
10116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		int chip, int tile)
102fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
10316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct hid_report *report1, *report2;
104fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	unsigned long flags;
105fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	u8 *tdata;
106fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	int i;
107fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
10816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, data->hdev);
10916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if (!report1 || report1->maxfield != 1)
11016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		return -ENODEV;
11116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	report2 = picolcd_out_report(REPORT_LCD_DATA, data->hdev);
11216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if (!report2 || report2->maxfield != 1)
113fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return -ENODEV;
114fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
115fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	spin_lock_irqsave(&data->lock, flags);
11616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if ((data->status & PICOLCD_FAILED)) {
11716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		spin_unlock_irqrestore(&data->lock, flags);
11816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		return -ENODEV;
11916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	}
120fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  0, chip << 2);
121fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  1, 0x02);
122fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  2, 0x00);
123fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  3, 0x00);
124fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  4, 0xb8 | tile);
125fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  5, 0x00);
126fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  6, 0x00);
127fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  7, 0x40);
128fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  8, 0x00);
129fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0],  9, 0x00);
130fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report1->field[0], 10,   32);
131fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
132fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report2->field[0],  0, (chip << 2) | 0x01);
133fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report2->field[0],  1, 0x00);
134fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report2->field[0],  2, 0x00);
135fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	hid_set_field(report2->field[0],  3,   32);
136fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
13716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	tdata = vbitmap + (tile * 4 + chip) * 64;
138fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	for (i = 0; i < 64; i++)
139fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		if (i < 32)
140fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			hid_set_field(report1->field[0], 11 + i, tdata[i]);
141fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		else
142fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			hid_set_field(report2->field[0], 4 + i - 32, tdata[i]);
143fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
144d881427253da011495f4193663d809d0e9dfa215Benjamin Tissoires	hid_hw_request(data->hdev, report1, HID_REQ_SET_REPORT);
145d881427253da011495f4193663d809d0e9dfa215Benjamin Tissoires	hid_hw_request(data->hdev, report2, HID_REQ_SET_REPORT);
146fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	spin_unlock_irqrestore(&data->lock, flags);
147fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return 0;
148fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
149fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
150fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Translate a single tile*/
151fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp,
152fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		int chip, int tile)
153fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
154fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	int i, b, changed = 0;
155fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	u8 tdata[64];
156fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	u8 *vdata = vbitmap + (tile * 4 + chip) * 64;
157fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
158fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (bpp == 1) {
159fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		for (b = 7; b >= 0; b--) {
160fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32;
161fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			for (i = 0; i < 64; i++) {
162fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				tdata[i] <<= 1;
163fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01;
164fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			}
165fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		}
166fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	} else if (bpp == 8) {
167fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		for (b = 7; b >= 0; b--) {
168fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8;
169fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			for (i = 0; i < 64; i++) {
170fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				tdata[i] <<= 1;
171fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00;
172fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			}
173fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		}
174fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	} else {
175fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		/* Oops, we should never get here! */
176fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		WARN_ON(1);
177fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return 0;
178fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
179fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
180fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	for (i = 0; i < 64; i++)
181fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		if (tdata[i] != vdata[i]) {
182fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			changed = 1;
183fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			vdata[i] = tdata[i];
184fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		}
185fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return changed;
186fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
187fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
188fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontvoid picolcd_fb_refresh(struct picolcd_data *data)
189fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
190fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (data->fb_info)
191fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		schedule_delayed_work(&data->fb_info->deferred_work, 0);
192fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
193fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
194fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Reconfigure LCD display */
195fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontint picolcd_fb_reset(struct picolcd_data *data, int clear)
196fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
197fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev);
19816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct picolcd_fb_data *fbdata = data->fb_info->par;
199fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	int i, j;
200fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	unsigned long flags;
201fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 };
202fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
203fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (!report || report->maxfield != 1)
204fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return -ENODEV;
205fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
206fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	spin_lock_irqsave(&data->lock, flags);
207fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	for (i = 0; i < 4; i++) {
208fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		for (j = 0; j < report->field[0]->maxusage; j++)
209fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			if (j == 0)
210fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				hid_set_field(report->field[0], j, i << 2);
211fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			else if (j < sizeof(mapcmd))
212fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				hid_set_field(report->field[0], j, mapcmd[j]);
213fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			else
214fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				hid_set_field(report->field[0], j, 0);
215d881427253da011495f4193663d809d0e9dfa215Benjamin Tissoires		hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT);
216fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
217fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	spin_unlock_irqrestore(&data->lock, flags);
218fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
21916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if (clear) {
22016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		memset(fbdata->vbitmap, 0, PICOLCDFB_SIZE);
22116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		memset(fbdata->bitmap, 0, PICOLCDFB_SIZE*fbdata->bpp);
222fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
22316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->force = 1;
224fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
225fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	/* schedule first output of framebuffer */
22616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if (fbdata->ready)
22716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		schedule_delayed_work(&data->fb_info->deferred_work, 0);
22816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	else
22916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		fbdata->ready = 1;
230fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
231fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return 0;
232fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
233fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
234fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Update fb_vbitmap from the screen_base and send changed tiles to device */
235a93ab8494873a88622bf74be861a93f875643524Bruno Prémontstatic void picolcd_fb_update(struct fb_info *info)
236fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
237fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	int chip, tile, n;
238fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	unsigned long flags;
23916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct picolcd_fb_data *fbdata = info->par;
240a93ab8494873a88622bf74be861a93f875643524Bruno Prémont	struct picolcd_data *data;
241fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
242a93ab8494873a88622bf74be861a93f875643524Bruno Prémont	mutex_lock(&info->lock);
243fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
24416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	spin_lock_irqsave(&fbdata->lock, flags);
24516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if (!fbdata->ready && fbdata->picolcd)
24616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		picolcd_fb_reset(fbdata->picolcd, 0);
24716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	spin_unlock_irqrestore(&fbdata->lock, flags);
248fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
249fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	/*
250fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	 * Translate the framebuffer into the format needed by the PicoLCD.
251fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	 * See display layout above.
252fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	 * Do this one tile after the other and push those tiles that changed.
253fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	 *
254fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	 * Wait for our IO to complete as otherwise we might flood the queue!
255fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	 */
256fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	n = 0;
257fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	for (chip = 0; chip < 4; chip++)
25816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		for (tile = 0; tile < 8; tile++) {
25916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			if (!fbdata->force && !picolcd_fb_update_tile(
26016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont					fbdata->vbitmap, fbdata->bitmap,
26116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont					fbdata->bpp, chip, tile))
26216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				continue;
26316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			n += 2;
26416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
26516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				spin_lock_irqsave(&fbdata->lock, flags);
26616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				data = fbdata->picolcd;
26716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				spin_unlock_irqrestore(&fbdata->lock, flags);
26816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				mutex_unlock(&info->lock);
26916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				if (!data)
27016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont					return;
271b7966a4d7be0a10329f03330390f4bdaf453d74aBenjamin Tissoires				hid_hw_wait(data->hdev);
27216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				mutex_lock(&info->lock);
27316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				n = 0;
274fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			}
27516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			spin_lock_irqsave(&fbdata->lock, flags);
27616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			data = fbdata->picolcd;
27716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			spin_unlock_irqrestore(&fbdata->lock, flags);
27816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			if (!data || picolcd_fb_send_tile(data,
27916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont					fbdata->vbitmap, chip, tile))
28016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont				goto out;
28116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		}
28216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->force = false;
283a93ab8494873a88622bf74be861a93f875643524Bruno Prémont	if (n) {
28416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		spin_lock_irqsave(&fbdata->lock, flags);
28516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		data = fbdata->picolcd;
28616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		spin_unlock_irqrestore(&fbdata->lock, flags);
287a93ab8494873a88622bf74be861a93f875643524Bruno Prémont		mutex_unlock(&info->lock);
28816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		if (data)
289b7966a4d7be0a10329f03330390f4bdaf453d74aBenjamin Tissoires			hid_hw_wait(data->hdev);
290a93ab8494873a88622bf74be861a93f875643524Bruno Prémont		return;
291a93ab8494873a88622bf74be861a93f875643524Bruno Prémont	}
292a93ab8494873a88622bf74be861a93f875643524Bruno Prémontout:
293a93ab8494873a88622bf74be861a93f875643524Bruno Prémont	mutex_unlock(&info->lock);
294fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
295fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
296fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Stub to call the system default and update the image on the picoLCD */
297fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic void picolcd_fb_fillrect(struct fb_info *info,
298fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		const struct fb_fillrect *rect)
299fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
300fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (!info->par)
301fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return;
302fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	sys_fillrect(info, rect);
303fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
304fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	schedule_delayed_work(&info->deferred_work, 0);
305fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
306fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
307fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Stub to call the system default and update the image on the picoLCD */
308fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic void picolcd_fb_copyarea(struct fb_info *info,
309fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		const struct fb_copyarea *area)
310fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
311fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (!info->par)
312fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return;
313fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	sys_copyarea(info, area);
314fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
315fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	schedule_delayed_work(&info->deferred_work, 0);
316fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
317fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
318fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Stub to call the system default and update the image on the picoLCD */
319fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image)
320fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
321fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (!info->par)
322fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return;
323fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	sys_imageblit(info, image);
324fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
325fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	schedule_delayed_work(&info->deferred_work, 0);
326fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
327fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
328fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/*
329fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * this is the slow path from userspace. they can seek and write to
330fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * the fb. it's inefficient to do anything less than a full screen draw
331fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont */
332fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf,
333fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		size_t count, loff_t *ppos)
334fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
335fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	ssize_t ret;
336fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (!info->par)
337fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return -ENODEV;
338fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	ret = fb_sys_write(info, buf, count, ppos);
339fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (ret >= 0)
340fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		schedule_delayed_work(&info->deferred_work, 0);
341fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return ret;
342fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
343fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
344fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic int picolcd_fb_blank(int blank, struct fb_info *info)
345fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
346fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	/* We let fb notification do this for us via lcd/backlight device */
347fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return 0;
348fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
349fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
350fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic void picolcd_fb_destroy(struct fb_info *info)
351fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
35216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct picolcd_fb_data *fbdata = info->par;
35316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont
3549966c37c467167f29850fd0a856fce7031353bf3Bruno Prémont	/* make sure no work is deferred */
355a93ab8494873a88622bf74be861a93f875643524Bruno Prémont	fb_deferred_io_cleanup(info);
356fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
35716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	/* No thridparty should ever unregister our framebuffer! */
35816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	WARN_ON(fbdata->picolcd != NULL);
35916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont
3609966c37c467167f29850fd0a856fce7031353bf3Bruno Prémont	vfree((u8 *)info->fix.smem_start);
3619966c37c467167f29850fd0a856fce7031353bf3Bruno Prémont	framebuffer_release(info);
362fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
363fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
364fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
365fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
366fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	__u32 bpp      = var->bits_per_pixel;
367fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	__u32 activate = var->activate;
368fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
369fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	/* only allow 1/8 bit depth (8-bit is grayscale) */
370fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	*var = picolcdfb_var;
371fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	var->activate = activate;
372fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (bpp >= 8) {
373fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		var->bits_per_pixel = 8;
374fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		var->red.length     = 8;
375fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		var->green.length   = 8;
376fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		var->blue.length    = 8;
377fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	} else {
378fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		var->bits_per_pixel = 1;
379fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		var->red.length     = 1;
380fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		var->green.length   = 1;
381fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		var->blue.length    = 1;
382fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
383fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return 0;
384fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
385fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
386fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic int picolcd_set_par(struct fb_info *info)
387fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
38816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct picolcd_fb_data *fbdata = info->par;
389fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	u8 *tmp_fb, *o_fb;
39016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if (info->var.bits_per_pixel == fbdata->bpp)
391fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return 0;
392fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	/* switch between 1/8 bit depths */
393fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8)
394fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return -EINVAL;
395fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
39616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	o_fb   = fbdata->bitmap;
397fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL);
398fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (!tmp_fb)
399fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return -ENOMEM;
400fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
401fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	/* translate FB content to new bits-per-pixel */
402fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (info->var.bits_per_pixel == 1) {
403fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		int i, b;
404fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		for (i = 0; i < PICOLCDFB_SIZE; i++) {
405fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			u8 p = 0;
406fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			for (b = 0; b < 8; b++) {
407fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				p <<= 1;
408fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont				p |= o_fb[i*8+b] ? 0x01 : 0x00;
409fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			}
410fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			tmp_fb[i] = p;
411fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		}
412fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		memcpy(o_fb, tmp_fb, PICOLCDFB_SIZE);
413fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		info->fix.visual = FB_VISUAL_MONO01;
414fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		info->fix.line_length = PICOLCDFB_WIDTH / 8;
415fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	} else {
416fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		int i;
417fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE);
418fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		for (i = 0; i < PICOLCDFB_SIZE * 8; i++)
419fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00;
420fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		info->fix.visual = FB_VISUAL_DIRECTCOLOR;
421fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		info->fix.line_length = PICOLCDFB_WIDTH;
422fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
423fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
424fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	kfree(tmp_fb);
42516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->bpp = info->var.bits_per_pixel;
426fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return 0;
427fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
428fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
429fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Note this can't be const because of struct fb_info definition */
430fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic struct fb_ops picolcdfb_ops = {
431fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.owner        = THIS_MODULE,
432fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_destroy   = picolcd_fb_destroy,
433fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_read      = fb_sys_read,
434fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_write     = picolcd_fb_write,
435fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_blank     = picolcd_fb_blank,
436fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_fillrect  = picolcd_fb_fillrect,
437fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_copyarea  = picolcd_fb_copyarea,
438fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_imageblit = picolcd_fb_imageblit,
439fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_check_var = picolcd_fb_check_var,
440fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.fb_set_par   = picolcd_set_par,
441fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont};
442fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
443fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
444fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* Callback from deferred IO workqueue */
445fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)
446fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
447a93ab8494873a88622bf74be861a93f875643524Bruno Prémont	picolcd_fb_update(info);
448fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
449fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
450fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic const struct fb_deferred_io picolcd_fb_defio = {
451fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT,
452fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	.deferred_io = picolcd_fb_deferred_io,
453fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont};
454fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
455fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
456fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/*
457fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * The "fb_update_rate" sysfs attribute
458fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont */
459fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic ssize_t picolcd_fb_update_rate_show(struct device *dev,
460fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		struct device_attribute *attr, char *buf)
461fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
462fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	struct picolcd_data *data = dev_get_drvdata(dev);
46316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct picolcd_fb_data *fbdata = data->fb_info->par;
46416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	unsigned i, fb_update_rate = fbdata->update_rate;
465fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	size_t ret = 0;
466fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
467fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++)
468fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		if (ret >= PAGE_SIZE)
469fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			break;
470fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		else if (i == fb_update_rate)
471fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i);
472fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		else
473fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont			ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i);
474fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (ret > 0)
475fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n';
476fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return ret;
477fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
478fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
479fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic ssize_t picolcd_fb_update_rate_store(struct device *dev,
480fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		struct device_attribute *attr, const char *buf, size_t count)
481fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
482fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	struct picolcd_data *data = dev_get_drvdata(dev);
48316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct picolcd_fb_data *fbdata = data->fb_info->par;
484fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	int i;
485fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	unsigned u;
486fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
487fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (count < 1 || count > 10)
488fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return -EINVAL;
489fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
490fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	i = sscanf(buf, "%u", &u);
491fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (i != 1)
492fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return -EINVAL;
493fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
494fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (u > PICOLCDFB_UPDATE_RATE_LIMIT)
495fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		return -ERANGE;
496fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	else if (u == 0)
497fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		u = PICOLCDFB_UPDATE_RATE_DEFAULT;
498fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
49916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->update_rate = u;
50016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	data->fb_info->fbdefio->delay = HZ / fbdata->update_rate;
501fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return count;
502fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
503fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
504f92201c34885cf0da5403c6959bc9bcd9a648963Rusty Russellstatic DEVICE_ATTR(fb_update_rate, 0664, picolcd_fb_update_rate_show,
505fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		picolcd_fb_update_rate_store);
506fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
507fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/* initialize Framebuffer device */
508fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontint picolcd_init_framebuffer(struct picolcd_data *data)
509fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
510fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	struct device *dev = &data->hdev->dev;
511fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	struct fb_info *info = NULL;
51216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	struct picolcd_fb_data *fbdata = NULL;
513fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	int i, error = -ENOMEM;
514fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	u32 *palette;
515fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
516fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	/* The extra memory is:
517fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	 * - 256*u32 for pseudo_palette
5189966c37c467167f29850fd0a856fce7031353bf3Bruno Prémont	 * - struct fb_deferred_io
519fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	 */
5209966c37c467167f29850fd0a856fce7031353bf3Bruno Prémont	info = framebuffer_alloc(256 * sizeof(u32) +
52116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			sizeof(struct fb_deferred_io) +
52216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			sizeof(struct picolcd_fb_data) +
52316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont			PICOLCDFB_SIZE, dev);
524fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (info == NULL) {
525fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		dev_err(dev, "failed to allocate a framebuffer\n");
526fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		goto err_nomem;
527fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
528fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
5299966c37c467167f29850fd0a856fce7031353bf3Bruno Prémont	info->fbdefio = info->par;
5309966c37c467167f29850fd0a856fce7031353bf3Bruno Prémont	*info->fbdefio = picolcd_fb_defio;
53116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	info->par += sizeof(struct fb_deferred_io);
53216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	palette = info->par;
53316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	info->par += 256 * sizeof(u32);
534fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	for (i = 0; i < 256; i++)
535fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		palette[i] = i > 0 && i < 16 ? 0xff : 0;
536fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	info->pseudo_palette = palette;
537fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	info->fbops = &picolcdfb_ops;
538fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	info->var = picolcdfb_var;
539fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	info->fix = picolcdfb_fix;
540fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	info->fix.smem_len   = PICOLCDFB_SIZE*8;
541fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	info->flags = FBINFO_FLAG_DEFAULT;
542fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
54316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata = info->par;
54416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	spin_lock_init(&fbdata->lock);
54516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->picolcd = data;
54616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;
54716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->bpp     = picolcdfb_var.bits_per_pixel;
54816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->force   = 1;
54916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->vbitmap = info->par + sizeof(struct picolcd_fb_data);
55016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->bitmap  = vmalloc(PICOLCDFB_SIZE*8);
55116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if (fbdata->bitmap == NULL) {
55216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		dev_err(dev, "can't get a free page for framebuffer\n");
55316048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		goto err_nomem;
55416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	}
55516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	info->screen_base = (char __force __iomem *)fbdata->bitmap;
55616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	info->fix.smem_start = (unsigned long)fbdata->bitmap;
55716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	memset(fbdata->vbitmap, 0xff, PICOLCDFB_SIZE);
55816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	data->fb_info = info;
55916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont
560fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	error = picolcd_fb_reset(data, 1);
561fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (error) {
562fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		dev_err(dev, "failed to configure display\n");
563fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		goto err_cleanup;
564fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
56516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont
566fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	error = device_create_file(dev, &dev_attr_fb_update_rate);
567fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (error) {
568fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		dev_err(dev, "failed to create sysfs attributes\n");
569fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		goto err_cleanup;
570fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
57116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont
572fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	fb_deferred_io_init(info);
573fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	error = register_framebuffer(info);
574fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	if (error) {
575fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		dev_err(dev, "failed to register framebuffer\n");
576fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont		goto err_sysfs;
577fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	}
578fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return 0;
579fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
580fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémonterr_sysfs:
581fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	device_remove_file(dev, &dev_attr_fb_update_rate);
58216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fb_deferred_io_cleanup(info);
583fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémonterr_cleanup:
584fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	data->fb_info    = NULL;
585fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
586fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémonterr_nomem:
58716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	if (fbdata)
58816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont		vfree(fbdata->bitmap);
589fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	framebuffer_release(info);
590fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	return error;
591fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
592fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
593fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontvoid picolcd_exit_framebuffer(struct picolcd_data *data)
594fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{
595fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	struct fb_info *info = data->fb_info;
5961cde501bb4655e98fb832194beb88ac73be5a05dBruno Prémont	struct picolcd_fb_data *fbdata;
59716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	unsigned long flags;
598fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont
5991cde501bb4655e98fb832194beb88ac73be5a05dBruno Prémont	if (!info)
6001cde501bb4655e98fb832194beb88ac73be5a05dBruno Prémont		return;
6011cde501bb4655e98fb832194beb88ac73be5a05dBruno Prémont
602fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
6031cde501bb4655e98fb832194beb88ac73be5a05dBruno Prémont	fbdata = info->par;
60416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont
60516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	/* disconnect framebuffer from HID dev */
60616048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	spin_lock_irqsave(&fbdata->lock, flags);
60716048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	fbdata->picolcd = NULL;
60816048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	spin_unlock_irqrestore(&fbdata->lock, flags);
60916048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont
61016048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	/* make sure there is no running update - thus that fbdata->picolcd
61116048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	 * once obtained under lock is guaranteed not to get free() under
61216048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	 * the feet of the deferred work */
613033d9959ed2dc1029217d4165f80a71702dc578eLinus Torvalds	flush_delayed_work(&info->deferred_work);
61416048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont
61516048709b2f6a7e721ac677f9a6741ac1c13ffd7Bruno Prémont	data->fb_info = NULL;
616fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont	unregister_framebuffer(info);
617fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont}
618