// SPDX-License-Identifier: GPL-2.0-only /*************************************************************************** * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> * * * * Based on Logitech G13 driver (v0.4) * * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> * * *
***************************************************************************/
#include <linux/hid.h> #include <linux/vmalloc.h>
#include <linux/fb.h> #include <linux/module.h>
#include"hid-picolcd.h"
/* Framebuffer * * The PicoLCD use a Topway LCD module of 256x64 pixel * This display area is tiled over 4 controllers with 8 tiles * each. Each tile has 8x64 pixel, each data byte representing * a 1-bit wide vertical line of the tile. * * The display can be updated at a tile granularity. * * Chip 1 Chip 2 Chip 3 Chip 4 * +----------------+----------------+----------------+----------------+ * | Tile 1 | Tile 1 | Tile 1 | Tile 1 | * +----------------+----------------+----------------+----------------+ * | Tile 2 | Tile 2 | Tile 2 | Tile 2 | * +----------------+----------------+----------------+----------------+ * ... * +----------------+----------------+----------------+----------------+ * | Tile 8 | Tile 8 | Tile 8 | Tile 8 | * +----------------+----------------+----------------+----------------+
*/ #define PICOLCDFB_NAME "picolcdfb" #define PICOLCDFB_WIDTH (256) #define PICOLCDFB_HEIGHT (64) #define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8)
/* Send a given tile to PicoLCD */ staticint picolcd_fb_send_tile(struct picolcd_data *data, u8 *vbitmap, int chip, int tile)
{ struct hid_report *report1, *report2; unsignedlong flags;
u8 *tdata; int i;
/* schedule first output of framebuffer */ if (fbdata->ready)
schedule_delayed_work(&data->fb_info->deferred_work, 0); else
fbdata->ready = 1;
return 0;
}
/* Update fb_vbitmap from the screen_buffer and send changed tiles to device */ staticvoid picolcd_fb_update(struct fb_info *info)
{ int chip, tile, n; unsignedlong flags; struct picolcd_fb_data *fbdata = info->par; struct picolcd_data *data;
mutex_lock(&info->lock);
spin_lock_irqsave(&fbdata->lock, flags); if (!fbdata->ready && fbdata->picolcd)
picolcd_fb_reset(fbdata->picolcd, 0);
spin_unlock_irqrestore(&fbdata->lock, flags);
/* * Translate the framebuffer into the format needed by the PicoLCD. * See display layout above. * Do this one tile after the other and push those tiles that changed. * * Wait for our IO to complete as otherwise we might flood the queue!
*/
n = 0; for (chip = 0; chip < 4; chip++) for (tile = 0; tile < 8; tile++) { if (!fbdata->force && !picolcd_fb_update_tile(
fbdata->vbitmap, fbdata->bitmap,
fbdata->bpp, chip, tile)) continue;
n += 2; if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
spin_lock_irqsave(&fbdata->lock, flags);
data = fbdata->picolcd;
spin_unlock_irqrestore(&fbdata->lock, flags);
mutex_unlock(&info->lock); if (!data) return;
hid_hw_wait(data->hdev);
mutex_lock(&info->lock);
n = 0;
}
spin_lock_irqsave(&fbdata->lock, flags);
data = fbdata->picolcd;
spin_unlock_irqrestore(&fbdata->lock, flags); if (!data || picolcd_fb_send_tile(data,
fbdata->vbitmap, chip, tile)) goto out;
}
fbdata->force = false; if (n) {
spin_lock_irqsave(&fbdata->lock, flags);
data = fbdata->picolcd;
spin_unlock_irqrestore(&fbdata->lock, flags);
mutex_unlock(&info->lock); if (data)
hid_hw_wait(data->hdev); return;
}
out:
mutex_unlock(&info->lock);
}
staticint picolcd_fb_blank(int blank, struct fb_info *info)
{ /* We let fb notification do this for us via lcd/backlight device */ return 0;
}
for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++) if (i == fb_update_rate)
ret += sysfs_emit_at(buf, ret, "[%u] ", i); else
ret += sysfs_emit_at(buf, ret, "%u ", i); if (ret > 0)
buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n'; return ret;
}
/* disconnect framebuffer from HID dev */
spin_lock_irqsave(&fbdata->lock, flags);
fbdata->picolcd = NULL;
spin_unlock_irqrestore(&fbdata->lock, flags);
/* make sure there is no running update - thus that fbdata->picolcd * once obtained under lock is guaranteed not to get free() under
* the feet of the deferred work */
flush_delayed_work(&info->deferred_work);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.