Skip to content

Commit 2141aee

Browse files
rpardiniclaude
andcommitted
auxdisplay: vk2c21: add sysfs brightness control
Expose a `brightness` sysfs attribute that drives the chip's IVASET register (0x8A): - "default" (the boot-time state): IVASET=0x0F, IVA off, bias driven by the board's external VR resistor (same as before this change). - 0..15: IVASET=0x3F..0x30 (IVA on, SEG mode); 15 = brightest (1.000 x VDD), 0 = dimmest (0.529 x VDD at 1/3 bias). SEG mode (0x30..0x3F) is used rather than VLCD-output mode (0x10..0x1F) because the R58X-Pro ties the VLCD pin to VDD through an external resistor: in VLCD-output mode the external circuit dominates the chip's internal driver and the brightness register has no visible effect. In SEG mode the chip ignores the VLCD pin and generates the bias voltage purely internally, so the 16 levels actually take effect. Probe-time behaviour is unchanged: init still sends IVA=0x0F, and the attribute reads back as "default" until userspace writes a number. Signed-off-by: Ricardo Pardini <ricardo@pardini.net> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 64a8171 commit 2141aee

1 file changed

Lines changed: 61 additions & 1 deletion

File tree

drivers/auxdisplay/lcd-vk2c21.c

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,23 @@
4141
#define VK2C21_SYS_OFF_LCD_OFF 0x00 /* System + LCD off */
4242
#define VK2C21_FRAME_80HZ 0x00 /* 80 Hz frame rate */
4343
#define VK2C21_BLINK_OFF 0x00 /* Blinking off */
44-
#define VK2C21_IVA_DEFAULT 0x0F /* VLCD selected, IVA off, R1 */
44+
#define VK2C21_IVA_DEFAULT 0x0F /* VLCD selected, IVA off, R1 (external VR) */
45+
46+
/*
47+
* IVA on, SEG mode: 16 internal bias-voltage levels.
48+
* 0x30 = level 0 = 1.000 x VDD (highest contrast / "brightest")
49+
* 0x3F = level 15 = 0.529 x VDD (lowest contrast / "dimmest")
50+
* SEG mode (vs. VLCD-output mode at 0x10..0x1F) is used because the R58X-Pro
51+
* has an external resistor on the VLCD pin: in VLCD-output mode the external
52+
* resistor dominates the chip's internal driver and the brightness register
53+
* has no visible effect. SEG mode tells the chip to ignore the VLCD pin and
54+
* generate the bias voltage purely internally.
55+
* Userspace brightness is inverted (0 = dimmest, 15 = brightest) so that
56+
* higher numbers look brighter, matching the usual convention.
57+
*/
58+
#define VK2C21_IVA_ON_BASE 0x30
59+
#define VK2C21_BRIGHTNESS_MAX 15
60+
#define VK2C21_BRIGHTNESS_DEFAULT (-1) /* IVA off, external VR in use */
4561

4662
/* Display RAM has 10 addressable bytes (SEG/COM matrix) */
4763
#define VK2C21_RAM_SIZE 10
@@ -185,6 +201,7 @@ struct vk2c21_data {
185201
unsigned int half_period_ns;
186202
u8 dispram[VK2C21_RAM_SIZE];
187203
char display_text[VK2C21_MAX_DIGITS + 1];
204+
int brightness; /* -1 = IVA off / "default", 0..15 = IVA level */
188205
};
189206

190207
/* ------------------------------------------------------------------ */
@@ -414,9 +431,51 @@ static ssize_t clear_store(struct device *dev,
414431
}
415432
static DEVICE_ATTR_WO(clear);
416433

434+
static ssize_t brightness_show(struct device *dev,
435+
struct device_attribute *attr, char *buf)
436+
{
437+
struct vk2c21_data *d = dev_get_drvdata(dev);
438+
439+
if (d->brightness == VK2C21_BRIGHTNESS_DEFAULT)
440+
return sysfs_emit(buf, "default\n");
441+
return sysfs_emit(buf, "%d\n", d->brightness);
442+
}
443+
444+
static ssize_t brightness_store(struct device *dev,
445+
struct device_attribute *attr,
446+
const char *buf, size_t count)
447+
{
448+
struct vk2c21_data *d = dev_get_drvdata(dev);
449+
int ret, level;
450+
u8 iva;
451+
452+
if (sysfs_streq(buf, "default")) {
453+
level = VK2C21_BRIGHTNESS_DEFAULT;
454+
iva = VK2C21_IVA_DEFAULT;
455+
} else {
456+
ret = kstrtoint(buf, 0, &level);
457+
if (ret)
458+
return ret;
459+
if (level < 0 || level > VK2C21_BRIGHTNESS_MAX)
460+
return -EINVAL;
461+
/* Invert: userspace 15 = brightest = IVA level 0 = 0x30 */
462+
iva = VK2C21_IVA_ON_BASE + (VK2C21_BRIGHTNESS_MAX - level);
463+
}
464+
465+
mutex_lock(&d->lock);
466+
ret = vk2c21_send_cmd(d, VK2C21_IVASET, iva);
467+
if (!ret)
468+
d->brightness = level;
469+
mutex_unlock(&d->lock);
470+
471+
return ret ? ret : count;
472+
}
473+
static DEVICE_ATTR_RW(brightness);
474+
417475
static struct attribute *vk2c21_sysfs_attrs[] = {
418476
&dev_attr_display.attr,
419477
&dev_attr_clear.attr,
478+
&dev_attr_brightness.attr,
420479
NULL,
421480
};
422481

@@ -445,6 +504,7 @@ static int vk2c21_probe(struct platform_device *pdev)
445504
return -ENOMEM;
446505

447506
d->dev = dev;
507+
d->brightness = VK2C21_BRIGHTNESS_DEFAULT;
448508
mutex_init(&d->lock);
449509
platform_set_drvdata(pdev, d);
450510

0 commit comments

Comments
 (0)