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