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