From 106b4a2a78b2a2f14885604891957b84bba502db Mon Sep 17 00:00:00 2001 From: Adrian Betschart Date: Fri, 11 Sep 2026 17:13:14 +0200 Subject: drm/amd/display: Try RGB before YCbCr 4:4:4 in stream validation amdgpu_dm_create_validate_stream_for_sink() walks encoding_order[] and uses the first encoding that validates. YCbCr 4:4:4 is listed before RGB, so an HDMI sink that advertises 4:4:4 gets YCbCr 4:4:4 whenever the "color format" property is left at AUTO, even though RGB fits the same link. That contradicts the documented AUTO behaviour for HDMI in enum drm_connector_color_format (RGB, falling back to YCbCr 4:2:0 only when the bandwidth is not available or the mode is 4:2:0-only), which the amdgpu implementation of the property also describes. It also leaves the "Broadcast RGB" property without effect on such sinks, since the quantization range it selects only applies to RGB output. Try RGB first. The mask still holds every encoding the sink supports, so a mode that cannot carry RGB falls back exactly as before. For reference, v7.2 picked RGB here unless YCbCr 4:4:4 was forced through debugfs, while earlier kernels picked YCbCr 4:4:4 for any HDMI sink that advertised it. Fixes: 0b0ff65d3ca1 ("drm/amd/display: Refactor stream validation") Suggested-by: Adolfo Rodrigues Assisted-by: Claude Code:claude-fable-5-1 Signed-off-by: Adrian Betschart Reviewed-by: Fangzhi Zuo Tested-by: Adolfo Rodrigues Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 7de432bc753133764dc40d37f9388da56de251e7) --- drivers/rtc/rtc-rx8010.c | 434 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 434 insertions(+) create mode 100644 drivers/rtc/rtc-rx8010.c (limited to 'drivers/rtc/rtc-rx8010.c') diff --git a/drivers/rtc/rtc-rx8010.c b/drivers/rtc/rtc-rx8010.c new file mode 100644 index 000000000..ce3954132 --- /dev/null +++ b/drivers/rtc/rtc-rx8010.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the Epson RTC module RX-8010 SJ + * + * Copyright(C) Timesys Corporation 2015 + * Copyright(C) General Electric Company 2015 + */ + +#include +#include +#include +#include +#include +#include +#include + +#define RX8010_SEC 0x10 +#define RX8010_MIN 0x11 +#define RX8010_HOUR 0x12 +#define RX8010_WDAY 0x13 +#define RX8010_MDAY 0x14 +#define RX8010_MONTH 0x15 +#define RX8010_YEAR 0x16 +#define RX8010_RESV17 0x17 +#define RX8010_ALMIN 0x18 +#define RX8010_ALHOUR 0x19 +#define RX8010_ALWDAY 0x1A +#define RX8010_TCOUNT0 0x1B +#define RX8010_TCOUNT1 0x1C +#define RX8010_EXT 0x1D +#define RX8010_FLAG 0x1E +#define RX8010_CTRL 0x1F +/* 0x20 to 0x2F are user registers */ +#define RX8010_RESV30 0x30 +#define RX8010_RESV31 0x31 +#define RX8010_IRQ 0x32 + +#define RX8010_EXT_WADA BIT(3) + +#define RX8010_FLAG_VLF BIT(1) +#define RX8010_FLAG_AF BIT(3) +#define RX8010_FLAG_TF BIT(4) +#define RX8010_FLAG_UF BIT(5) + +#define RX8010_CTRL_AIE BIT(3) +#define RX8010_CTRL_UIE BIT(5) +#define RX8010_CTRL_STOP BIT(6) +#define RX8010_CTRL_TEST BIT(7) + +#define RX8010_ALARM_AE BIT(7) + +static const struct i2c_device_id rx8010_id[] = { + { .name = "rx8010" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rx8010_id); + +static const __maybe_unused struct of_device_id rx8010_of_match[] = { + { .compatible = "epson,rx8010" }, + { } +}; +MODULE_DEVICE_TABLE(of, rx8010_of_match); + +struct rx8010_data { + struct regmap *regs; + struct rtc_device *rtc; + u8 ctrlreg; +}; + +static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct rx8010_data *rx8010 = i2c_get_clientdata(client); + int flagreg, err; + + rtc_lock(rx8010->rtc); + + err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg); + if (err) { + rtc_unlock(rx8010->rtc); + return IRQ_NONE; + } + + if (flagreg & RX8010_FLAG_VLF) + dev_warn(&client->dev, "Frequency stop detected\n"); + + if (flagreg & RX8010_FLAG_TF) { + flagreg &= ~RX8010_FLAG_TF; + rtc_update_irq(rx8010->rtc, 1, RTC_PF | RTC_IRQF); + } + + if (flagreg & RX8010_FLAG_AF) { + flagreg &= ~RX8010_FLAG_AF; + rtc_update_irq(rx8010->rtc, 1, RTC_AF | RTC_IRQF); + } + + if (flagreg & RX8010_FLAG_UF) { + flagreg &= ~RX8010_FLAG_UF; + rtc_update_irq(rx8010->rtc, 1, RTC_UF | RTC_IRQF); + } + + err = regmap_write(rx8010->regs, RX8010_FLAG, flagreg); + rtc_unlock(rx8010->rtc); + return err ? IRQ_NONE : IRQ_HANDLED; +} + +static int rx8010_get_time(struct device *dev, struct rtc_time *dt) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + u8 date[RX8010_YEAR - RX8010_SEC + 1]; + int flagreg, err; + + err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg); + if (err) + return err; + + if (flagreg & RX8010_FLAG_VLF) { + dev_warn(dev, "Frequency stop detected\n"); + return -EINVAL; + } + + err = regmap_bulk_read(rx8010->regs, RX8010_SEC, date, sizeof(date)); + if (err) + return err; + + dt->tm_sec = bcd2bin(date[RX8010_SEC - RX8010_SEC] & 0x7f); + dt->tm_min = bcd2bin(date[RX8010_MIN - RX8010_SEC] & 0x7f); + dt->tm_hour = bcd2bin(date[RX8010_HOUR - RX8010_SEC] & 0x3f); + dt->tm_mday = bcd2bin(date[RX8010_MDAY - RX8010_SEC] & 0x3f); + dt->tm_mon = bcd2bin(date[RX8010_MONTH - RX8010_SEC] & 0x1f) - 1; + dt->tm_year = bcd2bin(date[RX8010_YEAR - RX8010_SEC]) + 100; + dt->tm_wday = ffs(date[RX8010_WDAY - RX8010_SEC] & 0x7f); + + return 0; +} + +static int rx8010_set_time(struct device *dev, struct rtc_time *dt) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + u8 date[RX8010_YEAR - RX8010_SEC + 1]; + int err; + + /* set STOP bit before changing clock/calendar */ + err = regmap_set_bits(rx8010->regs, RX8010_CTRL, RX8010_CTRL_STOP); + if (err) + return err; + + date[RX8010_SEC - RX8010_SEC] = bin2bcd(dt->tm_sec); + date[RX8010_MIN - RX8010_SEC] = bin2bcd(dt->tm_min); + date[RX8010_HOUR - RX8010_SEC] = bin2bcd(dt->tm_hour); + date[RX8010_MDAY - RX8010_SEC] = bin2bcd(dt->tm_mday); + date[RX8010_MONTH - RX8010_SEC] = bin2bcd(dt->tm_mon + 1); + date[RX8010_YEAR - RX8010_SEC] = bin2bcd(dt->tm_year - 100); + date[RX8010_WDAY - RX8010_SEC] = bin2bcd(1 << dt->tm_wday); + + err = regmap_bulk_write(rx8010->regs, RX8010_SEC, date, sizeof(date)); + if (err) + return err; + + /* clear STOP bit after changing clock/calendar */ + err = regmap_clear_bits(rx8010->regs, RX8010_CTRL, RX8010_CTRL_STOP); + if (err) + return err; + + err = regmap_clear_bits(rx8010->regs, RX8010_FLAG, RX8010_FLAG_VLF); + if (err) + return err; + + return 0; +} + +static int rx8010_init(struct device *dev) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + u8 ctrl[2]; + int need_clear = 0, err; + + /* Initialize reserved registers as specified in datasheet */ + err = regmap_write(rx8010->regs, RX8010_RESV17, 0xD8); + if (err) + return err; + + err = regmap_write(rx8010->regs, RX8010_RESV30, 0x00); + if (err) + return err; + + err = regmap_write(rx8010->regs, RX8010_RESV31, 0x08); + if (err) + return err; + + err = regmap_write(rx8010->regs, RX8010_IRQ, 0x00); + if (err) + return err; + + err = regmap_bulk_read(rx8010->regs, RX8010_FLAG, ctrl, 2); + if (err) + return err; + + if (ctrl[0] & RX8010_FLAG_VLF) + dev_warn(dev, "Frequency stop was detected\n"); + + if (ctrl[0] & RX8010_FLAG_AF) { + dev_warn(dev, "Alarm was detected\n"); + need_clear = 1; + } + + if (ctrl[0] & RX8010_FLAG_TF) + need_clear = 1; + + if (ctrl[0] & RX8010_FLAG_UF) + need_clear = 1; + + if (need_clear) { + ctrl[0] &= ~(RX8010_FLAG_AF | RX8010_FLAG_TF | RX8010_FLAG_UF); + err = regmap_write(rx8010->regs, RX8010_FLAG, ctrl[0]); + if (err) + return err; + } + + rx8010->ctrlreg = (ctrl[1] & ~RX8010_CTRL_TEST); + + return 0; +} + +static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + u8 alarmvals[3]; + int flagreg, err; + + err = regmap_bulk_read(rx8010->regs, RX8010_ALMIN, alarmvals, 3); + if (err) + return err; + + err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg); + if (err) + return err; + + t->time.tm_sec = 0; + t->time.tm_min = bcd2bin(alarmvals[0] & 0x7f); + t->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f); + + if (!(alarmvals[2] & RX8010_ALARM_AE)) + t->time.tm_mday = bcd2bin(alarmvals[2] & 0x7f); + + t->enabled = !!(rx8010->ctrlreg & RX8010_CTRL_AIE); + t->pending = (flagreg & RX8010_FLAG_AF) && t->enabled; + + return 0; +} + +static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + u8 alarmvals[3]; + int err; + + if (rx8010->ctrlreg & (RX8010_CTRL_AIE | RX8010_CTRL_UIE)) { + rx8010->ctrlreg &= ~(RX8010_CTRL_AIE | RX8010_CTRL_UIE); + err = regmap_write(rx8010->regs, RX8010_CTRL, rx8010->ctrlreg); + if (err) + return err; + } + + err = regmap_clear_bits(rx8010->regs, RX8010_FLAG, RX8010_FLAG_AF); + if (err) + return err; + + alarmvals[0] = bin2bcd(t->time.tm_min); + alarmvals[1] = bin2bcd(t->time.tm_hour); + alarmvals[2] = bin2bcd(t->time.tm_mday); + + err = regmap_bulk_write(rx8010->regs, RX8010_ALMIN, alarmvals, 2); + if (err) + return err; + + err = regmap_clear_bits(rx8010->regs, RX8010_EXT, RX8010_EXT_WADA); + if (err) + return err; + + if (alarmvals[2] == 0) + alarmvals[2] |= RX8010_ALARM_AE; + + err = regmap_write(rx8010->regs, RX8010_ALWDAY, alarmvals[2]); + if (err) + return err; + + if (t->enabled) { + if (rx8010->rtc->uie_rtctimer.enabled) + rx8010->ctrlreg |= RX8010_CTRL_UIE; + if (rx8010->rtc->aie_timer.enabled) + rx8010->ctrlreg |= + (RX8010_CTRL_AIE | RX8010_CTRL_UIE); + + err = regmap_write(rx8010->regs, RX8010_CTRL, rx8010->ctrlreg); + if (err) + return err; + } + + return 0; +} + +static int rx8010_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + int err; + u8 ctrl; + + ctrl = rx8010->ctrlreg; + + if (enabled) { + if (rx8010->rtc->uie_rtctimer.enabled) + ctrl |= RX8010_CTRL_UIE; + if (rx8010->rtc->aie_timer.enabled) + ctrl |= (RX8010_CTRL_AIE | RX8010_CTRL_UIE); + } else { + if (!rx8010->rtc->uie_rtctimer.enabled) + ctrl &= ~RX8010_CTRL_UIE; + if (!rx8010->rtc->aie_timer.enabled) + ctrl &= ~RX8010_CTRL_AIE; + } + + err = regmap_clear_bits(rx8010->regs, RX8010_FLAG, RX8010_FLAG_AF); + if (err) + return err; + + if (ctrl != rx8010->ctrlreg) { + rx8010->ctrlreg = ctrl; + err = regmap_write(rx8010->regs, RX8010_CTRL, rx8010->ctrlreg); + if (err) + return err; + } + + return 0; +} + +static int rx8010_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + int tmp, flagreg, err; + + switch (cmd) { + case RTC_VL_READ: + err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg); + if (err) + return err; + + tmp = flagreg & RX8010_FLAG_VLF ? RTC_VL_DATA_INVALID : 0; + return put_user(tmp, (unsigned int __user *)arg); + + default: + return -ENOIOCTLCMD; + } +} + +static const struct rtc_class_ops rx8010_rtc_ops = { + .read_time = rx8010_get_time, + .set_time = rx8010_set_time, + .ioctl = rx8010_ioctl, + .read_alarm = rx8010_read_alarm, + .set_alarm = rx8010_set_alarm, + .alarm_irq_enable = rx8010_alarm_irq_enable, +}; + +static const struct regmap_config rx8010_regmap_config = { + .name = "rx8010-rtc", + .reg_bits = 8, + .val_bits = 8, +}; + +static int rx8010_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct rx8010_data *rx8010; + int err = 0; + + rx8010 = devm_kzalloc(dev, sizeof(*rx8010), GFP_KERNEL); + if (!rx8010) + return -ENOMEM; + + i2c_set_clientdata(client, rx8010); + + rx8010->regs = devm_regmap_init_i2c(client, &rx8010_regmap_config); + if (IS_ERR(rx8010->regs)) + return PTR_ERR(rx8010->regs); + + err = rx8010_init(dev); + if (err) + return err; + + rx8010->rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(rx8010->rtc)) + return PTR_ERR(rx8010->rtc); + + if (client->irq > 0) { + unsigned long irqflags = IRQF_TRIGGER_LOW; + + if (dev_fwnode(&client->dev)) + irqflags = 0; + + err = devm_request_threaded_irq(dev, client->irq, NULL, + rx8010_irq_1_handler, + irqflags | IRQF_ONESHOT, + "rx8010", client); + if (err) { + dev_err(dev, "unable to request IRQ\n"); + return err; + } + } else { + clear_bit(RTC_FEATURE_ALARM, rx8010->rtc->features); + } + + rx8010->rtc->ops = &rx8010_rtc_ops; + rx8010->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rx8010->rtc->range_max = RTC_TIMESTAMP_END_2099; + + return devm_rtc_register_device(rx8010->rtc); +} + +static struct i2c_driver rx8010_driver = { + .driver = { + .name = "rtc-rx8010", + .of_match_table = of_match_ptr(rx8010_of_match), + }, + .probe = rx8010_probe, + .id_table = rx8010_id, +}; + +module_i2c_driver(rx8010_driver); + +MODULE_AUTHOR("Akshay Bhat "); +MODULE_DESCRIPTION("Epson RX8010SJ RTC driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.3.1