From: sashiko-bot@kernel.org
To: "Torstein Eide" <torsteine+linux@gmail.com>
Cc: linux-input@vger.kernel.org, dmitry.torokhov@gmail.com
Subject: Re: [PATCH 2/4] HID: lg-g15: add LCD pixel-content framebuffer
Date: Mon, 09 Nov 2026 10:21:55 +0000 [thread overview]
Message-ID: <sashiko-outbox-204805@kernel.org> (raw)
In-Reply-To: <20261109094426.1432465-3-torsteine+linux@gmail.com>
Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [Medium] HID: lg-g15: reversed 1bpp pixel bit order in
lg_g15_fb_pack()
- [Low] HID: lg-g15: no-op lg_g15_fb_blank() breaks fbcon blanking and
blank state
- [Low] HID: lg-g15: race and missed redraw onpar->sent in
lg_g15_fb_redraw()
--
commit bcb1dfda67b01b4a6f0c45e68ab96dc227cf9166
Author: Torstein Eide <torsteine+linux@gmail.com>
HID: lg-g15: add LCD pixel-content framebuffer
This commit adds a 160x43 1bpp fbdev framebuffer using deferred I/O for
the GamePanel LCD on Logitech G15 and G15 v2 keyboards.
> diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c
> index 50abe6c58b10b..35d183787383a 100644
> --- a/drivers/hid/hid-lg-g15.c
> +++ b/drivers/hid/hid-lg-g15.c
[ ... ]
> @@ -1051,6 +1081,270 @@ static void lg_g15_setup_led_rgb(struct lg_g15_data *g15, int index)
> gled->mcdev.subled_info = subled_info;
> }
>
> +/********* G15 LCD framebuffer *********/
> +
> +#ifdef CONFIG_HID_LOGITECH_G15_FB
[ ... ]
> +static const struct fb_var_screeninfo lg_g15_fb_var = {
> + .xres = LG_G15_LCD_WIDTH,
> + .yres = LG_G15_LCD_HEIGHT,
> + .xres_virtual = LG_G15_LCD_WIDTH,
> + .yres_virtual = LG_G15_LCD_HEIGHT,
> + .bits_per_pixel = 1,
> + .red = { 0, 1, 0 },
> + .green = { 0, 1, 0 },
> + .blue = { 0, 1, 0 },
> + .vmode = FB_VMODE_NONINTERLACED,
> +};
> +
> +/*
> + * Packs pixels column-major (8 vertically-stacked source rows per
> + * destination byte), not the row-major layout the linear fb buffer holds.
> + *
> + * fb is LG_G15_LCD_WIRE_SIZE bytes (padded, not just LG_G15_LCD_FB_SIZE) -
> + * the last of the six 8-row groups only has 3 real pixel-rows (43 isn't a
> + * multiple of 8), and this reads past the logical frame into that padding
> + * for the other 5.
> + */
> +static void lg_g15_fb_pack(u8 *wire, const u8 *fb)
> +{
> + unsigned int output_offset = LG_G15_LCD_WIRE_OFFSET;
> + unsigned int base_offset = 0;
> + unsigned int row, col;
> + unsigned int height_in_bytes = DIV_ROUND_UP(LG_G15_LCD_HEIGHT, 8);
> +
> + for (row = 0; row < height_in_bytes; row++) {
> + for (col = 0; col < LG_G15_LCD_WIDTH; col++) {
> + unsigned int bit = col % 8;
> + unsigned int sub;
> + u8 packed = 0;
> +
> + for (sub = 0; sub < 8; sub++) {
> + unsigned int pixel_offset =
> + base_offset + sub * LG_G15_LCD_ROW_BYTES;
> +
> + packed |= ((fb[pixel_offset] << bit) & 0x80) >> (7 - sub);
> + }
[Severity: Medium]
Does lg_g15_fb_pack() extract bits from each source byte in reversed
horizontal order compared to standard 1bpp fbdev drawing helpers?
On little-endian architectures, sys_imageblit(), sys_fillrect(), and
sys_copyarea() (wired in via FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS without
FB_NONSTD_REV_PIX_IN_B) place the leftmost pixel of each 8-pixel group
(col % 8 == 0) in bit 0 (0x01) and col % 8 == 7 in bit 7 (0x80).
Shifting fb[pixel_offset] left by bit and masking with 0x80 reads bit 7
for col % 8 == 0 and bit 0 for col % 8 == 7, horizontally mirroring every
8-pixel byte group when rendering via fbcon or the sys_* drawing ops.
Extracting ((fb[pixel_offset] >> bit) & 1) << sub would match standard
1bpp ordering.
> +
> + wire[output_offset] = packed;
> + output_offset++;
> + if (bit == 7)
> + base_offset++;
> + }
> + base_offset += LG_G15_LCD_WIDTH - LG_G15_LCD_ROW_BYTES;
> + }
> +}
> +
> +/*
> + * Deferred IO callback, runs when userspace dirtied the framebuffer (through
> + * mmap, write() or the drawing ops). Writes made while a frame is being sent
> + * re-arm the deferred work, so no update is lost.
> + */
> +static void lg_g15_fb_deferred_io(struct fb_info *info, struct list_head *pagereflist)
> +{
> + struct lg_g15_fb_par *par = info->par;
> + struct lg_g15_data *g15;
> + u8 *wire;
> + int ret;
> +
> + mutex_lock(&par->lock);
> +
> + g15 = par->g15;
> + if (!g15)
> + goto out;
> +
> + wire = g15->fbdev.wire;
> + memset(wire, 0, LG_G15_LCD_WIRE_OFFSET);
> + lg_g15_fb_pack(wire, (const u8 *)info->screen_buffer);
> + wire[0] = LG_G15_LCD_OUTPUT_REPORT;
> +
> + ret = hid_hw_output_report(g15->hdev, wire, LG_G15_LCD_WIRE_SIZE);
> + if (ret < 0)
> + hid_err_ratelimited(g15->hdev, "Error writing LCD framebuffer: %d\n", ret);
> + else
> + par->sent = true;
> +
> +out:
> + mutex_unlock(&par->lock);
> +}
> +
> +static void lg_g15_fb_damage_range(struct fb_info *info, off_t off, size_t len)
> +{
> + schedule_delayed_work(&info->deferred_work, info->fbdefio->delay);
> +}
> +
> +static void lg_g15_fb_damage_area(struct fb_info *info, u32 x, u32 y,
> + u32 width, u32 height)
> +{
> + schedule_delayed_work(&info->deferred_work, info->fbdefio->delay);
> +}
> +
> +FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(lg_g15_fb,
> + lg_g15_fb_damage_range,
> + lg_g15_fb_damage_area)
> +
> +static int lg_g15_fb_blank(int blank, struct fb_info *info)
> +{
> + return 0;
> +}
> +
> +static void lg_g15_fb_destroy(struct fb_info *info)
> +{
> + struct lg_g15_fb_par *par = info->par;
> +
> + fb_deferred_io_cleanup(info);
> + mutex_destroy(&par->lock);
> + vfree(info->screen_buffer);
> + framebuffer_release(info);
> +}
> +
> +static const struct fb_ops lg_g15_fb_ops = {
> + .owner = THIS_MODULE,
> + FB_DEFAULT_DEFERRED_OPS(lg_g15_fb),
> + .fb_destroy = lg_g15_fb_destroy,
> + .fb_blank = lg_g15_fb_blank,
> +};
[Severity: Low]
Does providing a no-op lg_g15_fb_blank() that returns 0 prevent fbcon
from blanking the display?
When fb_ops->fb_blank is populated, do_register_framebuffer() initializes
info->blank to FB_BLANK_POWERDOWN, and fbcon_blank() only falls back to
clearing the screen in software via fbcon_generic_blank() when fb_blank()
returns non-zero. In addition, because info->bl_dev and info->lcd_dev
are NULL, returning 0 from fb_blank() causes fb_bl_notify_blank(),
fb_lcd_notify_blank(), and fb_ledtrig_backlight_notify_blank() to
broadcast blank events to unrelated backlight and LCD devices on the
system. Omitting .fb_blank from lg_g15_fb_ops avoids these side effects.
[ ... ]
> +/* The LCD loses its pixel content across a USB reset or power loss */
> +static void lg_g15_fb_redraw(struct lg_g15_data *g15)
> +{
> + struct fb_info *info = g15->fbdev.info;
> + struct lg_g15_fb_par *par;
> +
> + if (!info)
> + return;
> +
> + par = info->par;
> + if (par->sent)
> + schedule_delayed_work(&info->deferred_work, 0);
> +}
[Severity: Low]
Can lg_g15_fb_redraw() miss redrawing the initial frame across suspend and
resume?
In lg_g15_fb_deferred_io(), par->sent is only set when
hid_hw_output_report() succeeds. If the first frame's deferred I/O work
runs while the USB device is suspended, fb_deferred_io_work consumes the
dirty state and hid_hw_output_report() fails with -EHOSTUNREACH, leaving
par->sent false so lg_g15_fb_redraw() skips rescheduling the update on
resume.
Also, lg_g15_fb_redraw() reads par->sent without holding par->lock:
kworker CPU1
lg_g15_fb_deferred_io()
mutex_lock(&par->lock)
hid_hw_output_report(...)
lg_g15_resume()
lg_g15_fb_redraw()
if (par->sent) <-- reads false
par->sent = true
mutex_unlock(&par->lock)
Setting par->sent = true before hid_hw_output_report() in
lg_g15_fb_deferred_io() and taking par->lock in lg_g15_fb_redraw() would
ensure the frame is re-sent after resume.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261109094426.1432465-1-torsteine+linux@gmail.com?part=2
next prev parent reply other threads:[~2026-11-09 10:21 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-09 9:44 [PATCH 0/4] HID: lg-g15: G15 v2 LCD and game-mode support Torstein Eide
2026-11-09 9:44 ` [PATCH 1/4] HID: lg-g15: add LCD contrast LED Torstein Eide
2026-11-09 10:00 ` sashiko-bot
2026-11-09 9:44 ` [PATCH 2/4] HID: lg-g15: add LCD pixel-content framebuffer Torstein Eide
2026-11-09 10:21 ` sashiko-bot [this message]
2026-11-09 9:44 ` [PATCH 3/4] HID: lg-g15: report Super-key-lock state on G15 v2's keyboard interface Torstein Eide
2026-11-09 10:36 ` sashiko-bot
2026-11-09 9:44 ` [PATCH 4/4] HID: lg-g15: cancel LED work only after stopping the hardware Torstein Eide
2026-11-09 14:01 ` Hans de Goede
2026-11-09 13:49 ` [PATCH 0/4] HID: lg-g15: G15 v2 LCD and game-mode support Hans de Goede
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-204805@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=dmitry.torokhov@gmail.com \
--cc=linux-input@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=torsteine+linux@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.