cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hid-picolcd_fb.c (16771B)


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