From 512d1899b8968cde259ab9b9980fa51df1394128 Mon Sep 17 00:00:00 2001 From: Zelong Dong Date: Wed, 3 Jul 2024 17:38:58 +0800 Subject: media: rc: meson-ir: support PM suspend/resume IR Controller could be used and updated by other processor while kernel has been suspended. Reinitialize IR Controller just in case while kernel is resuming. Signed-off-by: Zelong Dong Signed-off-by: Sean Young Signed-off-by: Hans Verkuil --- drivers/media/rc/meson-ir.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers') diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c index 5303e6da5809..9cdb45821ecc 100644 --- a/drivers/media/rc/meson-ir.c +++ b/drivers/media/rc/meson-ir.c @@ -567,6 +567,32 @@ static void meson_ir_shutdown(struct platform_device *pdev) spin_unlock_irqrestore(&ir->lock, flags); } +static __maybe_unused int meson_ir_resume(struct device *dev) +{ + struct meson_ir *ir = dev_get_drvdata(dev); + + if (ir->param->support_hw_decoder) + meson_ir_hw_decoder_init(ir->rc, &ir->rc->enabled_protocols); + else + meson_ir_sw_decoder_init(ir->rc); + + return 0; +} + +static __maybe_unused int meson_ir_suspend(struct device *dev) +{ + struct meson_ir *ir = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&ir->lock, flags); + regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_ENABLE, 0); + spin_unlock_irqrestore(&ir->lock, flags); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(meson_ir_pm_ops, meson_ir_suspend, meson_ir_resume); + static const struct meson_ir_param meson6_ir_param = { .support_hw_decoder = false, .max_register = IR_DEC_REG1, @@ -607,6 +633,7 @@ static struct platform_driver meson_ir_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = meson_ir_match, + .pm = pm_ptr(&meson_ir_pm_ops), }, }; -- cgit v1.2.3-70-g09d2 From ba5c778cab1dd3e4918f940989e771e2818afee8 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sun, 30 Jun 2024 22:33:44 +0100 Subject: media: rc: remove unused tx_resolution field The tx_resolution field is never read. In theory you can imagine this field being useful for detecting whether the transmitter has the resolution for the message you are trying to send, but I am not aware of any hardware where this could be an issue. Just remove. Signed-off-by: Sean Young Signed-off-by: Hans Verkuil --- drivers/media/rc/ene_ir.c | 3 --- drivers/media/rc/ite-cir.c | 1 - drivers/media/rc/rc-loopback.c | 1 - include/media/rc-core.h | 2 -- 4 files changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index 11ee21a7db8f..67722e2e47ff 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -451,9 +451,6 @@ select_timeout: dev->rdev->max_timeout = 200000; } - if (dev->hw_learning_and_tx_capable) - dev->rdev->tx_resolution = sample_period; - if (dev->rdev->timeout > dev->rdev->max_timeout) dev->rdev->timeout = dev->rdev->max_timeout; if (dev->rdev->timeout < dev->rdev->min_timeout) diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index fcfadd7ea31c..2bacecb02262 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1380,7 +1380,6 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id rdev->timeout = IR_DEFAULT_TIMEOUT; rdev->max_timeout = 10 * IR_DEFAULT_TIMEOUT; rdev->rx_resolution = ITE_BAUDRATE_DIVISOR * sample_period / 1000; - rdev->tx_resolution = ITE_BAUDRATE_DIVISOR * sample_period / 1000; /* set up transmitter related values */ rdev->tx_ir = ite_tx_ir; diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c index b356041c5c00..8288366f891f 100644 --- a/drivers/media/rc/rc-loopback.c +++ b/drivers/media/rc/rc-loopback.c @@ -230,7 +230,6 @@ static int __init loop_init(void) rc->min_timeout = 1; rc->max_timeout = IR_MAX_TIMEOUT; rc->rx_resolution = 1; - rc->tx_resolution = 1; rc->s_tx_mask = loop_set_tx_mask; rc->s_tx_carrier = loop_set_tx_carrier; rc->s_tx_duty_cycle = loop_set_tx_duty_cycle; diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 803349599c27..d095908073ef 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -127,7 +127,6 @@ struct lirc_fh { * @min_timeout: minimum timeout supported by device * @max_timeout: maximum timeout supported by device * @rx_resolution : resolution (in us) of input sampler - * @tx_resolution: resolution (in us) of output sampler * @lirc_dev: lirc device * @lirc_cdev: lirc char cdev * @gap_start: start time for gap after timeout if non-zero @@ -194,7 +193,6 @@ struct rc_dev { u32 min_timeout; u32 max_timeout; u32 rx_resolution; - u32 tx_resolution; #ifdef CONFIG_LIRC struct device lirc_dev; struct cdev lirc_cdev; -- cgit v1.2.3-70-g09d2 From 613f21505b25a4f43f33de00f11afc059bedde2b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 4 Jul 2024 11:01:51 +0200 Subject: media: cec: core: add new CEC_MSG_FL_REPLY_VENDOR_ID flag If this flag is set, then the reply is expected to consist of the CEC_MSG_VENDOR_COMMAND_WITH_ID opcode followed by the Vendor ID (as used in bytes 1-4 of the message), followed by the struct cec_msg reply field. Note that this assumes that the byte after the Vendor ID is a vendor-specific opcode. This flag makes it easier to wait for replies to vendor commands, using the same CEC framework support for waiting for regular replies. Support for this flag is indicated by setting the new CEC_CAP_REPLY_VENDOR_ID capability. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/cec/cec-ioc-adap-g-caps.rst | 6 +++ .../userspace-api/media/cec/cec-ioc-receive.rst | 15 +++++++ drivers/media/cec/core/cec-adap.c | 52 +++++++++++++++------- drivers/media/cec/core/cec-core.c | 2 +- include/media/cec.h | 2 + include/uapi/linux/cec.h | 3 ++ 6 files changed, 64 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/Documentation/userspace-api/media/cec/cec-ioc-adap-g-caps.rst b/Documentation/userspace-api/media/cec/cec-ioc-adap-g-caps.rst index d5e014ce19b5..1d5248979a6d 100644 --- a/Documentation/userspace-api/media/cec/cec-ioc-adap-g-caps.rst +++ b/Documentation/userspace-api/media/cec/cec-ioc-adap-g-caps.rst @@ -137,6 +137,12 @@ returns the information to the application. The ioctl never fails. - 0x00000100 - If this capability is set, then :ref:`CEC_ADAP_G_CONNECTOR_INFO` can be used. + * .. _`CEC-CAP-REPLY-VENDOR-ID`: + + - ``CEC_CAP_REPLY_VENDOR_ID`` + - 0x00000200 + - If this capability is set, then + :ref:`CEC_MSG_FL_REPLY_VENDOR_ID ` can be used. Return Value ============ diff --git a/Documentation/userspace-api/media/cec/cec-ioc-receive.rst b/Documentation/userspace-api/media/cec/cec-ioc-receive.rst index 364938ad34df..3e6c511e054f 100644 --- a/Documentation/userspace-api/media/cec/cec-ioc-receive.rst +++ b/Documentation/userspace-api/media/cec/cec-ioc-receive.rst @@ -232,6 +232,21 @@ View On' messages from initiator 0xf ('Unregistered') to destination 0 ('TV'). capability. If that is not set, then the ``EPERM`` error code is returned. + * .. _`CEC-MSG-FL-REPLY-VENDOR-ID`: + + - ``CEC_MSG_FL_REPLY_VENDOR_ID`` + - 4 + - This flag is only available if the ``CEC_CAP_REPLY_VENDOR_ID`` capability + is set. If this flag is set, then the reply is expected to consist of + the ``CEC_MSG_VENDOR_COMMAND_WITH_ID`` opcode followed by the Vendor ID + (in bytes 1-4 of the message), followed by the ``struct cec_msg`` + ``reply`` field. + + Note that this assumes that the byte after the Vendor ID is a + vendor-specific opcode. + + This flag makes it easier to wait for replies to vendor commands. + .. tabularcolumns:: |p{5.6cm}|p{0.9cm}|p{10.8cm}| .. _cec-tx-status: diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index da09834990b8..c81b1ed7c08a 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -673,8 +673,9 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status, /* Retry this message */ data->attempts -= attempts_made; if (msg->timeout) - dprintk(2, "retransmit: %*ph (attempts: %d, wait for 0x%02x)\n", - msg->len, msg->msg, data->attempts, msg->reply); + dprintk(2, "retransmit: %*ph (attempts: %d, wait for %*ph)\n", + msg->len, msg->msg, data->attempts, + data->match_len, data->match_reply); else dprintk(2, "retransmit: %*ph (attempts: %d)\n", msg->len, msg->msg, data->attempts); @@ -780,6 +781,7 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, { struct cec_data *data; bool is_raw = msg_is_raw(msg); + bool reply_vendor_id = msg->flags & CEC_MSG_FL_REPLY_VENDOR_ID; int err; if (adap->devnode.unregistered) @@ -794,12 +796,13 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, msg->tx_low_drive_cnt = 0; msg->tx_error_cnt = 0; msg->sequence = 0; + msg->flags &= CEC_MSG_FL_REPLY_TO_FOLLOWERS | CEC_MSG_FL_RAW | + CEC_MSG_FL_REPLY_VENDOR_ID; - if (msg->reply && msg->timeout == 0) { + if ((reply_vendor_id || msg->reply) && msg->timeout == 0) { /* Make sure the timeout isn't 0. */ msg->timeout = 1000; } - msg->flags &= CEC_MSG_FL_REPLY_TO_FOLLOWERS | CEC_MSG_FL_RAW; if (!msg->timeout) msg->flags &= ~CEC_MSG_FL_REPLY_TO_FOLLOWERS; @@ -809,6 +812,11 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, dprintk(1, "%s: invalid length %d\n", __func__, msg->len); return -EINVAL; } + if (reply_vendor_id && + (msg->len < 6 || msg->msg[1] != CEC_MSG_VENDOR_COMMAND_WITH_ID)) { + dprintk(1, "%s: message too short or not \n", __func__); + return -EINVAL; + } memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len); @@ -900,8 +908,9 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, __func__); return -ENONET; } - if (msg->reply) { - dprintk(1, "%s: invalid msg->reply\n", __func__); + if (reply_vendor_id || msg->reply) { + dprintk(1, "%s: adapter is unconfigured so reply is not supported\n", + __func__); return -EINVAL; } } @@ -923,6 +932,14 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, data->fh = fh; data->adap = adap; data->blocking = block; + if (reply_vendor_id) { + memcpy(data->match_reply, msg->msg + 1, 4); + data->match_reply[4] = msg->reply; + data->match_len = 5; + } else if (msg->timeout) { + data->match_reply[0] = msg->reply; + data->match_len = 1; + } init_completion(&data->c); INIT_DELAYED_WORK(&data->work, cec_wait_timeout); @@ -1211,13 +1228,15 @@ void cec_received_msg_ts(struct cec_adapter *adap, if (!abort && dst->msg[1] == CEC_MSG_INITIATE_ARC && (cmd == CEC_MSG_REPORT_ARC_INITIATED || cmd == CEC_MSG_REPORT_ARC_TERMINATED) && - (dst->reply == CEC_MSG_REPORT_ARC_INITIATED || - dst->reply == CEC_MSG_REPORT_ARC_TERMINATED)) + (data->match_reply[0] == CEC_MSG_REPORT_ARC_INITIATED || + data->match_reply[0] == CEC_MSG_REPORT_ARC_TERMINATED)) { dst->reply = cmd; + data->match_reply[0] = cmd; + } /* Does the command match? */ if ((abort && cmd != dst->msg[1]) || - (!abort && cmd != dst->reply)) + (!abort && memcmp(data->match_reply, msg->msg + 1, data->match_len))) continue; /* Does the addressing match? */ @@ -2318,18 +2337,21 @@ int cec_adap_status(struct seq_file *file, void *priv) } data = adap->transmitting; if (data) - seq_printf(file, "transmitting message: %*ph (reply: %02x, timeout: %ums)\n", - data->msg.len, data->msg.msg, data->msg.reply, + seq_printf(file, "transmitting message: %*ph (reply: %*ph, timeout: %ums)\n", + data->msg.len, data->msg.msg, + data->match_len, data->match_reply, data->msg.timeout); seq_printf(file, "pending transmits: %u\n", adap->transmit_queue_sz); list_for_each_entry(data, &adap->transmit_queue, list) { - seq_printf(file, "queued tx message: %*ph (reply: %02x, timeout: %ums)\n", - data->msg.len, data->msg.msg, data->msg.reply, + seq_printf(file, "queued tx message: %*ph (reply: %*ph, timeout: %ums)\n", + data->msg.len, data->msg.msg, + data->match_len, data->match_reply, data->msg.timeout); } list_for_each_entry(data, &adap->wait_queue, list) { - seq_printf(file, "message waiting for reply: %*ph (reply: %02x, timeout: %ums)\n", - data->msg.len, data->msg.msg, data->msg.reply, + seq_printf(file, "message waiting for reply: %*ph (reply: %*ph, timeout: %ums)\n", + data->msg.len, data->msg.msg, + data->match_len, data->match_reply, data->msg.timeout); } diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index 6f940df0230c..e0756826d629 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -273,7 +273,7 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, adap->cec_pin_is_high = true; adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0; adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE; - adap->capabilities = caps; + adap->capabilities = caps | CEC_CAP_REPLY_VENDOR_ID; if (debug_phys_addr) adap->capabilities |= CEC_CAP_PHYS_ADDR; adap->needs_hpd = caps & CEC_CAP_NEEDS_HPD; diff --git a/include/media/cec.h b/include/media/cec.h index d131514032f2..07d2ee8a3904 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -66,6 +66,8 @@ struct cec_data { struct list_head xfer_list; struct cec_adapter *adap; struct cec_msg msg; + u8 match_len; + u8 match_reply[5]; struct cec_fh *fh; struct delayed_work work; struct completion c; diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h index b8e071abaea5..894fffc66f2c 100644 --- a/include/uapi/linux/cec.h +++ b/include/uapi/linux/cec.h @@ -165,6 +165,7 @@ static inline int cec_msg_recv_is_rx_result(const struct cec_msg *msg) /* cec_msg flags field */ #define CEC_MSG_FL_REPLY_TO_FOLLOWERS (1 << 0) #define CEC_MSG_FL_RAW (1 << 1) +#define CEC_MSG_FL_REPLY_VENDOR_ID (1 << 2) /* cec_msg tx/rx_status field */ #define CEC_TX_STATUS_OK (1 << 0) @@ -339,6 +340,8 @@ static inline int cec_is_unconfigured(__u16 log_addr_mask) #define CEC_CAP_MONITOR_PIN (1 << 7) /* CEC_ADAP_G_CONNECTOR_INFO is available */ #define CEC_CAP_CONNECTOR_INFO (1 << 8) +/* CEC_MSG_FL_REPLY_VENDOR_ID is available */ +#define CEC_CAP_REPLY_VENDOR_ID (1 << 9) /** * struct cec_caps - CEC capabilities structure. -- cgit v1.2.3-70-g09d2 From 812765cd69540b1e3ed5f02e25ccb9904f6a82f7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 25 Jul 2024 10:06:39 +0200 Subject: media: vivid: add support This makes it possible to test the new CEC_MSG_FL_REPLY_VENDOR_ID flag. The vivid driver will Feature Abort any messages that do not have exactly 1 payload byte. It ignores messages where the payload byte is even, and where it is odd it will reply with the payload byte incremented by 1. Basically a simple ping-pong command. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vivid/vivid-cec.c | 48 ++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/test-drivers/vivid/vivid-cec.c b/drivers/media/test-drivers/vivid/vivid-cec.c index 941ef4263214..356a988dd6a1 100644 --- a/drivers/media/test-drivers/vivid/vivid-cec.c +++ b/drivers/media/test-drivers/vivid/vivid-cec.c @@ -316,15 +316,16 @@ static int vivid_received(struct cec_adapter *adap, struct cec_msg *msg) struct vivid_dev *dev = cec_get_drvdata(adap); struct cec_msg reply; u8 dest = cec_msg_destination(msg); - u8 disp_ctl; - char osd[14]; if (cec_msg_is_broadcast(msg)) dest = adap->log_addrs.log_addr[0]; cec_msg_init(&reply, dest, cec_msg_initiator(msg)); switch (cec_msg_opcode(msg)) { - case CEC_MSG_SET_OSD_STRING: + case CEC_MSG_SET_OSD_STRING: { + u8 disp_ctl; + char osd[14]; + if (!cec_is_sink(adap)) return -ENOMSG; cec_ops_set_osd_string(msg, &disp_ctl, osd); @@ -348,6 +349,47 @@ static int vivid_received(struct cec_adapter *adap, struct cec_msg *msg) break; } break; + } + case CEC_MSG_VENDOR_COMMAND_WITH_ID: { + u32 vendor_id; + u8 size; + const u8 *vendor_cmd; + + /* + * If we receive with our vendor ID + * and with a payload of size 1, and the payload value is odd, + * then we reply with the same message, but with the payload + * byte incremented by 1. + * + * If the size is 1 and the payload value is even, then we + * ignore the message. + * + * The reason we reply to odd instead of even payload values + * is that it allows for testing of the corner case where the + * reply value is 0 (0xff + 1 % 256). + * + * For other sizes we Feature Abort. + * + * This is added for the specific purpose of testing the + * CEC_MSG_FL_REPLY_VENDOR_ID flag using vivid. + */ + cec_ops_vendor_command_with_id(msg, &vendor_id, &size, &vendor_cmd); + if (vendor_id != adap->log_addrs.vendor_id) + break; + if (size == 1) { + // Ignore even op values + if (!(vendor_cmd[0] & 1)) + break; + reply.len = msg->len; + memcpy(reply.msg + 1, msg->msg + 1, msg->len - 1); + reply.msg[msg->len - 1]++; + } else { + cec_msg_feature_abort(&reply, cec_msg_opcode(msg), + CEC_OP_ABORT_INVALID_OP); + } + cec_transmit_msg(adap, &reply, false); + break; + } default: return -ENOMSG; } -- cgit v1.2.3-70-g09d2 From e7311aa47a6ac355558bc65512aa7c381f8506ec Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 5 Aug 2024 07:59:43 +0000 Subject: media: siano: Simplify smscore_load_firmware_from_file The function is never called with a loadfirmware_handler, so we can remove some dead code. We can also use this as a excuse to remove some unused type definitions. This fixes the following smatch warning: drivers/media/common/siano/smscoreapi.c:1172 smscore_load_firmware_from_file() error: we previously assumed 'loadfirmware_handler' could be null (see line 1150) Signed-off-by: Ricardo Ribalda Reported-by: Hans Verkuil Closes: https://lore.kernel.org/linux-media/99bd75a0-a6f3-4c47-bc89-70ffd87da756@xs4all.nl/T/#t Signed-off-by: Hans Verkuil --- drivers/media/common/siano/smscoreapi.c | 15 +++++---------- drivers/media/common/siano/smscoreapi.h | 4 ---- 2 files changed, 5 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c index b6f1eb5dbbdf..3732367e0c62 100644 --- a/drivers/media/common/siano/smscoreapi.c +++ b/drivers/media/common/siano/smscoreapi.c @@ -1132,8 +1132,7 @@ static char *smscore_get_fw_filename(struct smscore_device_t *coredev, * return: 0 on success, <0 on error. */ static int smscore_load_firmware_from_file(struct smscore_device_t *coredev, - int mode, - loadfirmware_t loadfirmware_handler) + int mode) { int rc = -ENOENT; u8 *fw_buf; @@ -1147,8 +1146,7 @@ static int smscore_load_firmware_from_file(struct smscore_device_t *coredev, } pr_debug("Firmware name: %s\n", fw_filename); - if (!loadfirmware_handler && - !(coredev->device_flags & SMS_DEVICE_FAMILY2)) + if (!(coredev->device_flags & SMS_DEVICE_FAMILY2)) return -EINVAL; rc = request_firmware(&fw, fw_filename, coredev->device); @@ -1166,10 +1164,8 @@ static int smscore_load_firmware_from_file(struct smscore_device_t *coredev, memcpy(fw_buf, fw->data, fw->size); fw_buf_size = fw->size; - rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ? - smscore_load_firmware_family2(coredev, fw_buf, fw_buf_size) - : loadfirmware_handler(coredev->context, fw_buf, - fw_buf_size); + rc = smscore_load_firmware_family2(coredev, fw_buf, + fw_buf_size); } kfree(fw_buf); @@ -1353,8 +1349,7 @@ int smscore_set_device_mode(struct smscore_device_t *coredev, int mode) } if (!(coredev->modes_supported & (1 << mode))) { - rc = smscore_load_firmware_from_file(coredev, - mode, NULL); + rc = smscore_load_firmware_from_file(coredev, mode); if (rc >= 0) pr_debug("firmware download success\n"); } else { diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h index 82d9f8a64d99..3c15082ce0e3 100644 --- a/drivers/media/common/siano/smscoreapi.h +++ b/drivers/media/common/siano/smscoreapi.h @@ -97,7 +97,6 @@ typedef int (*hotplug_t)(struct smscore_device_t *coredev, typedef int (*setmode_t)(void *context, int mode); typedef void (*detectmode_t)(void *context, int *mode); typedef int (*sendrequest_t)(void *context, void *buffer, size_t size); -typedef int (*loadfirmware_t)(void *context, void *buffer, size_t size); typedef int (*preload_t)(void *context); typedef int (*postload_t)(void *context); @@ -1102,9 +1101,6 @@ extern int smscore_register_device(struct smsdevice_params_t *params, extern void smscore_unregister_device(struct smscore_device_t *coredev); extern int smscore_start_device(struct smscore_device_t *coredev); -extern int smscore_load_firmware(struct smscore_device_t *coredev, - char *filename, - loadfirmware_t loadfirmware_handler); extern int smscore_set_device_mode(struct smscore_device_t *coredev, int mode); extern int smscore_get_device_mode(struct smscore_device_t *coredev); -- cgit v1.2.3-70-g09d2 From 31aaa7d95e09892c81df0d7c49ae85640fa4e202 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 8 Aug 2024 11:09:18 +0200 Subject: media: cec: cec-adap.c: improve CEC_MSG_FL_REPLY_VENDOR_ID check The new CEC_MSG_FL_REPLY_VENDOR_ID flag only makes sense in combination with CEC_MSG_VENDOR_COMMAND_WITH_ID. So rather than reporting an error if that flag is set with another command, just clear the flag instead. Only keep the message length check, since otherwise the flag would not make sense. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-adap.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index c81b1ed7c08a..c7d36010c890 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -781,7 +781,8 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, { struct cec_data *data; bool is_raw = msg_is_raw(msg); - bool reply_vendor_id = msg->flags & CEC_MSG_FL_REPLY_VENDOR_ID; + bool reply_vendor_id = (msg->flags & CEC_MSG_FL_REPLY_VENDOR_ID) && + msg->len > 1 && msg->msg[1] == CEC_MSG_VENDOR_COMMAND_WITH_ID; int err; if (adap->devnode.unregistered) @@ -797,7 +798,7 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, msg->tx_error_cnt = 0; msg->sequence = 0; msg->flags &= CEC_MSG_FL_REPLY_TO_FOLLOWERS | CEC_MSG_FL_RAW | - CEC_MSG_FL_REPLY_VENDOR_ID; + (reply_vendor_id ? CEC_MSG_FL_REPLY_VENDOR_ID : 0); if ((reply_vendor_id || msg->reply) && msg->timeout == 0) { /* Make sure the timeout isn't 0. */ @@ -812,9 +813,9 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, dprintk(1, "%s: invalid length %d\n", __func__, msg->len); return -EINVAL; } - if (reply_vendor_id && - (msg->len < 6 || msg->msg[1] != CEC_MSG_VENDOR_COMMAND_WITH_ID)) { - dprintk(1, "%s: message too short or not \n", __func__); + if (reply_vendor_id && msg->len < 6) { + dprintk(1, "%s: message too short\n", + __func__); return -EINVAL; } -- cgit v1.2.3-70-g09d2 From f79f8a8a96046ed08b8ba71dc6ffecbbecec9f21 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 5 May 2024 09:24:02 +0200 Subject: media: i2c: tvp5150: Constify some structures 'vbi_ram_default' and 'tvp5150_config' are not modified in this diver and are only used as a const struct. Constifying these structures moves some data to a read-only section, so increase overall security. On a x86_64, with allmodconfig: Before: text data bss dec hex filename 57197 2936 36 60169 eb09 drivers/media/i2c/tvp5150.o After: text data bss dec hex filename 57517 2608 36 60161 eb01 drivers/media/i2c/tvp5150.o Signed-off-by: Christophe JAILLET Signed-off-by: Hans Verkuil --- drivers/media/i2c/tvp5150.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 64b91aa3c82a..0fe6a37bdb7f 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -514,7 +514,7 @@ struct i2c_vbi_ram_value { * and so on. There are 16 possible locations from 0 to 15. */ -static struct i2c_vbi_ram_value vbi_ram_default[] = { +static const struct i2c_vbi_ram_value vbi_ram_default[] = { /* * FIXME: Current api doesn't handle all VBI types, those not @@ -1812,7 +1812,7 @@ static const struct regmap_access_table tvp5150_readable_table = { .n_yes_ranges = ARRAY_SIZE(tvp5150_readable_ranges), }; -static struct regmap_config tvp5150_config = { +static const struct regmap_config tvp5150_config = { .reg_bits = 8, .val_bits = 8, .max_register = 0xff, -- cgit v1.2.3-70-g09d2 From 343cb1ec7b3dd626e4b93b27d7aff604b06453e9 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 5 May 2024 09:28:25 +0200 Subject: media: platform: allegro-dvt: Constify struct regmap_config 'allegro_regmap_config' and 'allegro_sram_config' are not modified in this diver and are only used as a const struct regmap_config. Constifying these structures moves some data to a read-only section, so increase overall security. On a x86_64, with allmodconfig: Before: text data bss dec hex filename 79587 3706 116 83409 145d1 drivers/media/platform/allegro-dvt/allegro-core.o After: text data bss dec hex filename 80219 3066 116 83401 145c9 drivers/media/platform/allegro-dvt/allegro-core.o Signed-off-by: Christophe JAILLET Reviewed-by: Michael Tretter Signed-off-by: Hans Verkuil --- drivers/media/platform/allegro-dvt/allegro-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index da61f9beb6b4..1a19fef62e81 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -179,7 +179,7 @@ struct allegro_dev { struct list_head channels; }; -static struct regmap_config allegro_regmap_config = { +static const struct regmap_config allegro_regmap_config = { .name = "regmap", .reg_bits = 32, .val_bits = 32, @@ -188,7 +188,7 @@ static struct regmap_config allegro_regmap_config = { .cache_type = REGCACHE_NONE, }; -static struct regmap_config allegro_sram_config = { +static const struct regmap_config allegro_sram_config = { .name = "sram", .reg_bits = 32, .val_bits = 32, -- cgit v1.2.3-70-g09d2 From cc4cbd4b4f97a968203530ef73598ad2eaf70dee Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 8 May 2024 15:20:42 +0200 Subject: media: Drop explicit initialization of struct i2c_device_id::driver_data to 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These drivers don't use the driver_data member of struct i2c_device_id, so don't explicitly initialize this member. This prepares putting driver_data in an anonymous union which requires either no initialization or named designators. But it's also a nice cleanup on its own. While add it, also remove commas after the sentinel entries. Signed-off-by: Uwe Kleine-König Signed-off-by: Hans Verkuil --- drivers/media/dvb-frontends/a8293.c | 2 +- drivers/media/dvb-frontends/af9013.c | 2 +- drivers/media/dvb-frontends/af9033.c | 2 +- drivers/media/dvb-frontends/au8522_decoder.c | 2 +- drivers/media/dvb-frontends/cxd2099.c | 2 +- drivers/media/dvb-frontends/cxd2820r_core.c | 2 +- drivers/media/dvb-frontends/lgdt3306a.c | 2 +- drivers/media/dvb-frontends/lgdt330x.c | 2 +- drivers/media/dvb-frontends/mn88472.c | 2 +- drivers/media/dvb-frontends/mn88473.c | 2 +- drivers/media/dvb-frontends/mxl692.c | 2 +- drivers/media/dvb-frontends/rtl2830.c | 2 +- drivers/media/dvb-frontends/rtl2832.c | 2 +- drivers/media/dvb-frontends/si2165.c | 2 +- drivers/media/dvb-frontends/si2168.c | 2 +- drivers/media/dvb-frontends/sp2.c | 2 +- drivers/media/dvb-frontends/stv090x.c | 2 +- drivers/media/dvb-frontends/stv6110x.c | 2 +- drivers/media/dvb-frontends/tda10071.c | 2 +- drivers/media/dvb-frontends/ts2020.c | 4 ++-- drivers/media/i2c/ad5820.c | 4 ++-- drivers/media/i2c/adp1653.c | 2 +- drivers/media/i2c/adv7170.c | 4 ++-- drivers/media/i2c/adv7175.c | 4 ++-- drivers/media/i2c/adv7183.c | 4 ++-- drivers/media/i2c/adv7343.c | 4 ++-- drivers/media/i2c/adv7393.c | 4 ++-- drivers/media/i2c/adv7511-v4l2.c | 2 +- drivers/media/i2c/adv7842.c | 2 +- drivers/media/i2c/ak881x.c | 4 ++-- drivers/media/i2c/bt819.c | 6 +++--- drivers/media/i2c/bt856.c | 2 +- drivers/media/i2c/bt866.c | 2 +- drivers/media/i2c/cs3308.c | 2 +- drivers/media/i2c/cs5345.c | 2 +- drivers/media/i2c/cs53l32a.c | 2 +- drivers/media/i2c/cx25840/cx25840-core.c | 2 +- drivers/media/i2c/ds90ub913.c | 5 ++++- drivers/media/i2c/dw9714.c | 4 ++-- drivers/media/i2c/et8ek8/et8ek8_driver.c | 2 +- drivers/media/i2c/imx274.c | 2 +- drivers/media/i2c/isl7998x.c | 4 ++-- drivers/media/i2c/ks0127.c | 6 +++--- drivers/media/i2c/lm3560.c | 4 ++-- drivers/media/i2c/lm3646.c | 2 +- drivers/media/i2c/m52790.c | 2 +- drivers/media/i2c/max2175.c | 4 ++-- drivers/media/i2c/ml86v7667.c | 4 ++-- drivers/media/i2c/msp3400-driver.c | 2 +- drivers/media/i2c/mt9m001.c | 2 +- drivers/media/i2c/mt9m111.c | 2 +- drivers/media/i2c/mt9t112.c | 2 +- drivers/media/i2c/mt9v011.c | 2 +- drivers/media/i2c/ov13858.c | 4 ++-- drivers/media/i2c/ov2640.c | 2 +- drivers/media/i2c/ov2659.c | 4 ++-- drivers/media/i2c/ov5640.c | 4 ++-- drivers/media/i2c/ov5645.c | 2 +- drivers/media/i2c/ov5647.c | 2 +- drivers/media/i2c/ov6650.c | 2 +- drivers/media/i2c/ov7640.c | 2 +- drivers/media/i2c/ov772x.c | 2 +- drivers/media/i2c/ov7740.c | 2 +- drivers/media/i2c/ov9640.c | 2 +- drivers/media/i2c/ov9650.c | 4 ++-- drivers/media/i2c/rj54n1cb0c.c | 2 +- drivers/media/i2c/s5c73m3/s5c73m3-core.c | 2 +- drivers/media/i2c/s5k5baf.c | 4 ++-- drivers/media/i2c/saa6588.c | 2 +- drivers/media/i2c/saa6752hs.c | 2 +- drivers/media/i2c/saa7110.c | 2 +- drivers/media/i2c/saa717x.c | 2 +- drivers/media/i2c/saa7185.c | 2 +- drivers/media/i2c/sony-btf-mpx.c | 2 +- drivers/media/i2c/tc358743.c | 2 +- drivers/media/i2c/tda7432.c | 2 +- drivers/media/i2c/tda9840.c | 2 +- drivers/media/i2c/tea6415c.c | 2 +- drivers/media/i2c/tea6420.c | 2 +- drivers/media/i2c/ths7303.c | 6 +++--- drivers/media/i2c/ths8200.c | 4 ++-- drivers/media/i2c/tlv320aic23b.c | 2 +- drivers/media/i2c/tvaudio.c | 2 +- drivers/media/i2c/tvp5150.c | 2 +- drivers/media/i2c/tvp7002.c | 2 +- drivers/media/i2c/tw2804.c | 2 +- drivers/media/i2c/tw9900.c | 2 +- drivers/media/i2c/tw9903.c | 2 +- drivers/media/i2c/tw9906.c | 2 +- drivers/media/i2c/tw9910.c | 2 +- drivers/media/i2c/uda1342.c | 2 +- drivers/media/i2c/upd64031a.c | 2 +- drivers/media/i2c/upd64083.c | 2 +- drivers/media/i2c/vp27smpx.c | 2 +- drivers/media/i2c/vpx3220.c | 6 +++--- drivers/media/i2c/wm8739.c | 2 +- drivers/media/i2c/wm8775.c | 2 +- drivers/media/radio/radio-tea5764.c | 2 +- drivers/media/radio/saa7706h.c | 4 ++-- drivers/media/radio/si470x/radio-si470x-i2c.c | 2 +- drivers/media/radio/si4713/si4713.c | 4 ++-- drivers/media/radio/tef6862.c | 4 ++-- drivers/media/test-drivers/vidtv/vidtv_demod.c | 2 +- drivers/media/test-drivers/vidtv/vidtv_tuner.c | 2 +- drivers/media/tuners/e4000.c | 2 +- drivers/media/tuners/fc2580.c | 2 +- drivers/media/tuners/m88rs6000t.c | 2 +- drivers/media/tuners/mt2060.c | 2 +- drivers/media/tuners/mxl301rf.c | 2 +- drivers/media/tuners/qm1d1b0004.c | 2 +- drivers/media/tuners/qm1d1c0042.c | 2 +- drivers/media/tuners/tda18212.c | 2 +- drivers/media/tuners/tda18250.c | 2 +- drivers/media/tuners/tua9001.c | 2 +- drivers/media/usb/go7007/s2250-board.c | 2 +- 115 files changed, 148 insertions(+), 145 deletions(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c index f39887c04978..bf2773c5b97a 100644 --- a/drivers/media/dvb-frontends/a8293.c +++ b/drivers/media/dvb-frontends/a8293.c @@ -256,7 +256,7 @@ static void a8293_remove(struct i2c_client *client) } static const struct i2c_device_id a8293_id_table[] = { - {"a8293", 0}, + { "a8293" }, {} }; MODULE_DEVICE_TABLE(i2c, a8293_id_table); diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c index 5afdbe244596..befd6a4eafd9 100644 --- a/drivers/media/dvb-frontends/af9013.c +++ b/drivers/media/dvb-frontends/af9013.c @@ -1553,7 +1553,7 @@ static void af9013_remove(struct i2c_client *client) } static const struct i2c_device_id af9013_id_table[] = { - {"af9013", 0}, + { "af9013" }, {} }; MODULE_DEVICE_TABLE(i2c, af9013_id_table); diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index 49b7b04a7899..eed2ea4da8fa 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -1173,7 +1173,7 @@ static void af9033_remove(struct i2c_client *client) } static const struct i2c_device_id af9033_id_table[] = { - {"af9033", 0}, + { "af9033" }, {} }; MODULE_DEVICE_TABLE(i2c, af9033_id_table); diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index d02a92a81c60..58c4c489bf97 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -767,7 +767,7 @@ static void au8522_remove(struct i2c_client *client) } static const struct i2c_device_id au8522_id[] = { - {"au8522", 0}, + { "au8522" }, {} }; diff --git a/drivers/media/dvb-frontends/cxd2099.c b/drivers/media/dvb-frontends/cxd2099.c index 3f3b85743666..5e6e18819a0d 100644 --- a/drivers/media/dvb-frontends/cxd2099.c +++ b/drivers/media/dvb-frontends/cxd2099.c @@ -672,7 +672,7 @@ static void cxd2099_remove(struct i2c_client *client) } static const struct i2c_device_id cxd2099_id[] = { - {"cxd2099", 0}, + { "cxd2099" }, {} }; MODULE_DEVICE_TABLE(i2c, cxd2099_id); diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c index 7feb08dccfa1..c3d8ced6c3ba 100644 --- a/drivers/media/dvb-frontends/cxd2820r_core.c +++ b/drivers/media/dvb-frontends/cxd2820r_core.c @@ -723,7 +723,7 @@ static void cxd2820r_remove(struct i2c_client *client) } static const struct i2c_device_id cxd2820r_id_table[] = { - {"cxd2820r", 0}, + { "cxd2820r" }, {} }; MODULE_DEVICE_TABLE(i2c, cxd2820r_id_table); diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c index b25d11be8611..6ab9d4de65ce 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.c +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -2244,7 +2244,7 @@ static void lgdt3306a_remove(struct i2c_client *client) } static const struct i2c_device_id lgdt3306a_id_table[] = { - {"lgdt3306a", 0}, + { "lgdt3306a" }, {} }; MODULE_DEVICE_TABLE(i2c, lgdt3306a_id_table); diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c index 081d6ad3ce72..cab442a350a5 100644 --- a/drivers/media/dvb-frontends/lgdt330x.c +++ b/drivers/media/dvb-frontends/lgdt330x.c @@ -983,7 +983,7 @@ static void lgdt330x_remove(struct i2c_client *client) } static const struct i2c_device_id lgdt330x_id_table[] = { - {"lgdt330x", 0}, + { "lgdt330x" }, {} }; MODULE_DEVICE_TABLE(i2c, lgdt330x_id_table); diff --git a/drivers/media/dvb-frontends/mn88472.c b/drivers/media/dvb-frontends/mn88472.c index 73d1e52de569..729751671c3d 100644 --- a/drivers/media/dvb-frontends/mn88472.c +++ b/drivers/media/dvb-frontends/mn88472.c @@ -708,7 +708,7 @@ static void mn88472_remove(struct i2c_client *client) } static const struct i2c_device_id mn88472_id_table[] = { - {"mn88472", 0}, + { "mn88472" }, {} }; MODULE_DEVICE_TABLE(i2c, mn88472_id_table); diff --git a/drivers/media/dvb-frontends/mn88473.c b/drivers/media/dvb-frontends/mn88473.c index eb50591c0e7a..fefc640d8afb 100644 --- a/drivers/media/dvb-frontends/mn88473.c +++ b/drivers/media/dvb-frontends/mn88473.c @@ -743,7 +743,7 @@ static void mn88473_remove(struct i2c_client *client) } static const struct i2c_device_id mn88473_id_table[] = { - {"mn88473", 0}, + { "mn88473" }, {} }; MODULE_DEVICE_TABLE(i2c, mn88473_id_table); diff --git a/drivers/media/dvb-frontends/mxl692.c b/drivers/media/dvb-frontends/mxl692.c index 2a31bde2630f..bbc2bc778225 100644 --- a/drivers/media/dvb-frontends/mxl692.c +++ b/drivers/media/dvb-frontends/mxl692.c @@ -1346,7 +1346,7 @@ static void mxl692_remove(struct i2c_client *client) } static const struct i2c_device_id mxl692_id_table[] = { - {"mxl692", 0}, + { "mxl692" }, {} }; MODULE_DEVICE_TABLE(i2c, mxl692_id_table); diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c index 30d10fe4b33e..a1e62d49200b 100644 --- a/drivers/media/dvb-frontends/rtl2830.c +++ b/drivers/media/dvb-frontends/rtl2830.c @@ -876,7 +876,7 @@ static void rtl2830_remove(struct i2c_client *client) } static const struct i2c_device_id rtl2830_id_table[] = { - {"rtl2830", 0}, + { "rtl2830" }, {} }; MODULE_DEVICE_TABLE(i2c, rtl2830_id_table); diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 5142820b1b3d..acb5c575bace 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -1125,7 +1125,7 @@ static void rtl2832_remove(struct i2c_client *client) } static const struct i2c_device_id rtl2832_id_table[] = { - {"rtl2832", 0}, + { "rtl2832" }, {} }; MODULE_DEVICE_TABLE(i2c, rtl2832_id_table); diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c index 013d423d3263..f87c9357cee3 100644 --- a/drivers/media/dvb-frontends/si2165.c +++ b/drivers/media/dvb-frontends/si2165.c @@ -1281,7 +1281,7 @@ static void si2165_remove(struct i2c_client *client) } static const struct i2c_device_id si2165_id_table[] = { - {"si2165", 0}, + { "si2165" }, {} }; MODULE_DEVICE_TABLE(i2c, si2165_id_table); diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 26828fd41e68..d6b6b8bc7d4e 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -788,7 +788,7 @@ static void si2168_remove(struct i2c_client *client) } static const struct i2c_device_id si2168_id_table[] = { - {"si2168", 0}, + { "si2168" }, {} }; MODULE_DEVICE_TABLE(i2c, si2168_id_table); diff --git a/drivers/media/dvb-frontends/sp2.c b/drivers/media/dvb-frontends/sp2.c index 4d7d0b8b51b4..75adf2a4589f 100644 --- a/drivers/media/dvb-frontends/sp2.c +++ b/drivers/media/dvb-frontends/sp2.c @@ -407,7 +407,7 @@ static void sp2_remove(struct i2c_client *client) } static const struct i2c_device_id sp2_id[] = { - {"sp2", 0}, + { "sp2" }, {} }; MODULE_DEVICE_TABLE(i2c, sp2_id); diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c index 3b02d504941f..f273efa330cf 100644 --- a/drivers/media/dvb-frontends/stv090x.c +++ b/drivers/media/dvb-frontends/stv090x.c @@ -5079,7 +5079,7 @@ error: EXPORT_SYMBOL_GPL(stv090x_attach); static const struct i2c_device_id stv090x_id_table[] = { - {"stv090x", 0}, + { "stv090x" }, {} }; MODULE_DEVICE_TABLE(i2c, stv090x_id_table); diff --git a/drivers/media/dvb-frontends/stv6110x.c b/drivers/media/dvb-frontends/stv6110x.c index c678f47d2449..33c8105da1c3 100644 --- a/drivers/media/dvb-frontends/stv6110x.c +++ b/drivers/media/dvb-frontends/stv6110x.c @@ -470,7 +470,7 @@ const struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, EXPORT_SYMBOL_GPL(stv6110x_attach); static const struct i2c_device_id stv6110x_id_table[] = { - {"stv6110x", 0}, + { "stv6110x" }, {} }; MODULE_DEVICE_TABLE(i2c, stv6110x_id_table); diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 6640851d8bbc..e23794b821cd 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -1230,7 +1230,7 @@ static void tda10071_remove(struct i2c_client *client) } static const struct i2c_device_id tda10071_id_table[] = { - {"tda10071_cx24118", 0}, + { "tda10071_cx24118" }, {} }; MODULE_DEVICE_TABLE(i2c, tda10071_id_table); diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index a5ebce57f35e..a5baca2449c7 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -710,8 +710,8 @@ static void ts2020_remove(struct i2c_client *client) } static const struct i2c_device_id ts2020_id_table[] = { - {"ts2020", 0}, - {"ts2022", 0}, + { "ts2020" }, + { "ts2022" }, {} }; MODULE_DEVICE_TABLE(i2c, ts2020_id_table); diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c index 1543d24f522c..f60271082fb5 100644 --- a/drivers/media/i2c/ad5820.c +++ b/drivers/media/i2c/ad5820.c @@ -347,8 +347,8 @@ static void ad5820_remove(struct i2c_client *client) } static const struct i2c_device_id ad5820_id_table[] = { - { "ad5820", 0 }, - { "ad5821", 0 }, + { "ad5820" }, + { "ad5821" }, { } }; MODULE_DEVICE_TABLE(i2c, ad5820_id_table); diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c index 5ace7b5804d4..391bc75bfcd0 100644 --- a/drivers/media/i2c/adp1653.c +++ b/drivers/media/i2c/adp1653.c @@ -522,7 +522,7 @@ static void adp1653_remove(struct i2c_client *client) } static const struct i2c_device_id adp1653_id_table[] = { - { ADP1653_NAME, 0 }, + { ADP1653_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, adp1653_id_table); diff --git a/drivers/media/i2c/adv7170.c b/drivers/media/i2c/adv7170.c index 4a2b9fd9e2da..ef8682b980b4 100644 --- a/drivers/media/i2c/adv7170.c +++ b/drivers/media/i2c/adv7170.c @@ -377,8 +377,8 @@ static void adv7170_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7170_id[] = { - { "adv7170", 0 }, - { "adv7171", 0 }, + { "adv7170" }, + { "adv7171" }, { } }; MODULE_DEVICE_TABLE(i2c, adv7170_id); diff --git a/drivers/media/i2c/adv7175.c b/drivers/media/i2c/adv7175.c index e454cba4b026..384da1ec5bf9 100644 --- a/drivers/media/i2c/adv7175.c +++ b/drivers/media/i2c/adv7175.c @@ -432,8 +432,8 @@ static void adv7175_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7175_id[] = { - { "adv7175", 0 }, - { "adv7176", 0 }, + { "adv7175" }, + { "adv7176" }, { } }; MODULE_DEVICE_TABLE(i2c, adv7175_id); diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index 2a2cace4a153..25a31a6dd456 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -619,8 +619,8 @@ static void adv7183_remove(struct i2c_client *client) } static const struct i2c_device_id adv7183_id[] = { - {"adv7183", 0}, - {}, + { "adv7183" }, + {} }; MODULE_DEVICE_TABLE(i2c, adv7183_id); diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c index 4fbe4e18570e..b96443404a26 100644 --- a/drivers/media/i2c/adv7343.c +++ b/drivers/media/i2c/adv7343.c @@ -502,8 +502,8 @@ static void adv7343_remove(struct i2c_client *client) } static const struct i2c_device_id adv7343_id[] = { - {"adv7343", 0}, - {}, + { "adv7343" }, + {} }; MODULE_DEVICE_TABLE(i2c, adv7343_id); diff --git a/drivers/media/i2c/adv7393.c b/drivers/media/i2c/adv7393.c index 7638af455cef..c7994bd0bbd4 100644 --- a/drivers/media/i2c/adv7393.c +++ b/drivers/media/i2c/adv7393.c @@ -446,8 +446,8 @@ static void adv7393_remove(struct i2c_client *client) } static const struct i2c_device_id adv7393_id[] = { - {"adv7393", 0}, - {}, + { "adv7393" }, + {} }; MODULE_DEVICE_TABLE(i2c, adv7393_id); diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index 261871be833f..e9406d552699 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -1949,7 +1949,7 @@ static void adv7511_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7511_id[] = { - { "adv7511-v4l2", 0 }, + { "adv7511-v4l2" }, { } }; MODULE_DEVICE_TABLE(i2c, adv7511_id); diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index f2d4217310e7..014fc913225c 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -3617,7 +3617,7 @@ static void adv7842_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id adv7842_id[] = { - { "adv7842", 0 }, + { "adv7842" }, { } }; MODULE_DEVICE_TABLE(i2c, adv7842_id); diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c index ce840adc2aa7..ee575d01a676 100644 --- a/drivers/media/i2c/ak881x.c +++ b/drivers/media/i2c/ak881x.c @@ -304,8 +304,8 @@ static void ak881x_remove(struct i2c_client *client) } static const struct i2c_device_id ak881x_id[] = { - { "ak8813", 0 }, - { "ak8814", 0 }, + { "ak8813" }, + { "ak8814" }, { } }; MODULE_DEVICE_TABLE(i2c, ak881x_id); diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c index b4a25cc996dc..f97245f91f88 100644 --- a/drivers/media/i2c/bt819.c +++ b/drivers/media/i2c/bt819.c @@ -457,9 +457,9 @@ static void bt819_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id bt819_id[] = { - { "bt819a", 0 }, - { "bt817a", 0 }, - { "bt815a", 0 }, + { "bt819a" }, + { "bt817a" }, + { "bt815a" }, { } }; MODULE_DEVICE_TABLE(i2c, bt819_id); diff --git a/drivers/media/i2c/bt856.c b/drivers/media/i2c/bt856.c index 814acbd6a5a8..6852aa47cafb 100644 --- a/drivers/media/i2c/bt856.c +++ b/drivers/media/i2c/bt856.c @@ -230,7 +230,7 @@ static void bt856_remove(struct i2c_client *client) } static const struct i2c_device_id bt856_id[] = { - { "bt856", 0 }, + { "bt856" }, { } }; MODULE_DEVICE_TABLE(i2c, bt856_id); diff --git a/drivers/media/i2c/bt866.c b/drivers/media/i2c/bt866.c index dada059cbce4..a2cc34d35ed2 100644 --- a/drivers/media/i2c/bt866.c +++ b/drivers/media/i2c/bt866.c @@ -197,7 +197,7 @@ static void bt866_remove(struct i2c_client *client) } static const struct i2c_device_id bt866_id[] = { - { "bt866", 0 }, + { "bt866" }, { } }; MODULE_DEVICE_TABLE(i2c, bt866_id); diff --git a/drivers/media/i2c/cs3308.c b/drivers/media/i2c/cs3308.c index 61afa3d799d2..078e0066ce4b 100644 --- a/drivers/media/i2c/cs3308.c +++ b/drivers/media/i2c/cs3308.c @@ -109,7 +109,7 @@ static void cs3308_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id cs3308_id[] = { - { "cs3308", 0 }, + { "cs3308" }, { } }; MODULE_DEVICE_TABLE(i2c, cs3308_id); diff --git a/drivers/media/i2c/cs5345.c b/drivers/media/i2c/cs5345.c index 3019a132e079..3a9797a50e82 100644 --- a/drivers/media/i2c/cs5345.c +++ b/drivers/media/i2c/cs5345.c @@ -189,7 +189,7 @@ static void cs5345_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id cs5345_id[] = { - { "cs5345", 0 }, + { "cs5345" }, { } }; MODULE_DEVICE_TABLE(i2c, cs5345_id); diff --git a/drivers/media/i2c/cs53l32a.c b/drivers/media/i2c/cs53l32a.c index 82881b79e730..c4cad3293905 100644 --- a/drivers/media/i2c/cs53l32a.c +++ b/drivers/media/i2c/cs53l32a.c @@ -200,7 +200,7 @@ static void cs53l32a_remove(struct i2c_client *client) } static const struct i2c_device_id cs53l32a_id[] = { - { "cs53l32a", 0 }, + { "cs53l32a" }, { } }; MODULE_DEVICE_TABLE(i2c, cs53l32a_id); diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index 04461c893d90..a90a9e5705a0 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -3964,7 +3964,7 @@ static void cx25840_remove(struct i2c_client *client) } static const struct i2c_device_id cx25840_id[] = { - { "cx25840", 0 }, + { "cx25840" }, { } }; MODULE_DEVICE_TABLE(i2c, cx25840_id); diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c index ca9bb29dab89..8eed4a200fd8 100644 --- a/drivers/media/i2c/ds90ub913.c +++ b/drivers/media/i2c/ds90ub913.c @@ -877,7 +877,10 @@ static void ub913_remove(struct i2c_client *client) ub913_gpiochip_remove(priv); } -static const struct i2c_device_id ub913_id[] = { { "ds90ub913a-q1", 0 }, {} }; +static const struct i2c_device_id ub913_id[] = { + { "ds90ub913a-q1" }, + {} +}; MODULE_DEVICE_TABLE(i2c, ub913_id); static const struct of_device_id ub913_dt_ids[] = { diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c index 0e88ce0ef8d7..2ddd7daa79e2 100644 --- a/drivers/media/i2c/dw9714.c +++ b/drivers/media/i2c/dw9714.c @@ -279,8 +279,8 @@ static int __maybe_unused dw9714_vcm_resume(struct device *dev) } static const struct i2c_device_id dw9714_id_table[] = { - { DW9714_NAME, 0 }, - { { 0 } } + { DW9714_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, dw9714_id_table); diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index e932d25ca7b3..7519863d77b1 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -1501,7 +1501,7 @@ static const struct of_device_id et8ek8_of_table[] = { MODULE_DEVICE_TABLE(of, et8ek8_of_table); static const struct i2c_device_id et8ek8_id_table[] = { - { ET8EK8_NAME, 0 }, + { ET8EK8_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, et8ek8_id_table); diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 3800de974e8a..a2b824986027 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -1949,7 +1949,7 @@ static const struct of_device_id imx274_of_id_table[] = { MODULE_DEVICE_TABLE(of, imx274_of_id_table); static const struct i2c_device_id imx274_id[] = { - { "IMX274", 0 }, + { "IMX274" }, { } }; MODULE_DEVICE_TABLE(i2c, imx274_id); diff --git a/drivers/media/i2c/isl7998x.c b/drivers/media/i2c/isl7998x.c index c7089035bbc1..5ffd53e005ee 100644 --- a/drivers/media/i2c/isl7998x.c +++ b/drivers/media/i2c/isl7998x.c @@ -1561,8 +1561,8 @@ static const struct of_device_id isl7998x_of_match[] = { MODULE_DEVICE_TABLE(of, isl7998x_of_match); static const struct i2c_device_id isl7998x_id[] = { - { "isl79987", 0 }, - { /* sentinel */ }, + { "isl79987" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, isl7998x_id); diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c index 9d0a763cd503..f3fba9179684 100644 --- a/drivers/media/i2c/ks0127.c +++ b/drivers/media/i2c/ks0127.c @@ -677,9 +677,9 @@ static void ks0127_remove(struct i2c_client *client) } static const struct i2c_device_id ks0127_id[] = { - { "ks0127", 0 }, - { "ks0127b", 0 }, - { "ks0122s", 0 }, + { "ks0127" }, + { "ks0127b" }, + { "ks0122s" }, { } }; MODULE_DEVICE_TABLE(i2c, ks0127_id); diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index 05283ac68f2d..f4cc844f4e3c 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -455,8 +455,8 @@ static void lm3560_remove(struct i2c_client *client) } static const struct i2c_device_id lm3560_id_table[] = { - {LM3559_NAME, 0}, - {LM3560_NAME, 0}, + { LM3559_NAME }, + { LM3560_NAME }, {} }; diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c index fab3a7e05f92..2d16e42ec224 100644 --- a/drivers/media/i2c/lm3646.c +++ b/drivers/media/i2c/lm3646.c @@ -386,7 +386,7 @@ static void lm3646_remove(struct i2c_client *client) } static const struct i2c_device_id lm3646_id_table[] = { - {LM3646_NAME, 0}, + { LM3646_NAME }, {} }; diff --git a/drivers/media/i2c/m52790.c b/drivers/media/i2c/m52790.c index f8a69142aae9..9e1ecfd01e2a 100644 --- a/drivers/media/i2c/m52790.c +++ b/drivers/media/i2c/m52790.c @@ -163,7 +163,7 @@ static void m52790_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id m52790_id[] = { - { "m52790", 0 }, + { "m52790" }, { } }; MODULE_DEVICE_TABLE(i2c, m52790_id); diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c index cd73d2096ae4..bf02ca23a284 100644 --- a/drivers/media/i2c/max2175.c +++ b/drivers/media/i2c/max2175.c @@ -1413,8 +1413,8 @@ static void max2175_remove(struct i2c_client *client) } static const struct i2c_device_id max2175_id[] = { - { DRIVER_NAME, 0}, - {}, + { DRIVER_NAME }, + {} }; MODULE_DEVICE_TABLE(i2c, max2175_id); diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c index 5b72d4434224..57ba3693649a 100644 --- a/drivers/media/i2c/ml86v7667.c +++ b/drivers/media/i2c/ml86v7667.c @@ -424,8 +424,8 @@ static void ml86v7667_remove(struct i2c_client *client) } static const struct i2c_device_id ml86v7667_id[] = { - {DRV_NAME, 0}, - {}, + { DRV_NAME }, + {} }; MODULE_DEVICE_TABLE(i2c, ml86v7667_id); diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c index 599a5bc7cbb3..4c0b0ad68c08 100644 --- a/drivers/media/i2c/msp3400-driver.c +++ b/drivers/media/i2c/msp3400-driver.c @@ -874,7 +874,7 @@ static const struct dev_pm_ops msp3400_pm_ops = { }; static const struct i2c_device_id msp_id[] = { - { "msp3400", 0 }, + { "msp3400" }, { } }; MODULE_DEVICE_TABLE(i2c, msp_id); diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index ad1a3ab77411..12d3e86bdc0f 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -854,7 +854,7 @@ static void mt9m001_remove(struct i2c_client *client) } static const struct i2c_device_id mt9m001_id[] = { - { "mt9m001", 0 }, + { "mt9m001" }, { } }; MODULE_DEVICE_TABLE(i2c, mt9m001_id); diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index ceeeb94c38d5..9aa5dcda3805 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -1383,7 +1383,7 @@ static const struct of_device_id mt9m111_of_match[] = { MODULE_DEVICE_TABLE(of, mt9m111_of_match); static const struct i2c_device_id mt9m111_id[] = { - { "mt9m111", 0 }, + { "mt9m111" }, { } }; MODULE_DEVICE_TABLE(i2c, mt9m111_id); diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c index fb1588c57cc8..878dff9b7577 100644 --- a/drivers/media/i2c/mt9t112.c +++ b/drivers/media/i2c/mt9t112.c @@ -1109,7 +1109,7 @@ static void mt9t112_remove(struct i2c_client *client) } static const struct i2c_device_id mt9t112_id[] = { - { "mt9t112", 0 }, + { "mt9t112" }, { } }; MODULE_DEVICE_TABLE(i2c, mt9t112_id); diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 8834ff8786e5..055b7915260a 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -582,7 +582,7 @@ static void mt9v011_remove(struct i2c_client *c) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id mt9v011_id[] = { - { "mt9v011", 0 }, + { "mt9v011" }, { } }; MODULE_DEVICE_TABLE(i2c, mt9v011_id); diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index 09387e335d80..7a3fc1d28514 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -1740,8 +1740,8 @@ static void ov13858_remove(struct i2c_client *client) } static const struct i2c_device_id ov13858_id_table[] = { - {"ov13858", 0}, - {}, + { "ov13858" }, + {} }; MODULE_DEVICE_TABLE(i2c, ov13858_id_table); diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index 67c4bd2916e8..d27fc2df64e6 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -1271,7 +1271,7 @@ static void ov2640_remove(struct i2c_client *client) } static const struct i2c_device_id ov2640_id[] = { - { "ov2640", 0 }, + { "ov2640" }, { } }; MODULE_DEVICE_TABLE(i2c, ov2640_id); diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index d1653d7431d0..06b7896c3eaf 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1551,8 +1551,8 @@ static const struct dev_pm_ops ov2659_pm_ops = { }; static const struct i2c_device_id ov2659_id[] = { - { "ov2659", 0 }, - { /* sentinel */ }, + { "ov2659" }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov2659_id); diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 5162d45fe73b..c1d3fce4a7d3 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -4003,8 +4003,8 @@ static const struct dev_pm_ops ov5640_pm_ops = { }; static const struct i2c_device_id ov5640_id[] = { - {"ov5640", 0}, - {}, + { "ov5640" }, + {} }; MODULE_DEVICE_TABLE(i2c, ov5640_id); diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index 3b22b9e12787..6c2d221f6973 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -1272,7 +1272,7 @@ static void ov5645_remove(struct i2c_client *client) } static const struct i2c_device_id ov5645_id[] = { - { "ov5645", 0 }, + { "ov5645" }, {} }; MODULE_DEVICE_TABLE(i2c, ov5645_id); diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 0fb4d7bff9d1..a727beb9d57e 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -1487,7 +1487,7 @@ static const struct dev_pm_ops ov5647_pm_ops = { }; static const struct i2c_device_id ov5647_id[] = { - { "ov5647", 0 }, + { "ov5647" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov5647_id); diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index b65befb22a79..9c7627161142 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -1128,7 +1128,7 @@ static void ov6650_remove(struct i2c_client *client) } static const struct i2c_device_id ov6650_id[] = { - { "ov6650", 0 }, + { "ov6650" }, { } }; MODULE_DEVICE_TABLE(i2c, ov6650_id); diff --git a/drivers/media/i2c/ov7640.c b/drivers/media/i2c/ov7640.c index 293f5f404358..9f68d89936eb 100644 --- a/drivers/media/i2c/ov7640.c +++ b/drivers/media/i2c/ov7640.c @@ -77,7 +77,7 @@ static void ov7640_remove(struct i2c_client *client) } static const struct i2c_device_id ov7640_id[] = { - { "ov7640", 0 }, + { "ov7640" }, { } }; MODULE_DEVICE_TABLE(i2c, ov7640_id); diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 3e36a55274ef..3b0fdb3c70c0 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -1546,7 +1546,7 @@ static void ov772x_remove(struct i2c_client *client) } static const struct i2c_device_id ov772x_id[] = { - { "ov772x", 0 }, + { "ov772x" }, { } }; MODULE_DEVICE_TABLE(i2c, ov772x_id); diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 47b1b14d8796..0830676e5d5a 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1152,7 +1152,7 @@ static int __maybe_unused ov7740_runtime_resume(struct device *dev) } static const struct i2c_device_id ov7740_id[] = { - { "ov7740", 0 }, + { "ov7740" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov7740_id); diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index e9a52a8a9dc0..01dbc0ba89c8 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -751,7 +751,7 @@ static void ov9640_remove(struct i2c_client *client) } static const struct i2c_device_id ov9640_id[] = { - { "ov9640", 0 }, + { "ov9640" }, { } }; MODULE_DEVICE_TABLE(i2c, ov9640_id); diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index 66cd0e9ddc9a..56df97c9886b 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -1566,8 +1566,8 @@ static void ov965x_remove(struct i2c_client *client) } static const struct i2c_device_id ov965x_id[] = { - { "OV9650", 0 }, - { "OV9652", 0 }, + { "OV9650" }, + { "OV9652" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, ov965x_id); diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c index a59db10153cd..b7ca39f63dba 100644 --- a/drivers/media/i2c/rj54n1cb0c.c +++ b/drivers/media/i2c/rj54n1cb0c.c @@ -1410,7 +1410,7 @@ static void rj54n1_remove(struct i2c_client *client) } static const struct i2c_device_id rj54n1_id[] = { - { "rj54n1cb0c", 0 }, + { "rj54n1cb0c" }, { } }; MODULE_DEVICE_TABLE(i2c, rj54n1_id); diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index cf6be509af33..e89e888f028e 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1724,7 +1724,7 @@ static void s5c73m3_remove(struct i2c_client *client) } static const struct i2c_device_id s5c73m3_id[] = { - { DRIVER_NAME, 0 }, + { DRIVER_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, s5c73m3_id); diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index 6b11039c3579..24f399cd2124 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -2018,8 +2018,8 @@ static void s5k5baf_remove(struct i2c_client *c) } static const struct i2c_device_id s5k5baf_id[] = { - { S5K5BAF_DRIVER_NAME, 0 }, - { }, + { S5K5BAF_DRIVER_NAME }, + { } }; MODULE_DEVICE_TABLE(i2c, s5k5baf_id); diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index dea9fc09356f..fb09e4560d8a 100644 --- a/drivers/media/i2c/saa6588.c +++ b/drivers/media/i2c/saa6588.c @@ -496,7 +496,7 @@ static void saa6588_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa6588_id[] = { - { "saa6588", 0 }, + { "saa6588" }, { } }; MODULE_DEVICE_TABLE(i2c, saa6588_id); diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index 897eaa669b86..1ed8b5edb3fb 100644 --- a/drivers/media/i2c/saa6752hs.c +++ b/drivers/media/i2c/saa6752hs.c @@ -770,7 +770,7 @@ static void saa6752hs_remove(struct i2c_client *client) } static const struct i2c_device_id saa6752hs_id[] = { - { "saa6752hs", 0 }, + { "saa6752hs" }, { } }; MODULE_DEVICE_TABLE(i2c, saa6752hs_id); diff --git a/drivers/media/i2c/saa7110.c b/drivers/media/i2c/saa7110.c index 1520790338ce..942aeeb40c52 100644 --- a/drivers/media/i2c/saa7110.c +++ b/drivers/media/i2c/saa7110.c @@ -439,7 +439,7 @@ static void saa7110_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa7110_id[] = { - { "saa7110", 0 }, + { "saa7110" }, { } }; MODULE_DEVICE_TABLE(i2c, saa7110_id); diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c index 933ec0171430..b0793bb0c02a 100644 --- a/drivers/media/i2c/saa717x.c +++ b/drivers/media/i2c/saa717x.c @@ -1334,7 +1334,7 @@ static void saa717x_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa717x_id[] = { - { "saa717x", 0 }, + { "saa717x" }, { } }; MODULE_DEVICE_TABLE(i2c, saa717x_id); diff --git a/drivers/media/i2c/saa7185.c b/drivers/media/i2c/saa7185.c index 5535d71f4860..c04e452a332b 100644 --- a/drivers/media/i2c/saa7185.c +++ b/drivers/media/i2c/saa7185.c @@ -334,7 +334,7 @@ static void saa7185_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id saa7185_id[] = { - { "saa7185", 0 }, + { "saa7185" }, { } }; MODULE_DEVICE_TABLE(i2c, saa7185_id); diff --git a/drivers/media/i2c/sony-btf-mpx.c b/drivers/media/i2c/sony-btf-mpx.c index 0f53834f3ae4..16072a9f8247 100644 --- a/drivers/media/i2c/sony-btf-mpx.c +++ b/drivers/media/i2c/sony-btf-mpx.c @@ -366,7 +366,7 @@ static void sony_btf_mpx_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id sony_btf_mpx_id[] = { - { "sony-btf-mpx", 0 }, + { "sony-btf-mpx" }, { } }; MODULE_DEVICE_TABLE(i2c, sony_btf_mpx_id); diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 0307fee3cce9..65d58ddf0287 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -2197,7 +2197,7 @@ static void tc358743_remove(struct i2c_client *client) } static const struct i2c_device_id tc358743_id[] = { - {"tc358743", 0}, + { "tc358743" }, {} }; diff --git a/drivers/media/i2c/tda7432.c b/drivers/media/i2c/tda7432.c index 6ecdc8e2e0c6..76ef0fdddf76 100644 --- a/drivers/media/i2c/tda7432.c +++ b/drivers/media/i2c/tda7432.c @@ -400,7 +400,7 @@ static void tda7432_remove(struct i2c_client *client) } static const struct i2c_device_id tda7432_id[] = { - { "tda7432", 0 }, + { "tda7432" }, { } }; MODULE_DEVICE_TABLE(i2c, tda7432_id); diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c index 1911ef2126be..d61da811c9da 100644 --- a/drivers/media/i2c/tda9840.c +++ b/drivers/media/i2c/tda9840.c @@ -182,7 +182,7 @@ static void tda9840_remove(struct i2c_client *client) } static const struct i2c_device_id tda9840_id[] = { - { "tda9840", 0 }, + { "tda9840" }, { } }; MODULE_DEVICE_TABLE(i2c, tda9840_id); diff --git a/drivers/media/i2c/tea6415c.c b/drivers/media/i2c/tea6415c.c index 3ed6e441d515..4aaf66353610 100644 --- a/drivers/media/i2c/tea6415c.c +++ b/drivers/media/i2c/tea6415c.c @@ -141,7 +141,7 @@ static void tea6415c_remove(struct i2c_client *client) } static const struct i2c_device_id tea6415c_id[] = { - { "tea6415c", 0 }, + { "tea6415c" }, { } }; MODULE_DEVICE_TABLE(i2c, tea6415c_id); diff --git a/drivers/media/i2c/tea6420.c b/drivers/media/i2c/tea6420.c index 63f23784bb41..5c5ea3973251 100644 --- a/drivers/media/i2c/tea6420.c +++ b/drivers/media/i2c/tea6420.c @@ -123,7 +123,7 @@ static void tea6420_remove(struct i2c_client *client) } static const struct i2c_device_id tea6420_id[] = { - { "tea6420", 0 }, + { "tea6420" }, { } }; MODULE_DEVICE_TABLE(i2c, tea6420_id); diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c index 49ed83a0ac94..7526fabc7ee4 100644 --- a/drivers/media/i2c/ths7303.c +++ b/drivers/media/i2c/ths7303.c @@ -369,9 +369,9 @@ static void ths7303_remove(struct i2c_client *client) } static const struct i2c_device_id ths7303_id[] = { - {"ths7303", 0}, - {"ths7353", 0}, - {}, + { "ths7303" }, + { "ths7353" }, + {} }; MODULE_DEVICE_TABLE(i2c, ths7303_id); diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index ce0a7f809f19..686f10641c7a 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -487,8 +487,8 @@ static void ths8200_remove(struct i2c_client *client) } static const struct i2c_device_id ths8200_id[] = { - { "ths8200", 0 }, - {}, + { "ths8200" }, + {} }; MODULE_DEVICE_TABLE(i2c, ths8200_id); diff --git a/drivers/media/i2c/tlv320aic23b.c b/drivers/media/i2c/tlv320aic23b.c index d800ff8af1ff..b7b31b6192af 100644 --- a/drivers/media/i2c/tlv320aic23b.c +++ b/drivers/media/i2c/tlv320aic23b.c @@ -188,7 +188,7 @@ static void tlv320aic23b_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id tlv320aic23b_id[] = { - { "tlv320aic23b", 0 }, + { "tlv320aic23b" }, { } }; MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id); diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index ba20f35cafd5..654725dfafac 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -2086,7 +2086,7 @@ static void tvaudio_remove(struct i2c_client *client) detect which device is present. So rather than listing all supported devices here, we pretend to support a single, fake device type. */ static const struct i2c_device_id tvaudio_id[] = { - { "tvaudio", 0 }, + { "tvaudio" }, { } }; MODULE_DEVICE_TABLE(i2c, tvaudio_id); diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 0fe6a37bdb7f..e3675c744d9e 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -2265,7 +2265,7 @@ static const struct dev_pm_ops tvp5150_pm_ops = { }; static const struct i2c_device_id tvp5150_id[] = { - { "tvp5150", 0 }, + { "tvp5150" }, { } }; MODULE_DEVICE_TABLE(i2c, tvp5150_id); diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index ea01bd86450e..c09a5bd71fd0 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -1070,7 +1070,7 @@ static void tvp7002_remove(struct i2c_client *c) /* I2C Device ID table */ static const struct i2c_device_id tvp7002_id[] = { - { "tvp7002", 0 }, + { "tvp7002" }, { } }; MODULE_DEVICE_TABLE(i2c, tvp7002_id); diff --git a/drivers/media/i2c/tw2804.c b/drivers/media/i2c/tw2804.c index 6a2521e3a25c..3d154f4fb5f9 100644 --- a/drivers/media/i2c/tw2804.c +++ b/drivers/media/i2c/tw2804.c @@ -414,7 +414,7 @@ static void tw2804_remove(struct i2c_client *client) } static const struct i2c_device_id tw2804_id[] = { - { "tw2804", 0 }, + { "tw2804" }, { } }; MODULE_DEVICE_TABLE(i2c, tw2804_id); diff --git a/drivers/media/i2c/tw9900.c b/drivers/media/i2c/tw9900.c index bc7623ec46e5..53efdeaed1db 100644 --- a/drivers/media/i2c/tw9900.c +++ b/drivers/media/i2c/tw9900.c @@ -753,7 +753,7 @@ static const struct dev_pm_ops tw9900_pm_ops = { }; static const struct i2c_device_id tw9900_id[] = { - { "tw9900", 0 }, + { "tw9900" }, { } }; MODULE_DEVICE_TABLE(i2c, tw9900_id); diff --git a/drivers/media/i2c/tw9903.c b/drivers/media/i2c/tw9903.c index 996be3960af3..b996a05e56f2 100644 --- a/drivers/media/i2c/tw9903.c +++ b/drivers/media/i2c/tw9903.c @@ -245,7 +245,7 @@ static void tw9903_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id tw9903_id[] = { - { "tw9903", 0 }, + { "tw9903" }, { } }; MODULE_DEVICE_TABLE(i2c, tw9903_id); diff --git a/drivers/media/i2c/tw9906.c b/drivers/media/i2c/tw9906.c index 25c625f6d6e4..6220f4fddbab 100644 --- a/drivers/media/i2c/tw9906.c +++ b/drivers/media/i2c/tw9906.c @@ -213,7 +213,7 @@ static void tw9906_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id tw9906_id[] = { - { "tw9906", 0 }, + { "tw9906" }, { } }; MODULE_DEVICE_TABLE(i2c, tw9906_id); diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index 6dffaaa9ed56..f3e400304e04 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -996,7 +996,7 @@ static void tw9910_remove(struct i2c_client *client) } static const struct i2c_device_id tw9910_id[] = { - { "tw9910", 0 }, + { "tw9910" }, { } }; MODULE_DEVICE_TABLE(i2c, tw9910_id); diff --git a/drivers/media/i2c/uda1342.c b/drivers/media/i2c/uda1342.c index abd052a44bd7..2e4540ee2df2 100644 --- a/drivers/media/i2c/uda1342.c +++ b/drivers/media/i2c/uda1342.c @@ -79,7 +79,7 @@ static void uda1342_remove(struct i2c_client *client) } static const struct i2c_device_id uda1342_id[] = { - { "uda1342", 0 }, + { "uda1342" }, { } }; MODULE_DEVICE_TABLE(i2c, uda1342_id); diff --git a/drivers/media/i2c/upd64031a.c b/drivers/media/i2c/upd64031a.c index 54c2ba0ba375..9d0b72a213be 100644 --- a/drivers/media/i2c/upd64031a.c +++ b/drivers/media/i2c/upd64031a.c @@ -219,7 +219,7 @@ static void upd64031a_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id upd64031a_id[] = { - { "upd64031a", 0 }, + { "upd64031a" }, { } }; MODULE_DEVICE_TABLE(i2c, upd64031a_id); diff --git a/drivers/media/i2c/upd64083.c b/drivers/media/i2c/upd64083.c index 2a820589a4cb..2e99ed5da42c 100644 --- a/drivers/media/i2c/upd64083.c +++ b/drivers/media/i2c/upd64083.c @@ -190,7 +190,7 @@ static void upd64083_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id upd64083_id[] = { - { "upd64083", 0 }, + { "upd64083" }, { } }; MODULE_DEVICE_TABLE(i2c, upd64083_id); diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c index 0ba3c2b68037..06fd46a63c72 100644 --- a/drivers/media/i2c/vp27smpx.c +++ b/drivers/media/i2c/vp27smpx.c @@ -172,7 +172,7 @@ static void vp27smpx_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static const struct i2c_device_id vp27smpx_id[] = { - { "vp27smpx", 0 }, + { "vp27smpx" }, { } }; MODULE_DEVICE_TABLE(i2c, vp27smpx_id); diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c index 1eaae886f217..5f1a22284168 100644 --- a/drivers/media/i2c/vpx3220.c +++ b/drivers/media/i2c/vpx3220.c @@ -535,9 +535,9 @@ static void vpx3220_remove(struct i2c_client *client) } static const struct i2c_device_id vpx3220_id[] = { - { "vpx3220a", 0 }, - { "vpx3216b", 0 }, - { "vpx3214c", 0 }, + { "vpx3220a" }, + { "vpx3216b" }, + { "vpx3214c" }, { } }; MODULE_DEVICE_TABLE(i2c, vpx3220_id); diff --git a/drivers/media/i2c/wm8739.c b/drivers/media/i2c/wm8739.c index 19bf7a00dff9..c091b78a5b41 100644 --- a/drivers/media/i2c/wm8739.c +++ b/drivers/media/i2c/wm8739.c @@ -243,7 +243,7 @@ static void wm8739_remove(struct i2c_client *client) } static const struct i2c_device_id wm8739_id[] = { - { "wm8739", 0 }, + { "wm8739" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8739_id); diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c index d1b716fd6f11..619b2988577c 100644 --- a/drivers/media/i2c/wm8775.c +++ b/drivers/media/i2c/wm8775.c @@ -289,7 +289,7 @@ static void wm8775_remove(struct i2c_client *client) } static const struct i2c_device_id wm8775_id[] = { - { "wm8775", 0 }, + { "wm8775" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8775_id); diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c index 14e7dd3889ff..dd85b0b1bcd9 100644 --- a/drivers/media/radio/radio-tea5764.c +++ b/drivers/media/radio/radio-tea5764.c @@ -502,7 +502,7 @@ static void tea5764_i2c_remove(struct i2c_client *client) /* I2C subsystem interface */ static const struct i2c_device_id tea5764_id[] = { - { "radio-tea5764", 0 }, + { "radio-tea5764" }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(i2c, tea5764_id); diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c index 91345198bbf1..d9eecddffd91 100644 --- a/drivers/media/radio/saa7706h.c +++ b/drivers/media/radio/saa7706h.c @@ -395,8 +395,8 @@ static void saa7706h_remove(struct i2c_client *client) } static const struct i2c_device_id saa7706h_id[] = { - {DRIVER_NAME, 0}, - {}, + { DRIVER_NAME }, + {} }; MODULE_DEVICE_TABLE(i2c, saa7706h_id); diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index fd449e42c191..cdd2ac198f2c 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -28,7 +28,7 @@ /* I2C Device ID List */ static const struct i2c_device_id si470x_i2c_id[] = { /* Generic Entry */ - { "si470x", 0 }, + { "si470x" }, /* Terminating entry */ { } }; diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index ddaf7a60b7d0..e71272c6de37 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -1639,8 +1639,8 @@ static void si4713_remove(struct i2c_client *client) /* si4713_i2c_driver - i2c driver interface */ static const struct i2c_device_id si4713_id[] = { - { "si4713" , 0 }, - { }, + { "si4713" }, + { } }; MODULE_DEVICE_TABLE(i2c, si4713_id); diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c index 215168aa1588..b00ccf651922 100644 --- a/drivers/media/radio/tef6862.c +++ b/drivers/media/radio/tef6862.c @@ -173,8 +173,8 @@ static void tef6862_remove(struct i2c_client *client) } static const struct i2c_device_id tef6862_id[] = { - {DRIVER_NAME, 0}, - {}, + { DRIVER_NAME }, + {} }; MODULE_DEVICE_TABLE(i2c, tef6862_id); diff --git a/drivers/media/test-drivers/vidtv/vidtv_demod.c b/drivers/media/test-drivers/vidtv/vidtv_demod.c index 7a0cd9601917..505f96fccbf3 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_demod.c +++ b/drivers/media/test-drivers/vidtv/vidtv_demod.c @@ -407,7 +407,7 @@ static const struct dvb_frontend_ops vidtv_demod_ops = { }; static const struct i2c_device_id vidtv_demod_i2c_id_table[] = { - {"dvb_vidtv_demod", 0}, + { "dvb_vidtv_demod" }, {} }; MODULE_DEVICE_TABLE(i2c, vidtv_demod_i2c_id_table); diff --git a/drivers/media/test-drivers/vidtv/vidtv_tuner.c b/drivers/media/test-drivers/vidtv/vidtv_tuner.c index a748737d47f3..4ba302d569d6 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_tuner.c +++ b/drivers/media/test-drivers/vidtv/vidtv_tuner.c @@ -385,7 +385,7 @@ static const struct dvb_tuner_ops vidtv_tuner_ops = { }; static const struct i2c_device_id vidtv_tuner_i2c_id_table[] = { - {"dvb_vidtv_tuner", 0}, + { "dvb_vidtv_tuner" }, {} }; MODULE_DEVICE_TABLE(i2c, vidtv_tuner_i2c_id_table); diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index 3893a00c18ce..549b2009f974 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c @@ -719,7 +719,7 @@ static void e4000_remove(struct i2c_client *client) } static const struct i2c_device_id e4000_id_table[] = { - {"e4000", 0}, + { "e4000" }, {} }; MODULE_DEVICE_TABLE(i2c, e4000_id_table); diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c index f6613dcf40a3..046389896dc5 100644 --- a/drivers/media/tuners/fc2580.c +++ b/drivers/media/tuners/fc2580.c @@ -600,7 +600,7 @@ static void fc2580_remove(struct i2c_client *client) } static const struct i2c_device_id fc2580_id_table[] = { - {"fc2580", 0}, + { "fc2580" }, {} }; MODULE_DEVICE_TABLE(i2c, fc2580_id_table); diff --git a/drivers/media/tuners/m88rs6000t.c b/drivers/media/tuners/m88rs6000t.c index 2cd7f0e0c70d..cc57980ed417 100644 --- a/drivers/media/tuners/m88rs6000t.c +++ b/drivers/media/tuners/m88rs6000t.c @@ -709,7 +709,7 @@ static void m88rs6000t_remove(struct i2c_client *client) } static const struct i2c_device_id m88rs6000t_id[] = { - {"m88rs6000t", 0}, + { "m88rs6000t" }, {} }; MODULE_DEVICE_TABLE(i2c, m88rs6000t_id); diff --git a/drivers/media/tuners/mt2060.c b/drivers/media/tuners/mt2060.c index 4205ed4cf467..4b9dca2f17cc 100644 --- a/drivers/media/tuners/mt2060.c +++ b/drivers/media/tuners/mt2060.c @@ -514,7 +514,7 @@ static void mt2060_remove(struct i2c_client *client) } static const struct i2c_device_id mt2060_id_table[] = { - {"mt2060", 0}, + { "mt2060" }, {} }; MODULE_DEVICE_TABLE(i2c, mt2060_id_table); diff --git a/drivers/media/tuners/mxl301rf.c b/drivers/media/tuners/mxl301rf.c index 9b2b237745ae..7c03d4132763 100644 --- a/drivers/media/tuners/mxl301rf.c +++ b/drivers/media/tuners/mxl301rf.c @@ -317,7 +317,7 @@ static void mxl301rf_remove(struct i2c_client *client) static const struct i2c_device_id mxl301rf_id[] = { - {"mxl301rf", 0}, + { "mxl301rf" }, {} }; MODULE_DEVICE_TABLE(i2c, mxl301rf_id); diff --git a/drivers/media/tuners/qm1d1b0004.c b/drivers/media/tuners/qm1d1b0004.c index af2d3618b9d5..c53aeb558413 100644 --- a/drivers/media/tuners/qm1d1b0004.c +++ b/drivers/media/tuners/qm1d1b0004.c @@ -243,7 +243,7 @@ static void qm1d1b0004_remove(struct i2c_client *client) static const struct i2c_device_id qm1d1b0004_id[] = { - {"qm1d1b0004", 0}, + { "qm1d1b0004" }, {} }; diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c index ce7223315b0c..c58f5b6526f1 100644 --- a/drivers/media/tuners/qm1d1c0042.c +++ b/drivers/media/tuners/qm1d1c0042.c @@ -434,7 +434,7 @@ static void qm1d1c0042_remove(struct i2c_client *client) static const struct i2c_device_id qm1d1c0042_id[] = { - {"qm1d1c0042", 0}, + { "qm1d1c0042" }, {} }; MODULE_DEVICE_TABLE(i2c, qm1d1c0042_id); diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c index 8d742bd61df0..39f2dc9c2845 100644 --- a/drivers/media/tuners/tda18212.c +++ b/drivers/media/tuners/tda18212.c @@ -254,7 +254,7 @@ static void tda18212_remove(struct i2c_client *client) } static const struct i2c_device_id tda18212_id[] = { - {"tda18212", 0}, + { "tda18212" }, {} }; MODULE_DEVICE_TABLE(i2c, tda18212_id); diff --git a/drivers/media/tuners/tda18250.c b/drivers/media/tuners/tda18250.c index 32ea473f3f49..68d0275f29e1 100644 --- a/drivers/media/tuners/tda18250.c +++ b/drivers/media/tuners/tda18250.c @@ -868,7 +868,7 @@ static void tda18250_remove(struct i2c_client *client) } static const struct i2c_device_id tda18250_id_table[] = { - {"tda18250", 0}, + { "tda18250" }, {} }; MODULE_DEVICE_TABLE(i2c, tda18250_id_table); diff --git a/drivers/media/tuners/tua9001.c b/drivers/media/tuners/tua9001.c index 03a3a022b0a8..562a7a5c26f5 100644 --- a/drivers/media/tuners/tua9001.c +++ b/drivers/media/tuners/tua9001.c @@ -245,7 +245,7 @@ static void tua9001_remove(struct i2c_client *client) } static const struct i2c_device_id tua9001_id_table[] = { - {"tua9001", 0}, + { "tua9001" }, {} }; MODULE_DEVICE_TABLE(i2c, tua9001_id_table); diff --git a/drivers/media/usb/go7007/s2250-board.c b/drivers/media/usb/go7007/s2250-board.c index db1fab96d529..a155b987282f 100644 --- a/drivers/media/usb/go7007/s2250-board.c +++ b/drivers/media/usb/go7007/s2250-board.c @@ -611,7 +611,7 @@ static void s2250_remove(struct i2c_client *client) } static const struct i2c_device_id s2250_id[] = { - { "s2250", 0 }, + { "s2250" }, { } }; MODULE_DEVICE_TABLE(i2c, s2250_id); -- cgit v1.2.3-70-g09d2 From 8bf5671e4515d2eb047d797dcbd1f80d875ebb9d Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Tue, 28 May 2024 15:03:14 +0200 Subject: media: verisilicon: Use fourcc format string There is a fourcc format string for printing formats. Use it instead of open coding the conversion. Signed-off-by: Michael Tretter Reviewed-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/hantro_v4l2.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c index df6f2536263b..62d3962c18d9 100644 --- a/drivers/media/platform/verisilicon/hantro_v4l2.c +++ b/drivers/media/platform/verisilicon/hantro_v4l2.c @@ -303,11 +303,7 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx, coded = capture == ctx->is_encoder; - vpu_debug(4, "trying format %c%c%c%c\n", - (pix_mp->pixelformat & 0x7f), - (pix_mp->pixelformat >> 8) & 0x7f, - (pix_mp->pixelformat >> 16) & 0x7f, - (pix_mp->pixelformat >> 24) & 0x7f); + vpu_debug(4, "trying format %p4cc\n", &pix_mp->pixelformat); fmt = hantro_find_format(ctx, pix_mp->pixelformat); if (!fmt) { -- cgit v1.2.3-70-g09d2 From 0554e280c58b92e548f9d1355087ed096ac02e19 Mon Sep 17 00:00:00 2001 From: Christian Hewitt Date: Tue, 4 Jun 2024 05:15:33 +0000 Subject: media: meson: vdec: add GXLX SoC platform Add the GXLX SoC platform which is based on GXL but omits the VP9 codec. Signed-off-by: Christian Hewitt Acked-by: Neil Armstrong Signed-off-by: Hans Verkuil --- drivers/staging/media/meson/vdec/vdec.c | 2 ++ drivers/staging/media/meson/vdec/vdec_platform.c | 44 ++++++++++++++++++++++++ drivers/staging/media/meson/vdec/vdec_platform.h | 2 ++ 3 files changed, 48 insertions(+) (limited to 'drivers') diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c index de3e0345ab7c..5e5b296f93ba 100644 --- a/drivers/staging/media/meson/vdec/vdec.c +++ b/drivers/staging/media/meson/vdec/vdec.c @@ -982,6 +982,8 @@ static const struct of_device_id vdec_dt_match[] = { .data = &vdec_platform_gxm }, { .compatible = "amlogic,gxl-vdec", .data = &vdec_platform_gxl }, + { .compatible = "amlogic,gxlx-vdec", + .data = &vdec_platform_gxlx }, { .compatible = "amlogic,g12a-vdec", .data = &vdec_platform_g12a }, { .compatible = "amlogic,sm1-vdec", diff --git a/drivers/staging/media/meson/vdec/vdec_platform.c b/drivers/staging/media/meson/vdec/vdec_platform.c index 70c9fd7c8bc5..66bb307db85a 100644 --- a/drivers/staging/media/meson/vdec/vdec_platform.c +++ b/drivers/staging/media/meson/vdec/vdec_platform.c @@ -101,6 +101,44 @@ static const struct amvdec_format vdec_formats_gxl[] = { }, }; +static const struct amvdec_format vdec_formats_gxlx[] = { + { + .pixfmt = V4L2_PIX_FMT_H264, + .min_buffers = 2, + .max_buffers = 24, + .max_width = 3840, + .max_height = 2160, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_h264_ops, + .firmware_path = "meson/vdec/gxl_h264.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED | + V4L2_FMT_FLAG_DYN_RESOLUTION, + }, { + .pixfmt = V4L2_PIX_FMT_MPEG1, + .min_buffers = 8, + .max_buffers = 8, + .max_width = 1920, + .max_height = 1080, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_mpeg12_ops, + .firmware_path = "meson/vdec/gxl_mpeg12.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, + }, { + .pixfmt = V4L2_PIX_FMT_MPEG2, + .min_buffers = 8, + .max_buffers = 8, + .max_width = 1920, + .max_height = 1080, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_mpeg12_ops, + .firmware_path = "meson/vdec/gxl_mpeg12.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + .flags = V4L2_FMT_FLAG_COMPRESSED, + }, +}; + static const struct amvdec_format vdec_formats_gxm[] = { { .pixfmt = V4L2_PIX_FMT_VP9, @@ -263,6 +301,12 @@ const struct vdec_platform vdec_platform_gxl = { .revision = VDEC_REVISION_GXL, }; +const struct vdec_platform vdec_platform_gxlx = { + .formats = vdec_formats_gxlx, + .num_formats = ARRAY_SIZE(vdec_formats_gxlx), + .revision = VDEC_REVISION_GXLX, +}; + const struct vdec_platform vdec_platform_gxm = { .formats = vdec_formats_gxm, .num_formats = ARRAY_SIZE(vdec_formats_gxm), diff --git a/drivers/staging/media/meson/vdec/vdec_platform.h b/drivers/staging/media/meson/vdec/vdec_platform.h index 731877a771f4..88ca4a9db8a8 100644 --- a/drivers/staging/media/meson/vdec/vdec_platform.h +++ b/drivers/staging/media/meson/vdec/vdec_platform.h @@ -14,6 +14,7 @@ struct amvdec_format; enum vdec_revision { VDEC_REVISION_GXBB, VDEC_REVISION_GXL, + VDEC_REVISION_GXLX, VDEC_REVISION_GXM, VDEC_REVISION_G12A, VDEC_REVISION_SM1, @@ -28,6 +29,7 @@ struct vdec_platform { extern const struct vdec_platform vdec_platform_gxbb; extern const struct vdec_platform vdec_platform_gxm; extern const struct vdec_platform vdec_platform_gxl; +extern const struct vdec_platform vdec_platform_gxlx; extern const struct vdec_platform vdec_platform_g12a; extern const struct vdec_platform vdec_platform_sm1; -- cgit v1.2.3-70-g09d2 From d66f9b2a47898ecfa50c80aa59a23002dad3a35a Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 24 Jun 2024 06:40:38 +0200 Subject: media: ti: cal: Constify struct media_entity_operations 'struct media_entity_operations' is not modified in this driver. Constifying this structure moves some data to a read-only section, so increase overall security. On a x86_64, with allmodconfig, as an example: Before: ====== text data bss dec hex filename 20694 1394 32 22120 5668 drivers/media/platform/ti/cal/cal-camerarx.o After: ===== text data bss dec hex filename 20726 1362 32 22120 5668 drivers/media/platform/ti/cal/cal-camerarx.o Signed-off-by: Christophe JAILLET Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/cal/cal-camerarx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c index 4afc2ad00330..42dfe08b765f 100644 --- a/drivers/media/platform/ti/cal/cal-camerarx.c +++ b/drivers/media/platform/ti/cal/cal-camerarx.c @@ -798,7 +798,7 @@ static const struct v4l2_subdev_internal_ops cal_camerarx_internal_ops = { .init_state = cal_camerarx_sd_init_state, }; -static struct media_entity_operations cal_camerarx_media_ops = { +static const struct media_entity_operations cal_camerarx_media_ops = { .link_validate = v4l2_subdev_link_validate, }; -- cgit v1.2.3-70-g09d2 From eefcd6161ba851731187b3fe13db2c3b04ac30e7 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 28 Jun 2024 15:11:15 +0000 Subject: media: imx-pxp: Rewrite coeff expression GCC5 cannot figure out that the expressions are constant, and that triggers a build failure. Rewrite the expressions. The following gcc5 error is workaround: #define BM_PXP_CSC1_COEF0_YCBCR_MODE 0x80000000 ^ BM_PXP_CSC1_COEF0_YCBCR_MODE | ^ #define BM_PXP_CSC1_COEF0_YCBCR_MODE 0x80000000 ^ drivers/media/platform/nxp/imx-pxp.c: In function 'pxp_setup_csc': drivers/media/platform/nxp/imx-pxp.h:582:38: error: initializer element is not constant drivers/media/platform/nxp/imx-pxp.c:374:4: note: in expansion of macro 'BM_PXP_CSC1_COEF0_YCBCR_MODE' drivers/media/platform/nxp/imx-pxp.h:582:38: note: (near initialization for 'csc1_coef_bt601_lim[0]') Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx-pxp.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/nxp/imx-pxp.h b/drivers/media/platform/nxp/imx-pxp.h index 44f95c749d2e..476f2042fa6f 100644 --- a/drivers/media/platform/nxp/imx-pxp.h +++ b/drivers/media/platform/nxp/imx-pxp.h @@ -594,12 +594,17 @@ (((v) << 18) & BM_PXP_CSC1_COEF0_C0) #define BP_PXP_CSC1_COEF0_UV_OFFSET 9 #define BM_PXP_CSC1_COEF0_UV_OFFSET 0x0003FE00 + +/* + * We use v * (1 << 9) instead of v << 9, to workaround a gcc5 bug. + * The compiler cannot understand that the expression is constant. + */ #define BF_PXP_CSC1_COEF0_UV_OFFSET(v) \ - (((v) << 9) & BM_PXP_CSC1_COEF0_UV_OFFSET) + (((v) * (1 << 9)) & BM_PXP_CSC1_COEF0_UV_OFFSET) #define BP_PXP_CSC1_COEF0_Y_OFFSET 0 #define BM_PXP_CSC1_COEF0_Y_OFFSET 0x000001FF #define BF_PXP_CSC1_COEF0_Y_OFFSET(v) \ - (((v) << 0) & BM_PXP_CSC1_COEF0_Y_OFFSET) + ((v) & BM_PXP_CSC1_COEF0_Y_OFFSET) #define HW_PXP_CSC1_COEF1 (0x000001b0) -- cgit v1.2.3-70-g09d2 From 04c8d3037ab5829ba413d8fd1f94ef92e6dc89d6 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 2 Jul 2024 10:57:11 +0800 Subject: media: i2c: thp7312: Convert comma to semicolon Replace a comma between expression statements by a semicolon. Signed-off-by: Chen Ni Reviewed-by: Paul Elder Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/i2c/thp7312.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/i2c/thp7312.c b/drivers/media/i2c/thp7312.c index 19bd923a7315..75225ff5eff6 100644 --- a/drivers/media/i2c/thp7312.c +++ b/drivers/media/i2c/thp7312.c @@ -1503,7 +1503,7 @@ static int __thp7312_flash_reg_read(struct thp7312_device *thp7312, msgs[0].addr = client->addr; msgs[0].flags = 0; - msgs[0].len = sizeof(thp7312_cmd_read_reg), + msgs[0].len = sizeof(thp7312_cmd_read_reg); msgs[0].buf = (u8 *)thp7312_cmd_read_reg; msgs[1].addr = client->addr; -- cgit v1.2.3-70-g09d2 From 8ae06f360cfaca2b88b98ca89144548b3186aab1 Mon Sep 17 00:00:00 2001 From: Junlin Li Date: Tue, 2 Jul 2024 21:24:13 +0800 Subject: drivers: media: dvb-frontends/rtl2832: fix an out-of-bounds write error Ensure index in rtl2832_pid_filter does not exceed 31 to prevent out-of-bounds access. dev->filters is a 32-bit value, so set_bit and clear_bit functions should only operate on indices from 0 to 31. If index is 32, it will attempt to access a non-existent 33rd bit, leading to out-of-bounds access. Change the boundary check from index > 32 to index >= 32 to resolve this issue. Signed-off-by: Junlin Li Signed-off-by: Hans Verkuil Fixes: 4b01e01a81b6 ("[media] rtl2832: implement PID filter") [hverkuil: added fixes tag, rtl2830_pid_filter -> rtl2832_pid_filter in logmsg] --- drivers/media/dvb-frontends/rtl2832.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index acb5c575bace..3b4e46dac1bf 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -983,7 +983,7 @@ static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, index, pid, onoff, dev->slave_ts); /* skip invalid PIDs (0x2000) */ - if (pid > 0x1fff || index > 32) + if (pid > 0x1fff || index >= 32) return 0; if (onoff) -- cgit v1.2.3-70-g09d2 From 46d7ebfe6a75a454a5fa28604f0ef1491f9d8d14 Mon Sep 17 00:00:00 2001 From: Junlin Li Date: Wed, 3 Jul 2024 01:50:23 +0800 Subject: drivers: media: dvb-frontends/rtl2830: fix an out-of-bounds write error Ensure index in rtl2830_pid_filter does not exceed 31 to prevent out-of-bounds access. dev->filters is a 32-bit value, so set_bit and clear_bit functions should only operate on indices from 0 to 31. If index is 32, it will attempt to access a non-existent 33rd bit, leading to out-of-bounds access. Change the boundary check from index > 32 to index >= 32 to resolve this issue. Fixes: df70ddad81b4 ("[media] rtl2830: implement PID filter") Signed-off-by: Junlin Li Signed-off-by: Hans Verkuil --- drivers/media/dvb-frontends/rtl2830.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c index a1e62d49200b..aa4ef9aedf17 100644 --- a/drivers/media/dvb-frontends/rtl2830.c +++ b/drivers/media/dvb-frontends/rtl2830.c @@ -609,7 +609,7 @@ static int rtl2830_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, int on index, pid, onoff); /* skip invalid PIDs (0x2000) */ - if (pid > 0x1fff || index > 32) + if (pid > 0x1fff || index >= 32) return 0; if (onoff) -- cgit v1.2.3-70-g09d2 From ac80fcad7eaf7f28af9e0a098284ee7e921c596e Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 9 Jul 2024 11:48:07 +0100 Subject: media: i2c: GC05A2: Fix spelling mistake "Horizental" -> "Horizontal" There is a spelling mistake in a string in the gc05a2_test_pattern_menu array. Fix it. Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil --- drivers/media/i2c/gc05a2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/i2c/gc05a2.c b/drivers/media/i2c/gc05a2.c index dcba29ee725c..0413c557e594 100644 --- a/drivers/media/i2c/gc05a2.c +++ b/drivers/media/i2c/gc05a2.c @@ -65,7 +65,7 @@ static const char *const gc05a2_test_pattern_menu[] = { "No Pattern", "Fade_to_gray_Color Bar", "Color Bar", - "PN9", "Horizental_gradient", "Checkboard Pattern", + "PN9", "Horizontal_gradient", "Checkboard Pattern", "Slant", "Resolution", "Solid Black", "Solid White", }; -- cgit v1.2.3-70-g09d2 From 74e07a9a26a49ab60ee8ef09238c63ff461dfdd0 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 9 Jul 2024 11:50:03 +0100 Subject: media: i2c: GC08A3: Fix spelling mistake "STRAEMING_REG" -> "STREAMING_REG" There is a spelling mistake in a dev_err message. Fix it. Signed-off-by: Colin Ian King Signed-off-by: Hans Verkuil --- drivers/media/i2c/gc08a3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/i2c/gc08a3.c b/drivers/media/i2c/gc08a3.c index 7680d807e7a5..84de5cff958d 100644 --- a/drivers/media/i2c/gc08a3.c +++ b/drivers/media/i2c/gc08a3.c @@ -948,7 +948,7 @@ static int gc08a3_start_streaming(struct gc08a3 *gc08a3) ret = cci_write(gc08a3->regmap, GC08A3_STREAMING_REG, 1, NULL); if (ret < 0) { - dev_err(gc08a3->dev, "write STRAEMING_REG failed: %d\n", ret); + dev_err(gc08a3->dev, "write STREAMING_REG failed: %d\n", ret); goto err_rpm_put; } -- cgit v1.2.3-70-g09d2 From e25cc4be4616fcf5689622b3226d648aab253cdb Mon Sep 17 00:00:00 2001 From: Roman Smirnov Date: Tue, 16 Jul 2024 12:10:40 +0300 Subject: Revert "media: tuners: fix error return code of hybrid_tuner_request_state()" This reverts commit b9302fa7ed979e84b454e4ca92192cf485a4ed41. As Fedor Pchelkin pointed out, this commit violates the convention of using the macro return value, which causes errors. For example, in functions tda18271_attach(), xc5000_attach(), simple_tuner_attach(). Link: https://lore.kernel.org/linux-media/20240424202031.syigrtrtipbq5f2l@fpc/ Suggested-by: Fedor Pchelkin Signed-off-by: Roman Smirnov Signed-off-by: Hans Verkuil --- drivers/media/tuners/tuner-i2c.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/tuners/tuner-i2c.h b/drivers/media/tuners/tuner-i2c.h index 07aeead0644a..724952e001cd 100644 --- a/drivers/media/tuners/tuner-i2c.h +++ b/drivers/media/tuners/tuner-i2c.h @@ -133,10 +133,8 @@ static inline int tuner_i2c_xfer_send_recv(struct tuner_i2c_props *props, } \ if (0 == __ret) { \ state = kzalloc(sizeof(type), GFP_KERNEL); \ - if (!state) { \ - __ret = -ENOMEM; \ + if (NULL == state) \ goto __fail; \ - } \ state->i2c_props.addr = i2caddr; \ state->i2c_props.adap = i2cadap; \ state->i2c_props.name = devname; \ -- cgit v1.2.3-70-g09d2 From 2b9e67861b7ea511d287bb02f461ed8316b51515 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 17 Jul 2024 11:58:45 +0200 Subject: media: v4l2-core: v4l2-ioctl: missing ', ' in create_bufs logging The v4l_print_create_buffers() function was missing a ', ' in the pr_cont call, leading to logs like this: [93293.533425] video0: VIDIOC_CREATE_BUFS: index=0, count=0, memory=dmabuf, capabilities=0x00000297, max num buffers=32type=vid-cap, width=0, height=0, pixelformat=.... little-endian (0x00000000), field=any, bytesperline=0, sizeimage=0, colorspace=0, flags=0x0, ycbcr_enc=0, quantization=0, xfer_func=0 Signed-off-by: Hans Verkuil Reviewed-by: Sebastian Fricke --- drivers/media/v4l2-core/v4l2-ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 5eb4d797d259..64c3e79d6378 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -484,7 +484,7 @@ static void v4l_print_create_buffers(const void *arg, bool write_only) { const struct v4l2_create_buffers *p = arg; - pr_cont("index=%d, count=%d, memory=%s, capabilities=0x%08x, max num buffers=%u", + pr_cont("index=%d, count=%d, memory=%s, capabilities=0x%08x, max num buffers=%u, ", p->index, p->count, prt_names(p->memory, v4l2_memory_names), p->capabilities, p->max_num_buffers); v4l_print_format(&p->format, write_only); -- cgit v1.2.3-70-g09d2 From c8ad75010c5bafe014860f33fc73a887ab561209 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 22 Jul 2024 17:28:28 +0200 Subject: media: raspberrypi: VIDEO_RASPBERRYPI_PISP_BE should depend on ARCH_BCM2835 Currently, the Raspberry Pi PiSP Backend (BE) ISP is only present on the Broadcom BCM2712-based Raspberry Pi 5. Hence add a dependency on ARCH_BCM2835, to prevent asking the user about this driver when configuring a kernel without Broadcom BCM2835 family support. The dependency can be relaxed if/when the encoder appears on other SoC families. Fixes: 12187bd5d4f8 ("media: raspberrypi: Add support for PiSP BE") Signed-off-by: Geert Uytterhoeven Acked-by: FLorian Fainelli Acked-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/raspberrypi/pisp_be/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/platform/raspberrypi/pisp_be/Kconfig b/drivers/media/platform/raspberrypi/pisp_be/Kconfig index 38c0f8305d62..46765a2e4c4d 100644 --- a/drivers/media/platform/raspberrypi/pisp_be/Kconfig +++ b/drivers/media/platform/raspberrypi/pisp_be/Kconfig @@ -2,6 +2,7 @@ config VIDEO_RASPBERRYPI_PISP_BE tristate "Raspberry Pi PiSP Backend (BE) ISP driver" depends on V4L_PLATFORM_DRIVERS depends on VIDEO_DEV + depends on ARCH_BCM2835 || COMPILE_TEST select VIDEO_V4L2_SUBDEV_API select MEDIA_CONTROLLER select VIDEOBUF2_DMA_CONTIG -- cgit v1.2.3-70-g09d2 From 447b7fdba9e9497f27b845ec069300c91ed5d625 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Mon, 29 Jul 2024 11:37:37 +0200 Subject: media: i2c: tda1997x: constify snd_soc_component_driver struct `tda1997x_codec_driver` is not modified after its declaration, and it is only passed to `devm_snd_soc_register_component()`, which expects a constant `snd_soc_component_driver`. Move `tda1997x_codec_driver` to a read-only section by declaring it const. Signed-off-by: Javier Carrasco Signed-off-by: Hans Verkuil --- drivers/media/i2c/tda1997x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 58ce8fec3041..3b7e5ff5b010 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -2514,7 +2514,7 @@ static void tda1997x_codec_remove(struct snd_soc_component *component) { } -static struct snd_soc_component_driver tda1997x_codec_driver = { +static const struct snd_soc_component_driver tda1997x_codec_driver = { .probe = tda1997x_codec_probe, .remove = tda1997x_codec_remove, .idle_bias_on = 1, -- cgit v1.2.3-70-g09d2 From 25f18cb1b673220b76a86ebef8e7fb79bd303b27 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 29 Jul 2024 13:42:02 +0100 Subject: media: qcom: camss: Remove use_count guard in stop_streaming The use_count check was introduced so that multiple concurrent Raw Data Interfaces RDIs could be driven by different virtual channels VCs on the CSIPHY input driving the video pipeline. This is an invalid use of use_count though as use_count pertains to the number of times a video entity has been opened by user-space not the number of active streams. If use_count and stream-on count don't agree then stop_streaming() will break as is currently the case and has become apparent when using CAMSS with libcamera's released softisp 0.3. The use of use_count like this is a bit hacky and right now breaks regular usage of CAMSS for a single stream case. Stopping qcam results in the splat below, and then it cannot be started again and any attempts to do so fails with -EBUSY. [ 1265.509831] WARNING: CPU: 5 PID: 919 at drivers/media/common/videobuf2/videobuf2-core.c:2183 __vb2_queue_cancel+0x230/0x2c8 [videobuf2_common] ... [ 1265.510630] Call trace: [ 1265.510636] __vb2_queue_cancel+0x230/0x2c8 [videobuf2_common] [ 1265.510648] vb2_core_streamoff+0x24/0xcc [videobuf2_common] [ 1265.510660] vb2_ioctl_streamoff+0x5c/0xa8 [videobuf2_v4l2] [ 1265.510673] v4l_streamoff+0x24/0x30 [videodev] [ 1265.510707] __video_do_ioctl+0x190/0x3f4 [videodev] [ 1265.510732] video_usercopy+0x304/0x8c4 [videodev] [ 1265.510757] video_ioctl2+0x18/0x34 [videodev] [ 1265.510782] v4l2_ioctl+0x40/0x60 [videodev] ... [ 1265.510944] videobuf2_common: driver bug: stop_streaming operation is leaving buffer 0 in active state [ 1265.511175] videobuf2_common: driver bug: stop_streaming operation is leaving buffer 1 in active state [ 1265.511398] videobuf2_common: driver bug: stop_streaming operation is leaving buffer 2 in active st One CAMSS specific way to handle multiple VCs on the same RDI might be: - Reference count each pipeline enable for CSIPHY, CSID, VFE and RDIx. - The video buffers are already associated with msm_vfeN_rdiX so release video buffers when told to do so by stop_streaming. - Only release the power-domains for the CSIPHY, CSID and VFE when their internal refcounts drop. Either way refusing to release video buffers based on use_count is erroneous and should be reverted. The silicon enabling code for selecting VCs is perfectly fine. Its a "known missing feature" that concurrent VCs won't work with CAMSS right now. Initial testing with this code didn't show an error but, SoftISP and "real" usage with Google Hangouts breaks the upstream code pretty quickly, we need to do a partial revert and take another pass at VCs. This commit partially reverts commit 89013969e232 ("media: camss: sm8250: Pipeline starting and stopping for multiple virtual channels") Fixes: 89013969e232 ("media: camss: sm8250: Pipeline starting and stopping for multiple virtual channels") Reported-by: Johan Hovold Closes: https://lore.kernel.org/lkml/ZoVNHOTI0PKMNt4_@hovoldconsulting.com/ Tested-by: Johan Hovold Cc: Signed-off-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/camss-video.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index cd72feca618c..3b8fc31d957c 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -297,12 +297,6 @@ static void video_stop_streaming(struct vb2_queue *q) ret = v4l2_subdev_call(subdev, video, s_stream, 0); - if (entity->use_count > 1) { - /* Don't stop if other instances of the pipeline are still running */ - dev_dbg(video->camss->dev, "Video pipeline still used, don't stop streaming.\n"); - return; - } - if (ret) { dev_err(video->camss->dev, "Video pipeline stop failed: %d\n", ret); return; -- cgit v1.2.3-70-g09d2 From a151766bd3688f6803e706c6433a7c8d3c6a6a94 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 29 Jul 2024 13:42:03 +0100 Subject: media: qcom: camss: Fix ordering of pm_runtime_enable pm_runtime_enable() should happen prior to vfe_get() since vfe_get() calls pm_runtime_resume_and_get(). This is a basic race condition that doesn't show up for most users so is not widely reported. If you blacklist qcom-camss in modules.d and then subsequently modprobe the module post-boot it is possible to reliably show this error up. The kernel log for this error looks like this: qcom-camss ac5a000.camss: Failed to power up pipeline: -13 Fixes: 02afa816dbbf ("media: camss: Add basic runtime PM support") Reported-by: Johan Hovold Closes: https://lore.kernel.org/lkml/ZoVNHOTI0PKMNt4_@hovoldconsulting.com/ Tested-by: Johan Hovold Cc: Signed-off-by: Bryan O'Donoghue Reviewed-by: Konrad Dybcio Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/camss.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 51b1d3550421..d64985ca6e88 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -2283,6 +2283,8 @@ static int camss_probe(struct platform_device *pdev) v4l2_async_nf_init(&camss->notifier, &camss->v4l2_dev); + pm_runtime_enable(dev); + num_subdevs = camss_of_parse_ports(camss); if (num_subdevs < 0) { ret = num_subdevs; @@ -2323,8 +2325,6 @@ static int camss_probe(struct platform_device *pdev) } } - pm_runtime_enable(dev); - return 0; err_register_subdevs: @@ -2332,6 +2332,7 @@ err_register_subdevs: err_v4l2_device_unregister: v4l2_device_unregister(&camss->v4l2_dev); v4l2_async_nf_cleanup(&camss->notifier); + pm_runtime_disable(dev); err_genpd_cleanup: camss_genpd_cleanup(camss); -- cgit v1.2.3-70-g09d2 From 68a1560c3b7f8bb23423b272334fc19279e82d9a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 30 Jul 2024 00:34:51 +0000 Subject: media: platform: microchip: use for_each_endpoint_of_node() We already have for_each_endpoint_of_node(), don't use of_graph_get_next_endpoint() directly. Replace it. Signed-off-by: Kuninori Morimoto Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../platform/microchip/microchip-sama5d2-isc.c | 21 ++++++++------------- .../platform/microchip/microchip-sama7g5-isc.c | 21 ++++++++------------- 2 files changed, 16 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/microchip/microchip-sama5d2-isc.c b/drivers/media/platform/microchip/microchip-sama5d2-isc.c index 5ac149cf3647..60b6d922d764 100644 --- a/drivers/media/platform/microchip/microchip-sama5d2-isc.c +++ b/drivers/media/platform/microchip/microchip-sama5d2-isc.c @@ -353,33 +353,29 @@ static const u32 isc_sama5d2_gamma_table[][GAMMA_ENTRIES] = { static int isc_parse_dt(struct device *dev, struct isc_device *isc) { struct device_node *np = dev->of_node; - struct device_node *epn = NULL; + struct device_node *epn; struct isc_subdev_entity *subdev_entity; unsigned int flags; - int ret; INIT_LIST_HEAD(&isc->subdev_entities); - while (1) { + for_each_endpoint_of_node(np, epn) { struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; - - epn = of_graph_get_next_endpoint(np, epn); - if (!epn) - return 0; + int ret; ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), &v4l2_epn); if (ret) { - ret = -EINVAL; + of_node_put(epn); dev_err(dev, "Could not parse the endpoint\n"); - break; + return -EINVAL; } subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), GFP_KERNEL); if (!subdev_entity) { - ret = -ENOMEM; - break; + of_node_put(epn); + return -ENOMEM; } subdev_entity->epn = epn; @@ -400,9 +396,8 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc) list_add_tail(&subdev_entity->list, &isc->subdev_entities); } - of_node_put(epn); - return ret; + return 0; } static int microchip_isc_probe(struct platform_device *pdev) diff --git a/drivers/media/platform/microchip/microchip-sama7g5-isc.c b/drivers/media/platform/microchip/microchip-sama7g5-isc.c index 73445f33d26b..e97abe3e35af 100644 --- a/drivers/media/platform/microchip/microchip-sama7g5-isc.c +++ b/drivers/media/platform/microchip/microchip-sama7g5-isc.c @@ -336,36 +336,32 @@ static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = { static int xisc_parse_dt(struct device *dev, struct isc_device *isc) { struct device_node *np = dev->of_node; - struct device_node *epn = NULL; + struct device_node *epn; struct isc_subdev_entity *subdev_entity; unsigned int flags; - int ret; bool mipi_mode; INIT_LIST_HEAD(&isc->subdev_entities); mipi_mode = of_property_read_bool(np, "microchip,mipi-mode"); - while (1) { + for_each_endpoint_of_node(np, epn) { struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; - - epn = of_graph_get_next_endpoint(np, epn); - if (!epn) - return 0; + int ret; ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), &v4l2_epn); if (ret) { - ret = -EINVAL; + of_node_put(epn); dev_err(dev, "Could not parse the endpoint\n"); - break; + return -EINVAL; } subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), GFP_KERNEL); if (!subdev_entity) { - ret = -ENOMEM; - break; + of_node_put(epn); + return -ENOMEM; } subdev_entity->epn = epn; @@ -389,9 +385,8 @@ static int xisc_parse_dt(struct device *dev, struct isc_device *isc) list_add_tail(&subdev_entity->list, &isc->subdev_entities); } - of_node_put(epn); - return ret; + return 0; } static int microchip_xisc_probe(struct platform_device *pdev) -- cgit v1.2.3-70-g09d2 From 7880333a5bedb2333f499477f3691cf356d02887 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 30 Jul 2024 00:35:00 +0000 Subject: media: platform: ti: use for_each_endpoint_of_node() We already have for_each_endpoint_of_node(), don't use of_graph_get_next_endpoint() directly. Replace it. Signed-off-by: Kuninori Morimoto Reviewed-by: Laurent Pinchart Reviewed-by: Prabhakar Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/am437x/am437x-vpfe.c | 12 +++++------- drivers/media/platform/ti/davinci/vpif_capture.c | 14 +++++++------- 2 files changed, 12 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/ti/am437x/am437x-vpfe.c b/drivers/media/platform/ti/am437x/am437x-vpfe.c index 77e12457d149..009ff68a2b43 100644 --- a/drivers/media/platform/ti/am437x/am437x-vpfe.c +++ b/drivers/media/platform/ti/am437x/am437x-vpfe.c @@ -2287,7 +2287,7 @@ static const struct v4l2_async_notifier_operations vpfe_async_ops = { static struct vpfe_config * vpfe_get_pdata(struct vpfe_device *vpfe) { - struct device_node *endpoint = NULL; + struct device_node *endpoint; struct device *dev = vpfe->pdev; struct vpfe_subdev_info *sdinfo; struct vpfe_config *pdata; @@ -2306,14 +2306,11 @@ vpfe_get_pdata(struct vpfe_device *vpfe) if (!pdata) return NULL; - for (i = 0; ; i++) { + i = 0; + for_each_endpoint_of_node(dev->of_node, endpoint) { struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; struct device_node *rem; - endpoint = of_graph_get_next_endpoint(dev->of_node, endpoint); - if (!endpoint) - break; - sdinfo = &pdata->sub_devs[i]; sdinfo->grp_id = 0; @@ -2371,9 +2368,10 @@ vpfe_get_pdata(struct vpfe_device *vpfe) of_node_put(rem); if (IS_ERR(pdata->asd[i])) goto cleanup; + + i++; } - of_node_put(endpoint); return pdata; cleanup: diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c index c28794b6677b..16326437767f 100644 --- a/drivers/media/platform/ti/davinci/vpif_capture.c +++ b/drivers/media/platform/ti/davinci/vpif_capture.c @@ -1487,7 +1487,7 @@ static struct vpif_capture_config * vpif_capture_get_pdata(struct platform_device *pdev, struct v4l2_device *v4l2_dev) { - struct device_node *endpoint = NULL; + struct device_node *endpoint; struct device_node *rem = NULL; struct vpif_capture_config *pdata; struct vpif_subdev_info *sdinfo; @@ -1517,16 +1517,12 @@ vpif_capture_get_pdata(struct platform_device *pdev, if (!pdata->subdev_info) return NULL; - for (i = 0; i < VPIF_CAPTURE_NUM_CHANNELS; i++) { + i = 0; + for_each_endpoint_of_node(pdev->dev.of_node, endpoint) { struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; unsigned int flags; int err; - endpoint = of_graph_get_next_endpoint(pdev->dev.of_node, - endpoint); - if (!endpoint) - break; - rem = of_graph_get_remote_port_parent(endpoint); if (!rem) { dev_dbg(&pdev->dev, "Remote device at %pOF not found\n", @@ -1577,6 +1573,10 @@ vpif_capture_get_pdata(struct platform_device *pdev, goto err_cleanup; of_node_put(rem); + + i++; + if (i >= VPIF_CAPTURE_NUM_CHANNELS) + break; } done: -- cgit v1.2.3-70-g09d2 From 18f9ca7edcbbbfeed574d439921a102cc4fa66ac Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 30 Jul 2024 00:35:10 +0000 Subject: media: platform: xilinx: use for_each_endpoint_of_node() We already have for_each_endpoint_of_node(), don't use of_graph_get_next_endpoint() directly. Replace it. Signed-off-by: Kuninori Morimoto Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/xilinx/xilinx-vipp.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c index 996684a73038..bfe48cc0ab52 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.c +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -199,18 +199,13 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev) struct media_pad *sink_pad; struct xvip_graph_entity *ent; struct v4l2_fwnode_link link; - struct device_node *ep = NULL; + struct device_node *ep; struct xvip_dma *dma; int ret = 0; dev_dbg(xdev->dev, "creating links for DMA engines\n"); - while (1) { - /* Get the next endpoint and parse its link. */ - ep = of_graph_get_next_endpoint(node, ep); - if (ep == NULL) - break; - + for_each_endpoint_of_node(node, ep) { dev_dbg(xdev->dev, "processing endpoint %pOF\n", ep); ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link); -- cgit v1.2.3-70-g09d2 From d2f035ef1c5ead354780ca15fc33a2f1f7889eb2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 30 Jul 2024 00:35:21 +0000 Subject: staging: media: atmel: use for_each_endpoint_of_node() We already have for_each_endpoint_of_node(), don't use of_graph_get_next_endpoint() directly. Replace it. Signed-off-by: Kuninori Morimoto Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c | 10 +++------- drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c | 10 +++------- 2 files changed, 6 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c index 31b2b48085c5..712f916f0935 100644 --- a/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c +++ b/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c @@ -333,20 +333,16 @@ static const u32 isc_sama5d2_gamma_table[][GAMMA_ENTRIES] = { static int isc_parse_dt(struct device *dev, struct isc_device *isc) { struct device_node *np = dev->of_node; - struct device_node *epn = NULL; + struct device_node *epn; struct isc_subdev_entity *subdev_entity; unsigned int flags; - int ret; + int ret = -EINVAL; INIT_LIST_HEAD(&isc->subdev_entities); - while (1) { + for_each_endpoint_of_node(np, epn) { struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; - epn = of_graph_get_next_endpoint(np, epn); - if (!epn) - return 0; - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), &v4l2_epn); if (ret) { diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c index 020034f631f5..9485167d5b7d 100644 --- a/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c +++ b/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c @@ -316,23 +316,19 @@ static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = { static int xisc_parse_dt(struct device *dev, struct isc_device *isc) { struct device_node *np = dev->of_node; - struct device_node *epn = NULL; + struct device_node *epn; struct isc_subdev_entity *subdev_entity; unsigned int flags; - int ret; + int ret = -EINVAL; bool mipi_mode; INIT_LIST_HEAD(&isc->subdev_entities); mipi_mode = of_property_read_bool(np, "microchip,mipi-mode"); - while (1) { + for_each_endpoint_of_node(np, epn) { struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; - epn = of_graph_get_next_endpoint(np, epn); - if (!epn) - return 0; - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), &v4l2_epn); if (ret) { -- cgit v1.2.3-70-g09d2 From 0016b5a5c71ef426d88bff2b14096ddf86c4181f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 5 Aug 2024 23:51:14 +0200 Subject: media: allegro: use 'time_left' variable with wait_for_completion_timeout() There is a confusing pattern in the kernel to use a variable named 'timeout' to store the result of wait_for_completion_timeout() causing patterns like: timeout = wait_for_completion_timeout(...) if (!timeout) return -ETIMEDOUT; with all kinds of permutations. Use 'time_left' as a variable to make the code self explaining, also for the code path using 'tmo' as a variable. Signed-off-by: Wolfram Sang Signed-off-by: Hans Verkuil --- drivers/media/platform/allegro-dvt/allegro-core.c | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c index 1a19fef62e81..73606cee586e 100644 --- a/drivers/media/platform/allegro-dvt/allegro-core.c +++ b/drivers/media/platform/allegro-dvt/allegro-core.c @@ -1415,11 +1415,11 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, static int allegro_mcu_wait_for_init_timeout(struct allegro_dev *dev, unsigned long timeout_ms) { - unsigned long tmo; + unsigned long time_left; - tmo = wait_for_completion_timeout(&dev->init_complete, - msecs_to_jiffies(timeout_ms)); - if (tmo == 0) + time_left = wait_for_completion_timeout(&dev->init_complete, + msecs_to_jiffies(timeout_ms)); + if (time_left == 0) return -ETIMEDOUT; reinit_completion(&dev->init_complete); @@ -2481,14 +2481,14 @@ static void allegro_mcu_interrupt(struct allegro_dev *dev) static void allegro_destroy_channel(struct allegro_channel *channel) { struct allegro_dev *dev = channel->dev; - unsigned long timeout; + unsigned long time_left; if (channel_exists(channel)) { reinit_completion(&channel->completion); allegro_mcu_send_destroy_channel(dev, channel); - timeout = wait_for_completion_timeout(&channel->completion, - msecs_to_jiffies(5000)); - if (timeout == 0) + time_left = wait_for_completion_timeout(&channel->completion, + msecs_to_jiffies(5000)); + if (time_left == 0) v4l2_warn(&dev->v4l2_dev, "channel %d: timeout while destroying\n", channel->mcu_channel_id); @@ -2544,7 +2544,7 @@ static void allegro_destroy_channel(struct allegro_channel *channel) static int allegro_create_channel(struct allegro_channel *channel) { struct allegro_dev *dev = channel->dev; - unsigned long timeout; + unsigned long time_left; if (channel_exists(channel)) { v4l2_warn(&dev->v4l2_dev, @@ -2595,9 +2595,9 @@ static int allegro_create_channel(struct allegro_channel *channel) reinit_completion(&channel->completion); allegro_mcu_send_create_channel(dev, channel); - timeout = wait_for_completion_timeout(&channel->completion, - msecs_to_jiffies(5000)); - if (timeout == 0) + time_left = wait_for_completion_timeout(&channel->completion, + msecs_to_jiffies(5000)); + if (time_left == 0) channel->error = -ETIMEDOUT; if (channel->error) goto err; -- cgit v1.2.3-70-g09d2 From 64979ac2aa7773d3a77c861c8eb4c006f9033eec Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 5 Aug 2024 23:51:15 +0200 Subject: media: atmel-isi: use 'time_left' variable with wait_for_completion_timeout() There is a confusing pattern in the kernel to use a variable named 'timeout' to store the result of wait_for_completion_timeout() causing patterns like: timeout = wait_for_completion_timeout(...) if (!timeout) return -ETIMEDOUT; with all kinds of permutations. Use 'time_left' as a variable to make the code self explaining. Signed-off-by: Wolfram Sang Signed-off-by: Hans Verkuil --- drivers/media/platform/atmel/atmel-isi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index c1108df72dd5..5c823d3f9cc0 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -242,7 +242,7 @@ static irqreturn_t isi_interrupt(int irq, void *dev_id) #define WAIT_ISI_DISABLE 0 static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset) { - unsigned long timeout; + unsigned long time_left; /* * The reset or disable will only succeed if we have a * pixel clock from the camera. @@ -257,9 +257,9 @@ static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset) isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS); } - timeout = wait_for_completion_timeout(&isi->complete, - msecs_to_jiffies(500)); - if (timeout == 0) + time_left = wait_for_completion_timeout(&isi->complete, + msecs_to_jiffies(500)); + if (time_left == 0) return -ETIMEDOUT; return 0; -- cgit v1.2.3-70-g09d2 From 9b2bf29410e962fd657cc50c7baa198506f1fc11 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 5 Aug 2024 23:51:16 +0200 Subject: media: bdisp: use 'time_left' variable with wait_event_timeout() There is a confusing pattern in the kernel to use a variable named 'timeout' to store the result of wait_event_timeout() causing patterns like: timeout = wait_event_timeout(...) if (!timeout) return -ETIMEDOUT; with all kinds of permutations. Use 'time_left' as a variable to make the code self explaining. Fix to the proper variable type 'long' while here. Signed-off-by: Wolfram Sang Signed-off-by: Hans Verkuil --- drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c index 1328b4eb6b9f..c7ee6e1a4451 100644 --- a/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c +++ b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c @@ -1160,7 +1160,7 @@ static void bdisp_irq_timeout(struct work_struct *ptr) static int bdisp_m2m_suspend(struct bdisp_dev *bdisp) { unsigned long flags; - int timeout; + long time_left; spin_lock_irqsave(&bdisp->slock, flags); if (!test_bit(ST_M2M_RUNNING, &bdisp->state)) { @@ -1171,13 +1171,13 @@ static int bdisp_m2m_suspend(struct bdisp_dev *bdisp) set_bit(ST_M2M_SUSPENDING, &bdisp->state); spin_unlock_irqrestore(&bdisp->slock, flags); - timeout = wait_event_timeout(bdisp->irq_queue, - test_bit(ST_M2M_SUSPENDED, &bdisp->state), - BDISP_WORK_TIMEOUT); + time_left = wait_event_timeout(bdisp->irq_queue, + test_bit(ST_M2M_SUSPENDED, &bdisp->state), + BDISP_WORK_TIMEOUT); clear_bit(ST_M2M_SUSPENDING, &bdisp->state); - if (!timeout) { + if (!time_left) { dev_err(bdisp->dev, "%s IRQ timeout\n", __func__); return -EAGAIN; } -- cgit v1.2.3-70-g09d2 From bafa00652c860665060b5f6ab01eb58e970c854e Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 5 Aug 2024 23:51:17 +0200 Subject: media: fimc-is: use 'time_left' variable with wait_event_timeout() There is a confusing pattern in the kernel to use a variable named 'timeout' to store the result of wait_event_timeout() causing patterns like: timeout = wait_event_timeout(...) if (!timeout) return -ETIMEDOUT; with all kinds of permutations. Use 'time_left' as a variable to make the code self explaining. Fix to the proper variable type 'long' while here. Signed-off-by: Wolfram Sang Signed-off-by: Hans Verkuil --- drivers/media/platform/samsung/exynos4-is/fimc-core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-core.c b/drivers/media/platform/samsung/exynos4-is/fimc-core.c index aae74b501a42..adfc2d73d04b 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-core.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-core.c @@ -822,7 +822,7 @@ err: static int fimc_m2m_suspend(struct fimc_dev *fimc) { unsigned long flags; - int timeout; + long time_left; spin_lock_irqsave(&fimc->slock, flags); if (!fimc_m2m_pending(fimc)) { @@ -833,12 +833,12 @@ static int fimc_m2m_suspend(struct fimc_dev *fimc) set_bit(ST_M2M_SUSPENDING, &fimc->state); spin_unlock_irqrestore(&fimc->slock, flags); - timeout = wait_event_timeout(fimc->irq_queue, - test_bit(ST_M2M_SUSPENDED, &fimc->state), - FIMC_SHUTDOWN_TIMEOUT); + time_left = wait_event_timeout(fimc->irq_queue, + test_bit(ST_M2M_SUSPENDED, &fimc->state), + FIMC_SHUTDOWN_TIMEOUT); clear_bit(ST_M2M_SUSPENDING, &fimc->state); - return timeout == 0 ? -EAGAIN : 0; + return time_left == 0 ? -EAGAIN : 0; } static int fimc_m2m_resume(struct fimc_dev *fimc) -- cgit v1.2.3-70-g09d2 From 24a19e4b3a1abc548e45d35d7ce8f2b6b6c65ce7 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 5 Aug 2024 23:51:18 +0200 Subject: media: platform: exynos-gsc: use 'time_left' variable with wait_event_timeout() There is a confusing pattern in the kernel to use a variable named 'timeout' to store the result of wait_event_timeout() causing patterns like: timeout = wait_event_timeout(...) if (!timeout) return -ETIMEDOUT; with all kinds of permutations. Use 'time_left' as a variable to make the code self explaining. Fix to the proper variable type 'long' while here. Signed-off-by: Wolfram Sang Signed-off-by: Hans Verkuil --- drivers/media/platform/samsung/exynos-gsc/gsc-core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/samsung/exynos-gsc/gsc-core.c b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c index 618ae55fe396..f45f5c8612a6 100644 --- a/drivers/media/platform/samsung/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c @@ -1225,7 +1225,7 @@ static void gsc_remove(struct platform_device *pdev) static int gsc_m2m_suspend(struct gsc_dev *gsc) { unsigned long flags; - int timeout; + long time_left; spin_lock_irqsave(&gsc->slock, flags); if (!gsc_m2m_pending(gsc)) { @@ -1236,12 +1236,12 @@ static int gsc_m2m_suspend(struct gsc_dev *gsc) set_bit(ST_M2M_SUSPENDING, &gsc->state); spin_unlock_irqrestore(&gsc->slock, flags); - timeout = wait_event_timeout(gsc->irq_queue, - test_bit(ST_M2M_SUSPENDED, &gsc->state), - GSC_SHUTDOWN_TIMEOUT); + time_left = wait_event_timeout(gsc->irq_queue, + test_bit(ST_M2M_SUSPENDED, &gsc->state), + GSC_SHUTDOWN_TIMEOUT); clear_bit(ST_M2M_SUSPENDING, &gsc->state); - return timeout == 0 ? -EAGAIN : 0; + return time_left == 0 ? -EAGAIN : 0; } static void gsc_m2m_resume(struct gsc_dev *gsc) -- cgit v1.2.3-70-g09d2 From b09b6f26adc407b0af7a7ad767f1e753a348d951 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 5 Aug 2024 23:51:19 +0200 Subject: media: solo6x10: use 'time_left' variable with wait_for_completion_timeout() There is a confusing pattern in the kernel to use a variable named 'timeout' to store the result of wait_for_completion_timeout() causing patterns like: timeout = wait_for_completion_timeout(...) if (!timeout) return -ETIMEDOUT; with all kinds of permutations. Use 'time_left' as a variable to make the code self explaining. Fix to the proper variable type 'unsigned long' while here. Signed-off-by: Wolfram Sang Acked-by: Ismael Luceno Signed-off-by: Hans Verkuil --- drivers/media/pci/solo6x10/solo6x10-p2m.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/solo6x10/solo6x10-p2m.c b/drivers/media/pci/solo6x10/solo6x10-p2m.c index ca70a864a3ef..5f100e5e03d9 100644 --- a/drivers/media/pci/solo6x10/solo6x10-p2m.c +++ b/drivers/media/pci/solo6x10/solo6x10-p2m.c @@ -57,7 +57,7 @@ int solo_p2m_dma_desc(struct solo_dev *solo_dev, int desc_cnt) { struct solo_p2m_dev *p2m_dev; - unsigned int timeout; + unsigned long time_left; unsigned int config = 0; int ret = 0; unsigned int p2m_id = 0; @@ -99,12 +99,12 @@ int solo_p2m_dma_desc(struct solo_dev *solo_dev, desc[1].ctrl); } - timeout = wait_for_completion_timeout(&p2m_dev->completion, - solo_dev->p2m_jiffies); + time_left = wait_for_completion_timeout(&p2m_dev->completion, + solo_dev->p2m_jiffies); if (WARN_ON_ONCE(p2m_dev->error)) ret = -EIO; - else if (timeout == 0) { + else if (time_left == 0) { solo_dev->p2m_timeouts++; ret = -EAGAIN; } -- cgit v1.2.3-70-g09d2 From f89906691495eff7a8b5809f63025bb02abd779e Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 5 Aug 2024 23:51:20 +0200 Subject: media: tegra-vde: use 'time_left' variable with wait_for_completion_interruptible_timeout() There is a confusing pattern in the kernel to use a variable named 'timeout' to store the result of wait_for_completion_interruptible_timeout() causing patterns like: timeout = wait_for_completion_interruptible_timeout(...) if (!timeout) return -ETIMEDOUT; with all kinds of permutations. Use 'time_left' as a variable to make the code self explaining. Signed-off-by: Wolfram Sang Acked-by: Thierry Reding Signed-off-by: Hans Verkuil --- drivers/media/platform/nvidia/tegra-vde/h264.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/nvidia/tegra-vde/h264.c b/drivers/media/platform/nvidia/tegra-vde/h264.c index d8812fc06c67..0e56a4331b0d 100644 --- a/drivers/media/platform/nvidia/tegra-vde/h264.c +++ b/drivers/media/platform/nvidia/tegra-vde/h264.c @@ -623,14 +623,14 @@ static int tegra_vde_decode_end(struct tegra_vde *vde) unsigned int read_bytes, macroblocks_nb; struct device *dev = vde->dev; dma_addr_t bsev_ptr; - long timeout; + long time_left; int ret; - timeout = wait_for_completion_interruptible_timeout( + time_left = wait_for_completion_interruptible_timeout( &vde->decode_completion, msecs_to_jiffies(1000)); - if (timeout < 0) { - ret = timeout; - } else if (timeout == 0) { + if (time_left < 0) { + ret = time_left; + } else if (time_left == 0) { bsev_ptr = tegra_vde_readl(vde, vde->bsev, 0x10); macroblocks_nb = tegra_vde_readl(vde, vde->sxe, 0xC8) & 0x1FFF; read_bytes = bsev_ptr ? bsev_ptr - vde->bitstream_data_addr : 0; -- cgit v1.2.3-70-g09d2 From c80bfa4f9e0ebfce6ac909488d412347acbcb4f9 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 5 Aug 2024 23:51:21 +0200 Subject: media: ti: cal: use 'time_left' variable with wait_event_timeout() There is a confusing pattern in the kernel to use a variable named 'timeout' to store the result of wait_event_timeout() causing patterns like: timeout = wait_event_timeout(...) if (!timeout) return -ETIMEDOUT; with all kinds of permutations. Use 'time_left' as a variable to make the code self explaining. Signed-off-by: Wolfram Sang Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/cal/cal.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/ti/cal/cal.c b/drivers/media/platform/ti/cal/cal.c index 528909ae4bd6..5c2c04142aee 100644 --- a/drivers/media/platform/ti/cal/cal.c +++ b/drivers/media/platform/ti/cal/cal.c @@ -549,7 +549,7 @@ void cal_ctx_start(struct cal_ctx *ctx) void cal_ctx_stop(struct cal_ctx *ctx) { struct cal_camerarx *phy = ctx->phy; - long timeout; + long time_left; WARN_ON(phy->vc_enable_count[ctx->vc] == 0); @@ -565,9 +565,9 @@ void cal_ctx_stop(struct cal_ctx *ctx) ctx->dma.state = CAL_DMA_STOP_REQUESTED; spin_unlock_irq(&ctx->dma.lock); - timeout = wait_event_timeout(ctx->dma.wait, cal_ctx_wr_dma_stopped(ctx), - msecs_to_jiffies(500)); - if (!timeout) { + time_left = wait_event_timeout(ctx->dma.wait, cal_ctx_wr_dma_stopped(ctx), + msecs_to_jiffies(500)); + if (!time_left) { ctx_err(ctx, "failed to disable dma cleanly\n"); cal_ctx_wr_dma_disable(ctx); } -- cgit v1.2.3-70-g09d2 From 983b32a29ea1e424caaf39d067c5883f6ab9aef3 Mon Sep 17 00:00:00 2001 From: Ondrej Jirman Date: Sat, 16 Mar 2024 00:02:41 +0100 Subject: media: rkisp1: Adapt to different SoCs having different size limits - RK3399 has input/output limit of main path 4416 x 3312 - PX30 has input/output limit of main path 3264 x 2448 - i.MX8MP has input/output limit of main path 4096 x 3072 Use rkisp1_info struct to encode the limits. Signed-off-by: Ondrej Jirman Reviewed-by: Laurent Pinchart Reviewed-by: Paul Elder Signed-off-by: Laurent Pinchart --- drivers/media/platform/rockchip/rkisp1/rkisp1-common.h | 11 ++++++++--- drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c | 5 +++-- drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c | 6 ++++++ drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c | 9 +++++---- drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c | 4 ++-- 5 files changed, 24 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index 26573f6ae575..3ce7d477e455 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -33,9 +33,10 @@ struct regmap; #define RKISP1_ISP_SD_SRC BIT(0) #define RKISP1_ISP_SD_SINK BIT(1) -/* min and max values for the widths and heights of the entities */ -#define RKISP1_ISP_MAX_WIDTH 4032 -#define RKISP1_ISP_MAX_HEIGHT 3024 +/* + * Minimum values for the width and height of entities. The maximum values are + * model-specific and stored in the rkisp1_info structure. + */ #define RKISP1_ISP_MIN_WIDTH 32 #define RKISP1_ISP_MIN_HEIGHT 32 @@ -140,6 +141,8 @@ enum rkisp1_feature { * @isr_size: number of entries in the @isrs array * @isp_ver: ISP version * @features: bitmask of rkisp1_feature features implemented by the ISP + * @max_width: maximum input frame width + * @max_height: maximum input frame height * * This structure contains information about the ISP specific to a particular * ISP model, version, or integration in a particular SoC. @@ -151,6 +154,8 @@ struct rkisp1_info { unsigned int isr_size; enum rkisp1_cif_isp_version isp_ver; unsigned int features; + unsigned int max_width; + unsigned int max_height; }; /* diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c index 4202642e0523..841e58c20f7f 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c @@ -307,6 +307,7 @@ static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { + struct rkisp1_csi *csi = to_rkisp1_csi(sd); const struct rkisp1_mbus_info *mbus_info; struct v4l2_mbus_framefmt *sink_fmt, *src_fmt; @@ -326,10 +327,10 @@ static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd, sink_fmt->width = clamp_t(u32, fmt->format.width, RKISP1_ISP_MIN_WIDTH, - RKISP1_ISP_MAX_WIDTH); + csi->rkisp1->info->max_width); sink_fmt->height = clamp_t(u32, fmt->format.height, RKISP1_ISP_MIN_HEIGHT, - RKISP1_ISP_MAX_HEIGHT); + csi->rkisp1->info->max_height); fmt->format = *sink_fmt; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c index bb0202386c70..0535ce57e862 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c @@ -510,6 +510,8 @@ static const struct rkisp1_info px30_isp_info = { .features = RKISP1_FEATURE_MIPI_CSI2 | RKISP1_FEATURE_SELF_PATH | RKISP1_FEATURE_DUAL_CROP, + .max_width = 3264, + .max_height = 2448, }; static const char * const rk3399_isp_clks[] = { @@ -531,6 +533,8 @@ static const struct rkisp1_info rk3399_isp_info = { .features = RKISP1_FEATURE_MIPI_CSI2 | RKISP1_FEATURE_SELF_PATH | RKISP1_FEATURE_DUAL_CROP, + .max_width = 4416, + .max_height = 3312, }; static const char * const imx8mp_isp_clks[] = { @@ -551,6 +555,8 @@ static const struct rkisp1_info imx8mp_isp_info = { .isp_ver = RKISP1_V_IMX8MP, .features = RKISP1_FEATURE_MAIN_STRIDE | RKISP1_FEATURE_DMA_34BIT, + .max_width = 4096, + .max_height = 3072, }; static const struct of_device_id rkisp1_of_match[] = { diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index 91301d17d356..d94917211828 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -517,6 +517,7 @@ static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { + struct rkisp1_isp *isp = to_rkisp1_isp(sd); const struct rkisp1_mbus_info *mbus_info; if (fse->pad == RKISP1_ISP_PAD_SINK_PARAMS || @@ -539,9 +540,9 @@ static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd, return -EINVAL; fse->min_width = RKISP1_ISP_MIN_WIDTH; - fse->max_width = RKISP1_ISP_MAX_WIDTH; + fse->max_width = isp->rkisp1->info->max_width; fse->min_height = RKISP1_ISP_MIN_HEIGHT; - fse->max_height = RKISP1_ISP_MAX_HEIGHT; + fse->max_height = isp->rkisp1->info->max_height; return 0; } @@ -772,10 +773,10 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp, sink_fmt->width = clamp_t(u32, format->width, RKISP1_ISP_MIN_WIDTH, - RKISP1_ISP_MAX_WIDTH); + isp->rkisp1->info->max_width); sink_fmt->height = clamp_t(u32, format->height, RKISP1_ISP_MIN_HEIGHT, - RKISP1_ISP_MAX_HEIGHT); + isp->rkisp1->info->max_height); /* * Adjust the color space fields. Accept any color primaries and diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index 1fa991227fa9..f073e72a0d37 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -494,10 +494,10 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz, sink_fmt->width = clamp_t(u32, format->width, RKISP1_ISP_MIN_WIDTH, - RKISP1_ISP_MAX_WIDTH); + rsz->rkisp1->info->max_width); sink_fmt->height = clamp_t(u32, format->height, RKISP1_ISP_MIN_HEIGHT, - RKISP1_ISP_MAX_HEIGHT); + rsz->rkisp1->info->max_height); /* * Adjust the color space fields. Accept any color primaries and -- cgit v1.2.3-70-g09d2 From 1fc379f6241b331207c4573c4ff43526fe404301 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 8 Aug 2024 22:40:55 +0200 Subject: media: uapi: videodev2: Add V4L2_META_FMT_RK_ISP1_EXT_PARAMS The rkisp1 driver stores ISP configuration parameters in the fixed rkisp1_params_cfg structure. As the members of the structure are part of the userspace API, the structure layout is immutable and cannot be extended further. Introducing new parameters or modifying the existing ones would change the buffer layout and cause breakages in existing applications. The allow for future extensions to the ISP parameters, introduce a new extensible parameters format, with a new format 4CC. Document usage of the new format in the rkisp1 admin guide. Signed-off-by: Jacopo Mondi Reviewed-by: Daniel Scally Reviewed-by: Paul Elder Reviewed-by: Laurent Pinchart Tested-by: Kieran Bingham Acked-by: Sakari Ailus Signed-off-by: Laurent Pinchart --- Documentation/admin-guide/media/rkisp1.rst | 11 ++++- .../userspace-api/media/v4l/metafmt-rkisp1.rst | 57 ++++++++++++++++++---- drivers/media/v4l2-core/v4l2-ioctl.c | 1 + include/uapi/linux/videodev2.h | 1 + 4 files changed, 59 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/Documentation/admin-guide/media/rkisp1.rst b/Documentation/admin-guide/media/rkisp1.rst index 6f14d9561fa5..6c878c71442f 100644 --- a/Documentation/admin-guide/media/rkisp1.rst +++ b/Documentation/admin-guide/media/rkisp1.rst @@ -114,11 +114,18 @@ to be applied to the hardware during a video stream, allowing userspace to dynamically modify values such as black level, cross talk corrections and others. -The buffer format is defined by struct :c:type:`rkisp1_params_cfg`, and -userspace should set +The ISP driver supports two different parameters configuration methods, the +`fixed parameters format` or the `extensible parameters format`. + +When using the `fixed parameters` method the buffer format is defined by struct +:c:type:`rkisp1_params_cfg`, and userspace should set :ref:`V4L2_META_FMT_RK_ISP1_PARAMS ` as the dataformat. +When using the `extensible parameters` method the buffer format is defined by +struct :c:type:`rkisp1_ext_params_cfg`, and userspace should set +:ref:`V4L2_META_FMT_RK_ISP1_EXT_PARAMS ` as +the dataformat. Capturing Video Frames Example ============================== diff --git a/Documentation/userspace-api/media/v4l/metafmt-rkisp1.rst b/Documentation/userspace-api/media/v4l/metafmt-rkisp1.rst index fa04f00bcd2e..959f6bde8695 100644 --- a/Documentation/userspace-api/media/v4l/metafmt-rkisp1.rst +++ b/Documentation/userspace-api/media/v4l/metafmt-rkisp1.rst @@ -1,28 +1,67 @@ .. SPDX-License-Identifier: GPL-2.0 -.. _v4l2-meta-fmt-rk-isp1-params: - .. _v4l2-meta-fmt-rk-isp1-stat-3a: -***************************************************************************** -V4L2_META_FMT_RK_ISP1_PARAMS ('rk1p'), V4L2_META_FMT_RK_ISP1_STAT_3A ('rk1s') -***************************************************************************** +************************************************************************************************************************ +V4L2_META_FMT_RK_ISP1_PARAMS ('rk1p'), V4L2_META_FMT_RK_ISP1_STAT_3A ('rk1s'), V4L2_META_FMT_RK_ISP1_EXT_PARAMS ('rk1e') +************************************************************************************************************************ +======================== Configuration parameters ======================== -The configuration parameters are passed to the +The configuration of the RkISP1 ISP is performed by userspace by providing +parameters for the ISP to the driver using the :c:type:`v4l2_meta_format` +interface. + +There are two methods that allow to configure the ISP, the `fixed parameters` +configuration format and the `extensible parameters` configuration +format. + +.. _v4l2-meta-fmt-rk-isp1-params: + +Fixed parameters configuration format +===================================== + +When using the fixed configuration format, parameters are passed to the :ref:`rkisp1_params ` metadata output video node, using -the :c:type:`v4l2_meta_format` interface. The buffer contains -a single instance of the C structure :c:type:`rkisp1_params_cfg` defined in -``rkisp1-config.h``. So the structure can be obtained from the buffer by: +the `V4L2_META_FMT_RK_ISP1_PARAMS` meta format. + +The buffer contains a single instance of the C structure +:c:type:`rkisp1_params_cfg` defined in ``rkisp1-config.h``. So the structure can +be obtained from the buffer by: .. code-block:: c struct rkisp1_params_cfg *params = (struct rkisp1_params_cfg*) buffer; +This method supports a subset of the ISP features only, new applications should +use the extensible parameters method. + +.. _v4l2-meta-fmt-rk-isp1-ext-params: + +Extensible parameters configuration format +========================================== + +When using the extensible configuration format, parameters are passed to the +:ref:`rkisp1_params ` metadata output video node, using +the `V4L2_META_FMT_RK_ISP1_EXT_PARAMS` meta format. + +The buffer contains a single instance of the C structure +:c:type:`rkisp1_ext_params_cfg` defined in ``rkisp1-config.h``. The +:c:type:`rkisp1_ext_params_cfg` structure is designed to allow userspace to +populate the data buffer with only the configuration data for the ISP blocks it +intends to configure. The extensible parameters format design allows developers +to define new block types to support new configuration parameters, and defines a +versioning scheme so that it can be extended and versioned without breaking +compatibility with existing applications. + +For these reasons, this configuration method is preferred over the `fixed +parameters` format alternative. + .. rkisp1_stat_buffer +=========================== 3A and histogram statistics =========================== diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 64c3e79d6378..e14db67be97c 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1458,6 +1458,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_META_FMT_VIVID: descr = "Vivid Metadata"; break; case V4L2_META_FMT_RK_ISP1_PARAMS: descr = "Rockchip ISP1 3A Parameters"; break; case V4L2_META_FMT_RK_ISP1_STAT_3A: descr = "Rockchip ISP1 3A Statistics"; break; + case V4L2_META_FMT_RK_ISP1_EXT_PARAMS: descr = "Rockchip ISP1 Ext 3A Params"; break; case V4L2_PIX_FMT_NV12_8L128: descr = "NV12 (8x128 Linear)"; break; case V4L2_PIX_FMT_NV12M_8L128: descr = "NV12M (8x128 Linear)"; break; case V4L2_PIX_FMT_NV12_10BE_8L128: descr = "10-bit NV12 (8x128 Linear, BE)"; break; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 4e91362da6da..725e86c4bbbd 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -854,6 +854,7 @@ struct v4l2_pix_format { /* Vendor specific - used for RK_ISP1 camera sub-system */ #define V4L2_META_FMT_RK_ISP1_PARAMS v4l2_fourcc('R', 'K', '1', 'P') /* Rockchip ISP1 3A Parameters */ #define V4L2_META_FMT_RK_ISP1_STAT_3A v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A Statistics */ +#define V4L2_META_FMT_RK_ISP1_EXT_PARAMS v4l2_fourcc('R', 'K', '1', 'E') /* Rockchip ISP1 3a Extensible Parameters */ /* Vendor specific - used for RaspberryPi PiSP */ #define V4L2_META_FMT_RPI_BE_CFG v4l2_fourcc('R', 'P', 'B', 'C') /* PiSP BE configuration */ -- cgit v1.2.3-70-g09d2 From 3bdae13a75de84722aa6a3b9b99524b26b859148 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 8 Aug 2024 22:40:56 +0200 Subject: media: rkisp1: Add struct rkisp1_params_buffer Create the 'struct rkisp1_params_buffer' type that wraps a vb2_v4l2_buffer to prepare to hold a copy of the parameters buffer that will be used to cache the user-provided configuration buffer in the following patches. Replace usage of 'struct rkisp1_buffer' with 'struct rkisp1_params_buffer' in rkisp1-params.c to prepare for that. Signed-off-by: Jacopo Mondi Reviewed-by: Daniel Scally Reviewed-by: Laurent Pinchart Reviewed-by: Paul Elder Tested-by: Kieran Bingham Acked-by: Sakari Ailus Signed-off-by: Laurent Pinchart --- .../media/platform/rockchip/rkisp1/rkisp1-common.h | 14 +++++++++++++- .../media/platform/rockchip/rkisp1/rkisp1-params.c | 21 +++++++++++---------- 2 files changed, 24 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index 3ce7d477e455..096d7962559c 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -237,7 +237,7 @@ struct rkisp1_vdev_node { /* * struct rkisp1_buffer - A container for the vb2 buffers used by the video devices: - * params, stats, mainpath, selfpath + * stats, mainpath, selfpath * * @vb: vb2 buffer * @queue: entry of the buffer in the queue @@ -249,6 +249,18 @@ struct rkisp1_buffer { dma_addr_t buff_addr[VIDEO_MAX_PLANES]; }; +/* + * struct rkisp1_params_buffer - A container for the vb2 buffers used by the + * params video device + * + * @vb: vb2 buffer + * @queue: entry of the buffer in the queue + */ +struct rkisp1_params_buffer { + struct vb2_v4l2_buffer vb; + struct list_head queue; +}; + /* * struct rkisp1_dummy_buffer - A buffer to write the next frame to in case * there are no vb2 buffers available. diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index 173d1ea41874..2844e55bc4f2 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -1502,20 +1502,21 @@ static void rkisp1_isp_isr_meas_config(struct rkisp1_params *params, } static bool rkisp1_params_get_buffer(struct rkisp1_params *params, - struct rkisp1_buffer **buf, + struct rkisp1_params_buffer **buf, struct rkisp1_params_cfg **cfg) { if (list_empty(¶ms->params)) return false; - *buf = list_first_entry(¶ms->params, struct rkisp1_buffer, queue); + *buf = list_first_entry(¶ms->params, struct rkisp1_params_buffer, + queue); *cfg = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0); return true; } static void rkisp1_params_complete_buffer(struct rkisp1_params *params, - struct rkisp1_buffer *buf, + struct rkisp1_params_buffer *buf, unsigned int frame_sequence) { list_del(&buf->queue); @@ -1528,7 +1529,7 @@ void rkisp1_params_isr(struct rkisp1_device *rkisp1) { struct rkisp1_params *params = &rkisp1->params; struct rkisp1_params_cfg *new_params; - struct rkisp1_buffer *cur_buf; + struct rkisp1_params_buffer *cur_buf; spin_lock(¶ms->config_lock); @@ -1604,7 +1605,7 @@ void rkisp1_params_pre_configure(struct rkisp1_params *params, { struct rkisp1_cif_isp_hst_config hst = rkisp1_hst_params_default_config; struct rkisp1_params_cfg *new_params; - struct rkisp1_buffer *cur_buf; + struct rkisp1_params_buffer *cur_buf; params->quantization = quantization; params->ycbcr_encoding = ycbcr_encoding; @@ -1650,7 +1651,7 @@ unlock: void rkisp1_params_post_configure(struct rkisp1_params *params) { struct rkisp1_params_cfg *new_params; - struct rkisp1_buffer *cur_buf; + struct rkisp1_params_buffer *cur_buf; spin_lock_irq(¶ms->config_lock); @@ -1821,8 +1822,8 @@ static int rkisp1_params_vb2_queue_setup(struct vb2_queue *vq, static void rkisp1_params_vb2_buf_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct rkisp1_buffer *params_buf = - container_of(vbuf, struct rkisp1_buffer, vb); + struct rkisp1_params_buffer *params_buf = + container_of(vbuf, struct rkisp1_params_buffer, vb); struct vb2_queue *vq = vb->vb2_queue; struct rkisp1_params *params = vq->drv_priv; @@ -1844,7 +1845,7 @@ static int rkisp1_params_vb2_buf_prepare(struct vb2_buffer *vb) static void rkisp1_params_vb2_stop_streaming(struct vb2_queue *vq) { struct rkisp1_params *params = vq->drv_priv; - struct rkisp1_buffer *buf; + struct rkisp1_params_buffer *buf; LIST_HEAD(tmp_list); /* @@ -1890,7 +1891,7 @@ static int rkisp1_params_init_vb2_queue(struct vb2_queue *q, q->drv_priv = params; q->ops = &rkisp1_params_vb2_ops; q->mem_ops = &vb2_vmalloc_memops; - q->buf_struct_size = sizeof(struct rkisp1_buffer); + q->buf_struct_size = sizeof(struct rkisp1_params_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &node->vlock; -- cgit v1.2.3-70-g09d2 From 092e276db9d979ed8ac63ae7ffb6b4da48d19dc1 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 8 Aug 2024 22:40:57 +0200 Subject: media: rkisp1: Copy the parameters buffer The ISP parameters buffers are queued by userspace to the params video device and appended by the driver to the list of available buffers for later consumption. As the parameters buffer is mapped in the userspace process memory, applications have access to the buffer content after the buffer has been queued. To prevent userspace from modifying the contents of the parameters buffer after it has been queued to the video device, add to 'struct rkisp1_params_buffer' a scratch buffer where to copy the parameters. Allocate the scratch buffer in the vb2 buf_init() operation and copy the buffer content in the buf_prepare() operation. Free the scratch buffer in the newly introduced buf_cleanup() operation handler. Modify the ISP configuration function to access the ISP configuration from the cached copy of the parameters buffer instead of using the userspace-mapped one. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Paul Elder Tested-by: Kieran Bingham Acked-by: Sakari Ailus Signed-off-by: Laurent Pinchart --- .../media/platform/rockchip/rkisp1/rkisp1-common.h | 8 +++ .../media/platform/rockchip/rkisp1/rkisp1-params.c | 83 ++++++++++++++-------- 2 files changed, 60 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index 096d7962559c..c25d846fc652 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -255,12 +255,20 @@ struct rkisp1_buffer { * * @vb: vb2 buffer * @queue: entry of the buffer in the queue + * @cfg: scratch buffer used for caching the ISP configuration parameters */ struct rkisp1_params_buffer { struct vb2_v4l2_buffer vb; struct list_head queue; + struct rkisp1_params_cfg *cfg; }; +static inline struct rkisp1_params_buffer * +to_rkisp1_params_buffer(struct vb2_v4l2_buffer *vbuf) +{ + return container_of(vbuf, struct rkisp1_params_buffer, vb); +} + /* * struct rkisp1_dummy_buffer - A buffer to write the next frame to in case * there are no vb2 buffers available. diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index 2844e55bc4f2..e333cda21fdd 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -5,6 +5,8 @@ * Copyright (C) 2017 Rockchip Electronics Co., Ltd. */ +#include + #include #include #include @@ -1501,20 +1503,6 @@ static void rkisp1_isp_isr_meas_config(struct rkisp1_params *params, } } -static bool rkisp1_params_get_buffer(struct rkisp1_params *params, - struct rkisp1_params_buffer **buf, - struct rkisp1_params_cfg **cfg) -{ - if (list_empty(¶ms->params)) - return false; - - *buf = list_first_entry(¶ms->params, struct rkisp1_params_buffer, - queue); - *cfg = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0); - - return true; -} - static void rkisp1_params_complete_buffer(struct rkisp1_params *params, struct rkisp1_params_buffer *buf, unsigned int frame_sequence) @@ -1528,17 +1516,18 @@ static void rkisp1_params_complete_buffer(struct rkisp1_params *params, void rkisp1_params_isr(struct rkisp1_device *rkisp1) { struct rkisp1_params *params = &rkisp1->params; - struct rkisp1_params_cfg *new_params; struct rkisp1_params_buffer *cur_buf; spin_lock(¶ms->config_lock); - if (!rkisp1_params_get_buffer(params, &cur_buf, &new_params)) + cur_buf = list_first_entry_or_null(¶ms->params, + struct rkisp1_params_buffer, queue); + if (!cur_buf) goto unlock; - rkisp1_isp_isr_other_config(params, new_params); - rkisp1_isp_isr_lsc_config(params, new_params); - rkisp1_isp_isr_meas_config(params, new_params); + rkisp1_isp_isr_other_config(params, cur_buf->cfg); + rkisp1_isp_isr_lsc_config(params, cur_buf->cfg); + rkisp1_isp_isr_meas_config(params, cur_buf->cfg); /* update shadow register immediately */ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, @@ -1604,7 +1593,6 @@ void rkisp1_params_pre_configure(struct rkisp1_params *params, enum v4l2_ycbcr_encoding ycbcr_encoding) { struct rkisp1_cif_isp_hst_config hst = rkisp1_hst_params_default_config; - struct rkisp1_params_cfg *new_params; struct rkisp1_params_buffer *cur_buf; params->quantization = quantization; @@ -1634,11 +1622,13 @@ void rkisp1_params_pre_configure(struct rkisp1_params *params, /* apply the first buffer if there is one already */ - if (!rkisp1_params_get_buffer(params, &cur_buf, &new_params)) + cur_buf = list_first_entry_or_null(¶ms->params, + struct rkisp1_params_buffer, queue); + if (!cur_buf) goto unlock; - rkisp1_isp_isr_other_config(params, new_params); - rkisp1_isp_isr_meas_config(params, new_params); + rkisp1_isp_isr_other_config(params, cur_buf->cfg); + rkisp1_isp_isr_meas_config(params, cur_buf->cfg); /* update shadow register immediately */ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, @@ -1650,7 +1640,6 @@ unlock: void rkisp1_params_post_configure(struct rkisp1_params *params) { - struct rkisp1_params_cfg *new_params; struct rkisp1_params_buffer *cur_buf; spin_lock_irq(¶ms->config_lock); @@ -1663,11 +1652,12 @@ void rkisp1_params_post_configure(struct rkisp1_params *params) * ordering doesn't affect other ISP versions negatively, do so * unconditionally. */ - - if (!rkisp1_params_get_buffer(params, &cur_buf, &new_params)) + cur_buf = list_first_entry_or_null(¶ms->params, + struct rkisp1_params_buffer, queue); + if (!cur_buf) goto unlock; - rkisp1_isp_isr_lsc_config(params, new_params); + rkisp1_isp_isr_lsc_config(params, cur_buf->cfg); /* update shadow register immediately */ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, @@ -1819,11 +1809,31 @@ static int rkisp1_params_vb2_queue_setup(struct vb2_queue *vq, return 0; } +static int rkisp1_params_vb2_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf); + + params_buf->cfg = kvmalloc(sizeof(*params_buf->cfg), GFP_KERNEL); + if (!params_buf->cfg) + return -ENOMEM; + + return 0; +} + +static void rkisp1_params_vb2_buf_cleanup(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf); + + kvfree(params_buf->cfg); + params_buf->cfg = NULL; +} + static void rkisp1_params_vb2_buf_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct rkisp1_params_buffer *params_buf = - container_of(vbuf, struct rkisp1_params_buffer, vb); + struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf); struct vb2_queue *vq = vb->vb2_queue; struct rkisp1_params *params = vq->drv_priv; @@ -1834,10 +1844,19 @@ static void rkisp1_params_vb2_buf_queue(struct vb2_buffer *vb) static int rkisp1_params_vb2_buf_prepare(struct vb2_buffer *vb) { - if (vb2_plane_size(vb, 0) < sizeof(struct rkisp1_params_cfg)) + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf); + struct rkisp1_params_cfg *cfg = + vb2_plane_vaddr(¶ms_buf->vb.vb2_buf, 0); + + if (vb2_get_plane_payload(vb, 0) != sizeof(*cfg)) return -EINVAL; - vb2_set_plane_payload(vb, 0, sizeof(struct rkisp1_params_cfg)); + /* + * Copy the parameters buffer to the internal scratch buffer to avoid + * userspace modifying the buffer content while the driver processes it. + */ + memcpy(params_buf->cfg, cfg, sizeof(*cfg)); return 0; } @@ -1863,6 +1882,8 @@ static void rkisp1_params_vb2_stop_streaming(struct vb2_queue *vq) static const struct vb2_ops rkisp1_params_vb2_ops = { .queue_setup = rkisp1_params_vb2_queue_setup, + .buf_init = rkisp1_params_vb2_buf_init, + .buf_cleanup = rkisp1_params_vb2_buf_cleanup, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, .buf_queue = rkisp1_params_vb2_buf_queue, -- cgit v1.2.3-70-g09d2 From 16398399b62bee12a4bdfbc51f432243e41537f7 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 8 Aug 2024 22:40:58 +0200 Subject: media: rkisp1: Cache the currently active format The rkisp1-params driver assumes the data buffer format is the only currently supported "fixed" one. The usage of the "fixed" format is assumed when allocating memory for the scratch buffers and when initializing the vb2 queue. In order to prepare to support the "extensible" format beside the existing "fixed" one, add support in the driver for both formats by caching a pointer to the active one in the driver structure and use it in the vb2 queue operations and subdev pad operations implementations. Do not yet allow userspace to select between the two formats as the support for the "extensible" format parsing will be introduced in a later patch in the series. While at it, document the un-documented ycbcr_encoding field of struct rkisp1_params_ops. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Paul Elder Tested-by: Kieran Bingham Acked-by: Sakari Ailus Signed-off-by: Laurent Pinchart --- .../media/platform/rockchip/rkisp1/rkisp1-common.h | 8 ++-- .../media/platform/rockchip/rkisp1/rkisp1-params.c | 52 ++++++++++++++-------- 2 files changed, 38 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index c25d846fc652..b4369bbccea3 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -260,7 +260,7 @@ struct rkisp1_buffer { struct rkisp1_params_buffer { struct vb2_v4l2_buffer vb; struct list_head queue; - struct rkisp1_params_cfg *cfg; + void *cfg; }; static inline struct rkisp1_params_buffer * @@ -397,8 +397,9 @@ struct rkisp1_params_ops { * @ops: pointer to the variant-specific operations * @config_lock: locks the buffer list 'params' * @params: queue of rkisp1_buffer - * @vdev_fmt: v4l2_format of the metadata format + * @metafmt the currently enabled metadata format * @quantization: the quantization configured on the isp's src pad + * @ycbcr_encoding the YCbCr encoding * @raw_type: the bayer pattern on the isp video sink pad */ struct rkisp1_params { @@ -408,7 +409,8 @@ struct rkisp1_params { spinlock_t config_lock; /* locks the buffers list 'params' */ struct list_head params; - struct v4l2_format vdev_fmt; + + const struct v4l2_meta_format *metafmt; enum v4l2_quantization quantization; enum v4l2_ycbcr_encoding ycbcr_encoding; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index e333cda21fdd..45c4b1bcee63 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -35,6 +35,22 @@ #define RKISP1_ISP_CC_COEFF(n) \ (RKISP1_CIF_ISP_CC_COEFF_0 + (n) * 4) +enum rkisp1_params_formats { + RKISP1_PARAMS_FIXED, + RKISP1_PARAMS_EXTENSIBLE, +}; + +static const struct v4l2_meta_format rkisp1_params_formats[] = { + [RKISP1_PARAMS_FIXED] = { + .dataformat = V4L2_META_FMT_RK_ISP1_PARAMS, + .buffersize = sizeof(struct rkisp1_params_cfg), + }, + [RKISP1_PARAMS_EXTENSIBLE] = { + .dataformat = V4L2_META_FMT_RK_ISP1_EXT_PARAMS, + .buffersize = sizeof(struct rkisp1_ext_params_cfg), + }, +}; + static inline void rkisp1_param_set_bits(struct rkisp1_params *params, u32 reg, u32 bit_mask) { @@ -1738,7 +1754,7 @@ static int rkisp1_params_enum_fmt_meta_out(struct file *file, void *priv, if (f->index > 0 || f->type != video->queue->type) return -EINVAL; - f->pixelformat = params->vdev_fmt.fmt.meta.dataformat; + f->pixelformat = params->metafmt->dataformat; return 0; } @@ -1754,8 +1770,8 @@ static int rkisp1_params_g_fmt_meta_out(struct file *file, void *fh, return -EINVAL; memset(meta, 0, sizeof(*meta)); - meta->dataformat = params->vdev_fmt.fmt.meta.dataformat; - meta->buffersize = params->vdev_fmt.fmt.meta.buffersize; + meta->dataformat = params->metafmt->dataformat; + meta->buffersize = params->metafmt->buffersize; return 0; } @@ -1798,13 +1814,15 @@ static int rkisp1_params_vb2_queue_setup(struct vb2_queue *vq, unsigned int sizes[], struct device *alloc_devs[]) { + struct rkisp1_params *params = vq->drv_priv; + *num_buffers = clamp_t(u32, *num_buffers, RKISP1_ISP_PARAMS_REQ_BUFS_MIN, RKISP1_ISP_PARAMS_REQ_BUFS_MAX); *num_planes = 1; - sizes[0] = sizeof(struct rkisp1_params_cfg); + sizes[0] = params->metafmt->buffersize; return 0; } @@ -1813,8 +1831,10 @@ static int rkisp1_params_vb2_buf_init(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf); + struct rkisp1_params *params = vb->vb2_queue->drv_priv; - params_buf->cfg = kvmalloc(sizeof(*params_buf->cfg), GFP_KERNEL); + params_buf->cfg = kvmalloc(params->metafmt->buffersize, + GFP_KERNEL); if (!params_buf->cfg) return -ENOMEM; @@ -1919,19 +1939,6 @@ static int rkisp1_params_init_vb2_queue(struct vb2_queue *q, return vb2_queue_init(q); } -static void rkisp1_init_params(struct rkisp1_params *params) -{ - params->vdev_fmt.fmt.meta.dataformat = - V4L2_META_FMT_RK_ISP1_PARAMS; - params->vdev_fmt.fmt.meta.buffersize = - sizeof(struct rkisp1_params_cfg); - - if (params->rkisp1->info->isp_ver == RKISP1_V12) - params->ops = &rkisp1_v12_params_ops; - else - params->ops = &rkisp1_v10_params_ops; -} - int rkisp1_params_register(struct rkisp1_device *rkisp1) { struct rkisp1_params *params = &rkisp1->params; @@ -1960,7 +1967,14 @@ int rkisp1_params_register(struct rkisp1_device *rkisp1) vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT; vdev->vfl_dir = VFL_DIR_TX; rkisp1_params_init_vb2_queue(vdev->queue, params); - rkisp1_init_params(params); + + params->metafmt = &rkisp1_params_formats[RKISP1_PARAMS_FIXED]; + + if (params->rkisp1->info->isp_ver == RKISP1_V12) + params->ops = &rkisp1_v12_params_ops; + else + params->ops = &rkisp1_v10_params_ops; + video_set_drvdata(vdev, params); node->pad.flags = MEDIA_PAD_FL_SOURCE; -- cgit v1.2.3-70-g09d2 From 044fcf738a56d915514e2d651333395b3f8daa62 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 24 Apr 2024 16:02:48 +0200 Subject: media: staging: media: starfive: camss: Drop obsolete return value documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recently the function stfcamss_remove() was changed to not return a value. Drop the documentation of the return value in the kernel doc. Fixes: b1f3677aebe5 ("media: staging: media: starfive: camss: Convert to platform remove callback returning void") Signed-off-by: Uwe Kleine-König Signed-off-by: Hans Verkuil --- drivers/staging/media/starfive/camss/stf-camss.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/starfive/camss/stf-camss.c b/drivers/staging/media/starfive/camss/stf-camss.c index fecd3e67c7a1..b6d34145bc19 100644 --- a/drivers/staging/media/starfive/camss/stf-camss.c +++ b/drivers/staging/media/starfive/camss/stf-camss.c @@ -358,8 +358,6 @@ err_cleanup_notifier: /* * stfcamss_remove - Remove STFCAMSS platform device * @pdev: Pointer to STFCAMSS platform device - * - * Always returns 0. */ static void stfcamss_remove(struct platform_device *pdev) { -- cgit v1.2.3-70-g09d2 From a5972ea0fb8c862d887b91a146908a17b56ee5a2 Mon Sep 17 00:00:00 2001 From: Martin Tůma Date: Mon, 5 Aug 2024 17:40:51 +0200 Subject: media: mgb4: Add support for YUV image formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent mgb4 firmwares support YUV in addition to the RGB image format. Enable YUV in the driver when the FW supports it. Signed-off-by: Martin Tůma Signed-off-by: Hans Verkuil --- drivers/media/pci/mgb4/mgb4_io.h | 7 +++ drivers/media/pci/mgb4/mgb4_vin.c | 107 +++++++++++++++++++++++++++++------- drivers/media/pci/mgb4/mgb4_vout.c | 109 ++++++++++++++++++++++++++++++------- 3 files changed, 183 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/mgb4/mgb4_io.h b/drivers/media/pci/mgb4/mgb4_io.h index 8698db1be4a9..204613a6685c 100644 --- a/drivers/media/pci/mgb4/mgb4_io.h +++ b/drivers/media/pci/mgb4/mgb4_io.h @@ -30,4 +30,11 @@ static inline struct mgb4_frame_buffer *to_frame_buffer(struct vb2_v4l2_buffer * return container_of(vbuf, struct mgb4_frame_buffer, vb); } +static inline bool has_yuv(struct mgb4_regs *video) +{ + u32 status = mgb4_read_reg(video, 0xD0); + + return (status & (1U << 8)); +} + #endif diff --git a/drivers/media/pci/mgb4/mgb4_vin.c b/drivers/media/pci/mgb4/mgb4_vin.c index 2cd78c539889..7fb14e867e8d 100644 --- a/drivers/media/pci/mgb4/mgb4_vin.c +++ b/drivers/media/pci/mgb4/mgb4_vin.c @@ -186,8 +186,11 @@ static int queue_setup(struct vb2_queue *q, unsigned int *nbuffers, struct device *alloc_devs[]) { struct mgb4_vin_dev *vindev = vb2_get_drv_priv(q); + struct mgb4_regs *video = &vindev->mgbdev->video; + u32 config = mgb4_read_reg(video, vindev->config->regs.config); + u32 pixelsize = (config & (1U << 16)) ? 2 : 4; unsigned int size = (vindev->timings.bt.width + vindev->padding) - * vindev->timings.bt.height * 4; + * vindev->timings.bt.height * pixelsize; /* * If I/O reconfiguration is in process, do not allow to start @@ -220,9 +223,12 @@ static int buffer_init(struct vb2_buffer *vb) static int buffer_prepare(struct vb2_buffer *vb) { struct mgb4_vin_dev *vindev = vb2_get_drv_priv(vb->vb2_queue); + struct mgb4_regs *video = &vindev->mgbdev->video; struct device *dev = &vindev->mgbdev->pdev->dev; + u32 config = mgb4_read_reg(video, vindev->config->regs.config); + u32 pixelsize = (config & (1U << 16)) ? 2 : 4; unsigned int size = (vindev->timings.bt.width + vindev->padding) - * vindev->timings.bt.height * 4; + * vindev->timings.bt.height * pixelsize; if (vb2_plane_size(vb, 0) < size) { dev_err(dev, "buffer too small (%lu < %u)\n", @@ -359,22 +365,30 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - if (f->index != 0) - return -EINVAL; - - f->pixelformat = V4L2_PIX_FMT_ABGR32; + struct mgb4_vin_dev *vindev = video_drvdata(file); + struct mgb4_regs *video = &vindev->mgbdev->video; - return 0; + if (f->index == 0) { + f->pixelformat = V4L2_PIX_FMT_ABGR32; + return 0; + } else if (f->index == 1 && has_yuv(video)) { + f->pixelformat = V4L2_PIX_FMT_YUYV; + return 0; + } else { + return -EINVAL; + } } static int vidioc_enum_frameintervals(struct file *file, void *priv, struct v4l2_frmivalenum *ival) { struct mgb4_vin_dev *vindev = video_drvdata(file); + struct mgb4_regs *video = &vindev->mgbdev->video; if (ival->index != 0) return -EINVAL; - if (ival->pixel_format != V4L2_PIX_FMT_ABGR32) + if (!(ival->pixel_format == V4L2_PIX_FMT_ABGR32 || + ((has_yuv(video) && ival->pixel_format == V4L2_PIX_FMT_YUYV)))) return -EINVAL; if (ival->width != vindev->timings.bt.width || ival->height != vindev->timings.bt.height) @@ -393,13 +407,29 @@ static int vidioc_enum_frameintervals(struct file *file, void *priv, static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct mgb4_vin_dev *vindev = video_drvdata(file); + struct mgb4_regs *video = &vindev->mgbdev->video; + u32 config = mgb4_read_reg(video, vindev->config->regs.config); - f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; f->fmt.pix.width = vindev->timings.bt.width; f->fmt.pix.height = vindev->timings.bt.height; f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; - f->fmt.pix.bytesperline = (f->fmt.pix.width + vindev->padding) * 4; + + if (config & (1U << 16)) { + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + if (config & (1U << 20)) { + f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; + } else { + if (config & (1U << 19)) + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + else + f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + } + f->fmt.pix.bytesperline = (f->fmt.pix.width + vindev->padding) * 2; + } else { + f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; + f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; + f->fmt.pix.bytesperline = (f->fmt.pix.width + vindev->padding) * 4; + } f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; return 0; @@ -408,14 +438,30 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct mgb4_vin_dev *vindev = video_drvdata(file); + struct mgb4_regs *video = &vindev->mgbdev->video; + u32 pixelsize; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; f->fmt.pix.width = vindev->timings.bt.width; f->fmt.pix.height = vindev->timings.bt.height; f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; - f->fmt.pix.bytesperline = max(f->fmt.pix.width * 4, - ALIGN_DOWN(f->fmt.pix.bytesperline, 4)); + + if (has_yuv(video) && f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) { + pixelsize = 2; + if (!(f->fmt.pix.colorspace == V4L2_COLORSPACE_REC709 || + f->fmt.pix.colorspace == V4L2_COLORSPACE_SMPTE170M)) + f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + } else { + pixelsize = 4; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; + f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; + } + + if (f->fmt.pix.bytesperline > f->fmt.pix.width * pixelsize && + f->fmt.pix.bytesperline < f->fmt.pix.width * pixelsize * 2) + f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline, + pixelsize); + else + f->fmt.pix.bytesperline = f->fmt.pix.width * pixelsize; f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; return 0; @@ -425,13 +471,36 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct mgb4_vin_dev *vindev = video_drvdata(file); struct mgb4_regs *video = &vindev->mgbdev->video; + u32 config, pixelsize; if (vb2_is_busy(&vindev->queue)) return -EBUSY; vidioc_try_fmt(file, priv, f); - vindev->padding = (f->fmt.pix.bytesperline - (f->fmt.pix.width * 4)) / 4; + config = mgb4_read_reg(video, vindev->config->regs.config); + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) { + pixelsize = 2; + config |= 1U << 16; + + if (f->fmt.pix.colorspace == V4L2_COLORSPACE_REC709) { + config |= 1U << 20; + config |= 1U << 19; + } else if (f->fmt.pix.colorspace == V4L2_COLORSPACE_SMPTE170M) { + config &= ~(1U << 20); + config |= 1U << 19; + } else { + config &= ~(1U << 20); + config &= ~(1U << 19); + } + } else { + pixelsize = 4; + config &= ~(1U << 16); + } + mgb4_write_reg(video, vindev->config->regs.config, config); + + vindev->padding = (f->fmt.pix.bytesperline - (f->fmt.pix.width + * pixelsize)) / pixelsize; mgb4_write_reg(video, vindev->config->regs.padding, vindev->padding); set_loopback_padding(vindev, vindev->padding); @@ -467,7 +536,8 @@ static int vidioc_enum_framesizes(struct file *file, void *fh, { struct mgb4_vin_dev *vindev = video_drvdata(file); - if (fsize->index != 0 || fsize->pixel_format != V4L2_PIX_FMT_ABGR32) + if (fsize->index != 0 || !(fsize->pixel_format == V4L2_PIX_FMT_ABGR32 || + fsize->pixel_format == V4L2_PIX_FMT_YUYV)) return -EINVAL; fsize->discrete.width = vindev->timings.bt.width; @@ -499,9 +569,6 @@ static int vidioc_parm(struct file *file, void *priv, .denominator = 125000000, }; - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - parm->parm.capture.readbuffers = 2; parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; parm->parm.capture.timeperframe = timeperframe; diff --git a/drivers/media/pci/mgb4/mgb4_vout.c b/drivers/media/pci/mgb4/mgb4_vout.c index 241353ee77a5..2ee9606d9072 100644 --- a/drivers/media/pci/mgb4/mgb4_vout.c +++ b/drivers/media/pci/mgb4/mgb4_vout.c @@ -59,7 +59,11 @@ static int queue_setup(struct vb2_queue *q, unsigned int *nbuffers, struct device *alloc_devs[]) { struct mgb4_vout_dev *voutdev = vb2_get_drv_priv(q); - unsigned int size; + struct mgb4_regs *video = &voutdev->mgbdev->video; + u32 config = mgb4_read_reg(video, voutdev->config->regs.config); + u32 pixelsize = (config & (1U << 16)) ? 2 : 4; + unsigned int size = (voutdev->width + voutdev->padding) * voutdev->height + * pixelsize; /* * If I/O reconfiguration is in process, do not allow to start @@ -69,8 +73,6 @@ static int queue_setup(struct vb2_queue *q, unsigned int *nbuffers, if (test_bit(0, &voutdev->mgbdev->io_reconfig)) return -EBUSY; - size = (voutdev->width + voutdev->padding) * voutdev->height * 4; - if (*nplanes) return sizes[0] < size ? -EINVAL : 0; *nplanes = 1; @@ -93,9 +95,11 @@ static int buffer_prepare(struct vb2_buffer *vb) { struct mgb4_vout_dev *voutdev = vb2_get_drv_priv(vb->vb2_queue); struct device *dev = &voutdev->mgbdev->pdev->dev; - unsigned int size; - - size = (voutdev->width + voutdev->padding) * voutdev->height * 4; + struct mgb4_regs *video = &voutdev->mgbdev->video; + u32 config = mgb4_read_reg(video, voutdev->config->regs.config); + u32 pixelsize = (config & (1U << 16)) ? 2 : 4; + unsigned int size = (voutdev->width + voutdev->padding) * voutdev->height + * pixelsize; if (vb2_plane_size(vb, 0) < size) { dev_err(dev, "buffer too small (%lu < %u)\n", @@ -194,24 +198,47 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - if (f->index != 0) - return -EINVAL; - - f->pixelformat = V4L2_PIX_FMT_ABGR32; + struct mgb4_vin_dev *voutdev = video_drvdata(file); + struct mgb4_regs *video = &voutdev->mgbdev->video; - return 0; + if (f->index == 0) { + f->pixelformat = V4L2_PIX_FMT_ABGR32; + return 0; + } else if (f->index == 1 && has_yuv(video)) { + f->pixelformat = V4L2_PIX_FMT_YUYV; + return 0; + } else { + return -EINVAL; + } } static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct mgb4_vout_dev *voutdev = video_drvdata(file); + struct mgb4_regs *video = &voutdev->mgbdev->video; + u32 config = mgb4_read_reg(video, voutdev->config->regs.config); - f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; f->fmt.pix.width = voutdev->width; f->fmt.pix.height = voutdev->height; f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; - f->fmt.pix.bytesperline = (f->fmt.pix.width + voutdev->padding) * 4; + + if (config & (1U << 16)) { + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + if (config & (1U << 20)) { + f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; + } else { + if (config & (1U << 19)) + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + else + f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + } + f->fmt.pix.bytesperline = (f->fmt.pix.width + voutdev->padding) * 2; + } else { + f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; + f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; + f->fmt.pix.bytesperline = (f->fmt.pix.width + voutdev->padding) * 4; + } + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; return 0; @@ -220,14 +247,30 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct mgb4_vout_dev *voutdev = video_drvdata(file); + struct mgb4_regs *video = &voutdev->mgbdev->video; + u32 pixelsize; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; f->fmt.pix.width = voutdev->width; f->fmt.pix.height = voutdev->height; f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; - f->fmt.pix.bytesperline = max(f->fmt.pix.width * 4, - ALIGN_DOWN(f->fmt.pix.bytesperline, 4)); + + if (has_yuv(video) && f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) { + pixelsize = 2; + if (!(f->fmt.pix.colorspace == V4L2_COLORSPACE_REC709 || + f->fmt.pix.colorspace == V4L2_COLORSPACE_SMPTE170M)) + f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + } else { + pixelsize = 4; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_ABGR32; + f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; + } + + if (f->fmt.pix.bytesperline > f->fmt.pix.width * pixelsize && + f->fmt.pix.bytesperline < f->fmt.pix.width * pixelsize * 2) + f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline, + pixelsize); + else + f->fmt.pix.bytesperline = f->fmt.pix.width * pixelsize; f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; return 0; @@ -237,13 +280,39 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) { struct mgb4_vout_dev *voutdev = video_drvdata(file); struct mgb4_regs *video = &voutdev->mgbdev->video; + u32 config, pixelsize; + int ret; if (vb2_is_busy(&voutdev->queue)) return -EBUSY; - vidioc_try_fmt(file, priv, f); + ret = vidioc_try_fmt(file, priv, f); + if (ret < 0) + return ret; + + config = mgb4_read_reg(video, voutdev->config->regs.config); + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) { + pixelsize = 2; + config |= 1U << 16; + + if (f->fmt.pix.colorspace == V4L2_COLORSPACE_REC709) { + config |= 1U << 20; + config |= 1U << 19; + } else if (f->fmt.pix.colorspace == V4L2_COLORSPACE_SMPTE170M) { + config &= ~(1U << 20); + config |= 1U << 19; + } else { + config &= ~(1U << 20); + config &= ~(1U << 19); + } + } else { + pixelsize = 4; + config &= ~(1U << 16); + } + mgb4_write_reg(video, voutdev->config->regs.config, config); - voutdev->padding = (f->fmt.pix.bytesperline - (f->fmt.pix.width * 4)) / 4; + voutdev->padding = (f->fmt.pix.bytesperline - (f->fmt.pix.width + * pixelsize)) / pixelsize; mgb4_write_reg(video, voutdev->config->regs.padding, voutdev->padding); return 0; -- cgit v1.2.3-70-g09d2 From 1724dcc9dd3cc1b58b9954ccafea46921546b4f0 Mon Sep 17 00:00:00 2001 From: Martin Tůma Date: Mon, 5 Aug 2024 17:40:52 +0200 Subject: media: mgb4: Add support for V4L2_CAP_TIMEPERFRAME MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent mgb4 firmwares have support for setting a variable framerate independent of the signal framerate. Add/fix (the mgb4 driver already did promote V4L2_CAP_TIMEPERFRAME, but it didn't work) support for V4L2_CAP_TIMEPERFRAME to the driver to enable this feature. Additionally add support for the DV timings API (VIDIOC_G_DV_TIMINGS, VIDIOC_ENUM_DV_TIMINGS, ...) for the outputs that was missing. The timings info is required/used for implementing the V4L2_CAP_TIMEPERFRAME functionality. Signed-off-by: Martin Tůma Signed-off-by: Hans Verkuil --- drivers/media/pci/mgb4/mgb4_core.c | 2 +- drivers/media/pci/mgb4/mgb4_core.h | 2 + drivers/media/pci/mgb4/mgb4_io.h | 24 +++- drivers/media/pci/mgb4/mgb4_sysfs_out.c | 4 +- drivers/media/pci/mgb4/mgb4_vin.c | 86 +++++++++++---- drivers/media/pci/mgb4/mgb4_vin.h | 3 +- drivers/media/pci/mgb4/mgb4_vout.c | 190 +++++++++++++++++++++++++++++++- drivers/media/pci/mgb4/mgb4_vout.h | 3 +- 8 files changed, 277 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/mgb4/mgb4_core.c b/drivers/media/pci/mgb4/mgb4_core.c index ab4f07e2e560..2819bbdab484 100644 --- a/drivers/media/pci/mgb4/mgb4_core.c +++ b/drivers/media/pci/mgb4/mgb4_core.c @@ -302,7 +302,7 @@ static int init_i2c(struct mgb4_dev *mgbdev) /* create dummy clock required by the xiic-i2c adapter */ snprintf(clk_name, sizeof(clk_name), "xiic-i2c.%d", id); mgbdev->i2c_clk = clk_hw_register_fixed_rate(NULL, clk_name, NULL, - 0, 125000000); + 0, MGB4_HW_FREQ); if (IS_ERR(mgbdev->i2c_clk)) { dev_err(dev, "failed to register I2C clock\n"); return PTR_ERR(mgbdev->i2c_clk); diff --git a/drivers/media/pci/mgb4/mgb4_core.h b/drivers/media/pci/mgb4/mgb4_core.h index 2a946e46aec1..b52cd67270b5 100644 --- a/drivers/media/pci/mgb4/mgb4_core.h +++ b/drivers/media/pci/mgb4/mgb4_core.h @@ -13,6 +13,8 @@ #include #include "mgb4_regs.h" +#define MGB4_HW_FREQ 125000000 + #define MGB4_VIN_DEVICES 2 #define MGB4_VOUT_DEVICES 2 diff --git a/drivers/media/pci/mgb4/mgb4_io.h b/drivers/media/pci/mgb4/mgb4_io.h index 204613a6685c..dd8696d7df31 100644 --- a/drivers/media/pci/mgb4/mgb4_io.h +++ b/drivers/media/pci/mgb4/mgb4_io.h @@ -7,11 +7,9 @@ #ifndef __MGB4_IO_H__ #define __MGB4_IO_H__ +#include #include - -#define MGB4_DEFAULT_WIDTH 1280 -#define MGB4_DEFAULT_HEIGHT 640 -#define MGB4_DEFAULT_PERIOD (125000000 / 60) +#include "mgb4_core.h" /* Register access error indication */ #define MGB4_ERR_NO_REG 0xFFFFFFFE @@ -20,6 +18,9 @@ #define MGB4_ERR_QUEUE_EMPTY 0xFFFFFFFC #define MGB4_ERR_QUEUE_FULL 0xFFFFFFFB +#define MGB4_PERIOD(numerator, denominator) \ + ((u32)div_u64((MGB4_HW_FREQ * (u64)(numerator)), (denominator))) + struct mgb4_frame_buffer { struct vb2_v4l2_buffer vb; struct list_head list; @@ -30,11 +31,24 @@ static inline struct mgb4_frame_buffer *to_frame_buffer(struct vb2_v4l2_buffer * return container_of(vbuf, struct mgb4_frame_buffer, vb); } -static inline bool has_yuv(struct mgb4_regs *video) +static inline bool has_yuv_and_timeperframe(struct mgb4_regs *video) { u32 status = mgb4_read_reg(video, 0xD0); return (status & (1U << 8)); } +#define has_yuv(video) has_yuv_and_timeperframe(video) +#define has_timeperframe(video) has_yuv_and_timeperframe(video) + +static inline u32 pixel_size(struct v4l2_dv_timings *timings) +{ + struct v4l2_bt_timings *bt = &timings->bt; + + u32 height = bt->height + bt->vfrontporch + bt->vsync + bt->vbackporch; + u32 width = bt->width + bt->hfrontporch + bt->hsync + bt->hbackporch; + + return width * height; +} + #endif diff --git a/drivers/media/pci/mgb4/mgb4_sysfs_out.c b/drivers/media/pci/mgb4/mgb4_sysfs_out.c index 9f6e81c57726..f67ff2a48329 100644 --- a/drivers/media/pci/mgb4/mgb4_sysfs_out.c +++ b/drivers/media/pci/mgb4/mgb4_sysfs_out.c @@ -231,7 +231,7 @@ static ssize_t frame_rate_show(struct device *dev, u32 period = mgb4_read_reg(&voutdev->mgbdev->video, voutdev->config->regs.frame_period); - return sprintf(buf, "%u\n", 125000000 / period); + return sprintf(buf, "%u\n", MGB4_HW_FREQ / period); } /* @@ -252,7 +252,7 @@ static ssize_t frame_rate_store(struct device *dev, return ret; mgb4_write_reg(&voutdev->mgbdev->video, - voutdev->config->regs.frame_period, 125000000 / val); + voutdev->config->regs.frame_period, MGB4_HW_FREQ / val); return count; } diff --git a/drivers/media/pci/mgb4/mgb4_vin.c b/drivers/media/pci/mgb4/mgb4_vin.c index 7fb14e867e8d..e9332abb3172 100644 --- a/drivers/media/pci/mgb4/mgb4_vin.c +++ b/drivers/media/pci/mgb4/mgb4_vin.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -34,8 +35,8 @@ ATTRIBUTE_GROUPS(mgb4_fpdl3_in); ATTRIBUTE_GROUPS(mgb4_gmsl_in); static const struct mgb4_vin_config vin_cfg[] = { - {0, 0, 0, 6, {0x10, 0x00, 0x04, 0x08, 0x1C, 0x14, 0x18, 0x20, 0x24, 0x28}}, - {1, 1, 1, 7, {0x40, 0x30, 0x34, 0x38, 0x4C, 0x44, 0x48, 0x50, 0x54, 0x58}} + {0, 0, 0, 6, {0x10, 0x00, 0x04, 0x08, 0x1C, 0x14, 0x18, 0x20, 0x24, 0x28, 0xE8}}, + {1, 1, 1, 7, {0x40, 0x30, 0x34, 0x38, 0x4C, 0x44, 0x48, 0x50, 0x54, 0x58, 0xEC}} }; static const struct i2c_board_info fpdl3_deser_info[] = { @@ -76,6 +77,9 @@ static const struct v4l2_dv_timings_cap video_timings_cap = { }, }; +/* Dummy timings when no signal present */ +static const struct v4l2_dv_timings cea1080p60 = V4L2_DV_BT_CEA_1920X1080P60; + /* * Returns the video output connected with the given video input if the input * is in loopback mode. @@ -318,7 +322,8 @@ static int fh_open(struct file *file) if (!v4l2_fh_is_singular_file(file)) goto out; - get_timings(vindev, &vindev->timings); + if (get_timings(vindev, &vindev->timings) < 0) + vindev->timings = cea1080p60; set_loopback_padding(vindev, vindev->padding); out: @@ -394,12 +399,13 @@ static int vidioc_enum_frameintervals(struct file *file, void *priv, ival->height != vindev->timings.bt.height) return -EINVAL; - ival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; - ival->stepwise.min.denominator = 60; - ival->stepwise.min.numerator = 1; - ival->stepwise.max.denominator = 1; - ival->stepwise.max.numerator = 1; - ival->stepwise.step = ival->stepwise.max; + ival->type = V4L2_FRMIVAL_TYPE_STEPWISE; + ival->stepwise.max.denominator = MGB4_HW_FREQ; + ival->stepwise.max.numerator = 0xFFFFFFFF; + ival->stepwise.min.denominator = vindev->timings.bt.pixelclock; + ival->stepwise.min.numerator = pixel_size(&vindev->timings); + ival->stepwise.step.denominator = MGB4_HW_FREQ; + ival->stepwise.step.numerator = 1; return 0; } @@ -558,24 +564,56 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) return 0; } -static int vidioc_parm(struct file *file, void *priv, - struct v4l2_streamparm *parm) +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) { struct mgb4_vin_dev *vindev = video_drvdata(file); struct mgb4_regs *video = &vindev->mgbdev->video; - const struct mgb4_vin_regs *regs = &vindev->config->regs; - struct v4l2_fract timeperframe = { - .numerator = mgb4_read_reg(video, regs->frame_period), - .denominator = 125000000, - }; + struct v4l2_fract *tpf = &parm->parm.output.timeperframe; + u32 timer; parm->parm.capture.readbuffers = 2; - parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - parm->parm.capture.timeperframe = timeperframe; + + if (has_timeperframe(video)) { + timer = mgb4_read_reg(video, vindev->config->regs.timer); + if (timer < 0xFFFF) { + tpf->numerator = pixel_size(&vindev->timings); + tpf->denominator = vindev->timings.bt.pixelclock; + } else { + tpf->numerator = timer; + tpf->denominator = MGB4_HW_FREQ; + } + + parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + } return 0; } +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct mgb4_vin_dev *vindev = video_drvdata(file); + struct mgb4_regs *video = &vindev->mgbdev->video; + struct v4l2_fract *tpf = &parm->parm.output.timeperframe; + u32 period, timer; + + if (has_timeperframe(video)) { + timer = tpf->denominator ? + MGB4_PERIOD(tpf->numerator, tpf->denominator) : 0; + if (timer) { + period = MGB4_PERIOD(pixel_size(&vindev->timings), + vindev->timings.bt.pixelclock); + if (timer < period) + timer = 0; + } + + mgb4_write_reg(video, vindev->config->regs.timer, timer); + } + + return vidioc_g_parm(file, priv, parm); +} + static int vidioc_s_dv_timings(struct file *file, void *fh, struct v4l2_dv_timings *timings) { @@ -659,8 +697,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_g_parm = vidioc_parm, - .vidioc_s_parm = vidioc_parm, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, .vidioc_dv_timings_cap = vidioc_dv_timings_cap, .vidioc_enum_dv_timings = vidioc_enum_dv_timings, .vidioc_g_dv_timings = vidioc_g_dv_timings, @@ -843,10 +881,16 @@ static void debugfs_init(struct mgb4_vin_dev *vindev) vindev->regs[7].offset = vindev->config->regs.signal2; vindev->regs[8].name = "PADDING_PIXELS"; vindev->regs[8].offset = vindev->config->regs.padding; + if (has_timeperframe(video)) { + vindev->regs[9].name = "TIMER"; + vindev->regs[9].offset = vindev->config->regs.timer; + vindev->regset.nregs = 10; + } else { + vindev->regset.nregs = 9; + } vindev->regset.base = video->membase; vindev->regset.regs = vindev->regs; - vindev->regset.nregs = ARRAY_SIZE(vindev->regs); debugfs_create_regset32("registers", 0444, vindev->debugfs, &vindev->regset); diff --git a/drivers/media/pci/mgb4/mgb4_vin.h b/drivers/media/pci/mgb4/mgb4_vin.h index 0249b400ad4d..9693bd0ce180 100644 --- a/drivers/media/pci/mgb4/mgb4_vin.h +++ b/drivers/media/pci/mgb4/mgb4_vin.h @@ -25,6 +25,7 @@ struct mgb4_vin_regs { u32 signal; u32 signal2; u32 padding; + u32 timer; }; struct mgb4_vin_config { @@ -59,7 +60,7 @@ struct mgb4_vin_dev { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; struct debugfs_regset32 regset; - struct debugfs_reg32 regs[9]; + struct debugfs_reg32 regs[sizeof(struct mgb4_vin_regs) / 4]; #endif }; diff --git a/drivers/media/pci/mgb4/mgb4_vout.c b/drivers/media/pci/mgb4/mgb4_vout.c index 2ee9606d9072..d98b6e87d71f 100644 --- a/drivers/media/pci/mgb4/mgb4_vout.c +++ b/drivers/media/pci/mgb4/mgb4_vout.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "mgb4_core.h" #include "mgb4_dma.h" #include "mgb4_sysfs.h" @@ -23,12 +24,16 @@ #include "mgb4_cmt.h" #include "mgb4_vout.h" +#define DEFAULT_WIDTH 1280 +#define DEFAULT_HEIGHT 640 +#define DEFAULT_PERIOD (MGB4_HW_FREQ / 60) + ATTRIBUTE_GROUPS(mgb4_fpdl3_out); ATTRIBUTE_GROUPS(mgb4_gmsl_out); static const struct mgb4_vout_config vout_cfg[] = { - {0, 0, 8, {0x78, 0x60, 0x64, 0x68, 0x74, 0x6C, 0x70, 0x7c}}, - {1, 1, 9, {0x98, 0x80, 0x84, 0x88, 0x94, 0x8c, 0x90, 0x9c}} + {0, 0, 8, {0x78, 0x60, 0x64, 0x68, 0x74, 0x6C, 0x70, 0x7C, 0xE0}}, + {1, 1, 9, {0x98, 0x80, 0x84, 0x88, 0x94, 0x8C, 0x90, 0x9C, 0xE4}} }; static const struct i2c_board_info fpdl3_ser_info[] = { @@ -40,6 +45,49 @@ static const struct mgb4_i2c_kv fpdl3_i2c[] = { {0x05, 0xFF, 0x04}, {0x06, 0xFF, 0x01}, {0xC2, 0xFF, 0x80} }; +static const struct v4l2_dv_timings_cap video_timings_cap = { + .type = V4L2_DV_BT_656_1120, + .bt = { + .min_width = 320, + .max_width = 4096, + .min_height = 240, + .max_height = 2160, + .min_pixelclock = 1843200, /* 320 x 240 x 24Hz */ + .max_pixelclock = 530841600, /* 4096 x 2160 x 60Hz */ + .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF, + .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_CUSTOM, + }, +}; + +static void get_timings(struct mgb4_vout_dev *voutdev, + struct v4l2_dv_timings *timings) +{ + struct mgb4_regs *video = &voutdev->mgbdev->video; + const struct mgb4_vout_regs *regs = &voutdev->config->regs; + + u32 hsync = mgb4_read_reg(video, regs->hsync); + u32 vsync = mgb4_read_reg(video, regs->vsync); + u32 resolution = mgb4_read_reg(video, regs->resolution); + + memset(timings, 0, sizeof(*timings)); + timings->type = V4L2_DV_BT_656_1120; + timings->bt.width = resolution >> 16; + timings->bt.height = resolution & 0xFFFF; + if (hsync & (1U << 31)) + timings->bt.polarities |= V4L2_DV_HSYNC_POS_POL; + if (vsync & (1U << 31)) + timings->bt.polarities |= V4L2_DV_VSYNC_POS_POL; + timings->bt.pixelclock = voutdev->freq * 1000; + timings->bt.hsync = (hsync & 0x00FF0000) >> 16; + timings->bt.vsync = (vsync & 0x00FF0000) >> 16; + timings->bt.hbackporch = (hsync & 0x0000FF00) >> 8; + timings->bt.hfrontporch = hsync & 0x000000FF; + timings->bt.vbackporch = (vsync & 0x0000FF00) >> 8; + timings->bt.vfrontporch = vsync & 0x000000FF; +} + static void return_all_buffers(struct mgb4_vout_dev *voutdev, enum vb2_buffer_state state) { @@ -336,11 +384,128 @@ static int vidioc_enum_output(struct file *file, void *priv, return -EINVAL; out->type = V4L2_OUTPUT_TYPE_ANALOG; + out->capabilities = V4L2_OUT_CAP_DV_TIMINGS; strscpy(out->name, "MGB4", sizeof(out->name)); return 0; } +static int vidioc_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *ival) +{ + struct mgb4_vout_dev *voutdev = video_drvdata(file); + struct mgb4_regs *video = &voutdev->mgbdev->video; + struct v4l2_dv_timings timings; + + if (ival->index != 0) + return -EINVAL; + if (!(ival->pixel_format == V4L2_PIX_FMT_ABGR32 || + ((has_yuv(video) && ival->pixel_format == V4L2_PIX_FMT_YUYV)))) + return -EINVAL; + if (ival->width != voutdev->width || ival->height != voutdev->height) + return -EINVAL; + + get_timings(voutdev, &timings); + + ival->type = V4L2_FRMIVAL_TYPE_STEPWISE; + ival->stepwise.max.denominator = MGB4_HW_FREQ; + ival->stepwise.max.numerator = 0xFFFFFFFF; + ival->stepwise.min.denominator = timings.bt.pixelclock; + ival->stepwise.min.numerator = pixel_size(&timings); + ival->stepwise.step.denominator = MGB4_HW_FREQ; + ival->stepwise.step.numerator = 1; + + return 0; +} + +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct mgb4_vout_dev *voutdev = video_drvdata(file); + struct mgb4_regs *video = &voutdev->mgbdev->video; + struct v4l2_fract *tpf = &parm->parm.output.timeperframe; + struct v4l2_dv_timings timings; + u32 timer; + + parm->parm.output.writebuffers = 2; + + if (has_timeperframe(video)) { + timer = mgb4_read_reg(video, voutdev->config->regs.timer); + if (timer < 0xFFFF) { + get_timings(voutdev, &timings); + tpf->numerator = pixel_size(&timings); + tpf->denominator = timings.bt.pixelclock; + } else { + tpf->numerator = timer; + tpf->denominator = MGB4_HW_FREQ; + } + + parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + } + + return 0; +} + +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct mgb4_vout_dev *voutdev = video_drvdata(file); + struct mgb4_regs *video = &voutdev->mgbdev->video; + struct v4l2_fract *tpf = &parm->parm.output.timeperframe; + struct v4l2_dv_timings timings; + u32 timer, period; + + if (has_timeperframe(video)) { + timer = tpf->denominator ? + MGB4_PERIOD(tpf->numerator, tpf->denominator) : 0; + if (timer) { + get_timings(voutdev, &timings); + period = MGB4_PERIOD(pixel_size(&timings), + timings.bt.pixelclock); + if (timer < period) + timer = 0; + } + + mgb4_write_reg(video, voutdev->config->regs.timer, timer); + } + + return vidioc_g_parm(file, priv, parm); +} + +static int vidioc_g_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct mgb4_vout_dev *voutdev = video_drvdata(file); + + get_timings(voutdev, timings); + + return 0; +} + +static int vidioc_s_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct mgb4_vout_dev *voutdev = video_drvdata(file); + + get_timings(voutdev, timings); + + return 0; +} + +static int vidioc_enum_dv_timings(struct file *file, void *fh, + struct v4l2_enum_dv_timings *timings) +{ + return v4l2_enum_dv_timings_cap(timings, &video_timings_cap, NULL, NULL); +} + +static int vidioc_dv_timings_cap(struct file *file, void *fh, + struct v4l2_dv_timings_cap *cap) +{ + *cap = video_timings_cap; + + return 0; +} + static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_out = vidioc_enum_fmt, @@ -348,8 +513,15 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_fmt_vid_out = vidioc_s_fmt, .vidioc_g_fmt_vid_out = vidioc_g_fmt, .vidioc_enum_output = vidioc_enum_output, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, .vidioc_g_output = vidioc_g_output, .vidioc_s_output = vidioc_s_output, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, + .vidioc_dv_timings_cap = vidioc_dv_timings_cap, + .vidioc_enum_dv_timings = vidioc_enum_dv_timings, + .vidioc_g_dv_timings = vidioc_g_dv_timings, + .vidioc_s_dv_timings = vidioc_s_dv_timings, .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_create_bufs = vb2_ioctl_create_bufs, .vidioc_prepare_buf = vb2_ioctl_prepare_buf, @@ -492,10 +664,10 @@ static void fpga_init(struct mgb4_vout_dev *voutdev) mgb4_write_reg(video, regs->config, 0x00000011); mgb4_write_reg(video, regs->resolution, - (MGB4_DEFAULT_WIDTH << 16) | MGB4_DEFAULT_HEIGHT); + (DEFAULT_WIDTH << 16) | DEFAULT_HEIGHT); mgb4_write_reg(video, regs->hsync, 0x00102020); mgb4_write_reg(video, regs->vsync, 0x40020202); - mgb4_write_reg(video, regs->frame_period, MGB4_DEFAULT_PERIOD); + mgb4_write_reg(video, regs->frame_period, DEFAULT_PERIOD); mgb4_write_reg(video, regs->padding, 0x00000000); voutdev->freq = mgb4_cmt_set_vout_freq(voutdev, 70000 >> 1) << 1; @@ -526,12 +698,18 @@ static void debugfs_init(struct mgb4_vout_dev *voutdev) voutdev->regs[4].offset = voutdev->config->regs.vsync; voutdev->regs[5].name = "FRAME_PERIOD"; voutdev->regs[5].offset = voutdev->config->regs.frame_period; - voutdev->regs[6].name = "PADDING"; + voutdev->regs[6].name = "PADDING_PIXELS"; voutdev->regs[6].offset = voutdev->config->regs.padding; + if (has_timeperframe(video)) { + voutdev->regs[7].name = "TIMER"; + voutdev->regs[7].offset = voutdev->config->regs.timer; + voutdev->regset.nregs = 8; + } else { + voutdev->regset.nregs = 7; + } voutdev->regset.base = video->membase; voutdev->regset.regs = voutdev->regs; - voutdev->regset.nregs = ARRAY_SIZE(voutdev->regs); debugfs_create_regset32("registers", 0444, voutdev->debugfs, &voutdev->regset); diff --git a/drivers/media/pci/mgb4/mgb4_vout.h b/drivers/media/pci/mgb4/mgb4_vout.h index b163dee711fd..ab9b58b1deb7 100644 --- a/drivers/media/pci/mgb4/mgb4_vout.h +++ b/drivers/media/pci/mgb4/mgb4_vout.h @@ -23,6 +23,7 @@ struct mgb4_vout_regs { u32 hsync; u32 vsync; u32 padding; + u32 timer; }; struct mgb4_vout_config { @@ -55,7 +56,7 @@ struct mgb4_vout_dev { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; struct debugfs_regset32 regset; - struct debugfs_reg32 regs[7]; + struct debugfs_reg32 regs[sizeof(struct mgb4_vout_regs) / 4]; #endif }; -- cgit v1.2.3-70-g09d2 From e3582018337f4fdd52f873235d4b96fc20b37446 Mon Sep 17 00:00:00 2001 From: Martin Tůma Date: Mon, 5 Aug 2024 17:40:53 +0200 Subject: media: mgb4: Fixed signal frame rate limit handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the default DV timings for the outputs to produce a better signal less "crippled" by the frame rate limiting. While the individual values are now different, the resulting signal still matches the same default display as before. Additionally fix the corner case when the frame rate limit is set to zero causing a "divide by zero" kernel panic. Signed-off-by: Martin Tůma Signed-off-by: Hans Verkuil --- drivers/media/pci/mgb4/mgb4_sysfs_out.c | 9 +++++---- drivers/media/pci/mgb4/mgb4_vout.c | 12 ++++++------ drivers/media/pci/mgb4/mgb4_vout.h | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/mgb4/mgb4_sysfs_out.c b/drivers/media/pci/mgb4/mgb4_sysfs_out.c index f67ff2a48329..573aa61c69d4 100644 --- a/drivers/media/pci/mgb4/mgb4_sysfs_out.c +++ b/drivers/media/pci/mgb4/mgb4_sysfs_out.c @@ -229,9 +229,9 @@ static ssize_t frame_rate_show(struct device *dev, struct video_device *vdev = to_video_device(dev); struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); u32 period = mgb4_read_reg(&voutdev->mgbdev->video, - voutdev->config->regs.frame_period); + voutdev->config->regs.frame_limit); - return sprintf(buf, "%u\n", MGB4_HW_FREQ / period); + return sprintf(buf, "%u\n", period ? MGB4_HW_FREQ / period : 0); } /* @@ -245,14 +245,15 @@ static ssize_t frame_rate_store(struct device *dev, struct video_device *vdev = to_video_device(dev); struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev); unsigned long val; - int ret; + int limit, ret; ret = kstrtoul(buf, 10, &val); if (ret) return ret; + limit = val ? MGB4_HW_FREQ / val : 0; mgb4_write_reg(&voutdev->mgbdev->video, - voutdev->config->regs.frame_period, MGB4_HW_FREQ / val); + voutdev->config->regs.frame_limit, limit); return count; } diff --git a/drivers/media/pci/mgb4/mgb4_vout.c b/drivers/media/pci/mgb4/mgb4_vout.c index d98b6e87d71f..998edcbd9723 100644 --- a/drivers/media/pci/mgb4/mgb4_vout.c +++ b/drivers/media/pci/mgb4/mgb4_vout.c @@ -665,12 +665,12 @@ static void fpga_init(struct mgb4_vout_dev *voutdev) mgb4_write_reg(video, regs->config, 0x00000011); mgb4_write_reg(video, regs->resolution, (DEFAULT_WIDTH << 16) | DEFAULT_HEIGHT); - mgb4_write_reg(video, regs->hsync, 0x00102020); - mgb4_write_reg(video, regs->vsync, 0x40020202); - mgb4_write_reg(video, regs->frame_period, DEFAULT_PERIOD); + mgb4_write_reg(video, regs->hsync, 0x00283232); + mgb4_write_reg(video, regs->vsync, 0x40141F1E); + mgb4_write_reg(video, regs->frame_limit, DEFAULT_PERIOD); mgb4_write_reg(video, regs->padding, 0x00000000); - voutdev->freq = mgb4_cmt_set_vout_freq(voutdev, 70000 >> 1) << 1; + voutdev->freq = mgb4_cmt_set_vout_freq(voutdev, 61150 >> 1) << 1; mgb4_write_reg(video, regs->config, (voutdev->config->id + MGB4_VIN_DEVICES) << 2 | 1 << 4); @@ -696,8 +696,8 @@ static void debugfs_init(struct mgb4_vout_dev *voutdev) voutdev->regs[3].offset = voutdev->config->regs.hsync; voutdev->regs[4].name = "VIDEO_PARAMS_2"; voutdev->regs[4].offset = voutdev->config->regs.vsync; - voutdev->regs[5].name = "FRAME_PERIOD"; - voutdev->regs[5].offset = voutdev->config->regs.frame_period; + voutdev->regs[5].name = "FRAME_LIMIT"; + voutdev->regs[5].offset = voutdev->config->regs.frame_limit; voutdev->regs[6].name = "PADDING_PIXELS"; voutdev->regs[6].offset = voutdev->config->regs.padding; if (has_timeperframe(video)) { diff --git a/drivers/media/pci/mgb4/mgb4_vout.h b/drivers/media/pci/mgb4/mgb4_vout.h index ab9b58b1deb7..adc8fe1e7ae6 100644 --- a/drivers/media/pci/mgb4/mgb4_vout.h +++ b/drivers/media/pci/mgb4/mgb4_vout.h @@ -19,7 +19,7 @@ struct mgb4_vout_regs { u32 config; u32 status; u32 resolution; - u32 frame_period; + u32 frame_limit; u32 hsync; u32 vsync; u32 padding; -- cgit v1.2.3-70-g09d2 From 6a9c97ab6b7e85697e0b74e86062192a5ffffd99 Mon Sep 17 00:00:00 2001 From: Yunke Cao Date: Wed, 14 Aug 2024 11:06:40 +0900 Subject: media: videobuf2-core: clear memory related fields in __vb2_plane_dmabuf_put() Clear vb2_plane's memory related fields in __vb2_plane_dmabuf_put(), including bytesused, length, fd and data_offset. Remove the duplicated code in __prepare_dmabuf(). Signed-off-by: Yunke Cao Acked-by: Tomasz Figa Signed-off-by: Hans Verkuil --- drivers/media/common/videobuf2/videobuf2-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 0217392fcc0d..4d232b08f950 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -311,6 +311,10 @@ static void __vb2_plane_dmabuf_put(struct vb2_buffer *vb, struct vb2_plane *p) p->mem_priv = NULL; p->dbuf = NULL; p->dbuf_mapped = 0; + p->bytesused = 0; + p->length = 0; + p->m.fd = 0; + p->data_offset = 0; } /* @@ -1420,10 +1424,6 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) /* Release previously acquired memory if present */ __vb2_plane_dmabuf_put(vb, &vb->planes[plane]); - vb->planes[plane].bytesused = 0; - vb->planes[plane].length = 0; - vb->planes[plane].m.fd = 0; - vb->planes[plane].data_offset = 0; /* Acquire each plane's memory */ mem_priv = call_ptr_memop(attach_dmabuf, -- cgit v1.2.3-70-g09d2 From 95af7c00f35b9cb0873afb8f191f42e04714ed2a Mon Sep 17 00:00:00 2001 From: Yunke Cao Date: Wed, 14 Aug 2024 11:06:41 +0900 Subject: media: videobuf2-core: release all planes first in __prepare_dmabuf() In the existing implementation, validating planes, checking if the planes changed, releasing previous planes and reaquiring new planes all happens in the same for loop. Split the for loop into 3 parts 1. In the first for loop, validate planes and check if planes changed. 2. Call __vb2_buf_dmabuf_put() to release all planes. 3. In the second for loop, reaquire new planes. Signed-off-by: Yunke Cao Acked-by: Tomasz Figa Signed-off-by: Hans Verkuil --- drivers/media/common/videobuf2/videobuf2-core.c | 115 ++++++++++++------------ 1 file changed, 59 insertions(+), 56 deletions(-) (limited to 'drivers') diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 4d232b08f950..b53d94659e30 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -1387,11 +1387,13 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) for (plane = 0; plane < vb->num_planes; ++plane) { struct dma_buf *dbuf = dma_buf_get(planes[plane].m.fd); + planes[plane].dbuf = dbuf; + if (IS_ERR_OR_NULL(dbuf)) { dprintk(q, 1, "invalid dmabuf fd for plane %d\n", plane); ret = -EINVAL; - goto err; + goto err_put_planes; } /* use DMABUF size if length is not provided */ @@ -1402,76 +1404,68 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) dprintk(q, 1, "invalid dmabuf length %u for plane %d, minimum length %u\n", planes[plane].length, plane, vb->planes[plane].min_length); - dma_buf_put(dbuf); ret = -EINVAL; - goto err; + goto err_put_planes; } /* Skip the plane if already verified */ if (dbuf == vb->planes[plane].dbuf && - vb->planes[plane].length == planes[plane].length) { - dma_buf_put(dbuf); + vb->planes[plane].length == planes[plane].length) continue; - } dprintk(q, 3, "buffer for plane %d changed\n", plane); - if (!reacquired) { - reacquired = true; + reacquired = true; + } + + if (reacquired) { + if (vb->planes[0].mem_priv) { vb->copied_timestamp = 0; call_void_vb_qop(vb, buf_cleanup, vb); + __vb2_buf_dmabuf_put(vb); } - /* Release previously acquired memory if present */ - __vb2_plane_dmabuf_put(vb, &vb->planes[plane]); - - /* Acquire each plane's memory */ - mem_priv = call_ptr_memop(attach_dmabuf, - vb, - q->alloc_devs[plane] ? : q->dev, - dbuf, - planes[plane].length); - if (IS_ERR(mem_priv)) { - dprintk(q, 1, "failed to attach dmabuf\n"); - ret = PTR_ERR(mem_priv); - dma_buf_put(dbuf); - goto err; - } - - vb->planes[plane].dbuf = dbuf; - vb->planes[plane].mem_priv = mem_priv; - } + for (plane = 0; plane < vb->num_planes; ++plane) { + /* Acquire each plane's memory */ + mem_priv = call_ptr_memop(attach_dmabuf, + vb, + q->alloc_devs[plane] ? : q->dev, + planes[plane].dbuf, + planes[plane].length); + if (IS_ERR(mem_priv)) { + dprintk(q, 1, "failed to attach dmabuf\n"); + ret = PTR_ERR(mem_priv); + goto err_put_vb2_buf; + } - /* - * This pins the buffer(s) with dma_buf_map_attachment()). It's done - * here instead just before the DMA, while queueing the buffer(s) so - * userspace knows sooner rather than later if the dma-buf map fails. - */ - for (plane = 0; plane < vb->num_planes; ++plane) { - if (vb->planes[plane].dbuf_mapped) - continue; + vb->planes[plane].dbuf = planes[plane].dbuf; + vb->planes[plane].mem_priv = mem_priv; - ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv); - if (ret) { - dprintk(q, 1, "failed to map dmabuf for plane %d\n", - plane); - goto err; + /* + * This pins the buffer(s) with dma_buf_map_attachment()). It's done + * here instead just before the DMA, while queueing the buffer(s) so + * userspace knows sooner rather than later if the dma-buf map fails. + */ + ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv); + if (ret) { + dprintk(q, 1, "failed to map dmabuf for plane %d\n", + plane); + goto err_put_vb2_buf; + } + vb->planes[plane].dbuf_mapped = 1; } - vb->planes[plane].dbuf_mapped = 1; - } - /* - * Now that everything is in order, copy relevant information - * provided by userspace. - */ - for (plane = 0; plane < vb->num_planes; ++plane) { - vb->planes[plane].bytesused = planes[plane].bytesused; - vb->planes[plane].length = planes[plane].length; - vb->planes[plane].m.fd = planes[plane].m.fd; - vb->planes[plane].data_offset = planes[plane].data_offset; - } + /* + * Now that everything is in order, copy relevant information + * provided by userspace. + */ + for (plane = 0; plane < vb->num_planes; ++plane) { + vb->planes[plane].bytesused = planes[plane].bytesused; + vb->planes[plane].length = planes[plane].length; + vb->planes[plane].m.fd = planes[plane].m.fd; + vb->planes[plane].data_offset = planes[plane].data_offset; + } - if (reacquired) { /* * Call driver-specific initialization on the newly acquired buffer, * if provided. @@ -1479,19 +1473,28 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) ret = call_vb_qop(vb, buf_init, vb); if (ret) { dprintk(q, 1, "buffer initialization failed\n"); - goto err; + goto err_put_vb2_buf; } + } else { + for (plane = 0; plane < vb->num_planes; ++plane) + dma_buf_put(planes[plane].dbuf); } ret = call_vb_qop(vb, buf_prepare, vb); if (ret) { dprintk(q, 1, "buffer preparation failed\n"); call_void_vb_qop(vb, buf_cleanup, vb); - goto err; + goto err_put_vb2_buf; } return 0; -err: + +err_put_planes: + for (plane = 0; plane < vb->num_planes; ++plane) { + if (!IS_ERR_OR_NULL(planes[plane].dbuf)) + dma_buf_put(planes[plane].dbuf); + } +err_put_vb2_buf: /* In case of errors, release planes that were already acquired */ __vb2_buf_dmabuf_put(vb); -- cgit v1.2.3-70-g09d2 From 1da4e16130d36c712e00bf045f09f194aac964b8 Mon Sep 17 00:00:00 2001 From: Yunke Cao Date: Wed, 14 Aug 2024 11:06:42 +0900 Subject: media: videobuf2-core: reverse the iteration order in __vb2_buf_dmabuf_put This patch prepares for allowing multiple planes to share the same DMA buffer attachment. Release the planes from num_planes - 1 to 0 so that we don't leave invalid mem_priv pointers behind. Signed-off-by: Yunke Cao Acked-by: Tomasz Figa Signed-off-by: Hans Verkuil --- drivers/media/common/videobuf2/videobuf2-core.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index b53d94659e30..e6af963307e3 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -323,9 +323,15 @@ static void __vb2_plane_dmabuf_put(struct vb2_buffer *vb, struct vb2_plane *p) */ static void __vb2_buf_dmabuf_put(struct vb2_buffer *vb) { - unsigned int plane; + int plane; - for (plane = 0; plane < vb->num_planes; ++plane) + /* + * When multiple planes share the same DMA buffer attachment, the plane + * with the lowest index owns the mem_priv. + * Put planes in the reversed order so that we don't leave invalid + * mem_priv behind. + */ + for (plane = vb->num_planes - 1; plane >= 0; --plane) __vb2_plane_dmabuf_put(vb, &vb->planes[plane]); } -- cgit v1.2.3-70-g09d2 From 03a979b74dc1ad5aeed8026a84d8771842cb1631 Mon Sep 17 00:00:00 2001 From: Yunke Cao Date: Wed, 14 Aug 2024 11:06:43 +0900 Subject: media: videobuf2-core: attach once if multiple planes share the same dbuf When multiple planes use the same dma buf, each plane will have its own dma buf attachment and mapping. It is a waste of IOVA space. This patch adds a dbuf_duplicated boolean in vb2_plane. If a plane's dbuf is the same as an existing plane, do not create another attachment and mapping. Signed-off-by: Yunke Cao Acked-by: Tomasz Figa Signed-off-by: Hans Verkuil --- drivers/media/common/videobuf2/videobuf2-core.c | 30 +++++++++++++++++++++---- include/media/videobuf2-core.h | 3 +++ 2 files changed, 29 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index e6af963307e3..500a4e0c84ab 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -303,10 +303,13 @@ static void __vb2_plane_dmabuf_put(struct vb2_buffer *vb, struct vb2_plane *p) if (!p->mem_priv) return; - if (p->dbuf_mapped) - call_void_memop(vb, unmap_dmabuf, p->mem_priv); + if (!p->dbuf_duplicated) { + if (p->dbuf_mapped) + call_void_memop(vb, unmap_dmabuf, p->mem_priv); + + call_void_memop(vb, detach_dmabuf, p->mem_priv); + } - call_void_memop(vb, detach_dmabuf, p->mem_priv); dma_buf_put(p->dbuf); p->mem_priv = NULL; p->dbuf = NULL; @@ -315,6 +318,7 @@ static void __vb2_plane_dmabuf_put(struct vb2_buffer *vb, struct vb2_plane *p) p->length = 0; p->m.fd = 0; p->data_offset = 0; + p->dbuf_duplicated = false; } /* @@ -1379,7 +1383,7 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) struct vb2_plane planes[VB2_MAX_PLANES]; struct vb2_queue *q = vb->vb2_queue; void *mem_priv; - unsigned int plane; + unsigned int plane, i; int ret = 0; bool reacquired = vb->planes[0].mem_priv == NULL; @@ -1432,6 +1436,24 @@ static int __prepare_dmabuf(struct vb2_buffer *vb) } for (plane = 0; plane < vb->num_planes; ++plane) { + /* + * This is an optimization to reduce dma_buf attachment/mapping. + * When the same dma_buf is used for multiple planes, there is no need + * to create duplicated attachments. + */ + for (i = 0; i < plane; ++i) { + if (planes[plane].dbuf == vb->planes[i].dbuf && + q->alloc_devs[plane] == q->alloc_devs[i]) { + vb->planes[plane].dbuf_duplicated = true; + vb->planes[plane].dbuf = vb->planes[i].dbuf; + vb->planes[plane].mem_priv = vb->planes[i].mem_priv; + break; + } + } + + if (vb->planes[plane].dbuf_duplicated) + continue; + /* Acquire each plane's memory */ mem_priv = call_ptr_memop(attach_dmabuf, vb, diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 955237ac503d..9b02aeba4108 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -154,6 +154,8 @@ struct vb2_mem_ops { * @mem_priv: private data with this plane. * @dbuf: dma_buf - shared buffer object. * @dbuf_mapped: flag to show whether dbuf is mapped or not + * @dbuf_duplicated: boolean to show whether dbuf is duplicated with a + * previous plane of the buffer. * @bytesused: number of bytes occupied by data in the plane (payload). * @length: size of this plane (NOT the payload) in bytes. The maximum * valid size is MAX_UINT - PAGE_SIZE. @@ -179,6 +181,7 @@ struct vb2_plane { void *mem_priv; struct dma_buf *dbuf; unsigned int dbuf_mapped; + bool dbuf_duplicated; unsigned int bytesused; unsigned int length; unsigned int min_length; -- cgit v1.2.3-70-g09d2 From 6c53a7b68c5d2a1dd4e8df52f01d75072ebc13bc Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 8 Aug 2024 22:40:59 +0200 Subject: media: rkisp1: Implement extensible params support Implement support in rkisp1-params for the extensible configuration parameters format. Create a list of handlers for each ISP block that wraps the existing configuration functions and handles the ISP block enablement. Parse the configuration parameters buffer in rkisp1_ext_params_config and filter the enable blocks by group, to allow setting the 'other' groups separately from the 'lsc' group to support the pre/post-configure operations. Implement parameter buffer validation for the extensible format at .buf_prepare() time. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Jacopo Mondi Reviewed-by: Paul Elder Tested-by: Kieran Bingham Acked-by: Sakari Ailus Signed-off-by: Laurent Pinchart --- .../media/platform/rockchip/rkisp1/rkisp1-common.h | 3 + .../media/platform/rockchip/rkisp1/rkisp1-params.c | 630 ++++++++++++++++++++- 2 files changed, 622 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index b4369bbccea3..c1689c0fa05a 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -401,6 +401,7 @@ struct rkisp1_params_ops { * @quantization: the quantization configured on the isp's src pad * @ycbcr_encoding the YCbCr encoding * @raw_type: the bayer pattern on the isp video sink pad + * @enabled_blocks: bitmask of enabled ISP blocks */ struct rkisp1_params { struct rkisp1_vdev_node vnode; @@ -415,6 +416,8 @@ struct rkisp1_params { enum v4l2_quantization quantization; enum v4l2_ycbcr_encoding ycbcr_encoding; enum rkisp1_fmt_raw_pat_type raw_type; + + u32 enabled_blocks; }; /* diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index 45c4b1bcee63..b48adff49307 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -35,6 +35,30 @@ #define RKISP1_ISP_CC_COEFF(n) \ (RKISP1_CIF_ISP_CC_COEFF_0 + (n) * 4) +#define RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS BIT(0) +#define RKISP1_EXT_PARAMS_BLOCK_GROUP_LSC BIT(1) + +union rkisp1_ext_params_config { + struct rkisp1_ext_params_block_header header; + struct rkisp1_ext_params_bls_config bls; + struct rkisp1_ext_params_dpcc_config dpcc; + struct rkisp1_ext_params_sdg_config sdg; + struct rkisp1_ext_params_lsc_config lsc; + struct rkisp1_ext_params_awb_gain_config awbg; + struct rkisp1_ext_params_flt_config flt; + struct rkisp1_ext_params_bdm_config bdm; + struct rkisp1_ext_params_ctk_config ctk; + struct rkisp1_ext_params_goc_config goc; + struct rkisp1_ext_params_dpf_config dpf; + struct rkisp1_ext_params_dpf_strength_config dpfs; + struct rkisp1_ext_params_cproc_config cproc; + struct rkisp1_ext_params_ie_config ie; + struct rkisp1_ext_params_awb_meas_config awbm; + struct rkisp1_ext_params_hst_config hst; + struct rkisp1_ext_params_aec_config aec; + struct rkisp1_ext_params_afc_config afc; +}; + enum rkisp1_params_formats { RKISP1_PARAMS_FIXED, RKISP1_PARAMS_EXTENSIBLE, @@ -1519,6 +1543,462 @@ static void rkisp1_isp_isr_meas_config(struct rkisp1_params *params, } } +/*------------------------------------------------------------------------------ + * Extensible parameters format handling + */ + +static void +rkisp1_ext_params_bls(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_bls_config *bls = &block->bls; + + if (bls->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_BLS_CTRL, + RKISP1_CIF_ISP_BLS_ENA); + return; + } + + rkisp1_bls_config(params, &bls->config); + + if ((bls->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(bls->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_BLS_CTRL, + RKISP1_CIF_ISP_BLS_ENA); +} + +static void +rkisp1_ext_params_dpcc(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_dpcc_config *dpcc = &block->dpcc; + + if (dpcc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DPCC_MODE, + RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE); + return; + } + + rkisp1_dpcc_config(params, &dpcc->config); + + if ((dpcc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(dpcc->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_DPCC_MODE, + RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE); +} + +static void +rkisp1_ext_params_sdg(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_sdg_config *sdg = &block->sdg; + + if (sdg->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA); + return; + } + + rkisp1_sdg_config(params, &sdg->config); + + if ((sdg->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(sdg->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA); +} + +static void +rkisp1_ext_params_lsc(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_lsc_config *lsc = &block->lsc; + + if (lsc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_LSC_CTRL, + RKISP1_CIF_ISP_LSC_CTRL_ENA); + return; + } + + rkisp1_lsc_config(params, &lsc->config); + + if ((lsc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(lsc->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_LSC_CTRL, + RKISP1_CIF_ISP_LSC_CTRL_ENA); +} + +static void +rkisp1_ext_params_awbg(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_awb_gain_config *awbg = &block->awbg; + + if (awbg->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA); + return; + } + + params->ops->awb_gain_config(params, &awbg->config); + + if ((awbg->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(awbg->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA); +} + +static void +rkisp1_ext_params_flt(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_flt_config *flt = &block->flt; + + if (flt->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_FILT_MODE, + RKISP1_CIF_ISP_FLT_ENA); + return; + } + + rkisp1_flt_config(params, &flt->config); + + if ((flt->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(flt->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_FILT_MODE, + RKISP1_CIF_ISP_FLT_ENA); +} + +static void +rkisp1_ext_params_bdm(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_bdm_config *bdm = &block->bdm; + + if (bdm->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DEMOSAIC, + RKISP1_CIF_ISP_DEMOSAIC_BYPASS); + return; + } + + rkisp1_bdm_config(params, &bdm->config); + + if ((bdm->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(bdm->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_DEMOSAIC, + RKISP1_CIF_ISP_DEMOSAIC_BYPASS); +} + +static void +rkisp1_ext_params_ctk(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_ctk_config *ctk = &block->ctk; + + if (ctk->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_ctk_enable(params, false); + return; + } + + rkisp1_ctk_config(params, &ctk->config); + + if ((ctk->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(ctk->header.type))) + rkisp1_ctk_enable(params, true); +} + +static void +rkisp1_ext_params_goc(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_goc_config *goc = &block->goc; + + if (goc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA); + return; + } + + params->ops->goc_config(params, &goc->config); + + /* + * Unconditionally re-enable the GOC module which gets disabled by + * goc_config(). + */ + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, + RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA); +} + +static void +rkisp1_ext_params_dpf(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_dpf_config *dpf = &block->dpf; + + if (dpf->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DPF_MODE, + RKISP1_CIF_ISP_DPF_MODE_EN); + return; + } + + rkisp1_dpf_config(params, &dpf->config); + + if ((dpf->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(dpf->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_DPF_MODE, + RKISP1_CIF_ISP_DPF_MODE_EN); +} + +static void +rkisp1_ext_params_dpfs(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_dpf_strength_config *dpfs = &block->dpfs; + + rkisp1_dpf_strength_config(params, &dpfs->config); +} + +static void +rkisp1_ext_params_cproc(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_cproc_config *cproc = &block->cproc; + + if (cproc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_C_PROC_CTRL, + RKISP1_CIF_C_PROC_CTR_ENABLE); + return; + } + + rkisp1_cproc_config(params, &cproc->config); + + if ((cproc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(cproc->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_C_PROC_CTRL, + RKISP1_CIF_C_PROC_CTR_ENABLE); +} + +static void +rkisp1_ext_params_ie(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_ie_config *ie = &block->ie; + + if (ie->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_ie_enable(params, false); + return; + } + + rkisp1_ie_config(params, &ie->config); + + if ((ie->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(ie->header.type))) + rkisp1_ie_enable(params, true); +} + +static void +rkisp1_ext_params_awbm(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_awb_meas_config *awbm = &block->awbm; + + if (awbm->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + params->ops->awb_meas_enable(params, &awbm->config, + false); + return; + } + + params->ops->awb_meas_config(params, &awbm->config); + + if ((awbm->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(awbm->header.type))) + params->ops->awb_meas_enable(params, &awbm->config, + true); +} + +static void +rkisp1_ext_params_hstm(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_hst_config *hst = &block->hst; + + if (hst->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + params->ops->hst_enable(params, &hst->config, false); + return; + } + + params->ops->hst_config(params, &hst->config); + + if ((hst->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(hst->header.type))) + params->ops->hst_enable(params, &hst->config, true); +} + +static void +rkisp1_ext_params_aecm(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_aec_config *aec = &block->aec; + + if (aec->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_EXP_CTRL, + RKISP1_CIF_ISP_EXP_ENA); + return; + } + + params->ops->aec_config(params, &aec->config); + + if ((aec->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(aec->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_EXP_CTRL, + RKISP1_CIF_ISP_EXP_ENA); +} + +static void +rkisp1_ext_params_afcm(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_afc_config *afc = &block->afc; + + if (afc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_AFM_CTRL, + RKISP1_CIF_ISP_AFM_ENA); + return; + } + + params->ops->afm_config(params, &afc->config); + + if ((afc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(afc->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_AFM_CTRL, + RKISP1_CIF_ISP_AFM_ENA); +} + +typedef void (*rkisp1_block_handler)(struct rkisp1_params *params, + const union rkisp1_ext_params_config *config); + +static const struct rkisp1_ext_params_handler { + size_t size; + rkisp1_block_handler handler; + unsigned int group; +} rkisp1_ext_params_handlers[] = { + [RKISP1_EXT_PARAMS_BLOCK_TYPE_BLS] = { + .size = sizeof(struct rkisp1_ext_params_bls_config), + .handler = rkisp1_ext_params_bls, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_DPCC] = { + .size = sizeof(struct rkisp1_ext_params_dpcc_config), + .handler = rkisp1_ext_params_dpcc, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_SDG] = { + .size = sizeof(struct rkisp1_ext_params_sdg_config), + .handler = rkisp1_ext_params_sdg, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_AWB_GAIN] = { + .size = sizeof(struct rkisp1_ext_params_awb_gain_config), + .handler = rkisp1_ext_params_awbg, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_FLT] = { + .size = sizeof(struct rkisp1_ext_params_flt_config), + .handler = rkisp1_ext_params_flt, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_BDM] = { + .size = sizeof(struct rkisp1_ext_params_bdm_config), + .handler = rkisp1_ext_params_bdm, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_CTK] = { + .size = sizeof(struct rkisp1_ext_params_ctk_config), + .handler = rkisp1_ext_params_ctk, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_GOC] = { + .size = sizeof(struct rkisp1_ext_params_goc_config), + .handler = rkisp1_ext_params_goc, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_DPF] = { + .size = sizeof(struct rkisp1_ext_params_dpf_config), + .handler = rkisp1_ext_params_dpf, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_DPF_STRENGTH] = { + .size = sizeof(struct rkisp1_ext_params_dpf_strength_config), + .handler = rkisp1_ext_params_dpfs, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_CPROC] = { + .size = sizeof(struct rkisp1_ext_params_cproc_config), + .handler = rkisp1_ext_params_cproc, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_IE] = { + .size = sizeof(struct rkisp1_ext_params_ie_config), + .handler = rkisp1_ext_params_ie, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_LSC] = { + .size = sizeof(struct rkisp1_ext_params_lsc_config), + .handler = rkisp1_ext_params_lsc, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_LSC, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_AWB_MEAS] = { + .size = sizeof(struct rkisp1_ext_params_awb_meas_config), + .handler = rkisp1_ext_params_awbm, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_HST_MEAS] = { + .size = sizeof(struct rkisp1_ext_params_hst_config), + .handler = rkisp1_ext_params_hstm, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_AEC_MEAS] = { + .size = sizeof(struct rkisp1_ext_params_aec_config), + .handler = rkisp1_ext_params_aecm, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_AFC_MEAS] = { + .size = sizeof(struct rkisp1_ext_params_afc_config), + .handler = rkisp1_ext_params_afcm, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + }, +}; + +static void rkisp1_ext_params_config(struct rkisp1_params *params, + struct rkisp1_ext_params_cfg *cfg, + u32 block_group_mask) +{ + size_t block_offset = 0; + + if (WARN_ON(!cfg)) + return; + + /* Walk the list of parameter blocks and process them. */ + while (block_offset < cfg->data_size) { + const struct rkisp1_ext_params_handler *block_handler; + const union rkisp1_ext_params_config *block; + + block = (const union rkisp1_ext_params_config *) + &cfg->data[block_offset]; + block_offset += block->header.size; + + /* Make sure the block is in the list of groups to configure. */ + block_handler = &rkisp1_ext_params_handlers[block->header.type]; + if (!(block_handler->group & block_group_mask)) + continue; + + block_handler->handler(params, block); + + if (block->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) + params->enabled_blocks &= ~BIT(block->header.type); + else if (block->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) + params->enabled_blocks |= BIT(block->header.type); + } +} + static void rkisp1_params_complete_buffer(struct rkisp1_params *params, struct rkisp1_params_buffer *buf, unsigned int frame_sequence) @@ -1541,9 +2021,15 @@ void rkisp1_params_isr(struct rkisp1_device *rkisp1) if (!cur_buf) goto unlock; - rkisp1_isp_isr_other_config(params, cur_buf->cfg); - rkisp1_isp_isr_lsc_config(params, cur_buf->cfg); - rkisp1_isp_isr_meas_config(params, cur_buf->cfg); + if (params->metafmt->dataformat == V4L2_META_FMT_RK_ISP1_PARAMS) { + rkisp1_isp_isr_other_config(params, cur_buf->cfg); + rkisp1_isp_isr_lsc_config(params, cur_buf->cfg); + rkisp1_isp_isr_meas_config(params, cur_buf->cfg); + } else { + rkisp1_ext_params_config(params, cur_buf->cfg, + RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS | + RKISP1_EXT_PARAMS_BLOCK_GROUP_LSC); + } /* update shadow register immediately */ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, @@ -1643,8 +2129,13 @@ void rkisp1_params_pre_configure(struct rkisp1_params *params, if (!cur_buf) goto unlock; - rkisp1_isp_isr_other_config(params, cur_buf->cfg); - rkisp1_isp_isr_meas_config(params, cur_buf->cfg); + if (params->metafmt->dataformat == V4L2_META_FMT_RK_ISP1_PARAMS) { + rkisp1_isp_isr_other_config(params, cur_buf->cfg); + rkisp1_isp_isr_meas_config(params, cur_buf->cfg); + } else { + rkisp1_ext_params_config(params, cur_buf->cfg, + RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS); + } /* update shadow register immediately */ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, @@ -1673,7 +2164,11 @@ void rkisp1_params_post_configure(struct rkisp1_params *params) if (!cur_buf) goto unlock; - rkisp1_isp_isr_lsc_config(params, cur_buf->cfg); + if (params->metafmt->dataformat == V4L2_META_FMT_RK_ISP1_PARAMS) + rkisp1_isp_isr_lsc_config(params, cur_buf->cfg); + else + rkisp1_ext_params_config(params, cur_buf->cfg, + RKISP1_EXT_PARAMS_BLOCK_GROUP_LSC); /* update shadow register immediately */ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL, @@ -1862,21 +2357,133 @@ static void rkisp1_params_vb2_buf_queue(struct vb2_buffer *vb) spin_unlock_irq(¶ms->config_lock); } +static int rkisp1_params_prepare_ext_params(struct rkisp1_params *params, + struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf); + size_t header_size = offsetof(struct rkisp1_ext_params_cfg, data); + struct rkisp1_ext_params_cfg *cfg = params_buf->cfg; + size_t payload_size = vb2_get_plane_payload(vb, 0); + struct rkisp1_ext_params_cfg *usr_cfg = + vb2_plane_vaddr(&vbuf->vb2_buf, 0); + size_t block_offset = 0; + size_t cfg_size; + + /* + * Validate the buffer payload size before copying the parameters. The + * payload has to be smaller than the destination buffer size and larger + * than the header size. + */ + if (payload_size > params->metafmt->buffersize) { + dev_dbg(params->rkisp1->dev, + "Too large buffer payload size %zu\n", payload_size); + return -EINVAL; + } + + if (payload_size < header_size) { + dev_dbg(params->rkisp1->dev, + "Buffer payload %zu smaller than header size %zu\n", + payload_size, header_size); + return -EINVAL; + } + + /* + * Copy the parameters buffer to the internal scratch buffer to avoid + * userspace modifying the buffer content while the driver processes it. + */ + memcpy(cfg, usr_cfg, payload_size); + + /* Only v1 is supported at the moment. */ + if (cfg->version != RKISP1_EXT_PARAM_BUFFER_V1) { + dev_dbg(params->rkisp1->dev, + "Unsupported extensible format version: %u\n", + cfg->version); + return -EINVAL; + } + + /* Validate the size reported in the parameters buffer header. */ + cfg_size = header_size + cfg->data_size; + if (cfg_size != payload_size) { + dev_dbg(params->rkisp1->dev, + "Data size %zu different than buffer payload size %zu\n", + cfg_size, payload_size); + return -EINVAL; + } + + /* Walk the list of parameter blocks and validate them. */ + cfg_size = cfg->data_size; + while (cfg_size >= sizeof(struct rkisp1_ext_params_block_header)) { + const struct rkisp1_ext_params_block_header *block; + const struct rkisp1_ext_params_handler *handler; + + block = (const struct rkisp1_ext_params_block_header *) + &cfg->data[block_offset]; + + if (block->type >= ARRAY_SIZE(rkisp1_ext_params_handlers)) { + dev_dbg(params->rkisp1->dev, + "Invalid parameters block type\n"); + return -EINVAL; + } + + if (block->size > cfg_size) { + dev_dbg(params->rkisp1->dev, + "Premature end of parameters data\n"); + return -EINVAL; + } + + if ((block->flags & (RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE | + RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE)) == + (RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE | + RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE)) { + dev_dbg(params->rkisp1->dev, + "Invalid parameters block flags\n"); + return -EINVAL; + } + + handler = &rkisp1_ext_params_handlers[block->type]; + if (block->size != handler->size) { + dev_dbg(params->rkisp1->dev, + "Invalid parameters block size\n"); + return -EINVAL; + } + + block_offset += block->size; + cfg_size -= block->size; + } + + if (cfg_size) { + dev_dbg(params->rkisp1->dev, + "Unexpected data after the parameters buffer end\n"); + return -EINVAL; + } + + return 0; +} + static int rkisp1_params_vb2_buf_prepare(struct vb2_buffer *vb) { + struct rkisp1_params *params = vb->vb2_queue->drv_priv; struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf); - struct rkisp1_params_cfg *cfg = - vb2_plane_vaddr(¶ms_buf->vb.vb2_buf, 0); + struct rkisp1_params_cfg *cfg = vb2_plane_vaddr(&vbuf->vb2_buf, 0); + size_t payload = vb2_get_plane_payload(vb, 0); - if (vb2_get_plane_payload(vb, 0) != sizeof(*cfg)) + if (params->metafmt->dataformat == V4L2_META_FMT_RK_ISP1_EXT_PARAMS) + return rkisp1_params_prepare_ext_params(params, vb); + + /* + * For the fixed parameters format the payload size must be exactly the + * size of the parameters structure. + */ + if (payload != sizeof(*cfg)) return -EINVAL; /* * Copy the parameters buffer to the internal scratch buffer to avoid * userspace modifying the buffer content while the driver processes it. */ - memcpy(params_buf->cfg, cfg, sizeof(*cfg)); + memcpy(params_buf->cfg, cfg, payload); return 0; } @@ -1898,6 +2505,8 @@ static void rkisp1_params_vb2_stop_streaming(struct vb2_queue *vq) list_for_each_entry(buf, &tmp_list, queue) vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + + params->enabled_blocks = 0; } static const struct vb2_ops rkisp1_params_vb2_ops = { @@ -1909,7 +2518,6 @@ static const struct vb2_ops rkisp1_params_vb2_ops = { .buf_queue = rkisp1_params_vb2_buf_queue, .buf_prepare = rkisp1_params_vb2_buf_prepare, .stop_streaming = rkisp1_params_vb2_stop_streaming, - }; static const struct v4l2_file_operations rkisp1_params_fops = { -- cgit v1.2.3-70-g09d2 From f848c0312e392d90556cbffa3b0632780c147f46 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Thu, 8 Aug 2024 22:41:00 +0200 Subject: media: rkisp1: Implement s_fmt/try_fmt Implement in the rkisp1 driver support for the s_fmt and try_fmt operation to allow userspace to select between the extensible and the fixed parameters formats. Implement enum_mbus_code to enumerate the fixed and the extensible formats and disallow changing the data format while the queue is busy. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Paul Elder Tested-by: Kieran Bingham Acked-by: Sakari Ailus Signed-off-by: Laurent Pinchart --- .../media/platform/rockchip/rkisp1/rkisp1-params.c | 58 +++++++++++++++++++--- 1 file changed, 50 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index b48adff49307..85ef92f3f364 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -75,6 +75,17 @@ static const struct v4l2_meta_format rkisp1_params_formats[] = { }, }; +static const struct v4l2_meta_format * +rkisp1_params_get_format_info(u32 dataformat) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(rkisp1_params_formats); i++) { + if (rkisp1_params_formats[i].dataformat == dataformat) + return &rkisp1_params_formats[i]; + } + + return &rkisp1_params_formats[RKISP1_PARAMS_FIXED]; +} + static inline void rkisp1_param_set_bits(struct rkisp1_params *params, u32 reg, u32 bit_mask) { @@ -2244,12 +2255,12 @@ static int rkisp1_params_enum_fmt_meta_out(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct video_device *video = video_devdata(file); - struct rkisp1_params *params = video_get_drvdata(video); - if (f->index > 0 || f->type != video->queue->type) + if (f->index >= ARRAY_SIZE(rkisp1_params_formats) || + f->type != video->queue->type) return -EINVAL; - f->pixelformat = params->metafmt->dataformat; + f->pixelformat = rkisp1_params_formats[f->index].dataformat; return 0; } @@ -2264,9 +2275,40 @@ static int rkisp1_params_g_fmt_meta_out(struct file *file, void *fh, if (f->type != video->queue->type) return -EINVAL; - memset(meta, 0, sizeof(*meta)); - meta->dataformat = params->metafmt->dataformat; - meta->buffersize = params->metafmt->buffersize; + *meta = *params->metafmt; + + return 0; +} + +static int rkisp1_params_try_fmt_meta_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct video_device *video = video_devdata(file); + struct v4l2_meta_format *meta = &f->fmt.meta; + + if (f->type != video->queue->type) + return -EINVAL; + + *meta = *rkisp1_params_get_format_info(meta->dataformat); + + return 0; +} + +static int rkisp1_params_s_fmt_meta_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct video_device *video = video_devdata(file); + struct rkisp1_params *params = video_get_drvdata(video); + struct v4l2_meta_format *meta = &f->fmt.meta; + + if (f->type != video->queue->type) + return -EINVAL; + + if (vb2_is_busy(video->queue)) + return -EBUSY; + + params->metafmt = rkisp1_params_get_format_info(meta->dataformat); + *meta = *params->metafmt; return 0; } @@ -2296,8 +2338,8 @@ static const struct v4l2_ioctl_ops rkisp1_params_ioctl = { .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_enum_fmt_meta_out = rkisp1_params_enum_fmt_meta_out, .vidioc_g_fmt_meta_out = rkisp1_params_g_fmt_meta_out, - .vidioc_s_fmt_meta_out = rkisp1_params_g_fmt_meta_out, - .vidioc_try_fmt_meta_out = rkisp1_params_g_fmt_meta_out, + .vidioc_s_fmt_meta_out = rkisp1_params_s_fmt_meta_out, + .vidioc_try_fmt_meta_out = rkisp1_params_try_fmt_meta_out, .vidioc_querycap = rkisp1_params_querycap, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -- cgit v1.2.3-70-g09d2 From d2db5694fde8212a8bd4269f1b60ed9a2a3b20cc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 8 Aug 2024 22:41:01 +0200 Subject: media: rkisp1: Add helper function to swap colour channels The BLS parameters passed by userspace are specified for named colour channels (R, Gr, Gb and B), while the hardware registers reference positions in the 2x2 CFA pattern (A, B, C and D). The BLS values are swapped based on the CFA pattern when writing to or reading from registers, using hand-roled switch statements. The logic is duplicated already, and new code will require similar processing. Move the swap logic to a shared function, using static data to control the channels order. Reviewed-by: Jacopo Mondi Reviewed-by: Paul Elder Signed-off-by: Jacopo Mondi Tested-by: Kieran Bingham Acked-by: Sakari Ailus Signed-off-by: Laurent Pinchart --- .../media/platform/rockchip/rkisp1/rkisp1-common.c | 14 +++++ .../media/platform/rockchip/rkisp1/rkisp1-common.h | 3 ++ .../media/platform/rockchip/rkisp1/rkisp1-params.c | 62 +++++----------------- .../media/platform/rockchip/rkisp1/rkisp1-stats.c | 51 +++++------------- 4 files changed, 45 insertions(+), 85 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c index f956b90a407a..60c97bb7b18b 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c @@ -178,3 +178,17 @@ void rkisp1_sd_adjust_crop(struct v4l2_rect *crop, rkisp1_sd_adjust_crop_rect(crop, &crop_bounds); } + +void rkisp1_bls_swap_regs(enum rkisp1_fmt_raw_pat_type pattern, + const u32 input[4], u32 output[4]) +{ + static const unsigned int swap[4][4] = { + [RKISP1_RAW_RGGB] = { 0, 1, 2, 3 }, + [RKISP1_RAW_GRBG] = { 1, 0, 3, 2 }, + [RKISP1_RAW_GBRG] = { 2, 3, 0, 1 }, + [RKISP1_RAW_BGGR] = { 3, 2, 1, 0 }, + }; + + for (unsigned int i = 0; i < 4; ++i) + output[i] = input[swap[pattern][i]]; +} diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index c1689c0fa05a..a52079261579 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -603,6 +603,9 @@ void rkisp1_sd_adjust_crop_rect(struct v4l2_rect *crop, void rkisp1_sd_adjust_crop(struct v4l2_rect *crop, const struct v4l2_mbus_framefmt *bounds); +void rkisp1_bls_swap_regs(enum rkisp1_fmt_raw_pat_type pattern, + const u32 input[4], u32 output[4]); + /* * rkisp1_mbus_info_get_by_code - get the isp info of the media bus code * diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index 85ef92f3f364..43df8079cf8f 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -165,54 +165,20 @@ static void rkisp1_bls_config(struct rkisp1_params *params, new_control &= RKISP1_CIF_ISP_BLS_ENA; /* fixed subtraction values */ if (!arg->enable_auto) { - const struct rkisp1_cif_isp_bls_fixed_val *pval = - &arg->fixed_val; - - switch (params->raw_type) { - case RKISP1_RAW_BGGR: - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED, - pval->r); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED, - pval->gr); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED, - pval->gb); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED, - pval->b); - break; - case RKISP1_RAW_GBRG: - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED, - pval->r); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED, - pval->gr); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED, - pval->gb); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED, - pval->b); - break; - case RKISP1_RAW_GRBG: - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED, - pval->r); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED, - pval->gr); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED, - pval->gb); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED, - pval->b); - break; - case RKISP1_RAW_RGGB: - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED, - pval->r); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED, - pval->gr); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED, - pval->gb); - rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED, - pval->b); - break; - default: - break; - } - + static const u32 regs[] = { + RKISP1_CIF_ISP_BLS_A_FIXED, + RKISP1_CIF_ISP_BLS_B_FIXED, + RKISP1_CIF_ISP_BLS_C_FIXED, + RKISP1_CIF_ISP_BLS_D_FIXED, + }; + u32 swapped[4]; + + rkisp1_bls_swap_regs(params->raw_type, regs, swapped); + + rkisp1_write(params->rkisp1, swapped[0], arg->fixed_val.r); + rkisp1_write(params->rkisp1, swapped[1], arg->fixed_val.gr); + rkisp1_write(params->rkisp1, swapped[2], arg->fixed_val.gb); + rkisp1_write(params->rkisp1, swapped[3], arg->fixed_val.b); } else { if (arg->en_windows & BIT(1)) { rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_H2_START, diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c index 2795eef91bdd..a502719e916a 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c @@ -304,48 +304,25 @@ static void rkisp1_stats_get_hst_meas_v12(struct rkisp1_stats *stats, static void rkisp1_stats_get_bls_meas(struct rkisp1_stats *stats, struct rkisp1_stat_buffer *pbuf) { + static const u32 regs[] = { + RKISP1_CIF_ISP_BLS_A_MEASURED, + RKISP1_CIF_ISP_BLS_B_MEASURED, + RKISP1_CIF_ISP_BLS_C_MEASURED, + RKISP1_CIF_ISP_BLS_D_MEASURED, + }; struct rkisp1_device *rkisp1 = stats->rkisp1; const struct rkisp1_mbus_info *in_fmt = rkisp1->isp.sink_fmt; struct rkisp1_cif_isp_bls_meas_val *bls_val; + u32 swapped[4]; + + rkisp1_bls_swap_regs(in_fmt->bayer_pat, regs, swapped); bls_val = &pbuf->params.ae.bls_val; - if (in_fmt->bayer_pat == RKISP1_RAW_BGGR) { - bls_val->meas_b = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_A_MEASURED); - bls_val->meas_gb = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_B_MEASURED); - bls_val->meas_gr = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_C_MEASURED); - bls_val->meas_r = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_D_MEASURED); - } else if (in_fmt->bayer_pat == RKISP1_RAW_GBRG) { - bls_val->meas_gb = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_A_MEASURED); - bls_val->meas_b = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_B_MEASURED); - bls_val->meas_r = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_C_MEASURED); - bls_val->meas_gr = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_D_MEASURED); - } else if (in_fmt->bayer_pat == RKISP1_RAW_GRBG) { - bls_val->meas_gr = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_A_MEASURED); - bls_val->meas_r = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_B_MEASURED); - bls_val->meas_b = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_C_MEASURED); - bls_val->meas_gb = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_D_MEASURED); - } else if (in_fmt->bayer_pat == RKISP1_RAW_RGGB) { - bls_val->meas_r = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_A_MEASURED); - bls_val->meas_gr = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_B_MEASURED); - bls_val->meas_gb = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_C_MEASURED); - bls_val->meas_b = - rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_D_MEASURED); - } + + bls_val->meas_r = rkisp1_read(rkisp1, swapped[0]); + bls_val->meas_gr = rkisp1_read(rkisp1, swapped[1]); + bls_val->meas_gb = rkisp1_read(rkisp1, swapped[2]); + bls_val->meas_b = rkisp1_read(rkisp1, swapped[3]); } static const struct rkisp1_stats_ops rkisp1_v10_stats_ops = { -- cgit v1.2.3-70-g09d2 From f1463972738f6849b6ac4454c37ae2785094a700 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 8 Aug 2024 22:41:02 +0200 Subject: media: rkisp1: Add features mask to extensible block handlers Future ISP parameter blocks for i.MX8MP-specific features will not support on Rockchip platforms as they lack the corresponding hardware. Introduce a features mask in the extensible block handlers to indicate which device features a block require, and ignore blocks that require unavailable features. Reviewed-by: Jacopo Mondi Reviewed-by: Paul Elder Signed-off-by: Jacopo Mondi Tested-by: Kieran Bingham Acked-by: Sakari Ailus Signed-off-by: Laurent Pinchart --- drivers/media/platform/rockchip/rkisp1/rkisp1-params.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index 43df8079cf8f..095f0189941e 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -1856,6 +1856,7 @@ static const struct rkisp1_ext_params_handler { size_t size; rkisp1_block_handler handler; unsigned int group; + unsigned int features; } rkisp1_ext_params_handlers[] = { [RKISP1_EXT_PARAMS_BLOCK_TYPE_BLS] = { .size = sizeof(struct rkisp1_ext_params_bls_config), @@ -1962,11 +1963,18 @@ static void rkisp1_ext_params_config(struct rkisp1_params *params, &cfg->data[block_offset]; block_offset += block->header.size; - /* Make sure the block is in the list of groups to configure. */ + /* + * Make sure the block is supported by the platform and in the + * list of groups to configure. + */ block_handler = &rkisp1_ext_params_handlers[block->header.type]; if (!(block_handler->group & block_group_mask)) continue; + if ((params->rkisp1->info->features & block_handler->features) != + block_handler->features) + continue; + block_handler->handler(params, block); if (block->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) -- cgit v1.2.3-70-g09d2 From a735e539758af44a508698e4f4041eb59b325b2d Mon Sep 17 00:00:00 2001 From: Paul Elder Date: Thu, 8 Aug 2024 22:41:03 +0200 Subject: media: rkisp1: Add register definitions for the companding block To prepare for adding support for the companding block to the rkisp1 driver for the version of the ISP on the i.MX8MP, add the register definitions for it, including relevant register field values. Signed-off-by: Paul Elder Reviewed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Tested-by: Kieran Bingham Acked-by: Sakari Ailus Signed-off-by: Laurent Pinchart --- .../media/platform/rockchip/rkisp1/rkisp1-regs.h | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers') diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h index fccf4c17ee8d..bf0260600a19 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h @@ -704,6 +704,12 @@ #define RKISP1_CIF_ISP_DPF_SPATIAL_COEFF_MAX 0x1f #define RKISP1_CIF_ISP_DPF_NLL_COEFF_N_MAX 0x3ff +/* COMPAND */ +#define RKISP1_CIF_ISP_COMPAND_CTRL_EXPAND_ENABLE BIT(0) +#define RKISP1_CIF_ISP_COMPAND_CTRL_COMPRESS_ENABLE BIT(1) +#define RKISP1_CIF_ISP_COMPAND_CTRL_SOFT_RESET_FLAG BIT(2) +#define RKISP1_CIF_ISP_COMPAND_CTRL_BLS_ENABLE BIT(3) + /* =================================================================== */ /* CIF Registers */ /* =================================================================== */ @@ -1394,6 +1400,23 @@ #define RKISP1_CIF_ISP_VSM_DELTA_H (RKISP1_CIF_ISP_VSM_BASE + 0x0000001c) #define RKISP1_CIF_ISP_VSM_DELTA_V (RKISP1_CIF_ISP_VSM_BASE + 0x00000020) +#define RKISP1_CIF_ISP_COMPAND_BASE 0x00003200 +#define RKISP1_CIF_ISP_COMPAND_CTRL (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000000) +#define RKISP1_CIF_ISP_COMPAND_BLS_A_FIXED (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000004) +#define RKISP1_CIF_ISP_COMPAND_BLS_B_FIXED (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000008) +#define RKISP1_CIF_ISP_COMPAND_BLS_C_FIXED (RKISP1_CIF_ISP_COMPAND_BASE + 0x0000000c) +#define RKISP1_CIF_ISP_COMPAND_BLS_D_FIXED (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000010) +#define RKISP1_CIF_ISP_COMPAND_EXPAND_PX_N(n) (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000014 + (n) * 4) +#define RKISP1_CIF_ISP_COMPAND_COMPRESS_PX_N(n) (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000040 + (n) * 4) +#define RKISP1_CIF_ISP_COMPAND_EXPAND_Y_ADDR (RKISP1_CIF_ISP_COMPAND_BASE + 0x0000006c) +#define RKISP1_CIF_ISP_COMPAND_EXPAND_Y_WRITE_DATA (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000070) +#define RKISP1_CIF_ISP_COMPAND_COMPRESS_Y_ADDR (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000074) +#define RKISP1_CIF_ISP_COMPAND_COMPRESS_Y_WRITE_DATA (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000078) +#define RKISP1_CIF_ISP_COMPAND_EXPAND_X_ADDR (RKISP1_CIF_ISP_COMPAND_BASE + 0x0000007c) +#define RKISP1_CIF_ISP_COMPAND_EXPAND_X_WRITE_DATA (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000080) +#define RKISP1_CIF_ISP_COMPAND_COMPRESS_X_ADDR (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000084) +#define RKISP1_CIF_ISP_COMPAND_COMPRESS_X_WRITE_DATA (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000088) + #define RKISP1_CIF_ISP_CSI0_BASE 0x00007000 #define RKISP1_CIF_ISP_CSI0_CTRL0 (RKISP1_CIF_ISP_CSI0_BASE + 0x00000000) -- cgit v1.2.3-70-g09d2 From 74a18d029fb948ad4902b55ab30190fcbef088e8 Mon Sep 17 00:00:00 2001 From: Paul Elder Date: Thu, 8 Aug 2024 22:41:04 +0200 Subject: media: rkisp1: Add feature flags for BLS and compand Add feature flags for the dedicated black level subtraction hardware block and for the compand hardware block. The companding feature flag is added on its own (as opposed to "the absence of BLS") because we will need it later for when we add support for the companding block. Skip BLS configuration when the BLS feature flag is unset, as devices without the dedicated BLS block cannot configure a hardware block that doesn't exist. Signed-off-by: Paul Elder Reviewed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Tested-by: Kieran Bingham Acked-by: Sakari Ailus Signed-off-by: Laurent Pinchart --- drivers/media/platform/rockchip/rkisp1/rkisp1-common.h | 4 ++++ drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c | 9 ++++++--- drivers/media/platform/rockchip/rkisp1/rkisp1-params.c | 7 +++++++ 3 files changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h index a52079261579..ca952fd0829b 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h @@ -116,6 +116,8 @@ enum rkisp1_isp_pad { * @RKISP1_FEATURE_SELF_PATH: The ISP has a self path * @RKISP1_FEATURE_DUAL_CROP: The ISP has the dual crop block at the resizer input * @RKISP1_FEATURE_DMA_34BIT: The ISP uses 34-bit DMA addresses + * @RKISP1_FEATURE_BLS: The ISP has a dedicated BLS block + * @RKISP1_FEATURE_COMPAND: The ISP has a companding block * * The ISP features are stored in a bitmask in &rkisp1_info.features and allow * the driver to implement support for features present in some ISP versions @@ -127,6 +129,8 @@ enum rkisp1_feature { RKISP1_FEATURE_SELF_PATH = BIT(2), RKISP1_FEATURE_DUAL_CROP = BIT(3), RKISP1_FEATURE_DMA_34BIT = BIT(4), + RKISP1_FEATURE_BLS = BIT(5), + RKISP1_FEATURE_COMPAND = BIT(6), }; #define rkisp1_has_feature(rkisp1, feature) \ diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c index 0535ce57e862..dd114ab77800 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c @@ -509,7 +509,8 @@ static const struct rkisp1_info px30_isp_info = { .isp_ver = RKISP1_V12, .features = RKISP1_FEATURE_MIPI_CSI2 | RKISP1_FEATURE_SELF_PATH - | RKISP1_FEATURE_DUAL_CROP, + | RKISP1_FEATURE_DUAL_CROP + | RKISP1_FEATURE_BLS, .max_width = 3264, .max_height = 2448, }; @@ -532,7 +533,8 @@ static const struct rkisp1_info rk3399_isp_info = { .isp_ver = RKISP1_V10, .features = RKISP1_FEATURE_MIPI_CSI2 | RKISP1_FEATURE_SELF_PATH - | RKISP1_FEATURE_DUAL_CROP, + | RKISP1_FEATURE_DUAL_CROP + | RKISP1_FEATURE_BLS, .max_width = 4416, .max_height = 3312, }; @@ -554,7 +556,8 @@ static const struct rkisp1_info imx8mp_isp_info = { .isr_size = ARRAY_SIZE(imx8mp_isp_isrs), .isp_ver = RKISP1_V_IMX8MP, .features = RKISP1_FEATURE_MAIN_STRIDE - | RKISP1_FEATURE_DMA_34BIT, + | RKISP1_FEATURE_DMA_34BIT + | RKISP1_FEATURE_COMPAND, .max_width = 4096, .max_height = 3072, }; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index 095f0189941e..9cbbc6adef39 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -1268,6 +1268,12 @@ rkisp1_isp_isr_other_config(struct rkisp1_params *params, module_cfg_update = new_params->module_cfg_update; module_ens = new_params->module_ens; + if (!rkisp1_has_feature(params->rkisp1, BLS)) { + module_en_update &= ~RKISP1_CIF_ISP_MODULE_BLS; + module_cfg_update &= ~RKISP1_CIF_ISP_MODULE_BLS; + module_ens &= ~RKISP1_CIF_ISP_MODULE_BLS; + } + /* update dpc config */ if (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPCC) rkisp1_dpcc_config(params, @@ -1862,6 +1868,7 @@ static const struct rkisp1_ext_params_handler { .size = sizeof(struct rkisp1_ext_params_bls_config), .handler = rkisp1_ext_params_bls, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + .features = RKISP1_FEATURE_BLS, }, [RKISP1_EXT_PARAMS_BLOCK_TYPE_DPCC] = { .size = sizeof(struct rkisp1_ext_params_dpcc_config), -- cgit v1.2.3-70-g09d2 From ac79beb913dc40b1a49b628e31afdfeb20194ab6 Mon Sep 17 00:00:00 2001 From: Paul Elder Date: Thu, 8 Aug 2024 22:41:05 +0200 Subject: media: rkisp1: Add support for the companding block Add support to the rkisp1 driver for the companding block that exists on the i.MX8MP version of the ISP. This requires usage of the new extensible parameters format, and showcases how the format allows for extensions without breaking backward compatibility. Signed-off-by: Paul Elder Reviewed-by: Jacopo Mondi Reviewed-by: Paul Elder Signed-off-by: Jacopo Mondi Tested-by: Kieran Bingham Acked-by: Sakari Ailus Signed-off-by: Laurent Pinchart --- .../media/platform/rockchip/rkisp1/rkisp1-params.c | 168 +++++++++++++++++++++ include/uapi/linux/rkisp1-config.h | 89 ++++++++++- 2 files changed, 256 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c index 9cbbc6adef39..320581a9f866 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c @@ -5,6 +5,7 @@ * Copyright (C) 2017 Rockchip Electronics Co., Ltd. */ +#include #include #include @@ -57,6 +58,8 @@ union rkisp1_ext_params_config { struct rkisp1_ext_params_hst_config hst; struct rkisp1_ext_params_aec_config aec; struct rkisp1_ext_params_afc_config afc; + struct rkisp1_ext_params_compand_bls_config compand_bls; + struct rkisp1_ext_params_compand_curve_config compand_curve; }; enum rkisp1_params_formats { @@ -1258,6 +1261,93 @@ rkisp1_dpf_strength_config(struct rkisp1_params *params, rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_STRENGTH_R, arg->r); } +static void rkisp1_compand_write_px_curve(struct rkisp1_params *params, + unsigned int addr, const u8 *curve) +{ + const unsigned int points_per_reg = 6; + const unsigned int num_regs = + DIV_ROUND_UP(RKISP1_CIF_ISP_COMPAND_NUM_POINTS, + points_per_reg); + + /* + * The compand curve is specified as a piecewise linear function with + * 64 points. X coordinates are stored as a log2 of the displacement + * from the previous point, in 5 bits, with 6 values per register. The + * last register stores 4 values. + */ + for (unsigned int reg = 0; reg < num_regs; ++reg) { + unsigned int num_points = + min(RKISP1_CIF_ISP_COMPAND_NUM_POINTS - + reg * points_per_reg, points_per_reg); + u32 val = 0; + + for (unsigned int i = 0; i < num_points; i++) + val |= (*curve++ & 0x1f) << (i * 5); + + rkisp1_write(params->rkisp1, addr, val); + addr += 4; + } +} + +static void +rkisp1_compand_write_curve_mem(struct rkisp1_params *params, + unsigned int reg_addr, unsigned int reg_data, + const u32 curve[RKISP1_CIF_ISP_COMPAND_NUM_POINTS]) +{ + for (unsigned int i = 0; i < RKISP1_CIF_ISP_COMPAND_NUM_POINTS; i++) { + rkisp1_write(params->rkisp1, reg_addr, i); + rkisp1_write(params->rkisp1, reg_data, curve[i]); + } +} + +static void +rkisp1_compand_bls_config(struct rkisp1_params *params, + const struct rkisp1_cif_isp_compand_bls_config *arg) +{ + static const u32 regs[] = { + RKISP1_CIF_ISP_COMPAND_BLS_A_FIXED, + RKISP1_CIF_ISP_COMPAND_BLS_B_FIXED, + RKISP1_CIF_ISP_COMPAND_BLS_C_FIXED, + RKISP1_CIF_ISP_COMPAND_BLS_D_FIXED, + }; + u32 swapped[4]; + + rkisp1_bls_swap_regs(params->raw_type, regs, swapped); + + rkisp1_write(params->rkisp1, swapped[0], arg->r); + rkisp1_write(params->rkisp1, swapped[1], arg->gr); + rkisp1_write(params->rkisp1, swapped[2], arg->gb); + rkisp1_write(params->rkisp1, swapped[3], arg->b); +} + +static void +rkisp1_compand_expand_config(struct rkisp1_params *params, + const struct rkisp1_cif_isp_compand_curve_config *arg) +{ + rkisp1_compand_write_px_curve(params, RKISP1_CIF_ISP_COMPAND_EXPAND_PX_N(0), + arg->px); + rkisp1_compand_write_curve_mem(params, RKISP1_CIF_ISP_COMPAND_EXPAND_Y_ADDR, + RKISP1_CIF_ISP_COMPAND_EXPAND_Y_WRITE_DATA, + arg->y); + rkisp1_compand_write_curve_mem(params, RKISP1_CIF_ISP_COMPAND_EXPAND_X_ADDR, + RKISP1_CIF_ISP_COMPAND_EXPAND_X_WRITE_DATA, + arg->x); +} + +static void +rkisp1_compand_compress_config(struct rkisp1_params *params, + const struct rkisp1_cif_isp_compand_curve_config *arg) +{ + rkisp1_compand_write_px_curve(params, RKISP1_CIF_ISP_COMPAND_COMPRESS_PX_N(0), + arg->px); + rkisp1_compand_write_curve_mem(params, RKISP1_CIF_ISP_COMPAND_COMPRESS_Y_ADDR, + RKISP1_CIF_ISP_COMPAND_COMPRESS_Y_WRITE_DATA, + arg->y); + rkisp1_compand_write_curve_mem(params, RKISP1_CIF_ISP_COMPAND_COMPRESS_X_ADDR, + RKISP1_CIF_ISP_COMPAND_COMPRESS_X_WRITE_DATA, + arg->x); +} + static void rkisp1_isp_isr_other_config(struct rkisp1_params *params, const struct rkisp1_params_cfg *new_params) @@ -1855,6 +1945,66 @@ rkisp1_ext_params_afcm(struct rkisp1_params *params, RKISP1_CIF_ISP_AFM_ENA); } +static void rkisp1_ext_params_compand_bls(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_compand_bls_config *bls = + &block->compand_bls; + + if (bls->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL, + RKISP1_CIF_ISP_COMPAND_CTRL_BLS_ENABLE); + return; + } + + rkisp1_compand_bls_config(params, &bls->config); + + if ((bls->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(bls->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL, + RKISP1_CIF_ISP_COMPAND_CTRL_BLS_ENABLE); +} + +static void rkisp1_ext_params_compand_expand(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_compand_curve_config *curve = + &block->compand_curve; + + if (curve->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL, + RKISP1_CIF_ISP_COMPAND_CTRL_EXPAND_ENABLE); + return; + } + + rkisp1_compand_expand_config(params, &curve->config); + + if ((curve->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(curve->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL, + RKISP1_CIF_ISP_COMPAND_CTRL_EXPAND_ENABLE); +} + +static void rkisp1_ext_params_compand_compress(struct rkisp1_params *params, + const union rkisp1_ext_params_config *block) +{ + const struct rkisp1_ext_params_compand_curve_config *curve = + &block->compand_curve; + + if (curve->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) { + rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL, + RKISP1_CIF_ISP_COMPAND_CTRL_COMPRESS_ENABLE); + return; + } + + rkisp1_compand_compress_config(params, &curve->config); + + if ((curve->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) && + !(params->enabled_blocks & BIT(curve->header.type))) + rkisp1_param_set_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL, + RKISP1_CIF_ISP_COMPAND_CTRL_COMPRESS_ENABLE); +} + typedef void (*rkisp1_block_handler)(struct rkisp1_params *params, const union rkisp1_ext_params_config *config); @@ -1950,6 +2100,24 @@ static const struct rkisp1_ext_params_handler { .handler = rkisp1_ext_params_afcm, .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_BLS] = { + .size = sizeof(struct rkisp1_ext_params_compand_bls_config), + .handler = rkisp1_ext_params_compand_bls, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + .features = RKISP1_FEATURE_COMPAND, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND] = { + .size = sizeof(struct rkisp1_ext_params_compand_curve_config), + .handler = rkisp1_ext_params_compand_expand, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + .features = RKISP1_FEATURE_COMPAND, + }, + [RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS] = { + .size = sizeof(struct rkisp1_ext_params_compand_curve_config), + .handler = rkisp1_ext_params_compand_compress, + .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS, + .features = RKISP1_FEATURE_COMPAND, + }, }; static void rkisp1_ext_params_config(struct rkisp1_params *params, diff --git a/include/uapi/linux/rkisp1-config.h b/include/uapi/linux/rkisp1-config.h index 9767bb19c72d..430daceafac7 100644 --- a/include/uapi/linux/rkisp1-config.h +++ b/include/uapi/linux/rkisp1-config.h @@ -164,6 +164,11 @@ #define RKISP1_CIF_ISP_DPF_MAX_NLF_COEFFS 17 #define RKISP1_CIF_ISP_DPF_MAX_SPATIAL_COEFFS 6 +/* + * Compand + */ +#define RKISP1_CIF_ISP_COMPAND_NUM_POINTS 64 + /* * Measurement types */ @@ -851,6 +856,39 @@ struct rkisp1_params_cfg { struct rkisp1_cif_isp_isp_other_cfg others; }; +/** + * struct rkisp1_cif_isp_compand_bls_config - Rockchip ISP1 Companding parameters (BLS) + * @r: Fixed subtraction value for Bayer pattern R + * @gr: Fixed subtraction value for Bayer pattern Gr + * @gb: Fixed subtraction value for Bayer pattern Gb + * @b: Fixed subtraction value for Bayer pattern B + * + * The values will be subtracted from the sensor values. Note that unlike the + * dedicated BLS block, the BLS values in the compander are 20-bit unsigned. + */ +struct rkisp1_cif_isp_compand_bls_config { + __u32 r; + __u32 gr; + __u32 gb; + __u32 b; +}; + +/** + * struct rkisp1_cif_isp_compand_curve_config - Rockchip ISP1 Companding + * parameters (expand and compression curves) + * @px: Compand curve x-values. Each value stores the distance from the + * previous x-value, expressed as log2 of the distance on 5 bits. + * @x: Compand curve x-values. The functionality of these parameters are + * unknown due to do a lack of hardware documentation, but these are left + * here for future compatibility purposes. + * @y: Compand curve y-values + */ +struct rkisp1_cif_isp_compand_curve_config { + __u8 px[RKISP1_CIF_ISP_COMPAND_NUM_POINTS]; + __u32 x[RKISP1_CIF_ISP_COMPAND_NUM_POINTS]; + __u32 y[RKISP1_CIF_ISP_COMPAND_NUM_POINTS]; +}; + /*---------- PART2: Measurement Statistics ------------*/ /** @@ -1018,6 +1056,9 @@ struct rkisp1_stat_buffer { * @RKISP1_EXT_PARAMS_BLOCK_TYPE_HST_MEAS: Histogram statistics * @RKISP1_EXT_PARAMS_BLOCK_TYPE_AEC_MEAS: Auto exposure statistics * @RKISP1_EXT_PARAMS_BLOCK_TYPE_AFC_MEAS: Auto-focus statistics + * @RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_BLS: BLS in the compand block + * @RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND: Companding expand curve + * @RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS: Companding compress curve */ enum rkisp1_ext_params_block_type { RKISP1_EXT_PARAMS_BLOCK_TYPE_BLS, @@ -1037,6 +1078,9 @@ enum rkisp1_ext_params_block_type { RKISP1_EXT_PARAMS_BLOCK_TYPE_HST_MEAS, RKISP1_EXT_PARAMS_BLOCK_TYPE_AEC_MEAS, RKISP1_EXT_PARAMS_BLOCK_TYPE_AFC_MEAS, + RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_BLS, + RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND, + RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS, }; #define RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE (1U << 0) @@ -1380,6 +1424,46 @@ struct rkisp1_ext_params_afc_config { struct rkisp1_cif_isp_afc_config config; } __attribute__((aligned(8))); +/** + * struct rkisp1_ext_params_compand_bls_config - RkISP1 extensible params + * Compand BLS config + * + * RkISP1 extensible parameters Companding configuration block (black level + * subtraction). Identified by :c:type:`RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_BLS`. + * + * @header: The RkISP1 extensible parameters header, see + * :c:type:`rkisp1_ext_params_block_header` + * @config: Companding BLS configuration, see + * :c:type:`rkisp1_cif_isp_compand_bls_config` + */ +struct rkisp1_ext_params_compand_bls_config { + struct rkisp1_ext_params_block_header header; + struct rkisp1_cif_isp_compand_bls_config config; +} __attribute__((aligned(8))); + +/** + * struct rkisp1_ext_params_compand_curve_config - RkISP1 extensible params + * Compand curve config + * + * RkISP1 extensible parameters Companding configuration block (expand and + * compression curves). Identified by + * :c:type:`RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND` or + * :c:type:`RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS`. + * + * @header: The RkISP1 extensible parameters header, see + * :c:type:`rkisp1_ext_params_block_header` + * @config: Companding curve configuration, see + * :c:type:`rkisp1_cif_isp_compand_curve_config` + */ +struct rkisp1_ext_params_compand_curve_config { + struct rkisp1_ext_params_block_header header; + struct rkisp1_cif_isp_compand_curve_config config; +} __attribute__((aligned(8))); + +/* + * The rkisp1_ext_params_compand_curve_config structure is counted twice as it + * is used for both the COMPAND_EXPAND and COMPAND_COMPRESS block types. + */ #define RKISP1_EXT_PARAMS_MAX_SIZE \ (sizeof(struct rkisp1_ext_params_bls_config) +\ sizeof(struct rkisp1_ext_params_dpcc_config) +\ @@ -1397,7 +1481,10 @@ struct rkisp1_ext_params_afc_config { sizeof(struct rkisp1_ext_params_awb_meas_config) +\ sizeof(struct rkisp1_ext_params_hst_config) +\ sizeof(struct rkisp1_ext_params_aec_config) +\ - sizeof(struct rkisp1_ext_params_afc_config)) + sizeof(struct rkisp1_ext_params_afc_config) +\ + sizeof(struct rkisp1_ext_params_compand_bls_config) +\ + sizeof(struct rkisp1_ext_params_compand_curve_config) +\ + sizeof(struct rkisp1_ext_params_compand_curve_config)) /** * enum rksip1_ext_param_buffer_version - RkISP1 extensible parameters version -- cgit v1.2.3-70-g09d2 From 07668fb0f867388bfdac0b60dbf51a4ad789f8e7 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 31 Jul 2024 17:49:32 +0100 Subject: media: platform: rzg2l-cru: rzg2l-csi2: Add missing MODULE_DEVICE_TABLE The rzg2l-csi2 driver can be compiled as a module, but lacks MODULE_DEVICE_TABLE() and will therefore not be loaded automatically. Fix this. Fixes: 51e8415e39a9 ("media: platform: Add Renesas RZ/G2L MIPI CSI-2 receiver driver") Signed-off-by: Biju Das Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240731164935.308994-1-biju.das.jz@bp.renesas.com Signed-off-by: Laurent Pinchart --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c index e68fcdaea207..c7fdee347ac8 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c @@ -865,6 +865,7 @@ static const struct of_device_id rzg2l_csi2_of_table[] = { { .compatible = "renesas,rzg2l-csi2", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, rzg2l_csi2_of_table); static struct platform_driver rzg2l_csi2_pdrv = { .remove_new = rzg2l_csi2_remove, -- cgit v1.2.3-70-g09d2 From 3f6a2f8768e91cdb69947cb589d0b3a09114be83 Mon Sep 17 00:00:00 2001 From: Matthias Fend Date: Tue, 23 Jul 2024 08:09:08 +0200 Subject: media: imx-mipi-csis: avoid logging while holding spinlock Refactor mipi_csis_log_counters() to prevent calling dev_info() while IRQs are disabled. This reduces crucial IRQs off time to a bare minimum. Signed-off-by: Matthias Fend Reviewed-by: Rui Miguel Silva Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240723060909.534584-1-matthias.fend@emfend.at Signed-off-by: Laurent Pinchart --- drivers/media/platform/nxp/imx-mipi-csis.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index b9729a8883d6..e0b1d3adda10 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -861,18 +861,21 @@ static void mipi_csis_log_counters(struct mipi_csis_device *csis, bool non_error { unsigned int num_events = non_errors ? MIPI_CSIS_NUM_EVENTS : MIPI_CSIS_NUM_EVENTS - 8; + unsigned int counters[MIPI_CSIS_NUM_EVENTS]; unsigned long flags; unsigned int i; spin_lock_irqsave(&csis->slock, flags); + for (i = 0; i < num_events; ++i) + counters[i] = csis->events[i].counter; + spin_unlock_irqrestore(&csis->slock, flags); for (i = 0; i < num_events; ++i) { - if (csis->events[i].counter > 0 || csis->debug.enable) + if (counters[i] > 0 || csis->debug.enable) dev_info(csis->dev, "%s events: %d\n", csis->events[i].name, - csis->events[i].counter); + counters[i]); } - spin_unlock_irqrestore(&csis->slock, flags); } static int mipi_csis_dump_regs(struct mipi_csis_device *csis) -- cgit v1.2.3-70-g09d2 From 4e3972533f8133cdc2ce5da9c46af8fbe14a573f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 7 Aug 2024 15:26:09 -0300 Subject: media: imx-mipi-csis: Switch to RUNTIME_PM_OPS() Replace SET_RUNTIME_PM_OPS() with its modern RUNTIME_PM_OPS() alternative. The combined usage of pm_ptr() and RUNTIME_PM_OPS() allows the compiler to evaluate if the runtime suspend/resume() functions are used at build time or are simply dead code. This allows removing the __maybe_unused notation from the runtime suspend/resume() functions. Signed-off-by: Fabio Estevam Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240807182610.81244-1-festevam@gmail.com Signed-off-by: Laurent Pinchart --- drivers/media/platform/nxp/imx-mipi-csis.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index e0b1d3adda10..44e1402e8be1 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -1347,7 +1347,7 @@ err_parse: * Suspend/resume */ -static int __maybe_unused mipi_csis_runtime_suspend(struct device *dev) +static int mipi_csis_runtime_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd); @@ -1362,7 +1362,7 @@ static int __maybe_unused mipi_csis_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused mipi_csis_runtime_resume(struct device *dev) +static int mipi_csis_runtime_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd); @@ -1382,8 +1382,8 @@ static int __maybe_unused mipi_csis_runtime_resume(struct device *dev) } static const struct dev_pm_ops mipi_csis_pm_ops = { - SET_RUNTIME_PM_OPS(mipi_csis_runtime_suspend, mipi_csis_runtime_resume, - NULL) + RUNTIME_PM_OPS(mipi_csis_runtime_suspend, mipi_csis_runtime_resume, + NULL) }; /* ----------------------------------------------------------------------------- @@ -1574,7 +1574,7 @@ static struct platform_driver mipi_csis_driver = { .driver = { .of_match_table = mipi_csis_of_match, .name = CSIS_DRIVER_NAME, - .pm = &mipi_csis_pm_ops, + .pm = pm_ptr(&mipi_csis_pm_ops), }, }; -- cgit v1.2.3-70-g09d2 From 4fb5babe965768daf497503f4082e74abd1c0fed Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 7 Aug 2024 15:26:10 -0300 Subject: media: imx8mq-mipi-csi2: Switch to RUNTIME/SYSTEM_SLEEP_PM_OPS() Replace SET_RUNTIME_PM_OPS()/SET SYSTEM_SLEEP_PM_OPS() with their modern RUNTIME_PM_OPS() and SYSTEM_SLEEP_PM_OPS() alternatives. The combined usage of pm_ptr() and RUNTIME_PM_OPS/SYSTEM_SLEEP_PM_OPS() allows the compiler to evaluate if the runtime suspend/resume() functions are used at build time or are simply dead code. This allows removing the __maybe_unused notation from the runtime suspend/resume() functions.. Signed-off-by: Fabio Estevam Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240807182610.81244-2-festevam@gmail.com Signed-off-by: Laurent Pinchart --- drivers/media/platform/nxp/imx8mq-mipi-csi2.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c index ba2e81f24965..d4a6c5532969 100644 --- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c +++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c @@ -693,7 +693,7 @@ unlock: return ret ? -EAGAIN : 0; } -static int __maybe_unused imx8mq_mipi_csi_suspend(struct device *dev) +static int imx8mq_mipi_csi_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct csi_state *state = mipi_sd_to_csi2_state(sd); @@ -705,7 +705,7 @@ static int __maybe_unused imx8mq_mipi_csi_suspend(struct device *dev) return 0; } -static int __maybe_unused imx8mq_mipi_csi_resume(struct device *dev) +static int imx8mq_mipi_csi_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct csi_state *state = mipi_sd_to_csi2_state(sd); @@ -716,7 +716,7 @@ static int __maybe_unused imx8mq_mipi_csi_resume(struct device *dev) return imx8mq_mipi_csi_pm_resume(dev); } -static int __maybe_unused imx8mq_mipi_csi_runtime_suspend(struct device *dev) +static int imx8mq_mipi_csi_runtime_suspend(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct csi_state *state = mipi_sd_to_csi2_state(sd); @@ -731,7 +731,7 @@ static int __maybe_unused imx8mq_mipi_csi_runtime_suspend(struct device *dev) return ret; } -static int __maybe_unused imx8mq_mipi_csi_runtime_resume(struct device *dev) +static int imx8mq_mipi_csi_runtime_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct csi_state *state = mipi_sd_to_csi2_state(sd); @@ -747,10 +747,9 @@ static int __maybe_unused imx8mq_mipi_csi_runtime_resume(struct device *dev) } static const struct dev_pm_ops imx8mq_mipi_csi_pm_ops = { - SET_RUNTIME_PM_OPS(imx8mq_mipi_csi_runtime_suspend, - imx8mq_mipi_csi_runtime_resume, - NULL) - SET_SYSTEM_SLEEP_PM_OPS(imx8mq_mipi_csi_suspend, imx8mq_mipi_csi_resume) + RUNTIME_PM_OPS(imx8mq_mipi_csi_runtime_suspend, + imx8mq_mipi_csi_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(imx8mq_mipi_csi_suspend, imx8mq_mipi_csi_resume) }; /* ----------------------------------------------------------------------------- @@ -958,7 +957,7 @@ static struct platform_driver imx8mq_mipi_csi_driver = { .driver = { .of_match_table = imx8mq_mipi_csi_of_match, .name = MIPI_CSI2_DRIVER_NAME, - .pm = &imx8mq_mipi_csi_pm_ops, + .pm = pm_ptr(&imx8mq_mipi_csi_pm_ops), }, }; -- cgit v1.2.3-70-g09d2 From 9a73e931308f89bfecf18787af29a21a92470fa8 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 4 Jul 2024 18:16:18 +0200 Subject: media: rcar-vin: Add family compatible for R-Car Gen4 family MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the Gen4 family compatible. This will be used instead of a SoC specific compatible for the new Gen4 SoC V4M. Two Gen4 boards (V3U and V4H) have already been added prior and their bindings need to be kept for backward compatibility. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240704161620.1425409-5-niklas.soderlund+renesas@ragnatech.se Signed-off-by: Laurent Pinchart --- drivers/media/platform/renesas/rcar-vin/rcar-core.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c index 809c3a38cc4a..695d884a22d1 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-core.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c @@ -1274,16 +1274,7 @@ static const struct rvin_info rcar_info_r8a77995 = { .scaler = rvin_scaler_gen3, }; -static const struct rvin_info rcar_info_r8a779a0 = { - .model = RCAR_GEN3, - .use_mc = true, - .use_isp = true, - .nv12 = true, - .max_width = 4096, - .max_height = 4096, -}; - -static const struct rvin_info rcar_info_r8a779g0 = { +static const struct rvin_info rcar_info_gen4 = { .model = RCAR_GEN3, .use_mc = true, .use_isp = true, @@ -1354,12 +1345,18 @@ static const struct of_device_id rvin_of_id_table[] = { .data = &rcar_info_r8a77995, }, { + /* Keep to be compatible with old DTS files. */ .compatible = "renesas,vin-r8a779a0", - .data = &rcar_info_r8a779a0, + .data = &rcar_info_gen4, }, { + /* Keep to be compatible with old DTS files. */ .compatible = "renesas,vin-r8a779g0", - .data = &rcar_info_r8a779g0, + .data = &rcar_info_gen4, + }, + { + .compatible = "renesas,rcar-gen4-vin", + .data = &rcar_info_gen4, }, { /* Sentinel */ }, }; -- cgit v1.2.3-70-g09d2 From b5dec48c2b85e6150b50fa22b181e6396c769c76 Mon Sep 17 00:00:00 2001 From: Changhuang Liang Date: Tue, 20 Aug 2024 04:20:02 -0700 Subject: staging: media: starfive: Add the dynamic resolution support Add the dynamic resolution support for video "capture_raw" device. Otherwise it will capture the wrong image data if the width is not 1920. Fixes: e080f339c80a ("media: staging: media: starfive: camss: Add capture driver") Signed-off-by: Changhuang Liang Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240820112002.560432-1-changhuang.liang@starfivetech.com Signed-off-by: Laurent Pinchart --- drivers/staging/media/starfive/camss/stf-capture.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/media/starfive/camss/stf-capture.c b/drivers/staging/media/starfive/camss/stf-capture.c index ec5169e7b391..e15d2e97eb0b 100644 --- a/drivers/staging/media/starfive/camss/stf-capture.c +++ b/drivers/staging/media/starfive/camss/stf-capture.c @@ -180,6 +180,8 @@ static void stf_channel_set(struct stfcamss_video *video) u32 val; if (cap->type == STF_CAPTURE_RAW) { + const struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix; + val = stf_syscon_reg_read(stfcamss, VIN_CHANNEL_SEL_EN); val &= ~U0_VIN_CHANNEL_SEL_MASK; val |= CHANNEL(0); @@ -193,7 +195,7 @@ static void stf_channel_set(struct stfcamss_video *video) val |= PIXEL_HEIGH_BIT_SEL(0); val &= ~U0_VIN_PIX_CNT_END_MASK; - val |= PIX_CNT_END(IMAGE_MAX_WIDTH / 4 - 1); + val |= PIX_CNT_END(pix->width / 4 - 1); stf_syscon_reg_write(stfcamss, VIN_INRT_PIX_CFG, val); } else if (cap->type == STF_CAPTURE_YUV) { -- cgit v1.2.3-70-g09d2 From c551551b677f213ef12d7ba213483f17304790a6 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 10 Sep 2023 17:01:25 +0100 Subject: media: mt9p031: Extend match support for OF tables The driver has an OF match table, still, it uses an ID lookup table for retrieving match data. Currently, the driver is working on the assumption that an I2C device registered via OF will always match a legacy I2C device ID. The correct approach is to have an OF device ID table using i2c_get_match_data() if the devices are registered via OF/ID. Unify the OF/ID table by using MEDIA_BUS_FMT as match data for both these tables and replace the ID lookup table for the match data by i2c_get_match_data() and simplifly probe() and mt9p031_init_state(). While at it, remove the trailing comma in the terminator entry for the OF table making code robust against (theoretical) misrebases or other similar things where the new entry goes _after_ the termination without the compiler noticing. Signed-off-by: Biju Das Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20230910160126.70122-2-biju.das.jz@bp.renesas.com Signed-off-by: Laurent Pinchart --- drivers/media/i2c/mt9p031.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index f4b481212356..a22b28bed9c7 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -112,11 +112,6 @@ #define MT9P031_TEST_PATTERN_RED 0xa2 #define MT9P031_TEST_PATTERN_BLUE 0xa3 -enum mt9p031_model { - MT9P031_MODEL_COLOR, - MT9P031_MODEL_MONOCHROME, -}; - struct mt9p031 { struct v4l2_subdev subdev; struct media_pad pad; @@ -129,7 +124,7 @@ struct mt9p031 { struct clk *clk; struct regulator_bulk_data regulators[3]; - enum mt9p031_model model; + u32 code; struct aptina_pll pll; unsigned int clk_div; bool use_pll; @@ -712,12 +707,7 @@ static int mt9p031_init_state(struct v4l2_subdev *subdev, crop->height = MT9P031_WINDOW_HEIGHT_DEF; format = __mt9p031_get_pad_format(mt9p031, sd_state, 0, which); - - if (mt9p031->model == MT9P031_MODEL_MONOCHROME) - format->code = MEDIA_BUS_FMT_Y12_1X12; - else - format->code = MEDIA_BUS_FMT_SGRBG12_1X12; - + format->code = mt9p031->code; format->width = MT9P031_WINDOW_WIDTH_DEF; format->height = MT9P031_WINDOW_HEIGHT_DEF; format->field = V4L2_FIELD_NONE; @@ -1102,7 +1092,6 @@ done: static int mt9p031_probe(struct i2c_client *client) { - const struct i2c_device_id *did = i2c_client_get_device_id(client); struct mt9p031_platform_data *pdata = mt9p031_get_pdata(client); struct i2c_adapter *adapter = client->adapter; struct mt9p031 *mt9p031; @@ -1127,7 +1116,7 @@ static int mt9p031_probe(struct i2c_client *client) mt9p031->pdata = pdata; mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF; mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC; - mt9p031->model = did->driver_data; + mt9p031->code = (uintptr_t)i2c_get_match_data(client); mt9p031->regulators[0].supply = "vdd"; mt9p031->regulators[1].supply = "vdd_io"; @@ -1224,19 +1213,19 @@ static void mt9p031_remove(struct i2c_client *client) } static const struct i2c_device_id mt9p031_id[] = { - { "mt9p006", MT9P031_MODEL_COLOR }, - { "mt9p031", MT9P031_MODEL_COLOR }, - { "mt9p031m", MT9P031_MODEL_MONOCHROME }, - { } + { "mt9p006", MEDIA_BUS_FMT_SGRBG12_1X12 }, + { "mt9p031", MEDIA_BUS_FMT_SGRBG12_1X12 }, + { "mt9p031m", MEDIA_BUS_FMT_Y12_1X12 }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, mt9p031_id); #if IS_ENABLED(CONFIG_OF) static const struct of_device_id mt9p031_of_match[] = { - { .compatible = "aptina,mt9p006", }, - { .compatible = "aptina,mt9p031", }, - { .compatible = "aptina,mt9p031m", }, - { /* sentinel */ }, + { .compatible = "aptina,mt9p006", .data = (void *)MEDIA_BUS_FMT_SGRBG12_1X12 }, + { .compatible = "aptina,mt9p031", .data = (void *)MEDIA_BUS_FMT_SGRBG12_1X12 }, + { .compatible = "aptina,mt9p031m", .data = (void *)MEDIA_BUS_FMT_Y12_1X12 }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mt9p031_of_match); #endif -- cgit v1.2.3-70-g09d2 From 8c8389bba49d8cd0ffc5bf2b27cb3233268a45d5 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 10 Sep 2023 17:01:26 +0100 Subject: media: mt9p031: Drop CONFIG_OF ifdeffery Drop of_match_ptr() from mt9p031_i2c_driver and get rid of ugly CONFIG_OF if check. This slightly increases the size of mt9p031_i2c_driver on non-OF system and shouldn't be an issue. Add mod_devicetable.h include. It also allows, in case if needed, to enumerate this device via ACPI with PRP0001 magic. Signed-off-by: Biju Das Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20230910160126.70122-3-biju.das.jz@bp.renesas.com Signed-off-by: Laurent Pinchart --- drivers/media/i2c/mt9p031.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index a22b28bed9c7..d8735c246e52 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1220,7 +1221,6 @@ static const struct i2c_device_id mt9p031_id[] = { }; MODULE_DEVICE_TABLE(i2c, mt9p031_id); -#if IS_ENABLED(CONFIG_OF) static const struct of_device_id mt9p031_of_match[] = { { .compatible = "aptina,mt9p006", .data = (void *)MEDIA_BUS_FMT_SGRBG12_1X12 }, { .compatible = "aptina,mt9p031", .data = (void *)MEDIA_BUS_FMT_SGRBG12_1X12 }, @@ -1228,11 +1228,10 @@ static const struct of_device_id mt9p031_of_match[] = { { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mt9p031_of_match); -#endif static struct i2c_driver mt9p031_i2c_driver = { .driver = { - .of_match_table = of_match_ptr(mt9p031_of_match), + .of_match_table = mt9p031_of_match, .name = "mt9p031", }, .probe = mt9p031_probe, -- cgit v1.2.3-70-g09d2 From a9fef294e8b1e53a7ea733576dd4788c84ec8a21 Mon Sep 17 00:00:00 2001 From: Deborah Brouwer Date: Thu, 9 Nov 2023 10:48:38 -0800 Subject: media: vicodec: allow en/decoder cmd w/o CAPTURE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vicodec will ignore en/decoder commands if the CAPTURE queue is not streaming. But this prevents CMD_STOP from being used during a dynamic resolution change to mark the last source buffer. Since CMD_STOP is ignored, but doesn’t fail, there is no warning that the command needs to be resent, and CAPTURE will continue to attempt to dequeue buffers waiting, futilely, for one with V4L2_BUF_FLAG_LAST. Fix this problem by carrying out en/decoder commands even if CAPTURE is not streaming. Fixes: d17589afa9706 vicodec: improve handling of ENC_CMD_STOP/START Reported by: Nicolas Dufresne Signed-off-by: Deborah Brouwer Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vicodec/vicodec-core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c index 3e011fe62ae1..846e90c06291 100644 --- a/drivers/media/test-drivers/vicodec/vicodec-core.c +++ b/drivers/media/test-drivers/vicodec/vicodec-core.c @@ -1215,8 +1215,7 @@ static int vicodec_encoder_cmd(struct file *file, void *fh, if (ret < 0) return ret; - if (!vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q) || - !vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q)) + if (!vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q)) return 0; ret = v4l2_m2m_ioctl_encoder_cmd(file, fh, ec); @@ -1250,8 +1249,7 @@ static int vicodec_decoder_cmd(struct file *file, void *fh, if (ret < 0) return ret; - if (!vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q) || - !vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q)) + if (!vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q)) return 0; ret = v4l2_m2m_ioctl_decoder_cmd(file, fh, dc); -- cgit v1.2.3-70-g09d2 From c2c982def1a3a1963ebddd41cc70ee026cb8b765 Mon Sep 17 00:00:00 2001 From: Anastasia Belova Date: Mon, 5 Feb 2024 18:23:50 +0300 Subject: media: coda: cast an operand of multiplication to a larger type If the width and height are 0xffff (or close), the result of a multiplication will overflow. Cast to a larger type to avoid undefined behavior. Such values are possible in CODA7, but unlikely. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 918c66fd4126 ("[media] coda: add CODA7541 decoding support") Signed-off-by: Anastasia Belova Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/chips-media/coda/coda-bit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/chips-media/coda/coda-bit.c b/drivers/media/platform/chips-media/coda/coda-bit.c index ed47d5bd8d61..84ded154adfe 100644 --- a/drivers/media/platform/chips-media/coda/coda-bit.c +++ b/drivers/media/platform/chips-media/coda/coda-bit.c @@ -585,7 +585,7 @@ static int coda_alloc_context_buffers(struct coda_ctx *ctx, if (!ctx->slicebuf.vaddr && q_data->fourcc == V4L2_PIX_FMT_H264) { /* worst case slice size */ - size = (DIV_ROUND_UP(q_data->rect.width, 16) * + size = (unsigned long)(DIV_ROUND_UP(q_data->rect.width, 16) * DIV_ROUND_UP(q_data->rect.height, 16)) * 3200 / 8 + 512; ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size, "slicebuf"); -- cgit v1.2.3-70-g09d2 From 3be11cda62e668c34c388e8d84c06c457abde9f2 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Thu, 28 Mar 2024 10:34:38 +0100 Subject: media: verisilicon: AV1: Be more flexible with postproc capabilities The RK3588 post-processor block is able to convert 10-bit pixel-formats into 8-bit pixel-formats. Fixes: 003afda97c65 ("media: verisilicon: Enable AV1 decoder on rk3588") Signed-off-by: Benjamin Gaignard Reviewed-by: Nicolas Dufresne Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/rockchip_vpu_hw.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c index f97527670783..964122e7c355 100644 --- a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c +++ b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c @@ -82,7 +82,6 @@ static const struct hantro_fmt rockchip_vpu981_postproc_fmts[] = { { .fourcc = V4L2_PIX_FMT_NV12, .codec_mode = HANTRO_MODE_NONE, - .match_depth = true, .postprocessed = true, .frmsize = { .min_width = ROCKCHIP_VPU981_MIN_SIZE, -- cgit v1.2.3-70-g09d2 From 65e9f52ffdc8af9c8f68f720554c944b429f7e06 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Thu, 28 Mar 2024 10:34:39 +0100 Subject: media: verisilicon: Fix auxiliary buffer allocation size Use v4l2_av1_tile_info->tile_cols to get the number of columns in the frame and make auxiliary buffer memory size computation more accurate. Fixes: 727a400686a2 ("media: verisilicon: Add Rockchip AV1 decoder") Signed-off-by: Benjamin Gaignard Reviewed-by: Nicolas Dufresne Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c index cc4483857489..65e8f2d07400 100644 --- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c +++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c @@ -257,7 +257,8 @@ static int rockchip_vpu981_av1_dec_tiles_reallocate(struct hantro_ctx *ctx) struct hantro_dev *vpu = ctx->dev; struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec; struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls; - unsigned int num_tile_cols = 1 << ctrls->tile_group_entry->tile_col; + const struct v4l2_av1_tile_info *tile_info = &ctrls->frame->tile_info; + unsigned int num_tile_cols = tile_info->tile_cols; unsigned int height = ALIGN(ctrls->frame->frame_height_minus_1 + 1, 64); unsigned int height_in_sb = height / 64; unsigned int stripe_num = ((height + 8) + 63) / 64; -- cgit v1.2.3-70-g09d2 From 2038c32162dc5929edf5b4ca56549eeb34ca4c13 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Tue, 21 May 2024 17:26:03 +0200 Subject: media: verisilicon: AV1: Correct some sizes/positions on register fields Some fields aren't well positioned or with an incorrect size inside the hardware registers. Fix them. This doesn't impact the Fluster score. Fixes: 727a400686a2 ("media: verisilicon: Add Rockchip AV1 decoder") Signed-off-by: Benjamin Gaignard Reviewed-by: Nicolas Dufresne Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/rockchip_vpu981_regs.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_regs.h b/drivers/media/platform/verisilicon/rockchip_vpu981_regs.h index 850ff0f84424..e4008da64f19 100644 --- a/drivers/media/platform/verisilicon/rockchip_vpu981_regs.h +++ b/drivers/media/platform/verisilicon/rockchip_vpu981_regs.h @@ -327,7 +327,7 @@ #define av1_apf_threshold AV1_DEC_REG(55, 0, 0xffff) #define av1_apf_single_pu_mode AV1_DEC_REG(55, 30, 0x1) -#define av1_apf_disable AV1_DEC_REG(55, 30, 0x1) +#define av1_apf_disable AV1_DEC_REG(55, 31, 0x1) #define av1_dec_max_burst AV1_DEC_REG(58, 0, 0xff) #define av1_dec_buswidth AV1_DEC_REG(58, 8, 0x7) @@ -337,10 +337,10 @@ #define av1_dec_mc_polltime AV1_DEC_REG(58, 17, 0x3ff) #define av1_dec_mc_pollmode AV1_DEC_REG(58, 27, 0x3) -#define av1_filt_ref_adj_3 AV1_DEC_REG(59, 0, 0x3f) -#define av1_filt_ref_adj_2 AV1_DEC_REG(59, 7, 0x3f) -#define av1_filt_ref_adj_1 AV1_DEC_REG(59, 14, 0x3f) -#define av1_filt_ref_adj_0 AV1_DEC_REG(59, 21, 0x3f) +#define av1_filt_ref_adj_3 AV1_DEC_REG(59, 0, 0x7f) +#define av1_filt_ref_adj_2 AV1_DEC_REG(59, 7, 0x7f) +#define av1_filt_ref_adj_1 AV1_DEC_REG(59, 14, 0x7f) +#define av1_filt_ref_adj_0 AV1_DEC_REG(59, 21, 0x7f) #define av1_ref0_sign_bias AV1_DEC_REG(59, 28, 0x1) #define av1_ref1_sign_bias AV1_DEC_REG(59, 29, 0x1) #define av1_ref2_sign_bias AV1_DEC_REG(59, 30, 0x1) -- cgit v1.2.3-70-g09d2 From 3e5111c51b8ae0022dc73821914bf198538e439f Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Fri, 21 Jun 2024 12:50:22 +0200 Subject: media: verisilicon: Move Rockchip hardware drivers to the corresponding option There is no need to compile the Rockchip specific drivers if CONFIG_VIDEO_HANTRO_ROCKCHIP is not set. All the driver functions are only referenced by rockchip_vpu_hw.c which is already under this option. Fixes: c9caebd57b3a ("media: hantro: merge Rockchip platform drivers") Fixes: c07665f99386 ("media: hantro: Add H.264 support for Rockchip VDPU2") Signed-off-by: Alexander Stein Reviewed-by: Nicolas Dufresne Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/verisilicon/Makefile b/drivers/media/platform/verisilicon/Makefile index eb38a1833b02..d483530b2fef 100644 --- a/drivers/media/platform/verisilicon/Makefile +++ b/drivers/media/platform/verisilicon/Makefile @@ -14,10 +14,6 @@ hantro-vpu-y += \ hantro_g2.o \ hantro_g2_hevc_dec.o \ hantro_g2_vp9_dec.o \ - rockchip_vpu2_hw_jpeg_enc.o \ - rockchip_vpu2_hw_h264_dec.o \ - rockchip_vpu2_hw_mpeg2_dec.o \ - rockchip_vpu2_hw_vp8_dec.o \ rockchip_vpu981_hw_av1_dec.o \ rockchip_av1_filmgrain.o \ rockchip_av1_entropymode.o \ @@ -35,6 +31,10 @@ hantro-vpu-$(CONFIG_VIDEO_HANTRO_SAMA5D4) += \ sama5d4_vdec_hw.o hantro-vpu-$(CONFIG_VIDEO_HANTRO_ROCKCHIP) += \ + rockchip_vpu2_hw_jpeg_enc.o \ + rockchip_vpu2_hw_h264_dec.o \ + rockchip_vpu2_hw_mpeg2_dec.o \ + rockchip_vpu2_hw_vp8_dec.o \ rockchip_vpu_hw.o hantro-vpu-$(CONFIG_VIDEO_HANTRO_SUNXI) += \ -- cgit v1.2.3-70-g09d2 From f579c748ffe02a9a335ed3a254e2b44b54f0cf7c Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Fri, 21 Jun 2024 12:50:23 +0200 Subject: media: verisilicon: Move Rockchip AV1 hardware drivers to the corresponding option There is no need to compile the Rockchip specific AV1 drivers if CONFIG_VIDEO_HANTRO_ROCKCHIP is not set. All the driver functions are only referenced by rockchip_vpu_hw.c which is already under this option. Fixes: 727a400686a2 ("media: verisilicon: Add Rockchip AV1 decoder") Fixes: d8ebe59e7b36 ("media: verisilicon: Add film grain feature to AV1 driver") Fixes: c0d0e579db4e ("media: verisilicon: Add AV1 entropy helpers") Signed-off-by: Alexander Stein Reviewed-by: Nicolas Dufresne Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/verisilicon/Makefile b/drivers/media/platform/verisilicon/Makefile index d483530b2fef..f6f019d04ff0 100644 --- a/drivers/media/platform/verisilicon/Makefile +++ b/drivers/media/platform/verisilicon/Makefile @@ -14,9 +14,6 @@ hantro-vpu-y += \ hantro_g2.o \ hantro_g2_hevc_dec.o \ hantro_g2_vp9_dec.o \ - rockchip_vpu981_hw_av1_dec.o \ - rockchip_av1_filmgrain.o \ - rockchip_av1_entropymode.o \ hantro_jpeg.o \ hantro_h264.o \ hantro_hevc.o \ @@ -35,6 +32,9 @@ hantro-vpu-$(CONFIG_VIDEO_HANTRO_ROCKCHIP) += \ rockchip_vpu2_hw_h264_dec.o \ rockchip_vpu2_hw_mpeg2_dec.o \ rockchip_vpu2_hw_vp8_dec.o \ + rockchip_vpu981_hw_av1_dec.o \ + rockchip_av1_filmgrain.o \ + rockchip_av1_entropymode.o \ rockchip_vpu_hw.o hantro-vpu-$(CONFIG_VIDEO_HANTRO_SUNXI) += \ -- cgit v1.2.3-70-g09d2 From c5a85ed88e043474161bbfe54002c89c1cb50ee2 Mon Sep 17 00:00:00 2001 From: Zheng Wang Date: Tue, 18 Jun 2024 14:55:59 +0530 Subject: media: venus: fix use after free bug in venus_remove due to race condition in venus_probe, core->work is bound with venus_sys_error_handler, which is used to handle error. The code use core->sys_err_done to make sync work. The core->work is started in venus_event_notify. If we call venus_remove, there might be an unfished work. The possible sequence is as follows: CPU0 CPU1 |venus_sys_error_handler venus_remove | hfi_destroy | venus_hfi_destroy | kfree(hdev); | |hfi_reinit |venus_hfi_queues_reinit |//use hdev Fix it by canceling the work in venus_remove. Cc: stable@vger.kernel.org Fixes: af2c3834c8ca ("[media] media: venus: adding core part and helper functions") Signed-off-by: Zheng Wang Signed-off-by: Dikshita Agarwal Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 165c947a6703..84e95a46dfc9 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -430,6 +430,7 @@ static void venus_remove(struct platform_device *pdev) struct device *dev = core->dev; int ret; + cancel_delayed_work_sync(&core->work); ret = pm_runtime_get_sync(dev); WARN_ON(ret < 0); -- cgit v1.2.3-70-g09d2 From cd7a060df5b7b03966af0f3474571bf85fadafaf Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 2 Jun 2024 16:27:34 +0200 Subject: media: venus: Constify struct dec_bufsize_ops and enc_bufsize_ops "struct dec_bufsize_ops and "struct enc_bufsize_ops" are not modified in this driver. Constifying these structures moves some data to a read-only section, so increase overall security. On a x86_64, with allmodconfig: Before: text data bss dec hex filename 12494 822 0 13316 3404 drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.o After: text data bss dec hex filename 12766 566 0 13332 3414 drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.o Signed-off-by: Christophe JAILLET Reviewed-by: Bryan O'Donoghue Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c index f5a655973c08..6289166786ec 100644 --- a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c +++ b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c @@ -1063,51 +1063,51 @@ struct enc_bufsize_ops { u32 (*persist)(void); }; -static struct dec_bufsize_ops dec_h264_ops = { +static const struct dec_bufsize_ops dec_h264_ops = { .scratch = h264d_scratch_size, .scratch1 = h264d_scratch1_size, .persist1 = h264d_persist1_size, }; -static struct dec_bufsize_ops dec_h265_ops = { +static const struct dec_bufsize_ops dec_h265_ops = { .scratch = h265d_scratch_size, .scratch1 = h265d_scratch1_size, .persist1 = h265d_persist1_size, }; -static struct dec_bufsize_ops dec_vp8_ops = { +static const struct dec_bufsize_ops dec_vp8_ops = { .scratch = vpxd_scratch_size, .scratch1 = vp8d_scratch1_size, .persist1 = vp8d_persist1_size, }; -static struct dec_bufsize_ops dec_vp9_ops = { +static const struct dec_bufsize_ops dec_vp9_ops = { .scratch = vpxd_scratch_size, .scratch1 = vp9d_scratch1_size, .persist1 = vp9d_persist1_size, }; -static struct dec_bufsize_ops dec_mpeg2_ops = { +static const struct dec_bufsize_ops dec_mpeg2_ops = { .scratch = mpeg2d_scratch_size, .scratch1 = mpeg2d_scratch1_size, .persist1 = mpeg2d_persist1_size, }; -static struct enc_bufsize_ops enc_h264_ops = { +static const struct enc_bufsize_ops enc_h264_ops = { .scratch = h264e_scratch_size, .scratch1 = h264e_scratch1_size, .scratch2 = enc_scratch2_size, .persist = enc_persist_size, }; -static struct enc_bufsize_ops enc_h265_ops = { +static const struct enc_bufsize_ops enc_h265_ops = { .scratch = h265e_scratch_size, .scratch1 = h265e_scratch1_size, .scratch2 = enc_scratch2_size, .persist = enc_persist_size, }; -static struct enc_bufsize_ops enc_vp8_ops = { +static const struct enc_bufsize_ops enc_vp8_ops = { .scratch = vp8e_scratch_size, .scratch1 = vp8e_scratch1_size, .scratch2 = enc_scratch2_size, @@ -1186,7 +1186,7 @@ static int bufreq_dec(struct hfi_plat_buffers_params *params, u32 buftype, u32 codec = params->codec; u32 width = params->width, height = params->height, out_min_count; u32 out_width = params->out_width, out_height = params->out_height; - struct dec_bufsize_ops *dec_ops; + const struct dec_bufsize_ops *dec_ops; bool is_secondary_output = params->dec.is_secondary_output; bool is_interlaced = params->dec.is_interlaced; u32 max_mbs_per_frame = params->dec.max_mbs_per_frame; @@ -1260,7 +1260,7 @@ static int bufreq_enc(struct hfi_plat_buffers_params *params, u32 buftype, struct hfi_buffer_requirements *bufreq) { enum hfi_version version = params->version; - struct enc_bufsize_ops *enc_ops; + const struct enc_bufsize_ops *enc_ops; u32 width = params->width; u32 height = params->height; bool is_tenbit = params->enc.is_tenbit; -- cgit v1.2.3-70-g09d2 From 46b63be830cae306e019447eeb0425cc627e6b88 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 14 Aug 2024 11:39:22 +0000 Subject: media: venus: Refactor struct hfi_uncompressed_plane_info This field is never used, but if we remove it we would change the size of the struct and can lead to behavior change. Stay on the safe side by replacing the single element array with a single element field. This fixes the following cocci warning: drivers/media/platform/qcom/venus/hfi_helper.h:1003:43-60: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) Signed-off-by: Ricardo Ribalda Reviewed-by: Bryan O'Donoghue Acked-by: Vikash Garodia Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/hfi_helper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index e4c05d62cfc7..5e91f3f6984d 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -1005,7 +1005,7 @@ struct hfi_uncompressed_plane_constraints { struct hfi_uncompressed_plane_info { u32 format; u32 num_planes; - struct hfi_uncompressed_plane_constraints plane_constraints[1]; + struct hfi_uncompressed_plane_constraints plane_constraints; }; struct hfi_uncompressed_format_supported { -- cgit v1.2.3-70-g09d2 From 886115c43e7d17fbc704b1cccf9315b9ae844e39 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 14 Aug 2024 11:39:23 +0000 Subject: media: venus: Refactor struct hfi_session_get_property_pkt The struct hfi_session_get_property_pkt is always used to fetch a single property. Make that explicit in the code and avoid a single element array at the end of the struct. This change fixes the following cocci warning: drivers/media/platform/qcom/venus/hfi_cmds.h:194:5-9: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) Signed-off-by: Ricardo Ribalda Reviewed-by: Bryan O'Donoghue Acked-by: Vikash Garodia Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/hfi_cmds.c | 4 ++-- drivers/media/platform/qcom/venus/hfi_cmds.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 3418d2dd9371..75f4a3d3e748 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -402,7 +402,7 @@ static int pkt_session_get_property_1x(struct hfi_session_get_property_pkt *pkt, pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_GET_PROPERTY; pkt->shdr.session_id = hash32_ptr(cookie); pkt->num_properties = 1; - pkt->data[0] = ptype; + pkt->data = ptype; return 0; } @@ -1110,7 +1110,7 @@ pkt_session_get_property_3xx(struct hfi_session_get_property_pkt *pkt, switch (ptype) { case HFI_PROPERTY_CONFIG_VDEC_ENTROPY: - pkt->data[0] = HFI_PROPERTY_CONFIG_VDEC_ENTROPY; + pkt->data = HFI_PROPERTY_CONFIG_VDEC_ENTROPY; break; default: ret = pkt_session_get_property_1x(pkt, cookie, ptype); diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.h b/drivers/media/platform/qcom/venus/hfi_cmds.h index 20acd412ee7b..9152fcdef369 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.h +++ b/drivers/media/platform/qcom/venus/hfi_cmds.h @@ -217,7 +217,7 @@ struct hfi_session_resume_pkt { struct hfi_session_get_property_pkt { struct hfi_session_hdr_pkt shdr; u32 num_properties; - u32 data[1]; + u32 data; }; struct hfi_session_release_buffer_pkt { -- cgit v1.2.3-70-g09d2 From 4aed1c43954d81dcfefca41a3b13a3ef4aa2cdb9 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 14 Aug 2024 11:39:24 +0000 Subject: media: venus: Refactor struct hfi_uncompressed_format_supported plane_info is not a typical array, the data is not contiguous: pinfo = (void *)pinfo + sizeof(*constr) * num_planes + 2 * sizeof(u32); Replace the single element array with a single element field. This fixes the following cocci warning: drivers/media/platform/qcom/venus/hfi_helper.h:1009:36-46: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) Signed-off-by: Ricardo Ribalda Reviewed-by: Bryan O'Donoghue Acked-by: Vikash Garodia Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/hfi_helper.h | 2 +- drivers/media/platform/qcom/venus/hfi_parser.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index 5e91f3f6984d..3edefa4edeb9 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -1011,7 +1011,7 @@ struct hfi_uncompressed_plane_info { struct hfi_uncompressed_format_supported { u32 buffer_type; u32 format_entries; - struct hfi_uncompressed_plane_info plane_info[1]; + struct hfi_uncompressed_plane_info plane_info; }; struct hfi_uncompressed_plane_actual { diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index c43839539d4d..3df241dc3a11 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -157,7 +157,7 @@ static void parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data) { struct hfi_uncompressed_format_supported *fmt = data; - struct hfi_uncompressed_plane_info *pinfo = fmt->plane_info; + struct hfi_uncompressed_plane_info *pinfo = &fmt->plane_info; struct hfi_uncompressed_plane_constraints *constr; struct raw_formats rawfmts[MAX_FMT_ENTRIES] = {}; u32 entries = fmt->format_entries; -- cgit v1.2.3-70-g09d2 From 3a5fde16fc2f06109eba04fcc6f3f1b25cd7fee5 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 14 Aug 2024 11:39:25 +0000 Subject: media: venus: Refactor hfi_session_empty_buffer_uncompressed_plane0_pkt The single element array data[1] is never used. Replace it with a padding field of the same size. This fixes the following cocci error: drivers/media/platform/qcom/venus/hfi_cmds.h:163:5-9: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) Signed-off-by: Ricardo Ribalda Acked-by: Vikash Garodia Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/hfi_cmds.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.h b/drivers/media/platform/qcom/venus/hfi_cmds.h index 9152fcdef369..40d19516f5a2 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.h +++ b/drivers/media/platform/qcom/venus/hfi_cmds.h @@ -168,7 +168,7 @@ struct hfi_session_empty_buffer_uncompressed_plane0_pkt { u32 input_tag; u32 packet_buffer; u32 extradata_buffer; - u32 data[1]; + u32 data; }; struct hfi_session_empty_buffer_uncompressed_plane1_pkt { -- cgit v1.2.3-70-g09d2 From fd694882754148274942b45a8113f461816730fb Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 14 Aug 2024 11:39:26 +0000 Subject: media: venus: Refactor hfi_session_empty_buffer_compressed_pkt The single element array data[1] is never used. Replace it with a padding field of the same size. This fixes the following cocci warning: drivers/media/platform/qcom/venus/hfi_cmds.h:146:5-9: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) Signed-off-by: Ricardo Ribalda Reviewed-by: Bryan O'Donoghue Acked-by: Vikash Garodia Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/hfi_cmds.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.h b/drivers/media/platform/qcom/venus/hfi_cmds.h index 40d19516f5a2..f2306e37078f 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.h +++ b/drivers/media/platform/qcom/venus/hfi_cmds.h @@ -151,7 +151,7 @@ struct hfi_session_empty_buffer_compressed_pkt { u32 input_tag; u32 packet_buffer; u32 extradata_buffer; - u32 data[1]; + u32 data; }; struct hfi_session_empty_buffer_uncompressed_plane0_pkt { -- cgit v1.2.3-70-g09d2 From d604a7bfd0f866cea85d295060c5dd853c421939 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 14 Aug 2024 11:39:27 +0000 Subject: media: venus: Refactor hfi_sys_get_property_pkt Replace a single length element array with an element. This fixes the following cocci warning: drivers/media/platform/qcom/venus/hfi_cmds.h:77:5-9: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) Signed-off-by: Ricardo Ribalda Acked-by: Vikash Garodia Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/hfi_cmds.c | 2 +- drivers/media/platform/qcom/venus/hfi_cmds.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 75f4a3d3e748..0a4de8ca1df5 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -156,7 +156,7 @@ void pkt_sys_image_version(struct hfi_sys_get_property_pkt *pkt) pkt->hdr.size = sizeof(*pkt); pkt->hdr.pkt_type = HFI_CMD_SYS_GET_PROPERTY; pkt->num_properties = 1; - pkt->data[0] = HFI_PROPERTY_SYS_IMAGE_VERSION; + pkt->data = HFI_PROPERTY_SYS_IMAGE_VERSION; } int pkt_session_init(struct hfi_session_init_pkt *pkt, void *cookie, diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.h b/drivers/media/platform/qcom/venus/hfi_cmds.h index f2306e37078f..617ec89fb02b 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.h +++ b/drivers/media/platform/qcom/venus/hfi_cmds.h @@ -74,7 +74,7 @@ struct hfi_sys_set_property_pkt { struct hfi_sys_get_property_pkt { struct hfi_pkt_hdr hdr; u32 num_properties; - u32 data[1]; + u32 data; }; struct hfi_sys_set_buffers_pkt { -- cgit v1.2.3-70-g09d2 From 87210c8052aec86955d613df9af8198b9c7c2cef Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 14 Aug 2024 11:39:28 +0000 Subject: media: venus: Refactor hfi_session_fill_buffer_pkt The single data array data[1] is only used to save the extradata_size. Replace it with a single element field. This fixes the following cocci warning: drivers/media/platform/qcom/venus/hfi_cmds.h:175:5-9: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) Signed-off-by: Ricardo Ribalda Reviewed-by: Bryan O'Donoghue Acked-by: Vikash Garodia Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/hfi_cmds.c | 2 +- drivers/media/platform/qcom/venus/hfi_cmds.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 0a4de8ca1df5..3ae063094e3e 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -331,7 +331,7 @@ int pkt_session_ftb(struct hfi_session_fill_buffer_pkt *pkt, void *cookie, pkt->alloc_len = out_frame->alloc_len; pkt->filled_len = out_frame->filled_len; pkt->offset = out_frame->offset; - pkt->data[0] = out_frame->extradata_size; + pkt->data = out_frame->extradata_size; return 0; } diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.h b/drivers/media/platform/qcom/venus/hfi_cmds.h index 617ec89fb02b..5c30babc2e3b 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.h +++ b/drivers/media/platform/qcom/venus/hfi_cmds.h @@ -198,7 +198,7 @@ struct hfi_session_fill_buffer_pkt { u32 output_tag; u32 packet_buffer; u32 extradata_buffer; - u32 data[1]; + u32 data; }; struct hfi_session_flush_pkt { -- cgit v1.2.3-70-g09d2 From a539fb1a894ea35ec8372cb0d8fe052d9a3bdfaa Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 14 Aug 2024 11:39:29 +0000 Subject: media: venus: Refactor hfi_buffer_alloc_mode_supported Replace the old style single element array at the end of the struct with a flex array. The code does not allocate this structure, so the size change should not be a problem. This fixes the following cocci warning: drivers/media/platform/qcom/venus/hfi_helper.h:1233:5-9: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) Signed-off-by: Ricardo Ribalda Acked-by: Vikash Garodia Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/hfi_helper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index 3edefa4edeb9..755aabcd8048 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -1264,7 +1264,7 @@ struct hfi_interlace_format_supported { struct hfi_buffer_alloc_mode_supported { u32 buffer_type; u32 num_entries; - u32 data[1]; + u32 data[]; }; struct hfi_mb_error_map { -- cgit v1.2.3-70-g09d2 From 909dc5ac4fd7689346efe9789f1b0dbef6b58678 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 14 Aug 2024 11:39:30 +0000 Subject: media: venus: Convert one-element-arrays to flex-arrays This structures are not used, and have a single element array at the end of them. This fix the following cocci warnings: drivers/media/platform/qcom/venus/hfi_helper.h:764:5-15: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) drivers/media/platform/qcom/venus/hfi_helper.h:1041:5-15: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) drivers/media/platform/qcom/venus/hfi_helper.h:1088:39-51: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) drivers/media/platform/qcom/venus/hfi_helper.h:1093:5-22: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) drivers/media/platform/qcom/venus/hfi_helper.h:1144:4-8: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) drivers/media/platform/qcom/venus/hfi_helper.h:1239:4-8: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) drivers/media/platform/qcom/venus/hfi_helper.h:1272:4-13: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) drivers/media/platform/qcom/venus/hfi_cmds.h:85:5-16: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) drivers/media/platform/qcom/venus/hfi_cmds.h:180:5-9: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) drivers/media/platform/qcom/venus/hfi_cmds.h:189:5-9: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) Signed-off-by: Ricardo Ribalda Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/hfi_cmds.h | 6 +++--- drivers/media/platform/qcom/venus/hfi_helper.h | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.h b/drivers/media/platform/qcom/venus/hfi_cmds.h index 5c30babc2e3b..c4d165a2d325 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.h +++ b/drivers/media/platform/qcom/venus/hfi_cmds.h @@ -82,7 +82,7 @@ struct hfi_sys_set_buffers_pkt { u32 buffer_type; u32 buffer_size; u32 num_buffers; - u32 buffer_addr[1]; + u32 buffer_addr[]; }; struct hfi_sys_ping_pkt { @@ -177,7 +177,7 @@ struct hfi_session_empty_buffer_uncompressed_plane1_pkt { u32 filled_len; u32 offset; u32 packet_buffer2; - u32 data[1]; + u32 data; }; struct hfi_session_empty_buffer_uncompressed_plane2_pkt { @@ -186,7 +186,7 @@ struct hfi_session_empty_buffer_uncompressed_plane2_pkt { u32 filled_len; u32 offset; u32 packet_buffer3; - u32 data[1]; + u32 data; }; struct hfi_session_fill_buffer_pkt { diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index 755aabcd8048..f44059f19505 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -761,7 +761,7 @@ struct hfi_multi_stream_3x { struct hfi_multi_view_format { u32 views; - u32 view_order[1]; + u32 view_order[]; }; #define HFI_MULTI_SLICE_OFF 0x1 @@ -1038,7 +1038,7 @@ struct hfi_codec_supported { struct hfi_properties_supported { u32 num_properties; - u32 properties[1]; + u32 properties[]; }; struct hfi_max_sessions_supported { @@ -1085,12 +1085,12 @@ struct hfi_resource_ocmem_requirement { struct hfi_resource_ocmem_requirement_info { u32 num_entries; - struct hfi_resource_ocmem_requirement requirements[1]; + struct hfi_resource_ocmem_requirement requirements[]; }; struct hfi_property_sys_image_version_info_type { u32 string_size; - u8 str_image_version[1]; + u8 str_image_version[]; }; struct hfi_codec_mask_supported { @@ -1141,7 +1141,7 @@ struct hfi_extradata_header { u32 port_index; u32 type; u32 data_size; - u8 data[1]; + u8 data[]; }; struct hfi_batch_info { @@ -1236,7 +1236,7 @@ static inline void hfi_bufreq_set_count_min_host(struct hfi_buffer_requirements struct hfi_data_payload { u32 size; - u8 data[1]; + u8 data[]; }; struct hfi_enable_picture { @@ -1269,7 +1269,7 @@ struct hfi_buffer_alloc_mode_supported { struct hfi_mb_error_map { u32 error_map_size; - u8 error_map[1]; + u8 error_map[]; }; struct hfi_metadata_pass_through { -- cgit v1.2.3-70-g09d2 From 7ce555252c711f7520be42abba5c7401b3b68456 Mon Sep 17 00:00:00 2001 From: Lu Baolu Date: Mon, 12 Aug 2024 15:21:05 +0800 Subject: media: venus: firmware: Use iommu_paging_domain_alloc() An iommu domain is allocated in venus_firmware_init() and is attached to core->fw.dev in the same function. Use iommu_paging_domain_alloc() to make it explicit. Signed-off-by: Lu Baolu Reviewed-by: Jason Gunthorpe Link: https://lore.kernel.org/r/20240610085555.88197-10-baolu.lu@linux.intel.com Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/firmware.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index fe7da2b30482..66a18830e66d 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -316,10 +316,10 @@ int venus_firmware_init(struct venus_core *core) core->fw.dev = &pdev->dev; - iommu_dom = iommu_domain_alloc(&platform_bus_type); - if (!iommu_dom) { + iommu_dom = iommu_paging_domain_alloc(core->fw.dev); + if (IS_ERR(iommu_dom)) { dev_err(core->fw.dev, "Failed to allocate iommu domain\n"); - ret = -ENOMEM; + ret = PTR_ERR(iommu_dom); goto err_unregister; } -- cgit v1.2.3-70-g09d2 From 06564411dcd10277eb5e192106603114cac3fd3f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 19 Jun 2024 02:42:34 +0300 Subject: media: microchip-isc: Drop v4l2_subdev_link_validate() for video devices The v4l2_subdev_link_validate() function is a helper designed to validate links whose sink is a subdev. When called on a link whose sink is a video device, it only prints a warning and returns. As the microchip-isc driver implements manual validate of the subdev to video device link, we can just drop the v4l2_subdev_link_validate() to avoid the warning. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Acked-by: Sakari Ailus --- drivers/media/platform/microchip/microchip-isc-base.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/microchip/microchip-isc-base.c b/drivers/media/platform/microchip/microchip-isc-base.c index f3a5cbacadbe..28e56f6a695d 100644 --- a/drivers/media/platform/microchip/microchip-isc-base.c +++ b/drivers/media/platform/microchip/microchip-isc-base.c @@ -902,8 +902,11 @@ static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f) return 0; } -static int isc_validate(struct isc_device *isc) +static int isc_link_validate(struct media_link *link) { + struct video_device *vdev = + media_entity_to_video_device(link->sink->entity); + struct isc_device *isc = video_get_drvdata(vdev); int ret; int i; struct isc_format *sd_fmt = NULL; @@ -1906,20 +1909,6 @@ int microchip_isc_pipeline_init(struct isc_device *isc) } EXPORT_SYMBOL_GPL(microchip_isc_pipeline_init); -static int isc_link_validate(struct media_link *link) -{ - struct video_device *vdev = - media_entity_to_video_device(link->sink->entity); - struct isc_device *isc = video_get_drvdata(vdev); - int ret; - - ret = v4l2_subdev_link_validate(link); - if (ret) - return ret; - - return isc_validate(isc); -} - static const struct media_entity_operations isc_entity_operations = { .link_validate = isc_link_validate, }; -- cgit v1.2.3-70-g09d2 From 2dc5d5d401f5c6cecd97800ffef82e8d17d228f0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 19 Jun 2024 02:46:16 +0300 Subject: media: sun4i_csi: Implement link validate for sun4i_csi subdev The sun4i_csi driver doesn't implement link validation for the subdev it registers, leaving the link between the subdev and its source unvalidated. Fix it, using the v4l2_subdev_link_validate() helper. Fixes: 577bbf23b758 ("media: sunxi: Add A10 CSI driver") Cc: stable@vger.kernel.org Signed-off-by: Laurent Pinchart Acked-by: Chen-Yu Tsai Reviewed-by: Tomi Valkeinen Acked-by: Sakari Ailus --- drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c index 097a3a08ef7d..dbb26c7b2f8d 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c @@ -39,6 +39,10 @@ static const struct media_entity_operations sun4i_csi_video_entity_ops = { .link_validate = v4l2_subdev_link_validate, }; +static const struct media_entity_operations sun4i_csi_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + static int sun4i_csi_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_subdev *subdev, struct v4l2_async_connection *asd) @@ -214,6 +218,7 @@ static int sun4i_csi_probe(struct platform_device *pdev) subdev->internal_ops = &sun4i_csi_subdev_internal_ops; subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + subdev->entity.ops = &sun4i_csi_subdev_entity_ops; subdev->owner = THIS_MODULE; snprintf(subdev->name, sizeof(subdev->name), "sun4i-csi-0"); v4l2_set_subdevdata(subdev, csi); -- cgit v1.2.3-70-g09d2 From 9bde4f7caf38df94257d2483e89f371b1a530e9a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 19 Jun 2024 02:46:16 +0300 Subject: media: sun4i_csi: Don't use v4l2_subdev_link_validate() for video device The v4l2_subdev_link_validate() function is a helper designed to validate links whose sink is a subdev. When called on a link whose sink is a video device, it only prints a warning and returns. Its usage in the sun4i_csi driver is wrong, leaving the link from the sub4i_csi subdev to the capture video device unvalidated. Planned improvements to the v4l2_subdev_link_validate() function will turn the warning into an error, breaking the sun4i_csi driver. As an interim measure, move the warning to the sun4i_csi driver in a custom validation handler, and drop the call to the helper. Signed-off-by: Laurent Pinchart Acked-by: Chen-Yu Tsai Reviewed-by: Tomi Valkeinen Acked-by: Sakari Ailus --- drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c index dbb26c7b2f8d..d07e980aba61 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c @@ -35,8 +35,15 @@ struct sun4i_csi_traits { bool has_isp; }; +static int sun4i_csi_video_link_validate(struct media_link *link) +{ + dev_warn_once(link->graph_obj.mdev->dev, + "Driver bug: link validation not implemented\n"); + return 0; +} + static const struct media_entity_operations sun4i_csi_video_entity_ops = { - .link_validate = v4l2_subdev_link_validate, + .link_validate = sun4i_csi_video_link_validate, }; static const struct media_entity_operations sun4i_csi_subdev_entity_ops = { -- cgit v1.2.3-70-g09d2 From d1307671e5221967809c4b626affbad29e371006 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 19 Jun 2024 02:56:01 +0300 Subject: media: v4l2-subdev: Refactor warnings in v4l2_subdev_link_validate() The v4l2_subdev_link_validate() function prints a one-time warning if it gets called on a link whose source or sink is not a subdev. As links get validated in the context of their sink, a call to the helper when the link's sink is not a subdev indicates that the driver has set its .link_validate() handler to v4l2_subdev_link_validate() on a non-subdev entity, which is a clear driver bug. On the other hand, the link's source not being a subdev indicates that the helper is used for a subdev connected to a video output device, which is a lesser issue, if only because this is currently common practice. There are no drivers left in the kernel that use v4l2_subdev_link_validate() in a context where it may get called on a non-subdev sink. Replace the pr_warn_once() with a WARN_ON_ONCE() in this case to make sure that new offenders won't be introduced. A subsequent change will improve the v4l2_subdev_link_validate() helper to properly support validating video device to subdev links. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Acked-by: Sakari Ailus --- drivers/media/v4l2-core/v4l2-subdev.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 7c5812d55315..d3196042d5c5 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1443,11 +1443,15 @@ int v4l2_subdev_link_validate(struct media_link *link) bool states_locked; int ret; - if (!is_media_entity_v4l2_subdev(link->sink->entity) || - !is_media_entity_v4l2_subdev(link->source->entity)) { - pr_warn_once("%s of link '%s':%u->'%s':%u is not a V4L2 sub-device, driver bug!\n", - !is_media_entity_v4l2_subdev(link->sink->entity) ? - "sink" : "source", + /* + * Links are validated in the context of the sink entity. Usage of this + * helper on a sink that is not a subdev is a clear driver bug. + */ + if (WARN_ON_ONCE(!is_media_entity_v4l2_subdev(link->sink->entity))) + return -EINVAL; + + if (!is_media_entity_v4l2_subdev(link->source->entity)) { + pr_warn_once("source of link '%s':%u->'%s':%u is not a V4L2 sub-device, driver bug!\n", link->source->entity->name, link->source->index, link->sink->entity->name, link->sink->index); return 0; -- cgit v1.2.3-70-g09d2 From 5fd3e2412ade67ea20d855f0aea821c650d27559 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 19 Jun 2024 03:02:42 +0300 Subject: media: v4l2-subdev: Support hybrid links in v4l2_subdev_link_validate() The v4l2_subdev_link_validate() helper function is meant to be used as a drop-in implementation of a V4L2 subdev entity .link_validate() handler. It supports subdev-to-subdev links only, and complains if one end of the link is not a subdev. This forces drivers that have video output devices connected to subdevs to implement a custom .link_validate() handler, calling v4l2_subdev_link_validate() for the subdev-to-subdev links, and performing manual link validation for the video-to-subdev links. Video devices embed a media entity, and therefore also have a .link_validate() operation. For video capture devices, the operation should be manually implemented by drivers for validate the subdev-to-video links. For video output devices, on the other hand, that operation is never called, as link validation is performed in the context of the sink entity. As a result, we end up forcing drivers to implement a custom .link_validate() handler for subdevs connected to video output devices, when the video devices provide an operation that could be used for that purpose. To improve that situation, make v4l2_subdev_link_validate() delegate link validation to the source's .link_validate() operation when the link source is a video device and the link sink is a subdev. This allows broader usage of v4l2_subdev_link_validate(), and simplifies drivers by making video device link validation easy to implement in the video device .link_validate(), regardless of whether the video device is an output device or a capture device. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Acked-by: Sakari Ailus --- drivers/media/v4l2-core/v4l2-subdev.c | 43 +++++++++++++++++++++++++++++++---- include/media/v4l2-subdev.h | 6 +++++ 2 files changed, 44 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index d3196042d5c5..3a4ba08810d2 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1450,13 +1450,46 @@ int v4l2_subdev_link_validate(struct media_link *link) if (WARN_ON_ONCE(!is_media_entity_v4l2_subdev(link->sink->entity))) return -EINVAL; - if (!is_media_entity_v4l2_subdev(link->source->entity)) { - pr_warn_once("source of link '%s':%u->'%s':%u is not a V4L2 sub-device, driver bug!\n", - link->source->entity->name, link->source->index, - link->sink->entity->name, link->sink->index); - return 0; + /* + * If the source is a video device, delegate link validation to it. This + * allows usage of this helper for subdev connected to a video output + * device, provided that the driver implement the video output device's + * .link_validate() operation. + */ + if (is_media_entity_v4l2_video_device(link->source->entity)) { + struct media_entity *source = link->source->entity; + + if (!source->ops || !source->ops->link_validate) { + /* + * Many existing drivers do not implement the required + * .link_validate() operation for their video devices. + * Print a warning to get the drivers fixed, and return + * 0 to avoid breaking userspace. This should + * eventually be turned into a WARN_ON() when all + * drivers will have been fixed. + */ + pr_warn_once("video device '%s' does not implement .link_validate(), driver bug!\n", + source->name); + return 0; + } + + /* + * Avoid infinite loops in case a video device incorrectly uses + * this helper function as its .link_validate() handler. + */ + if (WARN_ON(source->ops->link_validate == v4l2_subdev_link_validate)) + return -EINVAL; + + return source->ops->link_validate(link); } + /* + * If the source is still not a subdev, usage of this helper is a clear + * driver bug. + */ + if (WARN_ON(!is_media_entity_v4l2_subdev(link->source->entity))) + return -EINVAL; + sink_sd = media_entity_to_v4l2_subdev(link->sink->entity); source_sd = media_entity_to_v4l2_subdev(link->source->entity); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index bd235d325ff9..8daa0929865c 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1250,6 +1250,12 @@ int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, * calls v4l2_subdev_link_validate_default() to ensure that * width, height and the media bus pixel code are equal on both * source and sink of the link. + * + * The function can be used as a drop-in &media_entity_ops.link_validate + * implementation for v4l2_subdev instances. It supports all links between + * subdevs, as well as links between subdevs and video devices, provided that + * the video devices also implement their &media_entity_ops.link_validate + * operation. */ int v4l2_subdev_link_validate(struct media_link *link); -- cgit v1.2.3-70-g09d2 From a3d44f011c6b7a87c9ad0d5be2f5fa092fa419bf Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 18 Jun 2024 21:46:44 +0300 Subject: media: renesas: vsp1: Implement .link_validate() for video devices The v4l2_subdev_link_validate() helper prints a warning if the .link_validate() operation is not implemented for video devices connected to the subdevs. Implement the operation to silence the warning. Ideally validation of the link between the video device and the subdev should be implemented in that operation. That would however break userspace that does not configure formats on all video devices before starting streaming. While this mode of operation may not be considered valid by the V4L2 API specification (interpretation differ), it is nonetheless supported by the vsp1 driver at the moment and used by at least the vsp1 unit test suite, and possibly other userspace applications. Removing it would be a regression. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Acked-by: Sakari Ailus --- drivers/media/platform/renesas/vsp1/vsp1_video.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index fdb46ec0c872..e728f9f5160e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -1081,6 +1081,27 @@ static const struct v4l2_file_operations vsp1_video_fops = { .mmap = vb2_fop_mmap, }; +/* ----------------------------------------------------------------------------- + * Media entity operations + */ + +static int vsp1_video_link_validate(struct media_link *link) +{ + /* + * Ideally, link validation should be implemented here instead of + * calling vsp1_video_verify_format() in vsp1_video_streamon() + * manually. That would however break userspace that start one video + * device before configures formats on other video devices in the + * pipeline. This operation is just a no-op to silence the warnings + * from v4l2_subdev_link_validate(). + */ + return 0; +} + +static const struct media_entity_operations vsp1_video_media_ops = { + .link_validate = vsp1_video_link_validate, +}; + /* ----------------------------------------------------------------------------- * Suspend and Resume */ @@ -1215,6 +1236,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, /* ... and the video node... */ video->video.v4l2_dev = &video->vsp1->v4l2_dev; + video->video.entity.ops = &vsp1_video_media_ops; video->video.fops = &vsp1_video_fops; snprintf(video->video.name, sizeof(video->video.name), "%s %s", rwpf->entity.subdev.name, direction); -- cgit v1.2.3-70-g09d2 From e5700c9037727d5a69a677d6dba25010b485d65b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 26 Aug 2024 02:24:49 +0300 Subject: media: videobuf2: Drop minimum allocation requirement of 2 buffers When introducing the ability for drivers to indicate the minimum number of buffers they require an application to allocate, commit 6662edcd32cc ("media: videobuf2: Add min_reqbufs_allocation field to vb2_queue structure") also introduced a global minimum of 2 buffers. It turns out this breaks the Renesas R-Car VSP test suite, where a test that allocates a single buffer fails when two buffers are used. One may consider debatable whether test suite failures without failures in production use cases should be considered as a regression, but operation with a single buffer is a valid use case. While full frame rate can't be maintained, memory-to-memory devices can still be used with a decent efficiency, and requiring applications to allocate multiple buffers for single-shot use cases with capture devices would just waste memory. For those reasons, fix the regression by dropping the global minimum of buffers. Individual drivers can still set their own minimum. Fixes: 6662edcd32cc ("media: videobuf2: Add min_reqbufs_allocation field to vb2_queue structure") Cc: stable@vger.kernel.org Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Acked-by: Tomasz Figa Link: https://lore.kernel.org/r/20240825232449.25905-1-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart --- drivers/media/common/videobuf2/videobuf2-core.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 500a4e0c84ab..29a8d876e6c2 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -2632,13 +2632,6 @@ int vb2_core_queue_init(struct vb2_queue *q) if (WARN_ON(q->supports_requests && q->min_queued_buffers)) return -EINVAL; - /* - * The minimum requirement is 2: one buffer is used - * by the hardware while the other is being processed by userspace. - */ - if (q->min_reqbufs_allocation < 2) - q->min_reqbufs_allocation = 2; - /* * If the driver needs 'min_queued_buffers' in the queue before * calling start_streaming() then the minimum requirement is -- cgit v1.2.3-70-g09d2 From 9be85491619f1953b8a29590ca630be571941ffa Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Thu, 13 Jun 2024 17:33:55 +0800 Subject: media: mediatek: vcodec: Fix H264 multi stateless decoder smatch warning Fix a smatch static checker warning on vdec_h264_req_multi_if.c. Which leads to a kernel crash when fb is NULL. Fixes: 397edc703a10 ("media: mediatek: vcodec: add h264 decoder driver for mt8186") Signed-off-by: Yunfei Dong Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- .../mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c index 2d4611e7fa0b..1ed0ccec5665 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c @@ -724,11 +724,16 @@ static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs return vpu_dec_reset(vpu); fb = inst->ctx->dev->vdec_pdata->get_cap_buffer(inst->ctx); + if (!fb) { + mtk_vdec_err(inst->ctx, "fb buffer is NULL"); + return -ENOMEM; + } + src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer); dst_buf_info = container_of(fb, struct mtk_video_dec_buf, frame_buffer); - y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0; - c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0; + y_fb_dma = fb->base_y.dma_addr; + c_fb_dma = fb->base_c.dma_addr; mtk_vdec_debug(inst->ctx, "[h264-dec] [%d] y_dma=%llx c_dma=%llx", inst->ctx->decoded_frame_cnt, y_fb_dma, c_fb_dma); -- cgit v1.2.3-70-g09d2 From b113bc7c0e83b32f4dd2d291a2b6c4803e0a2c44 Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Thu, 13 Jun 2024 17:33:56 +0800 Subject: media: mediatek: vcodec: Fix VP8 stateless decoder smatch warning Fix a smatch static checker warning on vdec_vp8_req_if.c. Which leads to a kernel crash when fb is NULL. Fixes: 7a7ae26fd458 ("media: mediatek: vcodec: support stateless VP8 decoding") Signed-off-by: Yunfei Dong Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- .../platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c index e27e728f392e..232ef3bd246a 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c @@ -334,14 +334,18 @@ static int vdec_vp8_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer); fb = inst->ctx->dev->vdec_pdata->get_cap_buffer(inst->ctx); - dst_buf_info = container_of(fb, struct mtk_video_dec_buf, frame_buffer); + if (!fb) { + mtk_vdec_err(inst->ctx, "fb buffer is NULL"); + return -ENOMEM; + } - y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0; + dst_buf_info = container_of(fb, struct mtk_video_dec_buf, frame_buffer); + y_fb_dma = fb->base_y.dma_addr; if (inst->ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 1) c_fb_dma = y_fb_dma + inst->ctx->picinfo.buf_w * inst->ctx->picinfo.buf_h; else - c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0; + c_fb_dma = fb->base_c.dma_addr; inst->vsi->dec.bs_dma = (u64)bs->dma_addr; inst->vsi->dec.bs_sz = bs->size; -- cgit v1.2.3-70-g09d2 From 7878d3a385efab560dce793b595447867fb163f2 Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Thu, 13 Jun 2024 17:33:57 +0800 Subject: media: mediatek: vcodec: Fix H264 stateless decoder smatch warning Fix a smatch static checker warning on vdec_h264_req_if.c. Which leads to a kernel crash when fb is NULL. Fixes: 06fa5f757dc5 ("media: mtk-vcodec: vdec: support stateless H.264 decoding") Signed-off-by: Yunfei Dong Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- .../platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c index 73d5cef33b2a..1e1b32faac77 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c @@ -347,11 +347,16 @@ static int vdec_h264_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, return vpu_dec_reset(vpu); fb = inst->ctx->dev->vdec_pdata->get_cap_buffer(inst->ctx); + if (!fb) { + mtk_vdec_err(inst->ctx, "fb buffer is NULL"); + return -ENOMEM; + } + src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer); dst_buf_info = container_of(fb, struct mtk_video_dec_buf, frame_buffer); - y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0; - c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0; + y_fb_dma = fb->base_y.dma_addr; + c_fb_dma = fb->base_c.dma_addr; mtk_vdec_debug(inst->ctx, "+ [%d] FB y_dma=%llx c_dma=%llx va=%p", inst->num_nalu, y_fb_dma, c_fb_dma, fb); -- cgit v1.2.3-70-g09d2 From 41eb4fb5d936bfa711020af30fa444776c1a6884 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 16 Jun 2024 08:29:53 +0200 Subject: media: mediatek: vcodec: Constify struct vb2_ops "struct vb2_ops" are not modified in this driver. Constifying this structure moves some data to a read-only section, so increase overall security. On a x86_64, with allmodconfig: Before: ====== text data bss dec hex filename 18059 3096 16 21171 52b3 drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.o After: ===== text data bss dec hex filename 18171 2968 16 21155 52a3 drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.o Signed-off-by: Christophe JAILLET Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- .../media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c | 2 +- .../media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c index 11ca2c2fbaad..e62c1c18758b 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c @@ -595,7 +595,7 @@ static void mtk_init_vdec_params(struct mtk_vcodec_dec_ctx *ctx) } } -static struct vb2_ops mtk_vdec_frame_vb2_ops = { +static const struct vb2_ops mtk_vdec_frame_vb2_ops = { .queue_setup = vb2ops_vdec_queue_setup, .buf_prepare = vb2ops_vdec_buf_prepare, .wait_prepare = vb2_ops_wait_prepare, diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c index b903e39fee89..3307dc15fc1d 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c @@ -854,7 +854,7 @@ static int vb2ops_vdec_out_buf_validate(struct vb2_buffer *vb) return 0; } -static struct vb2_ops mtk_vdec_request_vb2_ops = { +static const struct vb2_ops mtk_vdec_request_vb2_ops = { .queue_setup = vb2ops_vdec_queue_setup, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, -- cgit v1.2.3-70-g09d2 From 5423e2d220bebb4304f7ff739d02180f676eefd0 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Thu, 16 May 2024 10:41:07 +0200 Subject: media: verisilicon: Add reference buffer compression feature Reference frame compression is a feature added in the G2 decoder to compress frame buffers so that the bandwidth of storing/loading reference frames can be reduced, especially with high resolution decoded streams. The impact of compressed frames is confirmed when using perf to monitor the number of memory accesses with or without the compression feature. The following command: perf stat -a -e \ imx8_ddr0/cycles/,imx8_ddr0/read-cycles/,imx8_ddr0/write-cycles/ \ gst-launch-1.0 filesrc \ location=Jockey_3840x2160_120fps_420_8bit_HEVC_RAW.hevc ! queue ! \ h265parse ! v4l2slh265dec ! video/x-raw,format=NV12 ! fakesink Gives us these results without the compression feature: Performance counter stats for 'system wide': 1711300345 imx8_ddr0/cycles/ 892207924 imx8_ddr0/read-cycles/ 1291785864 imx8_ddr0/write-cycles/ 13.760048353 seconds time elapsed With the compression feature: Performance counter stats for 'system wide': 274526799 imx8_ddr0/cycles/ 453120194 imx8_ddr0/read-cycles/ 833391434 imx8_ddr0/write-cycles/ 18.257831534 seconds time elapsed As expected the number of read/write cycles are really lower when compression is used. Since storing the compression data requires more memory a module parameter named 'hevc_use_compression' is used to enable/disable this feature and, by default, compression isn't used. Enabling the compression feature means that the output-frames of the decoder are stored with a specific compression pixel-format. Since this pixel format is unknown, this patch restrains the compression feature usage to the cases where post-processor pixel-formats (NV12 or NV15) are selected by the applications. The Fluster compliance HEVC test suite score is still 141/147 with this patch. Signed-off-by: Benjamin Gaignard Tested-by: Nicolas Dufresne Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/Kconfig | 8 +++++ drivers/media/platform/verisilicon/hantro_g2.c | 29 +++++++++++++++++ .../platform/verisilicon/hantro_g2_hevc_dec.c | 20 ++++++++++-- .../media/platform/verisilicon/hantro_g2_regs.h | 4 +++ drivers/media/platform/verisilicon/hantro_hevc.c | 8 +++++ drivers/media/platform/verisilicon/hantro_hw.h | 38 ++++++++++++++++++++++ .../media/platform/verisilicon/hantro_postproc.c | 6 +++- 7 files changed, 109 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/platform/verisilicon/Kconfig b/drivers/media/platform/verisilicon/Kconfig index 149d0b32c324..3272a24db71d 100644 --- a/drivers/media/platform/verisilicon/Kconfig +++ b/drivers/media/platform/verisilicon/Kconfig @@ -21,6 +21,14 @@ config VIDEO_HANTRO To compile this driver as a module, choose M here: the module will be called hantro-vpu. +config VIDEO_HANTRO_HEVC_RFC + bool "Use reference frame compression for HEVC" + depends on VIDEO_HANTRO + default n + help + Enable the reference frame compression feature for the HEVC codec. + It will use more memory but save bandwidth on memory bus. + config VIDEO_HANTRO_IMX8M bool "Hantro VPU i.MX8M support" depends on VIDEO_HANTRO diff --git a/drivers/media/platform/verisilicon/hantro_g2.c b/drivers/media/platform/verisilicon/hantro_g2.c index b880a6849d58..5c1d799d8618 100644 --- a/drivers/media/platform/verisilicon/hantro_g2.c +++ b/drivers/media/platform/verisilicon/hantro_g2.c @@ -56,3 +56,32 @@ size_t hantro_g2_motion_vectors_offset(struct hantro_ctx *ctx) return ALIGN((cr_offset * 3) / 2, G2_ALIGN); } + +static size_t hantro_g2_mv_size(struct hantro_ctx *ctx) +{ + const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; + const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; + unsigned int pic_width_in_ctbs, pic_height_in_ctbs; + unsigned int max_log2_ctb_size; + + max_log2_ctb_size = sps->log2_min_luma_coding_block_size_minus3 + 3 + + sps->log2_diff_max_min_luma_coding_block_size; + pic_width_in_ctbs = (sps->pic_width_in_luma_samples + + (1 << max_log2_ctb_size) - 1) >> max_log2_ctb_size; + pic_height_in_ctbs = (sps->pic_height_in_luma_samples + (1 << max_log2_ctb_size) - 1) + >> max_log2_ctb_size; + + return pic_width_in_ctbs * pic_height_in_ctbs * (1 << (2 * (max_log2_ctb_size - 4))) * 16; +} + +size_t hantro_g2_luma_compress_offset(struct hantro_ctx *ctx) +{ + return hantro_g2_motion_vectors_offset(ctx) + + hantro_g2_mv_size(ctx); +} + +size_t hantro_g2_chroma_compress_offset(struct hantro_ctx *ctx) +{ + return hantro_g2_luma_compress_offset(ctx) + + hantro_hevc_luma_compressed_size(ctx->dst_fmt.width, ctx->dst_fmt.height); +} diff --git a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c index d3f8c33eb16c..85a44143b378 100644 --- a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c +++ b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c @@ -367,11 +367,14 @@ static int set_ref(struct hantro_ctx *ctx) const struct v4l2_ctrl_hevc_decode_params *decode_params = ctrls->decode_params; const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb; dma_addr_t luma_addr, chroma_addr, mv_addr = 0; + dma_addr_t compress_luma_addr, compress_chroma_addr = 0; struct hantro_dev *vpu = ctx->dev; struct vb2_v4l2_buffer *vb2_dst; struct hantro_decoded_buffer *dst; size_t cr_offset = hantro_g2_chroma_offset(ctx); size_t mv_offset = hantro_g2_motion_vectors_offset(ctx); + size_t compress_luma_offset = hantro_g2_luma_compress_offset(ctx); + size_t compress_chroma_offset = hantro_g2_chroma_compress_offset(ctx); u32 max_ref_frames; u16 dpb_longterm_e; static const struct hantro_reg cur_poc[] = { @@ -445,6 +448,8 @@ static int set_ref(struct hantro_ctx *ctx) chroma_addr = luma_addr + cr_offset; mv_addr = luma_addr + mv_offset; + compress_luma_addr = luma_addr + compress_luma_offset; + compress_chroma_addr = luma_addr + compress_chroma_offset; if (dpb[i].flags & V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE) dpb_longterm_e |= BIT(V4L2_HEVC_DPB_ENTRIES_NUM_MAX - 1 - i); @@ -452,6 +457,8 @@ static int set_ref(struct hantro_ctx *ctx) hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), luma_addr); hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), chroma_addr); hantro_write_addr(vpu, G2_REF_MV_ADDR(i), mv_addr); + hantro_write_addr(vpu, G2_REF_COMP_LUMA_ADDR(i), compress_luma_addr); + hantro_write_addr(vpu, G2_REF_COMP_CHROMA_ADDR(i), compress_chroma_addr); } vb2_dst = hantro_get_dst_buf(ctx); @@ -465,19 +472,27 @@ static int set_ref(struct hantro_ctx *ctx) chroma_addr = luma_addr + cr_offset; mv_addr = luma_addr + mv_offset; + compress_luma_addr = luma_addr + compress_luma_offset; + compress_chroma_addr = luma_addr + compress_chroma_offset; hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), luma_addr); hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), chroma_addr); - hantro_write_addr(vpu, G2_REF_MV_ADDR(i++), mv_addr); + hantro_write_addr(vpu, G2_REF_MV_ADDR(i), mv_addr); + hantro_write_addr(vpu, G2_REF_COMP_LUMA_ADDR(i), compress_luma_addr); + hantro_write_addr(vpu, G2_REF_COMP_CHROMA_ADDR(i++), compress_chroma_addr); hantro_write_addr(vpu, G2_OUT_LUMA_ADDR, luma_addr); hantro_write_addr(vpu, G2_OUT_CHROMA_ADDR, chroma_addr); hantro_write_addr(vpu, G2_OUT_MV_ADDR, mv_addr); + hantro_write_addr(vpu, G2_OUT_COMP_LUMA_ADDR, compress_luma_addr); + hantro_write_addr(vpu, G2_OUT_COMP_CHROMA_ADDR, compress_chroma_addr); for (; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX; i++) { hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), 0); hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), 0); hantro_write_addr(vpu, G2_REF_MV_ADDR(i), 0); + hantro_write_addr(vpu, G2_REF_COMP_LUMA_ADDR(i), 0); + hantro_write_addr(vpu, G2_REF_COMP_CHROMA_ADDR(i), 0); } hantro_reg_write(vpu, &g2_refer_lterm_e, dpb_longterm_e); @@ -594,8 +609,7 @@ int hantro_g2_hevc_dec_run(struct hantro_ctx *ctx) /* Don't disable output */ hantro_reg_write(vpu, &g2_out_dis, 0); - /* Don't compress buffers */ - hantro_reg_write(vpu, &g2_ref_compress_bypass, 1); + hantro_reg_write(vpu, &g2_ref_compress_bypass, !ctx->hevc_dec.use_compression); /* Bus width and max burst */ hantro_reg_write(vpu, &g2_buswidth, BUS_WIDTH_128); diff --git a/drivers/media/platform/verisilicon/hantro_g2_regs.h b/drivers/media/platform/verisilicon/hantro_g2_regs.h index 82606783591a..b943b1816db7 100644 --- a/drivers/media/platform/verisilicon/hantro_g2_regs.h +++ b/drivers/media/platform/verisilicon/hantro_g2_regs.h @@ -318,6 +318,10 @@ #define G2_TILE_BSD_ADDR (G2_SWREG(183)) #define G2_DS_DST (G2_SWREG(186)) #define G2_DS_DST_CHR (G2_SWREG(188)) +#define G2_OUT_COMP_LUMA_ADDR (G2_SWREG(190)) +#define G2_REF_COMP_LUMA_ADDR(i) (G2_SWREG(192) + ((i) * 0x8)) +#define G2_OUT_COMP_CHROMA_ADDR (G2_SWREG(224)) +#define G2_REF_COMP_CHROMA_ADDR(i) (G2_SWREG(226) + ((i) * 0x8)) #define g2_strm_buffer_len G2_DEC_REG(258, 0, 0xffffffff) #define g2_strm_start_offset G2_DEC_REG(259, 0, 0xffffffff) diff --git a/drivers/media/platform/verisilicon/hantro_hevc.c b/drivers/media/platform/verisilicon/hantro_hevc.c index 2c14330bc562..83cd12b0ddd6 100644 --- a/drivers/media/platform/verisilicon/hantro_hevc.c +++ b/drivers/media/platform/verisilicon/hantro_hevc.c @@ -25,6 +25,11 @@ #define MAX_TILE_COLS 20 #define MAX_TILE_ROWS 22 +static bool hevc_use_compression = IS_ENABLED(CONFIG_VIDEO_HANTRO_HEVC_RFC); +module_param_named(hevc_use_compression, hevc_use_compression, bool, 0644); +MODULE_PARM_DESC(hevc_use_compression, + "Use reference frame compression for HEVC"); + void hantro_hevc_ref_init(struct hantro_ctx *ctx) { struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; @@ -275,5 +280,8 @@ int hantro_hevc_dec_init(struct hantro_ctx *ctx) hantro_hevc_ref_init(ctx); + hevc_dec->use_compression = + hevc_use_compression & hantro_needs_postproc(ctx, ctx->vpu_dst_fmt); + return 0; } diff --git a/drivers/media/platform/verisilicon/hantro_hw.h b/drivers/media/platform/verisilicon/hantro_hw.h index 7737320cc8cc..c9b6556f8b2b 100644 --- a/drivers/media/platform/verisilicon/hantro_hw.h +++ b/drivers/media/platform/verisilicon/hantro_hw.h @@ -42,6 +42,13 @@ #define MAX_POSTPROC_BUFFERS 64 +#define CBS_SIZE 16 /* compression table size in bytes */ +#define CBS_LUMA 8 /* luminance CBS is composed of 1 8x8 coded block */ +#define CBS_CHROMA_W (8 * 2) /* chrominance CBS is composed of two 8x4 coded + * blocks, with Cb CB first then Cr CB following + */ +#define CBS_CHROMA_H 4 + struct hantro_dev; struct hantro_ctx; struct hantro_buf; @@ -144,6 +151,7 @@ struct hantro_hevc_dec_ctrls { * @ref_bufs_used: Bitfield of used reference buffers * @ctrls: V4L2 controls attached to a run * @num_tile_cols_allocated: number of allocated tiles + * @use_compression: use reference buffer compression */ struct hantro_hevc_dec_hw_ctx { struct hantro_aux_buf tile_sizes; @@ -156,6 +164,7 @@ struct hantro_hevc_dec_hw_ctx { u32 ref_bufs_used; struct hantro_hevc_dec_ctrls ctrls; unsigned int num_tile_cols_allocated; + bool use_compression; }; /** @@ -510,6 +519,33 @@ hantro_hevc_mv_size(unsigned int width, unsigned int height) return width * height / 16; } +static inline size_t +hantro_hevc_luma_compressed_size(unsigned int width, unsigned int height) +{ + u32 pic_width_in_cbsy = + round_up((width + CBS_LUMA - 1) / CBS_LUMA, CBS_SIZE); + u32 pic_height_in_cbsy = (height + CBS_LUMA - 1) / CBS_LUMA; + + return round_up(pic_width_in_cbsy * pic_height_in_cbsy, CBS_SIZE); +} + +static inline size_t +hantro_hevc_chroma_compressed_size(unsigned int width, unsigned int height) +{ + u32 pic_width_in_cbsc = + round_up((width + CBS_CHROMA_W - 1) / CBS_CHROMA_W, CBS_SIZE); + u32 pic_height_in_cbsc = (height / 2 + CBS_CHROMA_H - 1) / CBS_CHROMA_H; + + return round_up(pic_width_in_cbsc * pic_height_in_cbsc, CBS_SIZE); +} + +static inline size_t +hantro_hevc_compressed_size(unsigned int width, unsigned int height) +{ + return hantro_hevc_luma_compressed_size(width, height) + + hantro_hevc_chroma_compressed_size(width, height); +} + static inline unsigned short hantro_av1_num_sbs(unsigned short dimension) { return DIV_ROUND_UP(dimension, 64); @@ -525,6 +561,8 @@ hantro_av1_mv_size(unsigned int width, unsigned int height) size_t hantro_g2_chroma_offset(struct hantro_ctx *ctx); size_t hantro_g2_motion_vectors_offset(struct hantro_ctx *ctx); +size_t hantro_g2_luma_compress_offset(struct hantro_ctx *ctx); +size_t hantro_g2_chroma_compress_offset(struct hantro_ctx *ctx); int hantro_g1_mpeg2_dec_run(struct hantro_ctx *ctx); int rockchip_vpu2_mpeg2_dec_run(struct hantro_ctx *ctx); diff --git a/drivers/media/platform/verisilicon/hantro_postproc.c b/drivers/media/platform/verisilicon/hantro_postproc.c index 41e93176300b..232c93eea7ee 100644 --- a/drivers/media/platform/verisilicon/hantro_postproc.c +++ b/drivers/media/platform/verisilicon/hantro_postproc.c @@ -213,9 +213,13 @@ static unsigned int hantro_postproc_buffer_size(struct hantro_ctx *ctx) else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_VP9_FRAME) buf_size += hantro_vp9_mv_size(pix_mp.width, pix_mp.height); - else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_HEVC_SLICE) + else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_HEVC_SLICE) { buf_size += hantro_hevc_mv_size(pix_mp.width, pix_mp.height); + if (ctx->hevc_dec.use_compression) + buf_size += hantro_hevc_compressed_size(pix_mp.width, + pix_mp.height); + } else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_AV1_FRAME) buf_size += hantro_av1_mv_size(pix_mp.width, pix_mp.height); -- cgit v1.2.3-70-g09d2 From ccdeb8d57f7fb3e5c05d72cb7dfb9bc78f09f542 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 18 Jun 2024 20:18:34 +0200 Subject: media: hantro: Disable multicore support Avoid exposing equal Hantro video codecs to userspace. Equal video codecs allow scheduling work between the cores. For that kernel support is required, which does not yet exist. Until that is implemented avoid exposing each core separately to userspace so that multicore can be added in the future without breaking userspace ABI. This was written with Rockchip RK3588 in mind (which has 4 Hantro H1 cores), but applies to all SoCs. Signed-off-by: Sebastian Reichel Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/hantro_drv.c | 47 +++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'drivers') diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c index 34b123dafd89..748187623990 100644 --- a/drivers/media/platform/verisilicon/hantro_drv.c +++ b/drivers/media/platform/verisilicon/hantro_drv.c @@ -992,6 +992,49 @@ static const struct media_device_ops hantro_m2m_media_ops = { .req_queue = v4l2_m2m_request_queue, }; +/* + * Some SoCs, like RK3588 have multiple identical Hantro cores, but the + * kernel is currently missing support for multi-core handling. Exposing + * separate devices for each core to userspace is bad, since that does + * not allow scheduling tasks properly (and creates ABI). With this workaround + * the driver will only probe for the first core and early exit for the other + * cores. Once the driver gains multi-core support, the same technique + * for detecting the main core can be used to cluster all cores together. + */ +static int hantro_disable_multicore(struct hantro_dev *vpu) +{ + struct device_node *node = NULL; + const char *compatible; + bool is_main_core; + int ret; + + /* Intentionally ignores the fallback strings */ + ret = of_property_read_string(vpu->dev->of_node, "compatible", &compatible); + if (ret) + return ret; + + /* The first compatible and available node found is considered the main core */ + do { + node = of_find_compatible_node(node, NULL, compatible); + if (of_device_is_available(node)) + break; + } while (node); + + if (!node) + return -EINVAL; + + is_main_core = (vpu->dev->of_node == node); + + of_node_put(node); + + if (!is_main_core) { + dev_info(vpu->dev, "missing multi-core support, ignoring this instance\n"); + return -ENODEV; + } + + return 0; +} + static int hantro_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -1011,6 +1054,10 @@ static int hantro_probe(struct platform_device *pdev) match = of_match_node(of_hantro_match, pdev->dev.of_node); vpu->variant = match->data; + ret = hantro_disable_multicore(vpu); + if (ret) + return ret; + /* * Support for nxp,imx8mq-vpu is kept for backwards compatibility * but it's deprecated. Please update your DTS file to use -- cgit v1.2.3-70-g09d2 From 90effee40b7b612e570f83416b6b6ff6f474fb9e Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 18 Jun 2024 20:18:35 +0200 Subject: media: hantro: Add RK3588 VEPU121 RK3588 handling is exactly the same as RK3568. This is not handled using fallback compatibles to avoid exposing multiple video devices on kernels not having the multicore disable patch. Signed-off-by: Sebastian Reichel Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/hantro_drv.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c index 748187623990..05bbac853c4f 100644 --- a/drivers/media/platform/verisilicon/hantro_drv.c +++ b/drivers/media/platform/verisilicon/hantro_drv.c @@ -722,6 +722,7 @@ static const struct of_device_id of_hantro_match[] = { { .compatible = "rockchip,rk3399-vpu", .data = &rk3399_vpu_variant, }, { .compatible = "rockchip,rk3568-vepu", .data = &rk3568_vepu_variant, }, { .compatible = "rockchip,rk3568-vpu", .data = &rk3568_vpu_variant, }, + { .compatible = "rockchip,rk3588-vepu121", .data = &rk3568_vepu_variant, }, { .compatible = "rockchip,rk3588-av1-vpu", .data = &rk3588_vpu981_variant, }, #endif #ifdef CONFIG_VIDEO_HANTRO_IMX8M -- cgit v1.2.3-70-g09d2 From afe6ec667e8846c8470d32789cebbc435588972d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 22 Jul 2024 17:25:53 +0200 Subject: media: imagination: VIDEO_E5010_JPEG_ENC should depend on ARCH_K3 Currently, the Imagination E5010 JPEG Encoder is only present on Texas Instruments K3 SoCs. Hence add a dependency on ARCH_K3, to prevent asking the user about this driver when configuring a kernel without TI K3 SoC support. The dependency can be relaxed if/when the encoder appears on other SoC families. Fixes: a1e294045885 ("media: imagination: Add E5010 JPEG Encoder driver") Signed-off-by: Geert Uytterhoeven Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/imagination/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/platform/imagination/Kconfig b/drivers/media/platform/imagination/Kconfig index 7139ae22219b..a302c955483d 100644 --- a/drivers/media/platform/imagination/Kconfig +++ b/drivers/media/platform/imagination/Kconfig @@ -2,6 +2,7 @@ config VIDEO_E5010_JPEG_ENC tristate "Imagination E5010 JPEG Encoder Driver" depends on VIDEO_DEV + depends on ARCH_K3 || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV -- cgit v1.2.3-70-g09d2 From f2b0effa9d079ab1a842c0c67c0ed01146305c6a Mon Sep 17 00:00:00 2001 From: Umang Jain Date: Tue, 13 Aug 2024 11:22:00 +0530 Subject: media: imx283: Add 3/3 binning mode IMX283 supports 12-bit 3/3 binning mode with 1824x1216 resolution. Introduce the mode config for the same. Signed-off-by: Umang Jain Reviewed-by: Kieran Bingham Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx283.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'drivers') diff --git a/drivers/media/i2c/imx283.c b/drivers/media/i2c/imx283.c index 8490618c5071..94276f4f2d83 100644 --- a/drivers/media/i2c/imx283.c +++ b/drivers/media/i2c/imx283.c @@ -465,6 +465,39 @@ static const struct imx283_mode supported_modes_12bit[] = { .horizontal_ob = 48, .vertical_ob = 4, + .crop = { + .top = 40, + .left = 108, + .width = 5472, + .height = 3648, + }, + }, + { + /* + * Readout mode 3 : 3/3 binned mode (1824x1216) + */ + .mode = IMX283_MODE_3, + .bpp = 12, + .width = 1824, + .height = 1216, + .min_hmax = 1894, /* Pixels (284 * 480MHz/72MHz + padding) */ + .min_vmax = 4200, /* Lines */ + + /* 60.00 fps */ + .default_hmax = 1900, /* 285 @ 480MHz/72Mhz */ + .default_vmax = 4200, + + .veff = 1234, + .vst = 0, + .vct = 0, + + .hbin_ratio = 3, + .vbin_ratio = 3, + + .min_shr = 16, + .horizontal_ob = 32, + .vertical_ob = 4, + .crop = { .top = 40, .left = 108, -- cgit v1.2.3-70-g09d2 From 1395ff433cde9074f11ea24f092368570b5a544e Mon Sep 17 00:00:00 2001 From: Max Staudt Date: Thu, 20 Jun 2024 23:45:41 +0900 Subject: staging: media: ipu3: Drop superfluous check in imgu_vb2_stop_streaming() The check for imgu_all_nodes_streaming() seems superfluous, since imgu->streaming can only become true once imgu_all_nodes_streaming() has been true. Hence, checking for imgu->streaming == true should imply imgu_all_nodes_streaming(), and therefore suffice. Signed-off-by: Max Staudt Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/staging/media/ipu3/ipu3-v4l2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c index 3df58eb3e882..541556037c42 100644 --- a/drivers/staging/media/ipu3/ipu3-v4l2.c +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c @@ -548,7 +548,7 @@ static void imgu_vb2_stop_streaming(struct vb2_queue *vq) mutex_lock(&imgu->streaming_lock); /* Was this the first node with streaming disabled? */ - if (imgu->streaming && imgu_all_nodes_streaming(imgu, node)) { + if (imgu->streaming) { /* Yes, really stop streaming now */ dev_dbg(dev, "IMGU streaming is ready to stop"); r = imgu_s_stream(imgu, false); -- cgit v1.2.3-70-g09d2 From cf27c5a1bd7cc0b8df26044b08be3488dec129f8 Mon Sep 17 00:00:00 2001 From: Max Staudt Date: Thu, 20 Jun 2024 23:45:42 +0900 Subject: staging: media: ipu3: Return buffers outside of needless locking In imgu_vb2_start_streaming()'s error path, imgu_return_all_buffers() is outside the streaming_lock and after the call to video_device_pipeline_stop(). Let's apply the same order in imgu_vb2_stop_streaming() as well. Signed-off-by: Max Staudt Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/staging/media/ipu3/ipu3-v4l2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c index 541556037c42..3ff390b04e1a 100644 --- a/drivers/staging/media/ipu3/ipu3-v4l2.c +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c @@ -556,10 +556,10 @@ static void imgu_vb2_stop_streaming(struct vb2_queue *vq) imgu->streaming = false; } - imgu_return_all_buffers(imgu, node, VB2_BUF_STATE_ERROR); mutex_unlock(&imgu->streaming_lock); video_device_pipeline_stop(&node->vdev); + imgu_return_all_buffers(imgu, node, VB2_BUF_STATE_ERROR); } /******************** v4l2_ioctl_ops ********************/ -- cgit v1.2.3-70-g09d2 From 5256cfec79d8c9a6a6075b317c5a46ef9ff0038f Mon Sep 17 00:00:00 2001 From: Max Staudt Date: Thu, 20 Jun 2024 23:45:43 +0900 Subject: staging: media: ipu3: Stop streaming in inverse order of starting imgu_vb2_stop_streaming() did not order shutdown items in the inverse order and count of what imgu_vb2_start_streaming() does. Consequently, v6.7's new WARN_ON in call_s_stream() started screaming because it was called multiple times on the entire pipe, yet it should only be called when the pipe is interrupted by any first node being taken offline. This reorders streamoff to be the inverse of streamon, and uses analogous conditions to decide when and how often to call additional teardown functions. v4l2_subdev_call(s_stream, 0) remains outside the streaming_lock, analogously to imgu_vb2_start_streaming(). Signed-off-by: Max Staudt Reviewed-by: Bingbu Cao Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/staging/media/ipu3/ipu3-v4l2.c | 36 +++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c index 3ff390b04e1a..e7aee7e3db5b 100644 --- a/drivers/staging/media/ipu3/ipu3-v4l2.c +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c @@ -535,29 +535,51 @@ static void imgu_vb2_stop_streaming(struct vb2_queue *vq) container_of(vq, struct imgu_video_device, vbq); int r; unsigned int pipe; + bool stop_streaming = false; + /* Verify that the node had been setup with imgu_v4l2_node_setup() */ WARN_ON(!node->enabled); pipe = node->pipe; dev_dbg(dev, "Try to stream off node [%u][%u]", pipe, node->id); - imgu_pipe = &imgu->imgu_pipe[pipe]; - r = v4l2_subdev_call(&imgu_pipe->imgu_sd.subdev, video, s_stream, 0); - if (r) - dev_err(&imgu->pci_dev->dev, - "failed to stop subdev streaming\n"); + /* + * When the first node of a streaming setup is stopped, the entire + * pipeline needs to stop before individual nodes are disabled. + * Perform the inverse of the initial setup. + * + * Part 1 - s_stream on the entire pipeline + */ mutex_lock(&imgu->streaming_lock); - /* Was this the first node with streaming disabled? */ if (imgu->streaming) { /* Yes, really stop streaming now */ dev_dbg(dev, "IMGU streaming is ready to stop"); r = imgu_s_stream(imgu, false); if (!r) imgu->streaming = false; + stop_streaming = true; } - mutex_unlock(&imgu->streaming_lock); + /* Part 2 - s_stream on subdevs + * + * If we call s_stream multiple times, Linux v6.7's call_s_stream() + * WARNs and aborts. Thus, disable all pipes at once, and only once. + */ + if (stop_streaming) { + for_each_set_bit(pipe, imgu->css.enabled_pipes, + IMGU_MAX_PIPE_NUM) { + imgu_pipe = &imgu->imgu_pipe[pipe]; + + r = v4l2_subdev_call(&imgu_pipe->imgu_sd.subdev, + video, s_stream, 0); + if (r) + dev_err(&imgu->pci_dev->dev, + "failed to stop subdev streaming\n"); + } + } + + /* Part 3 - individual node teardown */ video_device_pipeline_stop(&node->vdev); imgu_return_all_buffers(imgu, node, VB2_BUF_STATE_ERROR); } -- cgit v1.2.3-70-g09d2 From 2933d7e12ed792e12ab690efd15f6e7ce81ba739 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 16 Aug 2024 12:31:59 +0000 Subject: media: ar0521: Refactor ar0521_power_off() Factor out all the power off logic, except the clk_disable_unprepare(), to a new function __ar0521_power_off(). This allows ar0521_power_on() to explicitly clean-out the clock during the error-path. The following smatch warning is fixed: drivers/media/i2c/ar0521.c:912 ar0521_power_on() warn: 'sensor->extclk' from clk_prepare_enable() not released on lines: 912. Signed-off-by: Ricardo Ribalda Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ar0521.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/ar0521.c b/drivers/media/i2c/ar0521.c index 09331cf95c62..56a724b4d47e 100644 --- a/drivers/media/i2c/ar0521.c +++ b/drivers/media/i2c/ar0521.c @@ -835,14 +835,12 @@ static const struct initial_reg { be(0x0707)), /* 3F44: couple k factor 2 */ }; -static int ar0521_power_off(struct device *dev) +static void __ar0521_power_off(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ar0521_dev *sensor = to_ar0521_dev(sd); int i; - clk_disable_unprepare(sensor->extclk); - if (sensor->reset_gpio) gpiod_set_value(sensor->reset_gpio, 1); /* assert RESET signal */ @@ -850,6 +848,16 @@ static int ar0521_power_off(struct device *dev) if (sensor->supplies[i]) regulator_disable(sensor->supplies[i]); } +} + +static int ar0521_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ar0521_dev *sensor = to_ar0521_dev(sd); + + clk_disable_unprepare(sensor->extclk); + __ar0521_power_off(dev); + return 0; } @@ -908,7 +916,8 @@ static int ar0521_power_on(struct device *dev) return 0; off: - ar0521_power_off(dev); + clk_disable_unprepare(sensor->extclk); + __ar0521_power_off(dev); return ret; } -- cgit v1.2.3-70-g09d2 From 820d81a167d422027ca4af7f0511e101f21aa214 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 16 Aug 2024 12:32:00 +0000 Subject: media: i2c: ov5645: Refactor ov5645_set_power_off() Factor out all the power off logic, except clk_disable_unprepare(), to a new function __ov5645_set_power_off(). This allows ov5645_set_power_on() to excplicitly clean-out the clock during the error-path. The following smatch warning is fixed: drivers/media/i2c/ov5645.c:690 ov5645_set_power_on() warn: 'ov5645->xclk' from clk_prepare_enable() not released on lines: 690. Signed-off-by: Ricardo Ribalda Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov5645.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index 6c2d221f6973..0c32bd2940ec 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -635,7 +635,7 @@ static int ov5645_set_register_array(struct ov5645 *ov5645, return 0; } -static int ov5645_set_power_off(struct device *dev) +static void __ov5645_set_power_off(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov5645 *ov5645 = to_ov5645(sd); @@ -643,8 +643,16 @@ static int ov5645_set_power_off(struct device *dev) ov5645_write_reg(ov5645, OV5645_IO_MIPI_CTRL00, 0x58); gpiod_set_value_cansleep(ov5645->rst_gpio, 1); gpiod_set_value_cansleep(ov5645->enable_gpio, 0); - clk_disable_unprepare(ov5645->xclk); regulator_bulk_disable(OV5645_NUM_SUPPLIES, ov5645->supplies); +} + +static int ov5645_set_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov5645 *ov5645 = to_ov5645(sd); + + __ov5645_set_power_off(dev); + clk_disable_unprepare(ov5645->xclk); return 0; } @@ -686,7 +694,8 @@ static int ov5645_set_power_on(struct device *dev) return 0; exit: - ov5645_set_power_off(dev); + __ov5645_set_power_off(dev); + clk_disable_unprepare(ov5645->xclk); return ret; } -- cgit v1.2.3-70-g09d2 From 334af4c52eb2eba004d1419bf93d638fcf19abb8 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 16 Aug 2024 12:32:01 +0000 Subject: media: i2c: s5c73m3: Move clk_prepare to its own function Smatch is very confused by a clk_prepare_enable() being called in an error-path. Fix this warning by moving the clk_prepare_enable() to its own function. drivers/media/i2c/s5c73m3/s5c73m3-core.c:1425 __s5c73m3_power_off() warn: 'state->clock' from clk_prepare_enable() not released on lines: 1425. Signed-off-by: Ricardo Ribalda Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/s5c73m3/s5c73m3-core.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index e89e888f028e..7716dfe2b8c9 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1392,6 +1392,16 @@ err_reg_dis: return ret; } +/* + * This function has been created just to avoid a smatch warning, + * please do not merge into __s5c73m3_power_off() until you have + * confirmed that it does not introduce a new warning. + */ +static void s5c73m3_enable_clk(struct s5c73m3 *state) +{ + clk_prepare_enable(state->clock); +} + static int __s5c73m3_power_off(struct s5c73m3 *state) { int i, ret; @@ -1421,7 +1431,8 @@ err: state->supplies[i].supply, r); } - clk_prepare_enable(state->clock); + s5c73m3_enable_clk(state); + return ret; } -- cgit v1.2.3-70-g09d2 From 157c73682dc0d0913b2f4543d5589151587a81d7 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 16 Aug 2024 12:32:02 +0000 Subject: media: tc358746: Move clk_prepare to its own function Smatch is very confused by a clk_prepare_enable() being called in an error-path. Fix this warning by moving the clk_prepare_enable() to its own function. drivers/media/i2c/tc358746.c:1631 tc358746_suspend() warn: 'tc358746->refclk' from clk_prepare_enable() not released on lines: 1631. Signed-off-by: Ricardo Ribalda Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/tc358746.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c index edf79107adc5..389582420ba7 100644 --- a/drivers/media/i2c/tc358746.c +++ b/drivers/media/i2c/tc358746.c @@ -1616,6 +1616,16 @@ static void tc358746_remove(struct i2c_client *client) pm_runtime_dont_use_autosuspend(sd->dev); } +/* + * This function has been created just to avoid a smatch warning, + * please do not merge it into tc358746_suspend until you have + * confirmed that it does not introduce a new warning. + */ +static void tc358746_clk_enable(struct tc358746 *tc358746) +{ + clk_prepare_enable(tc358746->refclk); +} + static int tc358746_suspend(struct device *dev) { struct tc358746 *tc358746 = dev_get_drvdata(dev); @@ -1626,7 +1636,7 @@ static int tc358746_suspend(struct device *dev) err = regulator_bulk_disable(ARRAY_SIZE(tc358746_supplies), tc358746->supplies); if (err) - clk_prepare_enable(tc358746->refclk); + tc358746_clk_enable(tc358746); return err; } -- cgit v1.2.3-70-g09d2 From bc24a85d41272c6fac77c07da46882df7cd81799 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 16 Aug 2024 12:32:03 +0000 Subject: media: meson: vdec_1: Refactor vdec_1_stop() Factor out all the power off logic, except the clk_disable_unprepare(), to a new function __vdec_1_stop(). This allows vdec_1_start() to explicitly clean-out the clock during the error-path. The following smatch warning is fixed: drivers/staging/media/meson/vdec/vdec_1.c:239 vdec_1_start() warn: 'core->vdec_1_clk' from clk_prepare_enable() not released on lines: 239. Signed-off-by: Ricardo Ribalda Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/staging/media/meson/vdec/vdec_1.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/meson/vdec/vdec_1.c b/drivers/staging/media/meson/vdec/vdec_1.c index 3fe2de0c9331..a65cb4959446 100644 --- a/drivers/staging/media/meson/vdec/vdec_1.c +++ b/drivers/staging/media/meson/vdec/vdec_1.c @@ -129,7 +129,7 @@ static u32 vdec_1_vififo_level(struct amvdec_session *sess) return amvdec_read_dos(core, VLD_MEM_VIFIFO_LEVEL); } -static int vdec_1_stop(struct amvdec_session *sess) +static void __vdec_1_stop(struct amvdec_session *sess) { struct amvdec_core *core = sess->core; struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; @@ -158,10 +158,17 @@ static int vdec_1_stop(struct amvdec_session *sess) regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, GEN_PWR_VDEC_1, GEN_PWR_VDEC_1); - clk_disable_unprepare(core->vdec_1_clk); - if (sess->priv) codec_ops->stop(sess); +} + +static int vdec_1_stop(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + + __vdec_1_stop(sess); + + clk_disable_unprepare(core->vdec_1_clk); return 0; } @@ -235,7 +242,8 @@ static int vdec_1_start(struct amvdec_session *sess) return 0; stop: - vdec_1_stop(sess); + __vdec_1_stop(sess); + clk_disable_unprepare(core->vdec_1_clk); return ret; } -- cgit v1.2.3-70-g09d2 From 57343cbb09a3ae489c584b2d6039804c2d4d298d Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Fri, 16 Aug 2024 12:32:04 +0000 Subject: media: meson: vdec: hevc: Refactor vdec_hevc_start and vdec_hevc_stop Make a new function __vdec_hevc_start(), that does all the initialization, except the clock initialization for G12A and SM1. Factor out all the stop logic, except the clk_disable_unprepare(), to a new function __vdec_hevc_stop. This allows vdec_hevc_start() to explicitly celan-out the clock during the error-path. The following smatch warnings are fixed: drivers/staging/media/meson/vdec/vdec_hevc.c:227 vdec_hevc_start() warn: 'core->vdec_hevc_clk' from clk_prepare_enable() not released on lines: 227. drivers/staging/media/meson/vdec/vdec_hevc.c:227 vdec_hevc_start() warn: 'core->vdec_hevcf_clk' from clk_prepare_enable() not released on lines: 227. Signed-off-by: Ricardo Ribalda Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/staging/media/meson/vdec/vdec_hevc.c | 43 +++++++++++++++++++++------- 1 file changed, 32 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/meson/vdec/vdec_hevc.c b/drivers/staging/media/meson/vdec/vdec_hevc.c index afced435c907..1939c47def58 100644 --- a/drivers/staging/media/meson/vdec/vdec_hevc.c +++ b/drivers/staging/media/meson/vdec/vdec_hevc.c @@ -110,7 +110,7 @@ static u32 vdec_hevc_vififo_level(struct amvdec_session *sess) return readl_relaxed(sess->core->dos_base + HEVC_STREAM_LEVEL); } -static int vdec_hevc_stop(struct amvdec_session *sess) +static void __vdec_hevc_stop(struct amvdec_session *sess) { struct amvdec_core *core = sess->core; struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; @@ -142,6 +142,13 @@ static int vdec_hevc_stop(struct amvdec_session *sess) else regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, GEN_PWR_VDEC_HEVC, GEN_PWR_VDEC_HEVC); +} + +static int vdec_hevc_stop(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + + __vdec_hevc_stop(sess); clk_disable_unprepare(core->vdec_hevc_clk); if (core->platform->revision == VDEC_REVISION_G12A || @@ -151,20 +158,12 @@ static int vdec_hevc_stop(struct amvdec_session *sess) return 0; } -static int vdec_hevc_start(struct amvdec_session *sess) +static int __vdec_hevc_start(struct amvdec_session *sess) { int ret; struct amvdec_core *core = sess->core; struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; - if (core->platform->revision == VDEC_REVISION_G12A || - core->platform->revision == VDEC_REVISION_SM1) { - clk_set_rate(core->vdec_hevcf_clk, 666666666); - ret = clk_prepare_enable(core->vdec_hevcf_clk); - if (ret) - return ret; - } - clk_set_rate(core->vdec_hevc_clk, 666666666); ret = clk_prepare_enable(core->vdec_hevc_clk); if (ret) { @@ -223,10 +222,32 @@ static int vdec_hevc_start(struct amvdec_session *sess) return 0; stop: - vdec_hevc_stop(sess); + __vdec_hevc_stop(sess); + clk_disable_unprepare(core->vdec_hevc_clk); return ret; } +static int vdec_hevc_start(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + int ret; + + if (core->platform->revision == VDEC_REVISION_G12A || + core->platform->revision == VDEC_REVISION_SM1) { + clk_set_rate(core->vdec_hevcf_clk, 666666666); + ret = clk_prepare_enable(core->vdec_hevcf_clk); + if (ret) + return ret; + + ret = __vdec_hevc_start(sess); + if (ret) + clk_disable_unprepare(core->vdec_hevcf_clk); + return ret; + } + + return __vdec_hevc_start(sess); +} + struct amvdec_ops vdec_hevc_ops = { .start = vdec_hevc_start, .stop = vdec_hevc_stop, -- cgit v1.2.3-70-g09d2 From 19b5e5511ca4f8e933aa15e6f62dfefb6ab7808c Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Thu, 27 Jun 2024 17:18:06 +0200 Subject: media: i2c: max96717: add test pattern ctrl Add v4l2 test pattern control. Signed-off-by: Tommaso Merciai Reviewed-by: Julien Massot Tested-by: Julien Massot Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/max96717.c | 213 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 197 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c index 949306485873..859a439b64d9 100644 --- a/drivers/media/i2c/max96717.c +++ b/drivers/media/i2c/max96717.c @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -38,9 +39,35 @@ #define MAX96717_DEV_REV_MASK GENMASK(3, 0) /* VID_TX Z */ +#define MAX96717_VIDEO_TX0 CCI_REG8(0x110) +#define MAX96717_VIDEO_AUTO_BPP BIT(3) #define MAX96717_VIDEO_TX2 CCI_REG8(0x112) #define MAX96717_VIDEO_PCLKDET BIT(7) +/* VTX_Z */ +#define MAX96717_VTX0 CCI_REG8(0x24e) +#define MAX96717_VTX1 CCI_REG8(0x24f) +#define MAX96717_PATTERN_CLK_FREQ GENMASK(3, 1) +#define MAX96717_VTX_VS_DLY CCI_REG24(0x250) +#define MAX96717_VTX_VS_HIGH CCI_REG24(0x253) +#define MAX96717_VTX_VS_LOW CCI_REG24(0x256) +#define MAX96717_VTX_V2H CCI_REG24(0x259) +#define MAX96717_VTX_HS_HIGH CCI_REG16(0x25c) +#define MAX96717_VTX_HS_LOW CCI_REG16(0x25e) +#define MAX96717_VTX_HS_CNT CCI_REG16(0x260) +#define MAX96717_VTX_V2D CCI_REG24(0x262) +#define MAX96717_VTX_DE_HIGH CCI_REG16(0x265) +#define MAX96717_VTX_DE_LOW CCI_REG16(0x267) +#define MAX96717_VTX_DE_CNT CCI_REG16(0x269) +#define MAX96717_VTX29 CCI_REG8(0x26b) +#define MAX96717_VTX_MODE GENMASK(1, 0) +#define MAX96717_VTX_GRAD_INC CCI_REG8(0x26c) +#define MAX96717_VTX_CHKB_COLOR_A CCI_REG24(0x26d) +#define MAX96717_VTX_CHKB_COLOR_B CCI_REG24(0x270) +#define MAX96717_VTX_CHKB_RPT_CNT_A CCI_REG8(0x273) +#define MAX96717_VTX_CHKB_RPT_CNT_B CCI_REG8(0x274) +#define MAX96717_VTX_CHKB_ALT CCI_REG8(0x275) + /* GPIO */ #define MAX96717_NUM_GPIO 11 #define MAX96717_GPIO_REG_A(gpio) CCI_REG8(0x2be + (gpio) * 3) @@ -82,6 +109,12 @@ /* MISC */ #define PIO_SLEW_1 CCI_REG8(0x570) +enum max96717_vpg_mode { + MAX96717_VPG_DISABLED = 0, + MAX96717_VPG_CHECKERBOARD = 1, + MAX96717_VPG_GRADIENT = 2, +}; + struct max96717_priv { struct i2c_client *client; struct regmap *regmap; @@ -89,6 +122,7 @@ struct max96717_priv { struct v4l2_mbus_config_mipi_csi2 mipi_csi2; struct v4l2_subdev sd; struct media_pad pads[MAX96717_PORTS]; + struct v4l2_ctrl_handler ctrl_handler; struct v4l2_async_notifier notifier; struct v4l2_subdev *source_sd; u16 source_sd_pad; @@ -96,6 +130,7 @@ struct max96717_priv { u8 pll_predef_index; struct clk_hw clk_hw; struct gpio_chip gpio_chip; + enum max96717_vpg_mode pattern; }; static inline struct max96717_priv *sd_to_max96717(struct v4l2_subdev *sd) @@ -131,6 +166,118 @@ static inline int max96717_start_csi(struct max96717_priv *priv, bool start) start ? MAX96717_START_PORT_B : 0, NULL); } +static int max96717_apply_patgen_timing(struct max96717_priv *priv, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_state_get_format(state, MAX96717_PAD_SOURCE); + const u32 h_active = fmt->width; + const u32 h_fp = 88; + const u32 h_sw = 44; + const u32 h_bp = 148; + u32 h_tot; + const u32 v_active = fmt->height; + const u32 v_fp = 4; + const u32 v_sw = 5; + const u32 v_bp = 36; + u32 v_tot; + int ret = 0; + + h_tot = h_active + h_fp + h_sw + h_bp; + v_tot = v_active + v_fp + v_sw + v_bp; + + /* 75 Mhz pixel clock */ + cci_update_bits(priv->regmap, MAX96717_VTX1, + MAX96717_PATTERN_CLK_FREQ, 0xa, &ret); + + dev_info(&priv->client->dev, "height: %d width: %d\n", fmt->height, + fmt->width); + + cci_write(priv->regmap, MAX96717_VTX_VS_DLY, 0, &ret); + cci_write(priv->regmap, MAX96717_VTX_VS_HIGH, v_sw * h_tot, &ret); + cci_write(priv->regmap, MAX96717_VTX_VS_LOW, + (v_active + v_fp + v_bp) * h_tot, &ret); + cci_write(priv->regmap, MAX96717_VTX_HS_HIGH, h_sw, &ret); + cci_write(priv->regmap, MAX96717_VTX_HS_LOW, h_active + h_fp + h_bp, + &ret); + cci_write(priv->regmap, MAX96717_VTX_V2D, + h_tot * (v_sw + v_bp) + (h_sw + h_bp), &ret); + cci_write(priv->regmap, MAX96717_VTX_HS_CNT, v_tot, &ret); + cci_write(priv->regmap, MAX96717_VTX_DE_HIGH, h_active, &ret); + cci_write(priv->regmap, MAX96717_VTX_DE_LOW, h_fp + h_sw + h_bp, + &ret); + cci_write(priv->regmap, MAX96717_VTX_DE_CNT, v_active, &ret); + /* B G R */ + cci_write(priv->regmap, MAX96717_VTX_CHKB_COLOR_A, 0xfecc00, &ret); + /* B G R */ + cci_write(priv->regmap, MAX96717_VTX_CHKB_COLOR_B, 0x006aa7, &ret); + cci_write(priv->regmap, MAX96717_VTX_CHKB_RPT_CNT_A, 0x3c, &ret); + cci_write(priv->regmap, MAX96717_VTX_CHKB_RPT_CNT_B, 0x3c, &ret); + cci_write(priv->regmap, MAX96717_VTX_CHKB_ALT, 0x3c, &ret); + cci_write(priv->regmap, MAX96717_VTX_GRAD_INC, 0x10, &ret); + + return ret; +} + +static int max96717_apply_patgen(struct max96717_priv *priv, + struct v4l2_subdev_state *state) +{ + unsigned int val; + int ret = 0; + + if (priv->pattern) + ret = max96717_apply_patgen_timing(priv, state); + + cci_write(priv->regmap, MAX96717_VTX0, priv->pattern ? 0xfb : 0, + &ret); + + val = FIELD_PREP(MAX96717_VTX_MODE, priv->pattern); + cci_update_bits(priv->regmap, MAX96717_VTX29, MAX96717_VTX_MODE, + val, &ret); + return ret; +} + +static int max96717_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct max96717_priv *priv = + container_of(ctrl->handler, struct max96717_priv, ctrl_handler); + int ret; + + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + if (priv->enabled_source_streams) + return -EBUSY; + priv->pattern = ctrl->val; + break; + default: + return -EINVAL; + } + + /* Use bpp from bpp register */ + ret = cci_update_bits(priv->regmap, MAX96717_VIDEO_TX0, + MAX96717_VIDEO_AUTO_BPP, + priv->pattern ? 0 : MAX96717_VIDEO_AUTO_BPP, + NULL); + + /* + * Pattern generator doesn't work with tunnel mode. + * Needs RGB color format and deserializer tunnel mode must be disabled. + */ + return cci_update_bits(priv->regmap, MAX96717_MIPI_RX_EXT11, + MAX96717_TUN_MODE, + priv->pattern ? 0 : MAX96717_TUN_MODE, &ret); +} + +static const char * const max96717_test_pattern[] = { + "Disabled", + "Checkerboard", + "Gradient" +}; + +static const struct v4l2_ctrl_ops max96717_ctrl_ops = { + .s_ctrl = max96717_s_ctrl, +}; + static int max96717_gpiochip_get(struct gpio_chip *gpiochip, unsigned int offset) { @@ -352,20 +499,28 @@ static int max96717_enable_streams(struct v4l2_subdev *sd, u64 sink_streams; int ret; - sink_streams = v4l2_subdev_state_xlate_streams(state, - MAX96717_PAD_SOURCE, - MAX96717_PAD_SINK, - &streams_mask); - if (!priv->enabled_source_streams) max96717_start_csi(priv, true); - ret = v4l2_subdev_enable_streams(priv->source_sd, priv->source_sd_pad, - sink_streams); - if (ret) { - dev_err(dev, "Fail to start streams:%llu on remote subdev\n", - sink_streams); + ret = max96717_apply_patgen(priv, state); + if (ret) goto stop_csi; + + if (!priv->pattern) { + sink_streams = + v4l2_subdev_state_xlate_streams(state, + MAX96717_PAD_SOURCE, + MAX96717_PAD_SINK, + &streams_mask); + + ret = v4l2_subdev_enable_streams(priv->source_sd, + priv->source_sd_pad, + sink_streams); + if (ret) { + dev_err(dev, "Fail to start streams:%llu on remote subdev\n", + sink_streams); + goto stop_csi; + } } priv->enabled_source_streams |= streams_mask; @@ -394,13 +549,23 @@ static int max96717_disable_streams(struct v4l2_subdev *sd, if (!priv->enabled_source_streams) max96717_start_csi(priv, false); - sink_streams = v4l2_subdev_state_xlate_streams(state, - MAX96717_PAD_SOURCE, - MAX96717_PAD_SINK, - &streams_mask); + if (!priv->pattern) { + int ret; + + sink_streams = + v4l2_subdev_state_xlate_streams(state, + MAX96717_PAD_SOURCE, + MAX96717_PAD_SINK, + &streams_mask); - return v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad, - sink_streams); + ret = v4l2_subdev_disable_streams(priv->source_sd, + priv->source_sd_pad, + sink_streams); + if (ret) + return ret; + } + + return 0; } static const struct v4l2_subdev_pad_ops max96717_pad_ops = { @@ -513,6 +678,19 @@ static int max96717_subdev_init(struct max96717_priv *priv) v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96717_subdev_ops); priv->sd.internal_ops = &max96717_internal_ops; + v4l2_ctrl_handler_init(&priv->ctrl_handler, 1); + priv->sd.ctrl_handler = &priv->ctrl_handler; + + v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler, + &max96717_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(max96717_test_pattern) - 1, + 0, 0, max96717_test_pattern); + if (priv->ctrl_handler.error) { + ret = priv->ctrl_handler.error; + goto err_free_ctrl; + } + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; priv->sd.entity.ops = &max96717_entity_ops; @@ -552,6 +730,8 @@ err_free_state: v4l2_subdev_cleanup(&priv->sd); err_entity_cleanup: media_entity_cleanup(&priv->sd.entity); +err_free_ctrl: + v4l2_ctrl_handler_free(&priv->ctrl_handler); return ret; } @@ -563,6 +743,7 @@ static void max96717_subdev_uninit(struct max96717_priv *priv) v4l2_async_nf_cleanup(&priv->notifier); v4l2_subdev_cleanup(&priv->sd); media_entity_cleanup(&priv->sd.entity); + v4l2_ctrl_handler_free(&priv->ctrl_handler); } struct max96717_pll_predef_freq { -- cgit v1.2.3-70-g09d2 From 24fe3eb1a582ade9e2dc1d13969242bb0a817f59 Mon Sep 17 00:00:00 2001 From: Julien Massot Date: Mon, 1 Jul 2024 11:31:42 +0200 Subject: media: i2c: max96717: coding style fixes Coding style fixes suggested by Sakari during the driver review. Signed-off-by: Julien Massot Reviewed-by: Tommaso Merciai Tested-by: Tommaso Merciai Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/max96717.c | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c index 859a439b64d9..4e85b8eb1e77 100644 --- a/drivers/media/i2c/max96717.c +++ b/drivers/media/i2c/max96717.c @@ -25,6 +25,7 @@ #define MAX96717_PORTS 2 #define MAX96717_PAD_SINK 0 #define MAX96717_PAD_SOURCE 1 +#define MAX96717_CSI_NLANES 4 #define MAX96717_DEFAULT_CLKOUT_RATE 24000000UL @@ -495,7 +496,6 @@ static int max96717_enable_streams(struct v4l2_subdev *sd, u64 streams_mask) { struct max96717_priv *priv = sd_to_max96717(sd); - struct device *dev = &priv->client->dev; u64 sink_streams; int ret; @@ -516,11 +516,8 @@ static int max96717_enable_streams(struct v4l2_subdev *sd, ret = v4l2_subdev_enable_streams(priv->source_sd, priv->source_sd_pad, sink_streams); - if (ret) { - dev_err(dev, "Fail to start streams:%llu on remote subdev\n", - sink_streams); + if (ret) goto stop_csi; - } } priv->enabled_source_streams |= streams_mask; @@ -530,6 +527,7 @@ static int max96717_enable_streams(struct v4l2_subdev *sd, stop_csi: if (!priv->enabled_source_streams) max96717_start_csi(priv, false); + return ret; } @@ -769,11 +767,8 @@ max96717_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) static unsigned int max96717_clk_find_best_index(struct max96717_priv *priv, unsigned long rate) { - unsigned int i, idx; - unsigned long diff_new, diff_old; - - diff_old = U32_MAX; - idx = 0; + unsigned int i, idx = 0; + unsigned long diff_new, diff_old = U32_MAX; for (i = 0; i < ARRAY_SIZE(max96717_predef_freqs); i++) { diff_new = abs(rate - max96717_predef_freqs[i].freq); @@ -860,8 +855,7 @@ static int max96717_register_clkout(struct max96717_priv *priv) struct clk_init_data init = { .ops = &max96717_clk_ops }; int ret; - init.name = kasprintf(GFP_KERNEL, "max96717.%s.clk_out", - dev_name(dev)); + init.name = kasprintf(GFP_KERNEL, "max96717.%s.clk_out", dev_name(dev)); if (!init.name) return -ENOMEM; @@ -944,8 +938,9 @@ static int max96717_init_csi_lanes(struct max96717_priv *priv) * Unused lanes need to be mapped as well to not have * the same lanes mapped twice. */ - for (; lane < 4; lane++) { - unsigned int idx = find_first_zero_bit(&lanes_used, 4); + for (; lane < MAX96717_CSI_NLANES; lane++) { + unsigned int idx = find_first_zero_bit(&lanes_used, + MAX96717_CSI_NLANES); val |= idx << (lane * 2); lanes_used |= BIT(idx); @@ -999,9 +994,7 @@ static int max96717_hw_init(struct max96717_priv *priv) static int max96717_parse_dt(struct max96717_priv *priv) { struct device *dev = &priv->client->dev; - struct v4l2_fwnode_endpoint vep = { - .bus_type = V4L2_MBUS_CSI2_DPHY - }; + struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; struct fwnode_handle *ep_fwnode; unsigned char num_data_lanes; int ret; @@ -1019,11 +1012,11 @@ static int max96717_parse_dt(struct max96717_priv *priv) return dev_err_probe(dev, ret, "Failed to parse sink endpoint"); num_data_lanes = vep.bus.mipi_csi2.num_data_lanes; - if (num_data_lanes < 1 || num_data_lanes > 4) + if (num_data_lanes < 1 || num_data_lanes > MAX96717_CSI_NLANES) return dev_err_probe(dev, -EINVAL, "Invalid data lanes must be 1 to 4\n"); - memcpy(&priv->mipi_csi2, &vep.bus.mipi_csi2, sizeof(priv->mipi_csi2)); + priv->mipi_csi2 = vep.bus.mipi_csi2; return 0; } -- cgit v1.2.3-70-g09d2 From 4596c55e9a096b8a96d6e4dff6a8cdf440adbcef Mon Sep 17 00:00:00 2001 From: Julien Massot Date: Mon, 1 Jul 2024 11:31:43 +0200 Subject: media: i2c: max96714: coding style fixes Coding style fixes suggested by Sakari during the driver review. Reviewed-by: Tommaso Merciai Tested-by: Tommaso Merciai Signed-off-by: Julien Massot Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/max96714.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/max96714.c b/drivers/media/i2c/max96714.c index c97de66631e0..159753b13777 100644 --- a/drivers/media/i2c/max96714.c +++ b/drivers/media/i2c/max96714.c @@ -25,6 +25,7 @@ #define MAX96714_NPORTS 2 #define MAX96714_PAD_SINK 0 #define MAX96714_PAD_SOURCE 1 +#define MAX96714_CSI_NLANES 4 /* DEV */ #define MAX96714_REG13 CCI_REG8(0x0d) @@ -52,9 +53,9 @@ #define MAX96714_PATGEN_V2D CCI_REG24(0x254) #define MAX96714_PATGEN_DE_HIGH CCI_REG16(0x257) #define MAX96714_PATGEN_DE_LOW CCI_REG16(0x259) -#define MAX96714_PATGEN_DE_CNT CCI_REG16(0x25B) +#define MAX96714_PATGEN_DE_CNT CCI_REG16(0x25b) #define MAX96714_PATGEN_GRAD_INC CCI_REG8(0x25d) -#define MAX96714_PATGEN_CHKB_COLOR_A CCI_REG24(0x25E) +#define MAX96714_PATGEN_CHKB_COLOR_A CCI_REG24(0x25e) #define MAX96714_PATGEN_CHKB_COLOR_B CCI_REG24(0x261) #define MAX96714_PATGEN_CHKB_RPT_CNT_A CCI_REG8(0x264) #define MAX96714_PATGEN_CHKB_RPT_CNT_B CCI_REG8(0x265) @@ -724,8 +725,9 @@ static int max96714_init_tx_port(struct max96714_priv *priv) * Unused lanes need to be mapped as well to not have * the same lanes mapped twice. */ - for (; lane < 4; lane++) { - unsigned int idx = find_first_zero_bit(&lanes_used, 4); + for (; lane < MAX96714_CSI_NLANES; lane++) { + unsigned int idx = find_first_zero_bit(&lanes_used, + MAX96714_CSI_NLANES); val |= idx << (lane * 2); lanes_used |= BIT(idx); @@ -757,9 +759,7 @@ static int max96714_rxport_disable_poc(struct max96714_priv *priv) static int max96714_parse_dt_txport(struct max96714_priv *priv) { struct device *dev = &priv->client->dev; - struct v4l2_fwnode_endpoint vep = { - .bus_type = V4L2_MBUS_CSI2_DPHY - }; + struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; struct fwnode_handle *ep_fwnode; u32 num_data_lanes; int ret; @@ -791,14 +791,14 @@ static int max96714_parse_dt_txport(struct max96714_priv *priv) } num_data_lanes = vep.bus.mipi_csi2.num_data_lanes; - if (num_data_lanes < 1 || num_data_lanes > 4) { + if (num_data_lanes < 1 || num_data_lanes > MAX96714_CSI_NLANES) { dev_err(dev, "tx: invalid number of data lanes must be 1 to 4\n"); ret = -EINVAL; goto err_free_vep; } - memcpy(&priv->mipi_csi2, &vep.bus.mipi_csi2, sizeof(priv->mipi_csi2)); + priv->mipi_csi2 = vep.bus.mipi_csi2; err_free_vep: v4l2_fwnode_endpoint_free(&vep); -- cgit v1.2.3-70-g09d2 From a1be997964db5c98cb6fd90900adab59533c03e8 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Sun, 11 Aug 2024 13:17:04 +0200 Subject: media: i2c: imx355: Parse and register properties Analogous to e.g. the imx219 driver. This enables propagating the V4L2_CID_CAMERA_SENSOR_ROTATION and V4L2_CID_CAMERA_ORIENTATION values so that userspace - e.g. libcamera - can detect the correct rotation and orientation from the device tree. Signed-off-by: Robert Mader Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx355.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 7e9c2f65fa08..0dd25eeea60b 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -1520,6 +1520,7 @@ static const struct v4l2_subdev_internal_ops imx355_internal_ops = { static int imx355_init_controls(struct imx355 *imx355) { struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); + struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; s64 exposure_max; s64 vblank_def; @@ -1531,7 +1532,7 @@ static int imx355_init_controls(struct imx355 *imx355) int ret; ctrl_hdlr = &imx355->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12); if (ret) return ret; @@ -1603,6 +1604,15 @@ static int imx355_init_controls(struct imx355 *imx355) goto error; } + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx355_ctrl_ops, + &props); + if (ret) + goto error; + imx355->sd.ctrl_handler = ctrl_hdlr; return 0; -- cgit v1.2.3-70-g09d2 From 719ec29fceda2f19c833d2784b1574638320400f Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Sat, 13 Jul 2024 23:33:29 +0100 Subject: media: ov5675: Fix power on/off delay timings The ov5675 specification says that the gap between XSHUTDN deassert and the first I2C transaction should be a minimum of 8192 XVCLK cycles. Right now we use a usleep_rage() that gives a sleep time of between about 430 and 860 microseconds. On the Lenovo X13s we have observed that in about 1/20 cases the current timing is too tight and we start transacting before the ov5675's reset cycle completes, leading to I2C bus transaction failures. The reset racing is sometimes triggered at initial chip probe but, more usually on a subsequent power-off/power-on cycle e.g. [ 71.451662] ov5675 24-0010: failed to write reg 0x0103. error = -5 [ 71.451686] ov5675 24-0010: failed to set plls The current quiescence period we have is too tight. Instead of expressing the post reset delay in terms of the current XVCLK this patch converts the power-on and power-off delays to the maximum theoretical delay @ 6 MHz with an additional buffer. 1.365 milliseconds on the power-on path is 1.5 milliseconds with grace. 85.3 microseconds on the power-off path is 90 microseconds with grace. Fixes: 49d9ad719e89 ("media: ov5675: add device-tree support and support runtime PM") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue Tested-by: Johan Hovold Reviewed-by: Quentin Schulz Tested-by: Quentin Schulz # RK3399 Puma with Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov5675.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c index 3641911bc73f..5b5127f8953f 100644 --- a/drivers/media/i2c/ov5675.c +++ b/drivers/media/i2c/ov5675.c @@ -972,12 +972,10 @@ static int ov5675_set_stream(struct v4l2_subdev *sd, int enable) static int ov5675_power_off(struct device *dev) { - /* 512 xvclk cycles after the last SCCB transation or MIPI frame end */ - u32 delay_us = DIV_ROUND_UP(512, OV5675_XVCLK_19_2 / 1000 / 1000); struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov5675 *ov5675 = to_ov5675(sd); - usleep_range(delay_us, delay_us * 2); + usleep_range(90, 100); clk_disable_unprepare(ov5675->xvclk); gpiod_set_value_cansleep(ov5675->reset_gpio, 1); @@ -988,7 +986,6 @@ static int ov5675_power_off(struct device *dev) static int ov5675_power_on(struct device *dev) { - u32 delay_us = DIV_ROUND_UP(8192, OV5675_XVCLK_19_2 / 1000 / 1000); struct v4l2_subdev *sd = dev_get_drvdata(dev); struct ov5675 *ov5675 = to_ov5675(sd); int ret; @@ -1014,8 +1011,11 @@ static int ov5675_power_on(struct device *dev) gpiod_set_value_cansleep(ov5675->reset_gpio, 0); - /* 8192 xvclk cycles prior to the first SCCB transation */ - usleep_range(delay_us, delay_us * 2); + /* Worst case quiesence gap is 1.365 milliseconds @ 6MHz XVCLK + * Add an additional threshold grace period to ensure reset + * completion before initiating our first I2C transaction. + */ + usleep_range(1500, 1600); return 0; } -- cgit v1.2.3-70-g09d2 From d920b37a274f42353530127e0612ce4d6f74ec48 Mon Sep 17 00:00:00 2001 From: Hongbo Li Date: Wed, 28 Aug 2024 20:09:48 +0800 Subject: media: intel/ipu6: make use of dev_err_cast_probe() Using dev_err_cast_probe() to simplify the code. No functional change. Signed-off-by: Hongbo Li Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c index bbd646378ab3..d10cbdd088ab 100644 --- a/drivers/media/pci/intel/ipu6/ipu6.c +++ b/drivers/media/pci/intel/ipu6/ipu6.c @@ -390,20 +390,18 @@ ipu6_isys_init(struct pci_dev *pdev, struct device *parent, isys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, ctrl, IPU6_ISYS_NAME); if (IS_ERR(isys_adev)) { - dev_err_probe(dev, PTR_ERR(isys_adev), - "ipu6_bus_initialize_device isys failed\n"); kfree(pdata); - return ERR_CAST(isys_adev); + return dev_err_cast_probe(dev, isys_adev, + "ipu6_bus_initialize_device isys failed\n"); } isys_adev->mmu = ipu6_mmu_init(dev, base, ISYS_MMID, &ipdata->hw_variant); if (IS_ERR(isys_adev->mmu)) { - dev_err_probe(dev, PTR_ERR(isys_adev->mmu), - "ipu6_mmu_init(isys_adev->mmu) failed\n"); put_device(&isys_adev->auxdev.dev); kfree(pdata); - return ERR_CAST(isys_adev->mmu); + return dev_err_cast_probe(dev, isys_adev->mmu, + "ipu6_mmu_init(isys_adev->mmu) failed\n"); } isys_adev->mmu->dev = &isys_adev->auxdev.dev; @@ -436,20 +434,18 @@ ipu6_psys_init(struct pci_dev *pdev, struct device *parent, psys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, ctrl, IPU6_PSYS_NAME); if (IS_ERR(psys_adev)) { - dev_err_probe(&pdev->dev, PTR_ERR(psys_adev), - "ipu6_bus_initialize_device psys failed\n"); kfree(pdata); - return ERR_CAST(psys_adev); + return dev_err_cast_probe(&pdev->dev, psys_adev, + "ipu6_bus_initialize_device psys failed\n"); } psys_adev->mmu = ipu6_mmu_init(&pdev->dev, base, PSYS_MMID, &ipdata->hw_variant); if (IS_ERR(psys_adev->mmu)) { - dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu), - "ipu6_mmu_init(psys_adev->mmu) failed\n"); put_device(&psys_adev->auxdev.dev); kfree(pdata); - return ERR_CAST(psys_adev->mmu); + return dev_err_cast_probe(&pdev->dev, psys_adev->mmu, + "ipu6_mmu_init(psys_adev->mmu) failed\n"); } psys_adev->mmu->dev = &psys_adev->auxdev.dev; -- cgit v1.2.3-70-g09d2 From bee1aed819a8cda47927436685d216906ed17f62 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Thu, 29 Aug 2024 08:48:49 +0300 Subject: media: i2c: ar0521: Use cansleep version of gpiod_set_value() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we use GPIO reset from I2C port expander, we must use *_cansleep() variant of GPIO functions. This was not done in ar0521_power_on()/ar0521_power_off() functions. Let's fix that. ------------[ cut here ]------------ WARNING: CPU: 0 PID: 11 at drivers/gpio/gpiolib.c:3496 gpiod_set_value+0x74/0x7c Modules linked in: CPU: 0 PID: 11 Comm: kworker/u16:0 Not tainted 6.10.0 #53 Hardware name: Diasom DS-RK3568-SOM-EVB (DT) Workqueue: events_unbound deferred_probe_work_func pstate: 80400009 (Nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : gpiod_set_value+0x74/0x7c lr : ar0521_power_on+0xcc/0x290 sp : ffffff8001d7ab70 x29: ffffff8001d7ab70 x28: ffffff80027dcc90 x27: ffffff8003c82000 x26: ffffff8003ca9250 x25: ffffffc080a39c60 x24: ffffff8003ca9088 x23: ffffff8002402720 x22: ffffff8003ca9080 x21: ffffff8003ca9088 x20: 0000000000000000 x19: ffffff8001eb2a00 x18: ffffff80efeeac80 x17: 756d2d6332692f30 x16: 0000000000000000 x15: 0000000000000000 x14: ffffff8001d91d40 x13: 0000000000000016 x12: ffffffc080e98930 x11: ffffff8001eb2880 x10: 0000000000000890 x9 : ffffff8001d7a9f0 x8 : ffffff8001d92570 x7 : ffffff80efeeac80 x6 : 000000003fc6e780 x5 : ffffff8001d91c80 x4 : 0000000000000002 x3 : 0000000000000000 x2 : 0000000000000000 x1 : 0000000000000000 x0 : 0000000000000001 Call trace: gpiod_set_value+0x74/0x7c ar0521_power_on+0xcc/0x290 ... Signed-off-by: Alexander Shiyan Fixes: 852b50aeed15 ("media: On Semi AR0521 sensor driver") Cc: stable@vger.kernel.org Acked-by: Krzysztof Hałasa Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ar0521.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/ar0521.c b/drivers/media/i2c/ar0521.c index 56a724b4d47e..fc27238dd4d3 100644 --- a/drivers/media/i2c/ar0521.c +++ b/drivers/media/i2c/ar0521.c @@ -842,7 +842,8 @@ static void __ar0521_power_off(struct device *dev) int i; if (sensor->reset_gpio) - gpiod_set_value(sensor->reset_gpio, 1); /* assert RESET signal */ + /* assert RESET signal */ + gpiod_set_value_cansleep(sensor->reset_gpio, 1); for (i = ARRAY_SIZE(ar0521_supply_names) - 1; i >= 0; i--) { if (sensor->supplies[i]) @@ -886,7 +887,7 @@ static int ar0521_power_on(struct device *dev) if (sensor->reset_gpio) /* deassert RESET signal */ - gpiod_set_value(sensor->reset_gpio, 0); + gpiod_set_value_cansleep(sensor->reset_gpio, 0); usleep_range(4500, 5000); /* min 45000 clocks */ for (cnt = 0; cnt < ARRAY_SIZE(initial_regs); cnt++) { -- cgit v1.2.3-70-g09d2 From d7754949dbf90b46e4a1f502f0973f7dcd50c352 Mon Sep 17 00:00:00 2001 From: Yue Haibing Date: Fri, 23 Aug 2024 17:49:35 +0800 Subject: media: ccs: Remove unused declarations Commit 2989a457171d ("media: ccs: Refactor register reading a little") removed ccs_read_addr_no_quirk() but left declaration. Commit 529322112a3b ("media: ccs: Use V4L2 CCI for accessing sensor registers") removed ccs_write_addr_no_quirk() and ccs_reg_width() but leave their declarations. Signed-off-by: Yue Haibing Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ccs/ccs-reg-access.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/ccs/ccs-reg-access.h b/drivers/media/i2c/ccs/ccs-reg-access.h index 78c43f92d99a..4b56b21a26b5 100644 --- a/drivers/media/i2c/ccs/ccs-reg-access.h +++ b/drivers/media/i2c/ccs/ccs-reg-access.h @@ -21,16 +21,13 @@ struct ccs_sensor; -int ccs_read_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 *val); int ccs_read_addr(struct ccs_sensor *sensor, u32 reg, u32 *val); int ccs_read_addr_8only(struct ccs_sensor *sensor, u32 reg, u32 *val); int ccs_read_addr_noconv(struct ccs_sensor *sensor, u32 reg, u32 *val); -int ccs_write_addr_no_quirk(struct ccs_sensor *sensor, u32 reg, u32 val); int ccs_write_addr(struct ccs_sensor *sensor, u32 reg, u32 val); int ccs_write_data_regs(struct ccs_sensor *sensor, struct ccs_reg *regs, size_t num_regs); -unsigned int ccs_reg_width(u32 reg); u32 ccs_reg_conv(struct ccs_sensor *sensor, u32 reg, u32 val); #define ccs_read(sensor, reg_name, val) \ -- cgit v1.2.3-70-g09d2 From e7a9c987381a991f7ac54372ea45cfd4b160edcb Mon Sep 17 00:00:00 2001 From: Yue Haibing Date: Fri, 23 Aug 2024 17:48:39 +0800 Subject: media: siano: Remove unused declarations There is no caller and implementation in tree, so can remove them. Signed-off-by: Yue Haibing Reviewed-by: Ricardo Ribalda Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/common/siano/smscoreapi.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h index 3c15082ce0e3..d945a2d6d624 100644 --- a/drivers/media/common/siano/smscoreapi.h +++ b/drivers/media/common/siano/smscoreapi.h @@ -1115,12 +1115,6 @@ extern int smsclient_sendrequest(struct smscore_client_t *client, extern void smscore_onresponse(struct smscore_device_t *coredev, struct smscore_buffer_t *cb); -extern int smscore_get_common_buffer_size(struct smscore_device_t *coredev); -extern int smscore_map_common_buffer(struct smscore_device_t *coredev, - struct vm_area_struct *vma); -extern int smscore_send_fw_file(struct smscore_device_t *coredev, - u8 *ufwbuf, int size); - extern struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev); extern void smscore_putbuffer(struct smscore_device_t *coredev, -- cgit v1.2.3-70-g09d2 From 4f6bec9dba371d3d3fa6e17dd7a29534ff12fdc6 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Fri, 30 Aug 2024 09:34:55 +0300 Subject: media: i2c: og01a1b: Add OF support to the image sensor driver The OmniVision OG01A1B image sensor driver currently supports probing only on ACPI platforms, the changes adds support of OF platforms to the driver. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/og01a1b.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c index bac9597faf68..9e756c1c47df 100644 --- a/drivers/media/i2c/og01a1b.c +++ b/drivers/media/i2c/og01a1b.c @@ -1057,10 +1057,17 @@ static const struct acpi_device_id og01a1b_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, og01a1b_acpi_ids); #endif +static const struct of_device_id og01a1b_of_match[] = { + { .compatible = "ovti,og01a1b" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, og01a1b_of_match); + static struct i2c_driver og01a1b_i2c_driver = { .driver = { .name = "og01a1b", .acpi_match_table = ACPI_PTR(og01a1b_acpi_ids), + .of_match_table = og01a1b_of_match, }, .probe = og01a1b_probe, .remove = og01a1b_remove, -- cgit v1.2.3-70-g09d2 From 1c004ef7ffc37e81a1c1fac7fffb4f37b4b2a30e Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Fri, 30 Aug 2024 09:34:56 +0300 Subject: media: i2c: og01a1b: Add stubs of runtime power management functions Rearrange initializations and checks in probe before population of the power management functions. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/og01a1b.c | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c index 9e756c1c47df..d993ef4bad46 100644 --- a/drivers/media/i2c/og01a1b.c +++ b/drivers/media/i2c/og01a1b.c @@ -967,6 +967,19 @@ check_hwcfg_error: return ret; } +/* Power/clock management functions */ +static int og01a1b_power_on(struct device *dev) +{ + /* Device is already turned on by i2c-core with ACPI domain PM. */ + + return 0; +} + +static int og01a1b_power_off(struct device *dev) +{ + return 0; +} + static void og01a1b_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); @@ -984,6 +997,12 @@ static int og01a1b_probe(struct i2c_client *client) struct og01a1b *og01a1b; int ret; + og01a1b = devm_kzalloc(&client->dev, sizeof(*og01a1b), GFP_KERNEL); + if (!og01a1b) + return -ENOMEM; + + v4l2_i2c_subdev_init(&og01a1b->sd, client, &og01a1b_subdev_ops); + ret = og01a1b_check_hwcfg(&client->dev); if (ret) { dev_err(&client->dev, "failed to check HW configuration: %d", @@ -991,15 +1010,15 @@ static int og01a1b_probe(struct i2c_client *client) return ret; } - og01a1b = devm_kzalloc(&client->dev, sizeof(*og01a1b), GFP_KERNEL); - if (!og01a1b) - return -ENOMEM; + /* The sensor must be powered on to read the CHIP_ID register */ + ret = og01a1b_power_on(&client->dev); + if (ret) + return ret; - v4l2_i2c_subdev_init(&og01a1b->sd, client, &og01a1b_subdev_ops); ret = og01a1b_identify_module(og01a1b); if (ret) { dev_err(&client->dev, "failed to find sensor: %d", ret); - return ret; + goto power_off; } mutex_init(&og01a1b->mutex); @@ -1028,10 +1047,7 @@ static int og01a1b_probe(struct i2c_client *client) goto probe_error_media_entity_cleanup; } - /* - * Device is already turned on by i2c-core with ACPI domain PM. - * Enable runtime PM and turn off the device. - */ + /* Enable runtime PM and turn off the device */ pm_runtime_set_active(&client->dev); pm_runtime_enable(&client->dev); pm_runtime_idle(&client->dev); @@ -1045,9 +1061,16 @@ probe_error_v4l2_ctrl_handler_free: v4l2_ctrl_handler_free(og01a1b->sd.ctrl_handler); mutex_destroy(&og01a1b->mutex); +power_off: + og01a1b_power_off(&client->dev); + return ret; } +static const struct dev_pm_ops og01a1b_pm_ops = { + SET_RUNTIME_PM_OPS(og01a1b_power_off, og01a1b_power_on, NULL) +}; + #ifdef CONFIG_ACPI static const struct acpi_device_id og01a1b_acpi_ids[] = { {"OVTI01AC"}, @@ -1066,6 +1089,7 @@ MODULE_DEVICE_TABLE(of, og01a1b_of_match); static struct i2c_driver og01a1b_i2c_driver = { .driver = { .name = "og01a1b", + .pm = &og01a1b_pm_ops, .acpi_match_table = ACPI_PTR(og01a1b_acpi_ids), .of_match_table = og01a1b_of_match, }, -- cgit v1.2.3-70-g09d2 From a95ffde287835cc60df7dba2c15cfc07c6776e95 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Fri, 30 Aug 2024 09:34:57 +0300 Subject: media: i2c: og01a1b: Add support of xvclk supply clock in power management The OmniVision OG01A1B camera sensor has an xvclk supply clock, which could be described and then explicitly controlled on OF platforms. Signed-off-by: Vladimir Zapolskiy [Sakari Ailus: Use UL specifier for power-up delay cycle value.] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/og01a1b.c | 46 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c index d993ef4bad46..ac719611b194 100644 --- a/drivers/media/i2c/og01a1b.c +++ b/drivers/media/i2c/og01a1b.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -418,6 +419,8 @@ static const struct og01a1b_mode supported_modes[] = { }; struct og01a1b { + struct clk *xvclk; + struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler ctrl_handler; @@ -898,8 +901,10 @@ static int og01a1b_identify_module(struct og01a1b *og01a1b) return 0; } -static int og01a1b_check_hwcfg(struct device *dev) +static int og01a1b_check_hwcfg(struct og01a1b *og01a1b) { + struct i2c_client *client = v4l2_get_subdevdata(&og01a1b->sd); + struct device *dev = &client->dev; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); struct v4l2_fwnode_endpoint bus_cfg = { @@ -913,10 +918,13 @@ static int og01a1b_check_hwcfg(struct device *dev) return -ENXIO; ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); - if (ret) { - dev_err(dev, "can't get clock frequency"); - return ret; + if (!og01a1b->xvclk) { + dev_err(dev, "can't get clock frequency"); + return ret; + } + + mclk = clk_get_rate(og01a1b->xvclk); } if (mclk != OG01A1B_MCLK) { @@ -970,13 +978,32 @@ check_hwcfg_error: /* Power/clock management functions */ static int og01a1b_power_on(struct device *dev) { - /* Device is already turned on by i2c-core with ACPI domain PM. */ + unsigned long delay = DIV_ROUND_UP(8192UL * USEC_PER_SEC, OG01A1B_MCLK); + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct og01a1b *og01a1b = to_og01a1b(sd); + int ret; + + ret = clk_prepare_enable(og01a1b->xvclk); + if (ret) + return ret; + + if (og01a1b->xvclk) + usleep_range(delay, 2 * delay); return 0; } static int og01a1b_power_off(struct device *dev) { + unsigned long delay = DIV_ROUND_UP(512 * USEC_PER_SEC, OG01A1B_MCLK); + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct og01a1b *og01a1b = to_og01a1b(sd); + + if (og01a1b->xvclk) + usleep_range(delay, 2 * delay); + + clk_disable_unprepare(og01a1b->xvclk); + return 0; } @@ -1003,7 +1030,14 @@ static int og01a1b_probe(struct i2c_client *client) v4l2_i2c_subdev_init(&og01a1b->sd, client, &og01a1b_subdev_ops); - ret = og01a1b_check_hwcfg(&client->dev); + og01a1b->xvclk = devm_clk_get_optional(&client->dev, NULL); + if (IS_ERR(og01a1b->xvclk)) { + ret = PTR_ERR(og01a1b->xvclk); + dev_err(&client->dev, "failed to get xvclk clock: %d\n", ret); + return ret; + } + + ret = og01a1b_check_hwcfg(og01a1b); if (ret) { dev_err(&client->dev, "failed to check HW configuration: %d", ret); -- cgit v1.2.3-70-g09d2 From 1cb7b39901c2fb634994526d0ce541ee9274053f Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Fri, 30 Aug 2024 09:34:58 +0300 Subject: media: i2c: og01a1b: Add management of optional reset GPIO Omnivision OG01A1B camera sensor may have a connected active low GPIO to XSHUTDOWN pad, and if so, include it into sensor power up sequence. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/og01a1b.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c index ac719611b194..689e6a2db6ff 100644 --- a/drivers/media/i2c/og01a1b.c +++ b/drivers/media/i2c/og01a1b.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -420,6 +421,7 @@ static const struct og01a1b_mode supported_modes[] = { struct og01a1b { struct clk *xvclk; + struct gpio_desc *reset_gpio; struct v4l2_subdev sd; struct media_pad pad; @@ -987,7 +989,11 @@ static int og01a1b_power_on(struct device *dev) if (ret) return ret; - if (og01a1b->xvclk) + gpiod_set_value_cansleep(og01a1b->reset_gpio, 0); + + if (og01a1b->reset_gpio) + usleep_range(5 * USEC_PER_MSEC, 6 * USEC_PER_MSEC); + else if (og01a1b->xvclk) usleep_range(delay, 2 * delay); return 0; @@ -1004,6 +1010,8 @@ static int og01a1b_power_off(struct device *dev) clk_disable_unprepare(og01a1b->xvclk); + gpiod_set_value_cansleep(og01a1b->reset_gpio, 1); + return 0; } @@ -1044,6 +1052,13 @@ static int og01a1b_probe(struct i2c_client *client) return ret; } + og01a1b->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(og01a1b->reset_gpio)) { + dev_err(&client->dev, "cannot get reset GPIO\n"); + return PTR_ERR(og01a1b->reset_gpio); + } + /* The sensor must be powered on to read the CHIP_ID register */ ret = og01a1b_power_on(&client->dev); if (ret) -- cgit v1.2.3-70-g09d2 From 4a1b669ffe789ee2c08198ce6e042a089b7bea3a Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Fri, 30 Aug 2024 09:34:59 +0300 Subject: media: i2c: og01a1b: Add management of optional sensor supply lines Omnivision OG01A1B camera sensor is supplied by three power rails, if supplies are present as device properties, include them into sensor power up sequence. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/og01a1b.c | 81 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c index 689e6a2db6ff..e906435fc49a 100644 --- a/drivers/media/i2c/og01a1b.c +++ b/drivers/media/i2c/og01a1b.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -422,6 +423,9 @@ static const struct og01a1b_mode supported_modes[] = { struct og01a1b { struct clk *xvclk; struct gpio_desc *reset_gpio; + struct regulator *avdd; + struct regulator *dovdd; + struct regulator *dvdd; struct v4l2_subdev sd; struct media_pad pad; @@ -985,9 +989,27 @@ static int og01a1b_power_on(struct device *dev) struct og01a1b *og01a1b = to_og01a1b(sd); int ret; + if (og01a1b->avdd) { + ret = regulator_enable(og01a1b->avdd); + if (ret) + return ret; + } + + if (og01a1b->dovdd) { + ret = regulator_enable(og01a1b->dovdd); + if (ret) + goto avdd_disable; + } + + if (og01a1b->dvdd) { + ret = regulator_enable(og01a1b->dvdd); + if (ret) + goto dovdd_disable; + } + ret = clk_prepare_enable(og01a1b->xvclk); if (ret) - return ret; + goto dvdd_disable; gpiod_set_value_cansleep(og01a1b->reset_gpio, 0); @@ -997,6 +1019,18 @@ static int og01a1b_power_on(struct device *dev) usleep_range(delay, 2 * delay); return 0; + +dvdd_disable: + if (og01a1b->dvdd) + regulator_disable(og01a1b->dvdd); +dovdd_disable: + if (og01a1b->dovdd) + regulator_disable(og01a1b->dovdd); +avdd_disable: + if (og01a1b->avdd) + regulator_disable(og01a1b->avdd); + + return ret; } static int og01a1b_power_off(struct device *dev) @@ -1012,6 +1046,15 @@ static int og01a1b_power_off(struct device *dev) gpiod_set_value_cansleep(og01a1b->reset_gpio, 1); + if (og01a1b->dvdd) + regulator_disable(og01a1b->dvdd); + + if (og01a1b->dovdd) + regulator_disable(og01a1b->dovdd); + + if (og01a1b->avdd) + regulator_disable(og01a1b->avdd); + return 0; } @@ -1059,6 +1102,42 @@ static int og01a1b_probe(struct i2c_client *client) return PTR_ERR(og01a1b->reset_gpio); } + og01a1b->avdd = devm_regulator_get_optional(&client->dev, "avdd"); + if (IS_ERR(og01a1b->avdd)) { + ret = PTR_ERR(og01a1b->avdd); + if (ret != -ENODEV) { + dev_err_probe(&client->dev, ret, + "Failed to get 'avdd' regulator\n"); + return ret; + } + + og01a1b->avdd = NULL; + } + + og01a1b->dovdd = devm_regulator_get_optional(&client->dev, "dovdd"); + if (IS_ERR(og01a1b->dovdd)) { + ret = PTR_ERR(og01a1b->dovdd); + if (ret != -ENODEV) { + dev_err_probe(&client->dev, ret, + "Failed to get 'dovdd' regulator\n"); + return ret; + } + + og01a1b->dovdd = NULL; + } + + og01a1b->dvdd = devm_regulator_get_optional(&client->dev, "dvdd"); + if (IS_ERR(og01a1b->dvdd)) { + ret = PTR_ERR(og01a1b->dvdd); + if (ret != -ENODEV) { + dev_err_probe(&client->dev, ret, + "Failed to get 'dvdd' regulator\n"); + return ret; + } + + og01a1b->dvdd = NULL; + } + /* The sensor must be powered on to read the CHIP_ID register */ ret = og01a1b_power_on(&client->dev); if (ret) -- cgit v1.2.3-70-g09d2 From 99d30e2fdea4086be4e66e2deb10de854b547ab8 Mon Sep 17 00:00:00 2001 From: Umang Jain Date: Fri, 30 Aug 2024 11:41:52 +0530 Subject: media: imx335: Fix reset-gpio handling Rectify the logical value of reset-gpio so that it is set to 0 (disabled) during power-on and to 1 (enabled) during power-off. Set the reset-gpio to GPIO_OUT_HIGH at initialization time to make sure it starts off in reset. Also drop the "Set XCLR" comment which is not-so-informative. The existing usage of imx335 had reset-gpios polarity inverted (GPIO_ACTIVE_HIGH) in their device-tree sources. With this patch included, those DTS will not be able to stream imx335 anymore. The reset-gpio polarity will need to be rectified in the device-tree sources as shown in [1] example, in order to get imx335 functional again (as it remains in reset prior to this fix). Cc: stable@vger.kernel.org Fixes: 45d19b5fb9ae ("media: i2c: Add imx335 camera sensor driver") Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/linux-media/20240729110437.199428-1-umang.jain@ideasonboard.com/ Signed-off-by: Umang Jain Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx335.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c index 990d74214cc2..54a1de53d497 100644 --- a/drivers/media/i2c/imx335.c +++ b/drivers/media/i2c/imx335.c @@ -997,7 +997,7 @@ static int imx335_parse_hw_config(struct imx335 *imx335) /* Request optional reset pin */ imx335->reset_gpio = devm_gpiod_get_optional(imx335->dev, "reset", - GPIOD_OUT_LOW); + GPIOD_OUT_HIGH); if (IS_ERR(imx335->reset_gpio)) { dev_err(imx335->dev, "failed to get reset gpio %ld\n", PTR_ERR(imx335->reset_gpio)); @@ -1110,8 +1110,7 @@ static int imx335_power_on(struct device *dev) usleep_range(500, 550); /* Tlow */ - /* Set XCLR */ - gpiod_set_value_cansleep(imx335->reset_gpio, 1); + gpiod_set_value_cansleep(imx335->reset_gpio, 0); ret = clk_prepare_enable(imx335->inclk); if (ret) { @@ -1124,7 +1123,7 @@ static int imx335_power_on(struct device *dev) return 0; error_reset: - gpiod_set_value_cansleep(imx335->reset_gpio, 0); + gpiod_set_value_cansleep(imx335->reset_gpio, 1); regulator_bulk_disable(ARRAY_SIZE(imx335_supply_name), imx335->supplies); return ret; @@ -1141,7 +1140,7 @@ static int imx335_power_off(struct device *dev) struct v4l2_subdev *sd = dev_get_drvdata(dev); struct imx335 *imx335 = to_imx335(sd); - gpiod_set_value_cansleep(imx335->reset_gpio, 0); + gpiod_set_value_cansleep(imx335->reset_gpio, 1); clk_disable_unprepare(imx335->inclk); regulator_bulk_disable(ARRAY_SIZE(imx335_supply_name), imx335->supplies); -- cgit v1.2.3-70-g09d2 From 2c2d24dc5943da93561663d245d96cf6f8fe2691 Mon Sep 17 00:00:00 2001 From: Liao Chen Date: Thu, 29 Aug 2024 12:52:02 +0000 Subject: media: i2c: mt9v111: Enable module autoloading Add MODULE_DEVICE_TABLE(), so modules could be properly autoloaded based on the alias from of_device_id table. Signed-off-by: Liao Chen Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/mt9v111.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c index b0b98ed3c150..b6a2623798c5 100644 --- a/drivers/media/i2c/mt9v111.c +++ b/drivers/media/i2c/mt9v111.c @@ -1265,6 +1265,7 @@ static const struct of_device_id mt9v111_of_match[] = { { .compatible = "aptina,mt9v111", }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, mt9v111_of_match); static struct i2c_driver mt9v111_driver = { .driver = { -- cgit v1.2.3-70-g09d2 From b16042064919e81d84d5566fdf82f660f9616e7a Mon Sep 17 00:00:00 2001 From: Liao Chen Date: Thu, 29 Aug 2024 12:52:03 +0000 Subject: media: i2c: mt9v111: Drop redundant comma Drop the redundant comma from mt9v111_of_match array to make the code clean. Signed-off-by: Liao Chen Reviewed-by: Biju Das Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/mt9v111.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c index b6a2623798c5..723fe138e7bc 100644 --- a/drivers/media/i2c/mt9v111.c +++ b/drivers/media/i2c/mt9v111.c @@ -1263,7 +1263,7 @@ static void mt9v111_remove(struct i2c_client *client) static const struct of_device_id mt9v111_of_match[] = { { .compatible = "aptina,mt9v111", }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mt9v111_of_match); -- cgit v1.2.3-70-g09d2 From c9edd2e4fed5f8a6f14808a1fd17b7d56b6c00f3 Mon Sep 17 00:00:00 2001 From: Erling Ljunggren Date: Wed, 28 Sep 2022 13:21:47 +0200 Subject: media: v4l2-dev: handle V4L2_CAP_EDID When the V4L2_CAP_EDID capability flag is set, ioctls for enum inputs/outputs and get/set edid are automatically set. Signed-off-by: Erling Ljunggren Signed-off-by: Hans Verkuil Reviewed-by: Sebastian Fricke Reviewed-by: Ricardo Ribalda Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index be2ba7ca5de2..570ba00e00b3 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -557,6 +557,7 @@ static void determine_valid_ioctls(struct video_device *vdev) bool is_tx = vdev->vfl_dir != VFL_DIR_RX; bool is_io_mc = vdev->device_caps & V4L2_CAP_IO_MC; bool has_streaming = vdev->device_caps & V4L2_CAP_STREAMING; + bool is_edid = vdev->device_caps & V4L2_CAP_EDID; bitmap_zero(valid_ioctls, BASE_VIDIOC_PRIVATE); @@ -784,6 +785,20 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_S_TUNER, vidioc_s_tuner); SET_VALID_IOCTL(ops, VIDIOC_S_HW_FREQ_SEEK, vidioc_s_hw_freq_seek); } + if (is_edid) { + SET_VALID_IOCTL(ops, VIDIOC_G_EDID, vidioc_g_edid); + if (is_tx) { + SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output); + SET_VALID_IOCTL(ops, VIDIOC_S_OUTPUT, vidioc_s_output); + SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output); + } + if (is_rx) { + SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input); + SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input); + SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input); + SET_VALID_IOCTL(ops, VIDIOC_S_EDID, vidioc_s_edid); + } + } bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls, BASE_VIDIOC_PRIVATE); -- cgit v1.2.3-70-g09d2 From 6bb8ef90c444c2fd97208bfce42137c4add32bbb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 2 Aug 2024 12:19:48 +0200 Subject: media: cec: move cec_get/put_device to header Move cec_get/put_device to the media/cec.h header. This allows CEC drivers to use this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-api.c | 4 ++-- drivers/media/cec/core/cec-core.c | 29 ----------------------------- drivers/media/cec/core/cec-priv.h | 2 -- include/media/cec.h | 31 +++++++++++++++++++++++++++++++ 4 files changed, 33 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/media/cec/core/cec-api.c b/drivers/media/cec/core/cec-api.c index 3ef915344304..c75a4057f00e 100644 --- a/drivers/media/cec/core/cec-api.c +++ b/drivers/media/cec/core/cec-api.c @@ -580,7 +580,7 @@ static int cec_open(struct inode *inode, struct file *filp) fh->mode_initiator = CEC_MODE_INITIATOR; fh->adap = adap; - err = cec_get_device(devnode); + err = cec_get_device(adap); if (err) { kfree(fh); return err; @@ -686,7 +686,7 @@ static int cec_release(struct inode *inode, struct file *filp) mutex_unlock(&fh->lock); kfree(fh); - cec_put_device(devnode); + cec_put_device(adap); filp->private_data = NULL; return 0; } diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index e0756826d629..48282d272fe6 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -51,35 +51,6 @@ static struct dentry *top_cec_dir; /* dev to cec_devnode */ #define to_cec_devnode(cd) container_of(cd, struct cec_devnode, dev) -int cec_get_device(struct cec_devnode *devnode) -{ - /* - * Check if the cec device is available. This needs to be done with - * the devnode->lock held to prevent an open/unregister race: - * without the lock, the device could be unregistered and freed between - * the devnode->registered check and get_device() calls, leading to - * a crash. - */ - mutex_lock(&devnode->lock); - /* - * return ENODEV if the cec device has been removed - * already or if it is not registered anymore. - */ - if (!devnode->registered) { - mutex_unlock(&devnode->lock); - return -ENODEV; - } - /* and increase the device refcount */ - get_device(&devnode->dev); - mutex_unlock(&devnode->lock); - return 0; -} - -void cec_put_device(struct cec_devnode *devnode) -{ - put_device(&devnode->dev); -} - /* Called when the last user of the cec device exits. */ static void cec_devnode_release(struct device *cd) { diff --git a/drivers/media/cec/core/cec-priv.h b/drivers/media/cec/core/cec-priv.h index ed1f8c67626b..ce42a37c4ac0 100644 --- a/drivers/media/cec/core/cec-priv.h +++ b/drivers/media/cec/core/cec-priv.h @@ -37,8 +37,6 @@ static inline bool msg_is_raw(const struct cec_msg *msg) /* cec-core.c */ extern int cec_debug; -int cec_get_device(struct cec_devnode *devnode); -void cec_put_device(struct cec_devnode *devnode); /* cec-adap.c */ int cec_monitor_all_cnt_inc(struct cec_adapter *adap); diff --git a/include/media/cec.h b/include/media/cec.h index 07d2ee8a3904..16b412b3131b 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -298,6 +298,37 @@ struct cec_adapter { char input_phys[40]; }; +static inline int cec_get_device(struct cec_adapter *adap) +{ + struct cec_devnode *devnode = &adap->devnode; + + /* + * Check if the cec device is available. This needs to be done with + * the devnode->lock held to prevent an open/unregister race: + * without the lock, the device could be unregistered and freed between + * the devnode->registered check and get_device() calls, leading to + * a crash. + */ + mutex_lock(&devnode->lock); + /* + * return ENODEV if the cec device has been removed + * already or if it is not registered anymore. + */ + if (!devnode->registered) { + mutex_unlock(&devnode->lock); + return -ENODEV; + } + /* and increase the device refcount */ + get_device(&devnode->dev); + mutex_unlock(&devnode->lock); + return 0; +} + +static inline void cec_put_device(struct cec_adapter *adap) +{ + put_device(&adap->devnode.dev); +} + static inline void *cec_get_drvdata(const struct cec_adapter *adap) { return adap->priv; -- cgit v1.2.3-70-g09d2 From 056f2821b631df2b94d3b017fd1e1eef918ed98d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 6 Oct 2021 13:29:30 +0200 Subject: media: cec: extron-da-hd-4k-plus: add the Extron DA HD 4K Plus CEC driver Add support for the Extron DA HD 4K Plus series of 4K HDMI Distrubution Amplifiers (aka HDMI Splitters). These devices support CEC and this driver adds support for the CEC protocol for both the input and all outputs (2, 4 or 6 outputs, depending on the model). It also exports the EDID from the outputs and allows reading and setting the EDID of the input. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/admin-guide/media/cec.rst | 87 + MAINTAINERS | 7 + drivers/media/cec/usb/Kconfig | 1 + drivers/media/cec/usb/Makefile | 1 + drivers/media/cec/usb/extron-da-hd-4k-plus/Kconfig | 14 + .../media/cec/usb/extron-da-hd-4k-plus/Makefile | 8 + .../cec/usb/extron-da-hd-4k-plus/cec-splitter.c | 657 +++++++ .../cec/usb/extron-da-hd-4k-plus/cec-splitter.h | 51 + .../extron-da-hd-4k-plus/extron-da-hd-4k-plus.c | 1836 ++++++++++++++++++++ .../extron-da-hd-4k-plus/extron-da-hd-4k-plus.h | 118 ++ 10 files changed, 2780 insertions(+) create mode 100644 drivers/media/cec/usb/extron-da-hd-4k-plus/Kconfig create mode 100644 drivers/media/cec/usb/extron-da-hd-4k-plus/Makefile create mode 100644 drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.c create mode 100644 drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.h create mode 100644 drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c create mode 100644 drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.h (limited to 'drivers') diff --git a/Documentation/admin-guide/media/cec.rst b/Documentation/admin-guide/media/cec.rst index 6b30e355cf23..92690e1f2183 100644 --- a/Documentation/admin-guide/media/cec.rst +++ b/Documentation/admin-guide/media/cec.rst @@ -42,10 +42,14 @@ dongles): ``persistent_config``: by default this is off, but when set to 1 the driver will store the current settings to the device's internal eeprom and restore it the next time the device is connected to the USB port. + - RainShadow Tech. Note: this driver does not support the persistent_config module option of the Pulse-Eight driver. The hardware supports it, but I have no plans to add this feature. But I accept patches :-) +- Extron DA HD 4K PLUS HDMI Distribution Amplifier. See + :ref:`extron_da_hd_4k_plus` for more information. + Miscellaneous: - vivid: emulates a CEC receiver and CEC transmitter. @@ -378,3 +382,86 @@ it later using ``--analyze-pin``. You can also use this as a full-fledged CEC device by configuring it using ``cec-ctl --tv -p0.0.0.0`` or ``cec-ctl --playback -p1.0.0.0``. + +.. _extron_da_hd_4k_plus: + +Extron DA HD 4K PLUS CEC Adapter driver +======================================= + +This driver is for the Extron DA HD 4K PLUS series of HDMI Distribution +Amplifiers: https://www.extron.com/product/dahd4kplusseries + +The 2, 4 and 6 port models are supported. + +Firmware version 1.02.0001 or higher is required. + +Note that older Extron hardware revisions have a problem with the CEC voltage, +which may mean that CEC will not work. This is fixed in hardware revisions +E34814 and up. + +The CEC support has two modes: the first is a manual mode where userspace has +to manually control CEC for the HDMI Input and all HDMI Outputs. While this gives +full control, it is also complicated. + +The second mode is an automatic mode, which is selected if the module option +``vendor_id`` is set. In that case the driver controls CEC and CEC messages +received in the input will be distributed to the outputs. It is still possible +to use the /dev/cecX devices to talk to the connected devices directly, but it is +the driver that configures everything and deals with things like Hotplug Detect +changes. + +The driver also takes care of the EDIDs: /dev/videoX devices are created to +read the EDIDs and (for the HDMI Input port) to set the EDID. + +By default userspace is responsible to set the EDID for the HDMI Input +according to the EDIDs of the connected displays. But if the ``manufacturer_name`` +module option is set, then the driver will take care of setting the EDID +of the HDMI Input based on the supported resolutions of the connected displays. +Currently the driver only supports resolutions 1080p60 and 4kp60: if all connected +displays support 4kp60, then it will advertise 4kp60 on the HDMI input, otherwise +it will fall back to an EDID that just reports 1080p60. + +The status of the Extron is reported in ``/sys/kernel/debug/cec/cecX/status``. + +The extron-da-hd-4k-plus driver implements the following module options: + +``debug`` +--------- + +If set to 1, then all serial port traffic is shown. + +``vendor_id`` +------------- + +The CEC Vendor ID to report to connected displays. + +If set, then the driver will take care of distributing CEC messages received +on the input to the HDMI outputs. This is done for the following CEC messages: + +- +- and +- +- +- + +If not set, then userspace is responsible for this, and it will have to +configure the CEC devices for HDMI Input and the HDMI Outputs manually. + +``manufacturer_name`` +--------------------- + +A three character manufacturer name that is used in the EDID for the HDMI +Input. If not set, then userspace is reponsible for configuring an EDID. +If set, then the driver will update the EDID automatically based on the +resolutions supported by the connected displays, and it will not be possible +anymore to manually set the EDID for the HDMI Input. + +``hpd_never_low`` +----------------- + +If set, then the Hotplug Detect pin of the HDMI Input will always be high, +even if nothing is connected to the HDMI Outputs. If not set (the default) +then the Hotplug Detect pin of the HDMI input will go low if all the detected +Hotplug Detect pins of the HDMI Outputs are also low. + +This option may be changed dynamically. diff --git a/MAINTAINERS b/MAINTAINERS index e6d77bea5db5..6db07b8fa215 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8461,6 +8461,13 @@ F: lib/bootconfig.c F: tools/bootconfig/* F: tools/bootconfig/scripts/* +EXTRON DA HD 4K PLUS CEC DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: drivers/media/cec/usb/extron-da-hd-4k-plus/ + EXYNOS DP DRIVER M: Jingoo Han L: dri-devel@lists.freedesktop.org diff --git a/drivers/media/cec/usb/Kconfig b/drivers/media/cec/usb/Kconfig index 3f3a5c75287a..6faf4742981d 100644 --- a/drivers/media/cec/usb/Kconfig +++ b/drivers/media/cec/usb/Kconfig @@ -3,6 +3,7 @@ # USB drivers if USB_SUPPORT && TTY +source "drivers/media/cec/usb/extron-da-hd-4k-plus/Kconfig" source "drivers/media/cec/usb/pulse8/Kconfig" source "drivers/media/cec/usb/rainshadow/Kconfig" endif diff --git a/drivers/media/cec/usb/Makefile b/drivers/media/cec/usb/Makefile index e4183d1bfa9a..c082679f5318 100644 --- a/drivers/media/cec/usb/Makefile +++ b/drivers/media/cec/usb/Makefile @@ -2,5 +2,6 @@ # # Makefile for the CEC USB device drivers. # +obj-$(CONFIG_USB_EXTRON_DA_HD_4K_PLUS_CEC) += extron-da-hd-4k-plus/ obj-$(CONFIG_USB_PULSE8_CEC) += pulse8/ obj-$(CONFIG_USB_RAINSHADOW_CEC) += rainshadow/ diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/Kconfig b/drivers/media/cec/usb/extron-da-hd-4k-plus/Kconfig new file mode 100644 index 000000000000..5354f0eebe5c --- /dev/null +++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only +config USB_EXTRON_DA_HD_4K_PLUS_CEC + tristate "Extron DA HD 4K Plus CEC driver" + depends on VIDEO_DEV + depends on USB + depends on USB_ACM + select CEC_CORE + select SERIO + select SERIO_SERPORT + help + This is a CEC driver for the Extron DA HD 4K Plus HDMI Splitter. + + To compile this driver as a module, choose M here: the + module will be called extron-da-hd-4k-plus-cec. diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/Makefile b/drivers/media/cec/usb/extron-da-hd-4k-plus/Makefile new file mode 100644 index 000000000000..2e8f7f60263f --- /dev/null +++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/Makefile @@ -0,0 +1,8 @@ +extron-da-hd-4k-plus-cec-objs := extron-da-hd-4k-plus.o cec-splitter.o +obj-$(CONFIG_USB_EXTRON_DA_HD_4K_PLUS_CEC) := extron-da-hd-4k-plus-cec.o + +all: + $(MAKE) -C $(KDIR) M=$(shell pwd) modules + +install: + $(MAKE) -C $(KDIR) M=$(shell pwd) modules_install diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.c b/drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.c new file mode 100644 index 000000000000..73fdec4b791d --- /dev/null +++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.c @@ -0,0 +1,657 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright 2021-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + */ + +#include + +#include "cec-splitter.h" + +/* + * Helper function to reply to a received message with a Feature Abort + * message. + */ +static int cec_feature_abort_reason(struct cec_adapter *adap, + struct cec_msg *msg, u8 reason) +{ + struct cec_msg tx_msg = { }; + + /* + * Don't reply with CEC_MSG_FEATURE_ABORT to a CEC_MSG_FEATURE_ABORT + * message! + */ + if (msg->msg[1] == CEC_MSG_FEATURE_ABORT) + return 0; + /* Don't Feature Abort messages from 'Unregistered' */ + if (cec_msg_initiator(msg) == CEC_LOG_ADDR_UNREGISTERED) + return 0; + cec_msg_set_reply_to(&tx_msg, msg); + cec_msg_feature_abort(&tx_msg, msg->msg[1], reason); + return cec_transmit_msg(adap, &tx_msg, false); +} + +/* Transmit an Active Source message from this output port to a sink */ +static void cec_port_out_active_source(struct cec_splitter_port *p) +{ + struct cec_adapter *adap = p->adap; + struct cec_msg msg; + + if (!adap->is_configured) + return; + p->is_active_source = true; + cec_msg_init(&msg, adap->log_addrs.log_addr[0], 0); + cec_msg_active_source(&msg, adap->phys_addr); + cec_transmit_msg(adap, &msg, false); +} + +/* Transmit Active Source messages from all output ports to the sinks */ +static void cec_out_active_source(struct cec_splitter *splitter) +{ + unsigned int i; + + for (i = 0; i < splitter->num_out_ports; i++) + cec_port_out_active_source(splitter->ports[i]); +} + +/* Transmit a Standby message from this output port to a sink */ +static void cec_port_out_standby(struct cec_splitter_port *p) +{ + struct cec_adapter *adap = p->adap; + struct cec_msg msg; + + if (!adap->is_configured) + return; + cec_msg_init(&msg, adap->log_addrs.log_addr[0], 0); + cec_msg_standby(&msg); + cec_transmit_msg(adap, &msg, false); +} + +/* Transmit Standby messages from all output ports to the sinks */ +static void cec_out_standby(struct cec_splitter *splitter) +{ + unsigned int i; + + for (i = 0; i < splitter->num_out_ports; i++) + cec_port_out_standby(splitter->ports[i]); +} + +/* Transmit an Image/Text View On message from this output port to a sink */ +static void cec_port_out_wakeup(struct cec_splitter_port *p, u8 opcode) +{ + struct cec_adapter *adap = p->adap; + u8 la = adap->log_addrs.log_addr[0]; + struct cec_msg msg; + + if (la == CEC_LOG_ADDR_INVALID) + la = CEC_LOG_ADDR_UNREGISTERED; + cec_msg_init(&msg, la, 0); + msg.len = 2; + msg.msg[1] = opcode; + cec_transmit_msg(adap, &msg, false); +} + +/* Transmit Image/Text View On messages from all output ports to the sinks */ +static void cec_out_wakeup(struct cec_splitter *splitter, u8 opcode) +{ + unsigned int i; + + for (i = 0; i < splitter->num_out_ports; i++) + cec_port_out_wakeup(splitter->ports[i], opcode); +} + +/* + * Update the power state of the unconfigured CEC device to either + * Off or On depending on the current state of the splitter. + * This keeps the outputs in a consistent state. + */ +void cec_splitter_unconfigured_output(struct cec_splitter_port *p) +{ + p->video_latency = 1; + p->power_status = p->splitter->is_standby ? + CEC_OP_POWER_STATUS_TO_STANDBY : CEC_OP_POWER_STATUS_TO_ON; + + /* The adapter was unconfigured, so clear the sequence and ts values */ + p->out_give_device_power_status_seq = 0; + p->out_give_device_power_status_ts = ktime_set(0, 0); + p->out_request_current_latency_seq = 0; + p->out_request_current_latency_ts = ktime_set(0, 0); +} + +/* + * Update the power state of the newly configured CEC device to either + * Off or On depending on the current state of the splitter. + * This keeps the outputs in a consistent state. + */ +void cec_splitter_configured_output(struct cec_splitter_port *p) +{ + p->video_latency = 1; + p->power_status = p->splitter->is_standby ? + CEC_OP_POWER_STATUS_TO_STANDBY : CEC_OP_POWER_STATUS_TO_ON; + + if (p->splitter->is_standby) { + /* + * Some sinks only obey Standby if it comes from the + * active source. + */ + cec_port_out_active_source(p); + cec_port_out_standby(p); + } else { + cec_port_out_wakeup(p, CEC_MSG_IMAGE_VIEW_ON); + } +} + +/* Pass the in_msg on to all output ports */ +static void cec_out_passthrough(struct cec_splitter *splitter, + const struct cec_msg *in_msg) +{ + unsigned int i; + + for (i = 0; i < splitter->num_out_ports; i++) { + struct cec_splitter_port *p = splitter->ports[i]; + struct cec_adapter *adap = p->adap; + struct cec_msg msg; + + if (!adap->is_configured) + continue; + cec_msg_init(&msg, adap->log_addrs.log_addr[0], 0); + msg.len = in_msg->len; + memcpy(msg.msg + 1, in_msg->msg + 1, msg.len - 1); + cec_transmit_msg(adap, &msg, false); + } +} + +/* + * See if all output ports received the Report Current Latency message, + * and if so, transmit the result from the input port to the video source. + */ +static void cec_out_report_current_latency(struct cec_splitter *splitter, + struct cec_adapter *input_adap) +{ + struct cec_msg reply = {}; + unsigned int reply_lat = 0; + unsigned int cnt = 0; + unsigned int i; + + for (i = 0; i < splitter->num_out_ports; i++) { + struct cec_splitter_port *p = splitter->ports[i]; + struct cec_adapter *adap = p->adap; + + /* Skip unconfigured ports */ + if (!adap->is_configured) + continue; + /* Return if a port is still waiting for a reply */ + if (p->out_request_current_latency_seq) + return; + reply_lat += p->video_latency - 1; + cnt++; + } + + /* + * All ports that can reply, replied, so clear the sequence + * and timestamp values. + */ + for (i = 0; i < splitter->num_out_ports; i++) { + struct cec_splitter_port *p = splitter->ports[i]; + + p->out_request_current_latency_seq = 0; + p->out_request_current_latency_ts = ktime_set(0, 0); + } + + /* + * Return if there were no replies or the input port is no longer + * configured. + */ + if (!cnt || !input_adap->is_configured) + return; + + /* Reply with the average latency */ + reply_lat = 1 + reply_lat / cnt; + cec_msg_init(&reply, input_adap->log_addrs.log_addr[0], + splitter->request_current_latency_dest); + cec_msg_report_current_latency(&reply, input_adap->phys_addr, + reply_lat, 1, 1, 1); + cec_transmit_msg(input_adap, &reply, false); +} + +/* Transmit Request Current Latency to all output ports */ +static int cec_out_request_current_latency(struct cec_splitter *splitter) +{ + ktime_t now = ktime_get(); + bool error = true; + unsigned int i; + + for (i = 0; i < splitter->num_out_ports; i++) { + struct cec_splitter_port *p = splitter->ports[i]; + struct cec_adapter *adap = p->adap; + + if (!adap->is_configured) { + /* Clear if not configured */ + p->out_request_current_latency_seq = 0; + p->out_request_current_latency_ts = ktime_set(0, 0); + } else if (!p->out_request_current_latency_seq) { + /* + * Keep the old ts if an earlier request is still + * pending. This ensures that the request will + * eventually time out based on the timestamp of + * the first request if the sink is unresponsive. + */ + p->out_request_current_latency_ts = now; + } + } + + for (i = 0; i < splitter->num_out_ports; i++) { + struct cec_splitter_port *p = splitter->ports[i]; + struct cec_adapter *adap = p->adap; + struct cec_msg msg; + + if (!adap->is_configured) + continue; + cec_msg_init(&msg, adap->log_addrs.log_addr[0], 0); + cec_msg_request_current_latency(&msg, true, adap->phys_addr); + if (cec_transmit_msg(adap, &msg, false)) + continue; + p->out_request_current_latency_seq = msg.sequence | (1U << 31); + error = false; + } + return error ? -ENODEV : 0; +} + +/* + * See if all output ports received the Report Power Status message, + * and if so, transmit the result from the input port to the video source. + */ +static void cec_out_report_power_status(struct cec_splitter *splitter, + struct cec_adapter *input_adap) +{ + struct cec_msg reply = {}; + /* The target power status of the splitter itself */ + u8 splitter_pwr = splitter->is_standby ? + CEC_OP_POWER_STATUS_STANDBY : CEC_OP_POWER_STATUS_ON; + /* + * The transient power status of the splitter, used if not all + * output report the target power status. + */ + u8 splitter_transient_pwr = splitter->is_standby ? + CEC_OP_POWER_STATUS_TO_STANDBY : CEC_OP_POWER_STATUS_TO_ON; + u8 reply_pwr = splitter_pwr; + unsigned int i; + + for (i = 0; i < splitter->num_out_ports; i++) { + struct cec_splitter_port *p = splitter->ports[i]; + + /* Skip if no sink was found (HPD was low for more than 5s) */ + if (!p->found_sink) + continue; + + /* Return if a port is still waiting for a reply */ + if (p->out_give_device_power_status_seq) + return; + if (p->power_status != splitter_pwr) + reply_pwr = splitter_transient_pwr; + } + + /* + * All ports that can reply, replied, so clear the sequence + * and timestamp values. + */ + for (i = 0; i < splitter->num_out_ports; i++) { + struct cec_splitter_port *p = splitter->ports[i]; + + p->out_give_device_power_status_seq = 0; + p->out_give_device_power_status_ts = ktime_set(0, 0); + } + + /* Return if the input port is no longer configured. */ + if (!input_adap->is_configured) + return; + + /* Reply with the new power status */ + cec_msg_init(&reply, input_adap->log_addrs.log_addr[0], + splitter->give_device_power_status_dest); + cec_msg_report_power_status(&reply, reply_pwr); + cec_transmit_msg(input_adap, &reply, false); +} + +/* Transmit Give Device Power Status to all output ports */ +static int cec_out_give_device_power_status(struct cec_splitter *splitter) +{ + ktime_t now = ktime_get(); + bool error = true; + unsigned int i; + + for (i = 0; i < splitter->num_out_ports; i++) { + struct cec_splitter_port *p = splitter->ports[i]; + struct cec_adapter *adap = p->adap; + + /* + * Keep the old ts if an earlier request is still + * pending. This ensures that the request will + * eventually time out based on the timestamp of + * the first request if the sink is unresponsive. + */ + if (adap->is_configured && !p->out_give_device_power_status_seq) + p->out_give_device_power_status_ts = now; + } + + for (i = 0; i < splitter->num_out_ports; i++) { + struct cec_splitter_port *p = splitter->ports[i]; + struct cec_adapter *adap = p->adap; + struct cec_msg msg; + + if (!adap->is_configured) + continue; + + cec_msg_init(&msg, adap->log_addrs.log_addr[0], 0); + cec_msg_give_device_power_status(&msg, true); + if (cec_transmit_msg(adap, &msg, false)) + continue; + p->out_give_device_power_status_seq = msg.sequence | (1U << 31); + error = false; + } + return error ? -ENODEV : 0; +} + +/* + * CEC messages received on the HDMI input of the splitter are + * forwarded (if relevant) to the HDMI outputs of the splitter. + */ +int cec_splitter_received_input(struct cec_splitter_port *p, struct cec_msg *msg) +{ + if (!cec_msg_status_is_ok(msg)) + return 0; + + if (msg->len < 2) + return -ENOMSG; + + switch (msg->msg[1]) { + case CEC_MSG_DEVICE_VENDOR_ID: + case CEC_MSG_REPORT_POWER_STATUS: + case CEC_MSG_SET_STREAM_PATH: + case CEC_MSG_ROUTING_CHANGE: + case CEC_MSG_REQUEST_ACTIVE_SOURCE: + case CEC_MSG_SYSTEM_AUDIO_MODE_STATUS: + return 0; + + case CEC_MSG_STANDBY: + p->splitter->is_standby = true; + cec_out_standby(p->splitter); + return 0; + + case CEC_MSG_IMAGE_VIEW_ON: + case CEC_MSG_TEXT_VIEW_ON: + p->splitter->is_standby = false; + cec_out_wakeup(p->splitter, msg->msg[1]); + return 0; + + case CEC_MSG_ACTIVE_SOURCE: + cec_out_active_source(p->splitter); + return 0; + + case CEC_MSG_SET_SYSTEM_AUDIO_MODE: + cec_out_passthrough(p->splitter, msg); + return 0; + + case CEC_MSG_GIVE_DEVICE_POWER_STATUS: + p->splitter->give_device_power_status_dest = + cec_msg_initiator(msg); + if (cec_out_give_device_power_status(p->splitter)) + cec_feature_abort_reason(p->adap, msg, + CEC_OP_ABORT_INCORRECT_MODE); + return 0; + + case CEC_MSG_REQUEST_CURRENT_LATENCY: { + u16 pa; + + p->splitter->request_current_latency_dest = + cec_msg_initiator(msg); + cec_ops_request_current_latency(msg, &pa); + if (pa == p->adap->phys_addr && + cec_out_request_current_latency(p->splitter)) + cec_feature_abort_reason(p->adap, msg, + CEC_OP_ABORT_INCORRECT_MODE); + return 0; + } + + default: + return -ENOMSG; + } + return -ENOMSG; +} + +void cec_splitter_nb_transmit_canceled_output(struct cec_splitter_port *p, + const struct cec_msg *msg, + struct cec_adapter *input_adap) +{ + struct cec_splitter *splitter = p->splitter; + u32 seq = msg->sequence | (1U << 31); + + /* + * If this is the result of a failed non-blocking transmit, or it is + * the result of the failed reply to a non-blocking transmit, then + * check if the original transmit was to get the current power status + * or latency and, if so, assume that the remove device is for one + * reason or another unavailable and assume that it is in the same + * power status as the splitter, or has no video latency. + */ + if ((cec_msg_recv_is_tx_result(msg) && !(msg->tx_status & CEC_TX_STATUS_OK)) || + (cec_msg_recv_is_rx_result(msg) && !(msg->rx_status & CEC_RX_STATUS_OK))) { + u8 tx_op = msg->msg[1]; + + if (msg->len < 2) + return; + if (cec_msg_recv_is_rx_result(msg) && + (msg->rx_status & CEC_RX_STATUS_FEATURE_ABORT)) + tx_op = msg->msg[2]; + switch (tx_op) { + case CEC_MSG_GIVE_DEVICE_POWER_STATUS: + if (p->out_give_device_power_status_seq != seq) + break; + p->out_give_device_power_status_seq = 0; + p->out_give_device_power_status_ts = ktime_set(0, 0); + p->power_status = splitter->is_standby ? + CEC_OP_POWER_STATUS_STANDBY : + CEC_OP_POWER_STATUS_ON; + cec_out_report_power_status(splitter, input_adap); + break; + case CEC_MSG_REQUEST_CURRENT_LATENCY: + if (p->out_request_current_latency_seq != seq) + break; + p->video_latency = 1; + p->out_request_current_latency_seq = 0; + p->out_request_current_latency_ts = ktime_set(0, 0); + cec_out_report_current_latency(splitter, input_adap); + break; + } + return; + } + + if (cec_msg_recv_is_tx_result(msg)) { + if (p->out_request_current_latency_seq != seq) + return; + p->out_request_current_latency_ts = ns_to_ktime(msg->tx_ts); + return; + } +} + +/* + * CEC messages received on an HDMI output of the splitter + * are processed here. + */ +int cec_splitter_received_output(struct cec_splitter_port *p, struct cec_msg *msg, + struct cec_adapter *input_adap) +{ + struct cec_adapter *adap = p->adap; + struct cec_splitter *splitter = p->splitter; + u32 seq = msg->sequence | (1U << 31); + struct cec_msg reply = {}; + u16 pa; + + if (!adap->is_configured || msg->len < 2) + return -ENOMSG; + + switch (msg->msg[1]) { + case CEC_MSG_REPORT_POWER_STATUS: { + u8 pwr; + + cec_ops_report_power_status(msg, &pwr); + if (pwr > CEC_OP_POWER_STATUS_TO_STANDBY) + pwr = splitter->is_standby ? + CEC_OP_POWER_STATUS_TO_STANDBY : + CEC_OP_POWER_STATUS_TO_ON; + p->power_status = pwr; + if (p->out_give_device_power_status_seq == seq) { + p->out_give_device_power_status_seq = 0; + p->out_give_device_power_status_ts = ktime_set(0, 0); + } + cec_out_report_power_status(splitter, input_adap); + return 0; + } + + case CEC_MSG_REPORT_CURRENT_LATENCY: { + u8 video_lat; + u8 low_lat_mode; + u8 audio_out_comp; + u8 audio_out_delay; + + cec_ops_report_current_latency(msg, &pa, + &video_lat, &low_lat_mode, + &audio_out_comp, &audio_out_delay); + if (!video_lat || video_lat >= 252) + video_lat = 1; + p->video_latency = video_lat; + if (p->out_request_current_latency_seq == seq) { + p->out_request_current_latency_seq = 0; + p->out_request_current_latency_ts = ktime_set(0, 0); + } + cec_out_report_current_latency(splitter, input_adap); + return 0; + } + + case CEC_MSG_STANDBY: + case CEC_MSG_ROUTING_CHANGE: + case CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS: + return 0; + + case CEC_MSG_ACTIVE_SOURCE: + cec_ops_active_source(msg, &pa); + if (pa == 0) + p->is_active_source = false; + return 0; + + case CEC_MSG_REQUEST_ACTIVE_SOURCE: + if (!p->is_active_source) + return 0; + cec_msg_set_reply_to(&reply, msg); + cec_msg_active_source(&reply, adap->phys_addr); + cec_transmit_msg(adap, &reply, false); + return 0; + + case CEC_MSG_GIVE_DEVICE_POWER_STATUS: + cec_msg_set_reply_to(&reply, msg); + cec_msg_report_power_status(&reply, splitter->is_standby ? + CEC_OP_POWER_STATUS_STANDBY : + CEC_OP_POWER_STATUS_ON); + cec_transmit_msg(adap, &reply, false); + return 0; + + case CEC_MSG_SET_STREAM_PATH: + cec_ops_set_stream_path(msg, &pa); + if (pa == adap->phys_addr) { + cec_msg_set_reply_to(&reply, msg); + cec_msg_active_source(&reply, pa); + cec_transmit_msg(adap, &reply, false); + } + return 0; + + default: + return -ENOMSG; + } + return -ENOMSG; +} + +/* + * Called every second to check for timed out messages and whether there + * still is a video sink connected or not. + * + * Returns true if sinks were lost. + */ +bool cec_splitter_poll(struct cec_splitter *splitter, + struct cec_adapter *input_adap, bool debug) +{ + ktime_t now = ktime_get(); + u8 pwr = splitter->is_standby ? + CEC_OP_POWER_STATUS_STANDBY : CEC_OP_POWER_STATUS_ON; + unsigned int max_delay_ms = input_adap->xfer_timeout_ms + 2000; + unsigned int i; + bool res = false; + + for (i = 0; i < splitter->num_out_ports; i++) { + struct cec_splitter_port *p = splitter->ports[i]; + s64 pwr_delta, lat_delta; + bool pwr_timeout, lat_timeout; + + if (!p) + continue; + + pwr_delta = ktime_ms_delta(now, p->out_give_device_power_status_ts); + pwr_timeout = p->out_give_device_power_status_seq && + pwr_delta >= max_delay_ms; + lat_delta = ktime_ms_delta(now, p->out_request_current_latency_ts); + lat_timeout = p->out_request_current_latency_seq && + lat_delta >= max_delay_ms; + + /* + * If the HPD is low for more than 5 seconds, then assume no display + * is connected. + */ + if (p->found_sink && ktime_to_ns(p->lost_sink_ts) && + ktime_ms_delta(now, p->lost_sink_ts) > 5000) { + if (debug) + dev_info(splitter->dev, + "port %u: HPD low for more than 5s, assume no sink is connected.\n", + p->port); + p->found_sink = false; + p->lost_sink_ts = ktime_set(0, 0); + res = true; + } + + /* + * If the power status request timed out, then set the port's + * power status to that of the splitter, ensuring a consistent + * power state. + */ + if (pwr_timeout) { + mutex_lock(&p->adap->lock); + if (debug) + dev_info(splitter->dev, + "port %u: give up on power status for seq %u\n", + p->port, + p->out_give_device_power_status_seq & ~(1 << 31)); + p->power_status = pwr; + p->out_give_device_power_status_seq = 0; + p->out_give_device_power_status_ts = ktime_set(0, 0); + mutex_unlock(&p->adap->lock); + cec_out_report_power_status(splitter, input_adap); + } + + /* + * If the current latency request timed out, then set the port's + * latency to 1. + */ + if (lat_timeout) { + mutex_lock(&p->adap->lock); + if (debug) + dev_info(splitter->dev, + "port %u: give up on latency for seq %u\n", + p->port, + p->out_request_current_latency_seq & ~(1 << 31)); + p->video_latency = 1; + p->out_request_current_latency_seq = 0; + p->out_request_current_latency_ts = ktime_set(0, 0); + mutex_unlock(&p->adap->lock); + cec_out_report_current_latency(splitter, input_adap); + } + } + return res; +} diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.h b/drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.h new file mode 100644 index 000000000000..7422f7c5719e --- /dev/null +++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Copyright 2021-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + */ + +#ifndef _CEC_SPLITTER_H_ +#define _CEC_SPLITTER_H_ + +struct cec_splitter; + +#define STATE_CHANGE_MAX_REPEATS 2 + +struct cec_splitter_port { + struct cec_splitter *splitter; + struct cec_adapter *adap; + unsigned int port; + bool is_active_source; + bool found_sink; + ktime_t lost_sink_ts; + u32 out_request_current_latency_seq; + ktime_t out_request_current_latency_ts; + u8 video_latency; + u32 out_give_device_power_status_seq; + ktime_t out_give_device_power_status_ts; + u8 power_status; +}; + +struct cec_splitter { + struct device *dev; + unsigned int num_out_ports; + struct cec_splitter_port **ports; + + /* High-level splitter state */ + u8 request_current_latency_dest; + u8 give_device_power_status_dest; + bool is_standby; +}; + +void cec_splitter_unconfigured_output(struct cec_splitter_port *port); +void cec_splitter_configured_output(struct cec_splitter_port *port); +int cec_splitter_received_input(struct cec_splitter_port *port, struct cec_msg *msg); +int cec_splitter_received_output(struct cec_splitter_port *port, struct cec_msg *msg, + struct cec_adapter *input_adap); +void cec_splitter_nb_transmit_canceled_output(struct cec_splitter_port *port, + const struct cec_msg *msg, + struct cec_adapter *input_adap); +bool cec_splitter_poll(struct cec_splitter *splitter, + struct cec_adapter *input_adap, bool debug); + +#endif diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c new file mode 100644 index 000000000000..8526f613a40e --- /dev/null +++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c @@ -0,0 +1,1836 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2021-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + */ + +/* + * Currently this driver does not fully support the serial port of the + * Extron, only the USB port is fully supported. + * + * Issues specific to using the serial port instead of the USB since the + * serial port doesn't detect if the device is powered off: + * + * - Some periodic ping mechanism is needed to detect when the Extron is + * powered off and when it is powered on again. + * - What to do when it is powered off and the driver is modprobed? Keep + * trying to contact the Extron indefinitely? + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "extron-da-hd-4k-plus.h" + +MODULE_AUTHOR("Hans Verkuil "); +MODULE_DESCRIPTION("Extron DA HD 4K PLUS HDMI CEC driver"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-1)"); + +static unsigned int vendor_id; +module_param(vendor_id, uint, 0444); +MODULE_PARM_DESC(vendor_id, "CEC Vendor ID"); + +static char manufacturer_name[4]; +module_param_string(manufacturer_name, manufacturer_name, + sizeof(manufacturer_name), 0644); +MODULE_PARM_DESC(manufacturer_name, + "EDID Vendor String (3 uppercase characters)"); + +static bool hpd_never_low; +module_param(hpd_never_low, bool, 0644); +MODULE_PARM_DESC(hpd_never_low, "Input HPD will never go low (1), or go low if all output HPDs are low (0, default)"); + +#define EXTRON_TIMEOUT_SECS 6 + +static const u8 hdmi_edid[256] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x20, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78, + 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, + 0x0f, 0x50, 0x54, 0x20, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, + 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, + 0x45, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, + 0x87, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x68, + 0x64, 0x6d, 0x69, 0x2d, 0x31, 0x30, 0x38, 0x30, + 0x70, 0x36, 0x30, 0x0a, 0x00, 0x00, 0x00, 0xfe, + 0x00, 0x73, 0x65, 0x72, 0x69, 0x6f, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x95, + + 0x02, 0x03, 0x1b, 0xf1, 0x42, 0x10, 0x01, 0x23, + 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x68, + 0x03, 0x0c, 0x00, 0x10, 0x00, 0x00, 0x21, 0x01, + 0xe2, 0x00, 0xca, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, +}; + +static const u8 hdmi_edid_4k_300[256] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x20, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78, + 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, + 0x0f, 0x50, 0x54, 0x20, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, + 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, + 0x45, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, + 0x87, 0x3c, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x68, + 0x64, 0x6d, 0x69, 0x2d, 0x34, 0x6b, 0x2d, 0x36, + 0x30, 0x30, 0x0a, 0x20, 0x00, 0x00, 0x00, 0xfe, + 0x00, 0x73, 0x65, 0x72, 0x69, 0x6f, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x87, + + 0x02, 0x03, 0x1f, 0xf1, 0x43, 0x10, 0x5f, 0x01, + 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, + 0x6b, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x00, 0x3c, + 0x21, 0x00, 0x20, 0x01, 0xe2, 0x00, 0xca, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, +}; + +static const u8 hdmi_edid_4k_600[256] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x20, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78, + 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, + 0x0f, 0x50, 0x54, 0x20, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, 0xe8, + 0x00, 0x30, 0xf2, 0x70, 0x5a, 0x80, 0xb0, 0x58, + 0x8a, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18, + 0x87, 0x3c, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x68, + 0x64, 0x6d, 0x69, 0x2d, 0x34, 0x6b, 0x2d, 0x36, + 0x30, 0x30, 0x0a, 0x20, 0x00, 0x00, 0x00, 0xfe, + 0x00, 0x73, 0x65, 0x72, 0x69, 0x6f, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x4c, + + 0x02, 0x03, 0x28, 0xf1, 0x44, 0x61, 0x5f, 0x10, + 0x01, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, + 0x00, 0x6b, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x00, + 0x3c, 0x21, 0x00, 0x20, 0x01, 0x67, 0xd8, 0x5d, + 0xc4, 0x01, 0x78, 0x00, 0x00, 0xe2, 0x00, 0xca, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, +}; + +static int extron_send_byte(struct serio *serio, char byte) +{ + int err, i; + + for (i = 0; i < 100; i++) { + err = serio_write(serio, byte); + if (!err) + break; + usleep_range(80, 120); + } + if (err) + dev_warn(&serio->dev, "unable to write byte after 100 attempts\n"); + return err ? -EIO : 0; +} + +static int extron_send_len(struct serio *serio, const char *command, + const unsigned char *bin, unsigned int len) +{ + int err = 0; + + for (; !err && *command; command++) + err = extron_send_byte(serio, *command); + if (!err) + err = extron_send_byte(serio, '\r'); + if (bin) + for (; !err && len; len--) + err = extron_send_byte(serio, *bin++); + return err; +} + +static int extron_send_and_wait_len(struct extron *extron, struct extron_port *port, + const char *cmd, const unsigned char *bin, + unsigned int len, const char *response) +{ + int timeout = EXTRON_TIMEOUT_SECS * HZ; + int err; + + if (debug) { + if (response) + dev_info(extron->dev, "transmit %s (response: %s)\n", + cmd, response); + else + dev_info(extron->dev, "transmit %s\n", cmd); + } + + mutex_lock(&extron->serio_lock); + if (port) { + init_completion(&port->cmd_done); + port->cmd_error = 0; + port->response = response; + } else { + init_completion(&extron->cmd_done); + extron->cmd_error = 0; + extron->response = response; + } + err = extron_send_len(extron->serio, cmd, bin, len); + + if (!err && response && + !wait_for_completion_timeout(port ? &port->cmd_done : &extron->cmd_done, timeout)) { + dev_info(extron->dev, "transmit %s failed with %s (expected: %s)\n", + cmd, extron->reply, response); + err = -ETIMEDOUT; + } + + if (!err && response && (port ? port->cmd_error : extron->cmd_error)) { + dev_info(extron->dev, "transmit %s failed with E%02u (expected: %s)\n", + cmd, port ? port->cmd_error : extron->cmd_error, response); + if (port) + port->cmd_error = 0; + else + extron->cmd_error = 0; + err = -EPROTO; + } + if (port) + port->response = NULL; + else + extron->response = NULL; + mutex_unlock(&extron->serio_lock); + return err; +} + +static int extron_send_and_wait(struct extron *extron, struct extron_port *port, + const char *cmd, const char *response) +{ + return extron_send_and_wait_len(extron, port, cmd, NULL, 0, response); +} + +static void extron_parse_edid(struct extron_port *port) +{ + const u8 *edid = port->edid; + unsigned int i, end; + u8 d; + + port->has_4kp30 = false; + port->has_4kp60 = false; + port->has_qy = false; + port->has_qs = false; + /* Store Established Timings 1 and 2 */ + port->est_i = edid[0x23]; + port->est_ii = edid[0x24]; + + // Check DTDs in base block + for (i = 0; i < 4; i++) { + const u8 *dtd = edid + 0x36 + i * 18; + unsigned int w, h; + unsigned int mhz; + u64 pclk; + + if (!dtd[0] && !dtd[1]) + continue; + w = dtd[2] + ((dtd[4] & 0xf0) << 4); + h = dtd[5] + ((dtd[7] & 0xf0) << 4); + if (w != 3840 || h != 2160) + continue; + + w += dtd[3] + ((dtd[4] & 0x0f) << 8); + h += dtd[6] + ((dtd[7] & 0x0f) << 8); + pclk = dtd[0] + (dtd[1] << 8); + pclk *= 100000; + mhz = div_u64(pclk, w * h); + if (mhz >= 297) + port->has_4kp30 = true; + if (mhz >= 594) + port->has_4kp60 = true; + } + + if (port->edid_blocks == 1) + return; + + edid += 128; + + /* Return if not a CTA-861 extension block */ + if (edid[0] != 0x02 || edid[1] != 0x03) + return; + + /* search Video Data Block (tag 2) */ + d = edid[2] & 0x7f; + /* Check if there are Data Blocks */ + if (d <= 4) + return; + + i = 4; + end = d; + + do { + u8 tag = edid[i] >> 5; + u8 len = edid[i] & 0x1f; + + /* Avoid buffer overrun in case the EDID is malformed */ + if (i + len + 1 > 0x7f) + return; + + switch (tag) { + case 2: /* Video Data Block */ + /* Search for VIC 97 */ + if (memchr(edid + i + 1, 97, len)) + port->has_4kp60 = true; + /* Search for VIC 95 */ + if (memchr(edid + i + 1, 95, len)) + port->has_4kp30 = true; + break; + + case 7: /* Use Extended Tag */ + switch (edid[i + 1]) { + case 0: /* Video Capability Data Block */ + if (edid[i + 2] & 0x80) + port->has_qy = true; + if (edid[i + 2] & 0x40) + port->has_qs = true; + break; + } + break; + } + i += len + 1; + } while (i < end); +} + +static int get_edid_tag_location(const u8 *edid, unsigned int size, + u8 want_tag, u8 ext_tag) +{ + unsigned int offset = 128; + int i, end; + u8 d; + + edid += offset; + + /* Return if not a CTA-861 extension block */ + if (size < 256 || edid[0] != 0x02 || edid[1] != 0x03) + return -1; + + /* search tag */ + d = edid[0x02] & 0x7f; + if (d <= 4) + return -1; + + i = 0x04; + end = 0x00 + d; + + do { + unsigned char tag = edid[i] >> 5; + unsigned char len = edid[i] & 0x1f; + + if (tag != want_tag || i + len > end) { + i += len + 1; + continue; + } + + if (tag < 7 || (len >= 1 && edid[i + 1] == ext_tag)) + return offset + i; + i += len + 1; + } while (i < end); + return -1; +} + +static void extron_edid_crc(u8 *edid) +{ + u8 sum = 0; + int offset; + + /* Update CRC */ + for (offset = 0; offset < 127; offset++) + sum += edid[offset]; + edid[127] = 256 - sum; +} + +/* + * Fill in EDID string. As per VESA EDID-1.3, strings are at most 13 chars + * long. If shorter then add a 0x0a character after the string and pad the + * remainder with spaces. + */ +static void extron_set_edid_string(u8 *start, const char *s) +{ + const unsigned int max_len = 13; + int len = strlen(s); + + memset(start, ' ', max_len); + if (len > max_len) + len = max_len; + memcpy(start, s, len); + if (len < max_len) + start[len] = 0x0a; +} + +static void extron_update_edid(struct extron_port *port, unsigned int blocks) +{ + int offset; + u8 c1, c2; + + c1 = ((manufacturer_name[0] - '@') << 2) | + (((manufacturer_name[1] - '@') >> 3) & 0x03); + c2 = (((manufacturer_name[1] - '@') & 0x07) << 5) | + ((manufacturer_name[2] - '@') & 0x1f); + + port->edid_tmp[8] = c1; + port->edid_tmp[9] = c2; + + /* Set Established Timings, but always enable VGA */ + port->edid_tmp[0x23] = port->est_i | 0x20; + port->edid_tmp[0x24] = port->est_ii; + + /* Set the Monitor Name to the unit name */ + extron_set_edid_string(port->edid_tmp + 0x5f, port->extron->unit_name); + /* Set the ASCII String to the CEC adapter name */ + extron_set_edid_string(port->edid_tmp + 0x71, port->adap->name); + + extron_edid_crc(port->edid_tmp); + + /* Find Video Capability Data Block */ + offset = get_edid_tag_location(port->edid_tmp, blocks * 128, 7, 0); + if (offset > 0) { + port->edid_tmp[offset + 2] &= ~0xc0; + if (port->has_qy) + port->edid_tmp[offset + 2] |= 0x80; + if (port->has_qs) + port->edid_tmp[offset + 2] |= 0x40; + } + + extron_edid_crc(port->edid_tmp + 128); +} + +static int extron_write_edid(struct extron_port *port, + const u8 *edid, unsigned int blocks) +{ + struct extron *extron = port->extron; + u16 phys_addr = CEC_PHYS_ADDR_INVALID; + int ret; + + if (cec_get_edid_spa_location(edid, blocks * 128)) + phys_addr = 0; + + if (mutex_lock_interruptible(&extron->edid_lock)) + return -EINTR; + + memcpy(port->edid_tmp, edid, blocks * 128); + + if (manufacturer_name[0]) + extron_update_edid(port, blocks); + + ret = extron_send_and_wait_len(port->extron, port, "W+UF256,in.bin", + port->edid_tmp, sizeof(port->edid_tmp), + "Upl"); + if (ret) + goto unlock; + ret = extron_send_and_wait(port->extron, port, "WI1,in.binEDID", + "EdidI01"); + if (ret) + goto unlock; + + port->edid_blocks = blocks; + memcpy(port->edid, port->edid_tmp, blocks * 128); + port->read_edid = true; + mutex_unlock(&extron->edid_lock); + + cec_s_phys_addr(port->adap, phys_addr, false); + return 0; + +unlock: + mutex_unlock(&extron->edid_lock); + return ret; +} + +static void update_edid_work(struct work_struct *w) +{ + struct extron *extron = container_of(w, struct extron, + work_update_edid.work); + struct extron_port *in = extron->ports[extron->num_out_ports]; + struct extron_port *p; + bool has_edid = false; + bool has_4kp30 = true; + bool has_4kp60 = true; + bool has_qy = true; + bool has_qs = true; + u8 est_i = 0xff; + u8 est_ii = 0xff; + unsigned int out; + + for (out = 0; has_4kp60 && out < extron->num_out_ports; out++) { + p = extron->ports[out]; + if (p->read_edid) { + has_4kp60 = p->has_4kp60; + est_i &= p->est_i; + est_ii &= p->est_ii; + has_edid = true; + } + } + for (out = 0; has_4kp30 && out < extron->num_out_ports; out++) + if (extron->ports[out]->read_edid) + has_4kp30 = extron->ports[out]->has_4kp30; + + for (out = 0; has_qy && out < extron->num_out_ports; out++) + if (extron->ports[out]->read_edid) + has_qy = extron->ports[out]->has_qy; + + for (out = 0; has_qs && out < extron->num_out_ports; out++) + if (extron->ports[out]->read_edid) + has_qs = extron->ports[out]->has_qs; + + /* exit if no output port had an EDID */ + if (!has_edid) + return; + + /* exit if the input EDID properties remained unchanged */ + if (has_4kp60 == in->has_4kp60 && has_4kp30 == in->has_4kp30 && + has_qy == in->has_qy && has_qs == in->has_qs && + est_i == in->est_i && est_ii == in->est_ii) + return; + + in->has_4kp60 = has_4kp60; + in->has_4kp30 = has_4kp30; + in->has_qy = has_qy; + in->has_qs = has_qs; + in->est_i = est_i; + in->est_ii = est_ii; + extron_write_edid(extron->ports[extron->num_out_ports], + has_4kp60 ? hdmi_edid_4k_600 : + (has_4kp30 ? hdmi_edid_4k_300 : hdmi_edid), 2); +} + +static void extron_read_edid(struct extron_port *port) +{ + struct extron *extron = port->extron; + char cmd[10], reply[10]; + unsigned int idx; + + idx = port->port.port + (port->is_input ? 0 : extron->num_in_ports); + snprintf(cmd, sizeof(cmd), "WR%uEDID", idx); + snprintf(reply, sizeof(reply), "EdidR%u", idx); + if (mutex_lock_interruptible(&extron->edid_lock)) + return; + if (port->read_edid) + goto unlock; + extron->edid_bytes_read = 0; + extron->edid_port = port; + port->edid_blocks = 0; + if (!port->has_edid) + goto no_edid; + + extron->edid_reading = true; + + if (!extron_send_and_wait(extron, port, cmd, reply)) + wait_for_completion_killable_timeout(&extron->edid_completion, + msecs_to_jiffies(1000)); + if (port->edid_blocks) { + extron_parse_edid(port); + port->read_edid = true; + if (!port->is_input) + v4l2_ctrl_s_ctrl(port->ctrl_tx_edid_present, 1); + } +no_edid: + extron->edid_reading = false; +unlock: + mutex_unlock(&extron->edid_lock); + cancel_delayed_work_sync(&extron->work_update_edid); + if (manufacturer_name[0]) + schedule_delayed_work(&extron->work_update_edid, + msecs_to_jiffies(1000)); +} + +static void extron_irq_work_handler(struct work_struct *work) +{ + struct extron_port *port = + container_of(work, struct extron_port, irq_work); + struct extron *extron = port->extron; + unsigned long flags; + bool update_pa; + u16 pa; + bool update_has_signal; + bool has_signal; + bool update_has_edid; + bool has_edid; + u32 status; + + spin_lock_irqsave(&port->msg_lock, flags); + while (port->rx_msg_num) { + spin_unlock_irqrestore(&port->msg_lock, flags); + cec_received_msg(port->adap, + &port->rx_msg[port->rx_msg_cur_idx]); + spin_lock_irqsave(&port->msg_lock, flags); + if (port->rx_msg_num) + port->rx_msg_num--; + port->rx_msg_cur_idx = + (port->rx_msg_cur_idx + 1) % NUM_MSGS; + } + update_pa = port->update_phys_addr; + pa = port->phys_addr; + port->update_phys_addr = false; + update_has_signal = port->update_has_signal; + has_signal = port->has_signal; + port->update_has_signal = false; + update_has_edid = port->update_has_edid; + has_edid = port->has_edid; + port->update_has_edid = false; + status = port->tx_done_status; + port->tx_done_status = 0; + spin_unlock_irqrestore(&port->msg_lock, flags); + + if (status) + cec_transmit_done(port->adap, status, 0, 0, 0, 0); + + if (update_has_signal && port->is_input) + v4l2_ctrl_s_ctrl(port->ctrl_rx_power_present, has_signal); + + if (update_has_edid && !port->is_input) { + v4l2_ctrl_s_ctrl(port->ctrl_tx_hotplug, + port->has_edid); + if (port->has_edid) { + port->port.found_sink = true; + port->port.lost_sink_ts = ktime_set(0, 0); + } else { + port->port.lost_sink_ts = ktime_get(); + } + if (!has_edid) { + port->edid_blocks = 0; + port->read_edid = false; + if (extron->edid_reading && !has_edid && + extron->edid_port == port) + extron->edid_reading = false; + v4l2_ctrl_s_ctrl(port->ctrl_tx_edid_present, 0); + } else if (!extron->edid_reading || extron->edid_port != port) { + extron_read_edid(port); + } + } + if (update_pa) + cec_s_phys_addr(port->adap, pa, false); +} + +static void extron_process_received(struct extron_port *port, const char *data) +{ + struct cec_msg msg = {}; + unsigned int len = strlen(data); + unsigned long irq_flags; + unsigned int idx; + + if (!port || port->disconnected) + return; + + if (len < 5 || (len - 2) % 3 || data[len - 2] != '*') + goto malformed; + + while (*data != '*') { + int v = hex2bin(&msg.msg[msg.len], data + 1, 1); + + if (*data != '%' || v) + goto malformed; + msg.len++; + data += 3; + } + + spin_lock_irqsave(&port->msg_lock, irq_flags); + idx = (port->rx_msg_cur_idx + port->rx_msg_num) % + NUM_MSGS; + if (port->rx_msg_num == NUM_MSGS) { + dev_warn(port->dev, + "message queue is full, dropping %*ph\n", + msg.len, msg.msg); + spin_unlock_irqrestore(&port->msg_lock, + irq_flags); + return; + } + port->rx_msg_num++; + port->rx_msg[idx] = msg; + spin_unlock_irqrestore(&port->msg_lock, irq_flags); + if (!port->disconnected) + schedule_work(&port->irq_work); + return; + +malformed: + dev_info(port->extron->dev, "malformed msg received: '%s'\n", data); +} + +static void extron_port_signal_change(struct extron_port *port, bool has_sig) +{ + unsigned long irq_flags; + bool update = false; + + if (!port) + return; + + spin_lock_irqsave(&port->msg_lock, irq_flags); + if (!port->update_has_signal && port->has_signal != has_sig) { + port->update_has_signal = true; + update = true; + } + port->has_signal = has_sig; + spin_unlock_irqrestore(&port->msg_lock, irq_flags); + if (update && !port->disconnected) + schedule_work(&port->irq_work); +} + +static void extron_process_signal_change(struct extron *extron, const char *data) +{ + unsigned int i; + + extron_port_signal_change(extron->ports[extron->num_out_ports], + data[0] == '1'); + for (i = 0; i < extron->num_out_ports; i++) + extron_port_signal_change(extron->ports[i], + data[2 + 2 * i] != '0'); +} + +static void extron_port_edid_change(struct extron_port *port, bool has_edid) +{ + unsigned long irq_flags; + bool update = false; + + if (!port) + return; + + spin_lock_irqsave(&port->msg_lock, irq_flags); + if (!port->update_has_edid && port->has_edid != has_edid) { + port->update_has_edid = true; + update = true; + } + port->has_edid = has_edid; + spin_unlock_irqrestore(&port->msg_lock, irq_flags); + if (update && !port->disconnected) + schedule_work(&port->irq_work); +} + +static void extron_process_edid_change(struct extron *extron, const char *data) +{ + unsigned int i; + + /* + * Do nothing if the Extron isn't ready yet. Trying to do this + * while the Extron firmware is still settling will fail. + */ + if (!extron->is_ready) + return; + + for (i = 0; i < extron->num_out_ports; i++) + extron_port_edid_change(extron->ports[i], + data[2 + 2 * i] != '0'); +} + +static void extron_phys_addr_change(struct extron_port *port, u16 pa) +{ + unsigned long irq_flags; + bool update = false; + + if (!port) + return; + + spin_lock_irqsave(&port->msg_lock, irq_flags); + if (!port->update_phys_addr && port->phys_addr != pa) { + update = true; + port->update_phys_addr = true; + } + port->phys_addr = pa; + spin_unlock_irqrestore(&port->msg_lock, irq_flags); + if (update && !port->disconnected) + schedule_work(&port->irq_work); +} + +static void extron_process_tx_done(struct extron_port *port, char status) +{ + unsigned long irq_flags; + unsigned int tx_status; + + if (!port) + return; + + switch (status) { + case '0': + tx_status = CEC_TX_STATUS_NACK | CEC_TX_STATUS_MAX_RETRIES; + break; + case '1': + tx_status = CEC_TX_STATUS_OK; + break; + default: + tx_status = CEC_TX_STATUS_ERROR; + break; + } + spin_lock_irqsave(&port->msg_lock, irq_flags); + port->tx_done_status = tx_status; + spin_unlock_irqrestore(&port->msg_lock, irq_flags); + if (!port->disconnected) + schedule_work(&port->irq_work); +} + +static void extron_add_edid(struct extron_port *port, const char *hex) +{ + struct extron *extron = port ? port->extron : NULL; + + if (!port || port != extron->edid_port) + return; + while (extron->edid_bytes_read < sizeof(port->edid) && *hex) { + int err = hex2bin(&port->edid[extron->edid_bytes_read], hex, 1); + + if (err) { + extron->edid_reading = false; + complete(&extron->edid_completion); + break; + } + extron->edid_bytes_read++; + hex += 2; + } + if (extron->edid_bytes_read == 128 && + port->edid[126] == 0) { + /* There are no extension blocks, we're done */ + port->edid_blocks = 1; + extron->edid_reading = false; + complete(&extron->edid_completion); + } + if (extron->edid_bytes_read < sizeof(port->edid)) + return; + if (!*hex) + port->edid_blocks = 2; + extron->edid_reading = false; + complete(&extron->edid_completion); +} + +static irqreturn_t extron_interrupt(struct serio *serio, unsigned char data, + unsigned int flags) +{ + struct extron *extron = serio_get_drvdata(serio); + struct extron_port *port = NULL; + bool found_response; + unsigned int p; + + if (data == '\r' || data == '\n') { + if (extron->idx == 0) + return IRQ_HANDLED; + memcpy(extron->data, extron->buf, extron->idx); + extron->len = extron->idx; + extron->data[extron->len] = 0; + if (debug) + dev_info(extron->dev, "received %s\n", extron->data); + extron->idx = 0; + if (!memcmp(extron->data, "Sig", 3) && + extron->data[4] == '*') { + extron_process_signal_change(extron, extron->data + 3); + } else if (!memcmp(extron->data, "Hdcp", 4) && + extron->data[5] == '*') { + extron_process_edid_change(extron, extron->data + 4); + } else if (!memcmp(extron->data, "DcecI", 5) && + extron->data[5] >= '1' && + extron->data[5] < '1' + extron->num_in_ports) { + unsigned int p = extron->data[5] - '1'; + + p += extron->num_out_ports; + extron_process_tx_done(extron->ports[p], + extron->data[extron->len - 1]); + } else if (!memcmp(extron->data, "Ceci", 4) && + extron->data[4] >= '1' && + extron->data[4] < '1' + extron->num_in_ports && + extron->data[5] == '*') { + unsigned int p = extron->data[4] - '1'; + + p += extron->num_out_ports; + extron_process_received(extron->ports[p], + extron->data + 6); + } else if (!memcmp(extron->data, "DcecO", 5) && + extron->data[5] >= '1' && + extron->data[5] < '1' + extron->num_out_ports) { + unsigned int p = extron->data[5] - '1'; + + extron_process_tx_done(extron->ports[p], + extron->data[extron->len - 1]); + } else if (!memcmp(extron->data, "Ceco", 4) && + extron->data[4] >= '1' && + extron->data[4] < '1' + extron->num_out_ports && + extron->data[5] == '*') { + unsigned int p = extron->data[4] - '1'; + + extron_process_received(extron->ports[p], + extron->data + 6); + } else if (!memcmp(extron->data, "Pceco", 5) && + extron->data[5] >= '1' && + extron->data[5] < '1' + extron->num_out_ports) { + unsigned int p = extron->data[5] - '1'; + unsigned int tmp_pa[2] = { 0xff, 0xff }; + + if (sscanf(extron->data + 7, "%%%02x%%%02x", + &tmp_pa[0], &tmp_pa[1]) == 2) + extron_phys_addr_change(extron->ports[p], + tmp_pa[0] << 8 | tmp_pa[1]); + } else if (!memcmp(extron->data, "Pceci", 5) && + extron->data[5] >= '1' && + extron->data[5] < '1' + extron->num_in_ports) { + unsigned int p = extron->data[5] - '1'; + unsigned int tmp_pa[2] = { 0xff, 0xff }; + + p += extron->num_out_ports; + if (sscanf(extron->data + 7, "%%%02x%%%02x", + &tmp_pa[0], &tmp_pa[1]) == 2) + extron_phys_addr_change(extron->ports[p], + tmp_pa[0] << 8 | tmp_pa[1]); + } else if (!memcmp(extron->data, "EdidR", 5) && + extron->data[5] >= '1' && + extron->data[5] < '1' + extron->num_ports && + extron->data[6] == '*') { + unsigned int p = extron->data[5] - '1'; + + if (p) + p--; + else + p = extron->num_out_ports; + extron_add_edid(extron->ports[p], extron->data + 7); + } else if (extron->edid_reading && extron->len == 32 && + extron->edid_port) { + extron_add_edid(extron->edid_port, extron->data); + } + + found_response = false; + if (extron->response && + !strncmp(extron->response, extron->data, + strlen(extron->response))) + found_response = true; + + for (p = 0; !found_response && p < extron->num_ports; p++) { + port = extron->ports[p]; + if (port && port->response && + !strncmp(port->response, extron->data, + strlen(port->response))) + found_response = true; + } + + if (!found_response && extron->response && + extron->data[0] == 'E' && + isdigit(extron->data[1]) && + isdigit(extron->data[2]) && + !extron->data[3]) { + extron->cmd_error = (extron->data[1] - '0') * 10 + + extron->data[2] - '0'; + extron->response = NULL; + complete(&extron->cmd_done); + } + + if (!found_response) + return IRQ_HANDLED; + + memcpy(extron->reply, extron->data, extron->len); + extron->reply[extron->len] = 0; + if (!port) { + extron->response = NULL; + complete(&extron->cmd_done); + } else { + port->response = NULL; + complete(&port->cmd_done); + } + return IRQ_HANDLED; + } + + if (extron->idx >= DATA_SIZE - 1) { + dev_info(extron->dev, + "throwing away %d bytes of garbage\n", extron->idx); + extron->idx = 0; + } + extron->buf[extron->idx++] = (char)data; + return IRQ_HANDLED; +} + +static int extron_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + struct extron_port *port = cec_get_drvdata(adap); + + return (port->disconnected && enable) ? -ENODEV : 0; +} + +static int extron_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) +{ + struct extron_port *port = cec_get_drvdata(adap); + char cmd[26]; + char resp[25]; + u8 la = log_addr == CEC_LOG_ADDR_INVALID ? 15 : log_addr; + int err; + + if (port->disconnected) + return -ENODEV; + snprintf(cmd, sizeof(cmd), "W%c%u*%uLCEC", + port->direction, port->port.port, la); + snprintf(resp, sizeof(resp), "Lcec%c%u*%u", + port->direction, port->port.port, la); + err = extron_send_and_wait(port->extron, port, cmd, resp); + return log_addr != CEC_LOG_ADDR_INVALID && err ? err : 0; +} + +static int extron_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct extron_port *port = cec_get_drvdata(adap); + char buf[CEC_MAX_MSG_SIZE * 3 + 1]; + char cmd[CEC_MAX_MSG_SIZE * 3 + 13]; + unsigned int i; + + if (port->disconnected) + return -ENODEV; + buf[0] = 0; + for (i = 0; i < msg->len - 1; i++) + sprintf(buf + i * 3, "%%%02X", msg->msg[i + 1]); + snprintf(cmd, sizeof(cmd), "W%c%u*%u*%u*%sDCEC", + port->direction, port->port.port, + cec_msg_initiator(msg), cec_msg_destination(msg), buf); + return extron_send_and_wait(port->extron, port, cmd, NULL); +} + +static void extron_cec_adap_unconfigured(struct cec_adapter *adap) +{ + struct extron_port *port = cec_get_drvdata(adap); + + if (port->disconnected) + return; + if (debug) + dev_info(port->extron->dev, "unconfigured port %d (%s)\n", + port->port.port, + port->extron->splitter.is_standby ? "Off" : "On"); + if (!port->is_input) + cec_splitter_unconfigured_output(&port->port); +} + +static void extron_cec_configured(struct cec_adapter *adap) +{ + struct extron_port *port = cec_get_drvdata(adap); + + if (port->disconnected) + return; + if (debug) + dev_info(port->extron->dev, "configured port %d (%s)\n", + port->port.port, + port->extron->splitter.is_standby ? "Off" : "On"); + if (!port->is_input) + cec_splitter_configured_output(&port->port); +} + +static void extron_cec_adap_nb_transmit_canceled(struct cec_adapter *adap, + const struct cec_msg *msg) +{ + struct extron_port *port = cec_get_drvdata(adap); + struct cec_adapter *input_adap; + + if (!vendor_id) + return; + if (port->disconnected || port->is_input) + return; + input_adap = port->extron->ports[port->extron->num_out_ports]->adap; + cec_splitter_nb_transmit_canceled_output(&port->port, msg, input_adap); +} + +static int extron_received(struct cec_adapter *adap, struct cec_msg *msg) +{ + struct extron_port *port = cec_get_drvdata(adap); + + if (!vendor_id) + return -ENOMSG; + if (port->disconnected) + return -ENOMSG; + if (port->is_input) + return cec_splitter_received_input(&port->port, msg); + return cec_splitter_received_output(&port->port, msg, + port->extron->ports[port->extron->num_out_ports]->adap); +} + +#define log_printf(adap, file, fmt, arg...) \ + do { \ + if (file) \ + seq_printf((file), fmt, ## arg); \ + else \ + pr_info("cec-%s: " fmt, (adap)->name, ## arg); \ + } while (0) + +static const char * const pwr_state[] = { + "on", + "standby", + "to on", + "to standby", +}; + +static void extron_adap_status_port(struct extron_port *port, struct seq_file *file) +{ + struct cec_adapter *adap = port->adap; + + if (port->disconnected) { + log_printf(adap, file, + "\tport %u: disconnected\n", port->port.port); + return; + } + if (port->is_input) + log_printf(adap, file, + "\tport %u: %s signal, %s edid, %s 4kp30, %s 4kp60, %sQS/%sQY, is %s\n", + port->port.port, + port->has_signal ? "has" : "no", + port->has_edid ? "has" : "no", + port->has_4kp30 ? "has" : "no", + port->has_4kp60 ? "has" : "no", + port->has_qs ? "" : "no ", + port->has_qy ? "" : "no ", + !port->port.adap->is_configured ? "not configured" : + pwr_state[port->extron->splitter.is_standby]); + else + log_printf(adap, file, + "\tport %u: %s sink, %s signal, %s edid, %s 4kp30, %s 4kp60, %sQS/%sQY, is %sactive source, is %s\n", + port->port.port, + port->port.found_sink ? "found" : "no", + port->has_signal ? "has" : "no", + port->has_edid ? "has" : "no", + port->has_4kp30 ? "has" : "no", + port->has_4kp60 ? "has" : "no", + port->has_qs ? "" : "no ", + port->has_qy ? "" : "no ", + port->port.is_active_source ? "" : "not ", + !port->port.adap->is_configured ? "not configured" : + pwr_state[port->port.power_status & 3]); + if (port->port.out_give_device_power_status_seq) + log_printf(adap, file, + "\tport %u: querying power status (%u, %lldms)\n", + port->port.port, + port->port.out_give_device_power_status_seq & ~(1 << 31), + ktime_ms_delta(ktime_get(), + port->port.out_give_device_power_status_ts)); + if (port->port.out_request_current_latency_seq) + log_printf(adap, file, + "\tport %u: querying latency (%u, %lldms)\n", + port->port.port, + port->port.out_request_current_latency_seq & ~(1 << 31), + ktime_ms_delta(ktime_get(), + port->port.out_request_current_latency_ts)); +} + +static void extron_adap_status(struct cec_adapter *adap, struct seq_file *file) +{ + struct extron_port *port = cec_get_drvdata(adap); + struct extron *extron = port->extron; + unsigned int i; + + log_printf(adap, file, "name: %s type: %s\n", + extron->unit_name, extron->unit_type); + log_printf(adap, file, "model: 60-160%c-01 (1 input, %u outputs)\n", + '6' + extron->num_out_ports / 2, extron->num_out_ports); + log_printf(adap, file, "firmware version: %s CEC engine version: %s\n", + extron->unit_fw_version, extron->unit_cec_engine_version); + if (extron->hpd_never_low) + log_printf(adap, file, "always keep input HPD high\n"); + else + log_printf(adap, file, + "pull input HPD low if all output HPDs are low\n"); + if (vendor_id) + log_printf(adap, file, + "splitter vendor ID: 0x%06x\n", vendor_id); + if (manufacturer_name[0]) + log_printf(adap, file, "splitter manufacturer name: %s\n", + manufacturer_name); + log_printf(adap, file, "splitter power status: %s\n", + pwr_state[extron->splitter.is_standby]); + log_printf(adap, file, "%s port: %d (%s)\n", + port->is_input ? "input" : "output", + port->port.port, port->name); + log_printf(adap, file, "splitter input port:\n"); + extron_adap_status_port(extron->ports[extron->num_out_ports], file); + + log_printf(adap, file, "splitter output ports:\n"); + for (i = 0; i < extron->num_out_ports; i++) + extron_adap_status_port(extron->ports[i], file); + + if (!port->has_edid || !port->read_edid) + return; + + for (i = 0; i < port->edid_blocks * 128; i += 16) { + if (i % 128 == 0) + log_printf(adap, file, "\n"); + log_printf(adap, file, "EDID: %*ph\n", 16, port->edid + i); + } +} + +static const struct cec_adap_ops extron_cec_adap_ops = { + .adap_enable = extron_cec_adap_enable, + .adap_log_addr = extron_cec_adap_log_addr, + .adap_transmit = extron_cec_adap_transmit, + .adap_nb_transmit_canceled = extron_cec_adap_nb_transmit_canceled, + .adap_unconfigured = extron_cec_adap_unconfigured, + .adap_status = extron_adap_status, + .configured = extron_cec_configured, + .received = extron_received, +}; + +static int extron_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct extron_port *port = video_drvdata(file); + + strscpy(cap->driver, "extron-da-hd-4k-plus-cec", sizeof(cap->driver)); + strscpy(cap->card, cap->driver, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "serio:%s", port->name); + return 0; +} + +static int extron_enum_input(struct file *file, void *priv, struct v4l2_input *inp) +{ + struct extron_port *port = video_drvdata(file); + + if (inp->index) + return -EINVAL; + inp->type = V4L2_INPUT_TYPE_CAMERA; + snprintf(inp->name, sizeof(inp->name), "HDMI IN %u", port->port.port); + inp->status = v4l2_ctrl_g_ctrl(port->ctrl_rx_power_present) ? + 0 : V4L2_IN_ST_NO_SIGNAL; + return 0; +} + +static int extron_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int extron_s_input(struct file *file, void *priv, unsigned int i) +{ + return i ? -EINVAL : 0; +} + +static int extron_enum_output(struct file *file, void *priv, struct v4l2_output *out) +{ + struct extron_port *port = video_drvdata(file); + + if (out->index) + return -EINVAL; + out->type = V4L2_OUTPUT_TYPE_ANALOG; + snprintf(out->name, sizeof(out->name), "HDMI OUT %u", port->port.port); + return 0; +} + +static int extron_g_output(struct file *file, void *priv, unsigned int *o) +{ + *o = 0; + return 0; +} + +static int extron_s_output(struct file *file, void *priv, unsigned int o) +{ + return o ? -EINVAL : 0; +} + +static int extron_g_edid(struct file *file, void *_fh, + struct v4l2_edid *edid) +{ + struct extron_port *port = video_drvdata(file); + + memset(edid->reserved, 0, sizeof(edid->reserved)); + if (port->disconnected) + return -ENODEV; + if (edid->pad) + return -EINVAL; + if (!port->has_edid) + return -ENODATA; + if (!port->read_edid) + extron_read_edid(port); + if (!port->read_edid) + return -ENODATA; + if (edid->start_block == 0 && edid->blocks == 0) { + edid->blocks = port->edid_blocks; + return 0; + } + if (edid->start_block >= port->edid_blocks) + return -EINVAL; + if (edid->blocks > port->edid_blocks - edid->start_block) + edid->blocks = port->edid_blocks - edid->start_block; + memcpy(edid->edid, port->edid + edid->start_block * 128, edid->blocks * 128); + return 0; +} + +static int extron_s_edid(struct file *file, void *_fh, struct v4l2_edid *edid) +{ + struct extron_port *port = video_drvdata(file); + + memset(edid->reserved, 0, sizeof(edid->reserved)); + if (port->disconnected) + return -ENODEV; + if (edid->pad) + return -EINVAL; + + /* Unfortunately it is not possible to clear the EDID */ + if (edid->blocks == 0) + return -EINVAL; + + if (edid->blocks > MAX_EDID_BLOCKS) { + edid->blocks = MAX_EDID_BLOCKS; + return -E2BIG; + } + + if (cec_get_edid_spa_location(edid->edid, edid->blocks * 128)) + v4l2_set_edid_phys_addr(edid->edid, edid->blocks * 128, 0); + extron_parse_edid(port); + return extron_write_edid(port, edid->edid, edid->blocks); +} + +static int extron_log_status(struct file *file, void *priv) +{ + struct extron_port *port = video_drvdata(file); + + extron_adap_status(port->adap, NULL); + return v4l2_ctrl_log_status(file, priv); +} + +static const struct v4l2_ioctl_ops extron_ioctl_ops = { + .vidioc_querycap = extron_querycap, + .vidioc_enum_input = extron_enum_input, + .vidioc_g_input = extron_g_input, + .vidioc_s_input = extron_s_input, + .vidioc_enum_output = extron_enum_output, + .vidioc_g_output = extron_g_output, + .vidioc_s_output = extron_s_output, + .vidioc_g_edid = extron_g_edid, + .vidioc_s_edid = extron_s_edid, + .vidioc_log_status = extron_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_file_operations extron_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct video_device extron_videodev = { + .name = "extron-da-hd-4k-plus-cec", + .vfl_dir = VFL_DIR_RX, + .fops = &extron_fops, + .ioctl_ops = &extron_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, +}; + +static void extron_disconnect(struct serio *serio) +{ + struct extron *extron = serio_get_drvdata(serio); + unsigned int p; + + kthread_stop(extron->kthread_setup); + + for (p = 0; p < extron->num_ports; p++) { + struct extron_port *port = extron->ports[p]; + + if (!port) + continue; + port->disconnected = true; + cancel_work_sync(&port->irq_work); + } + cancel_delayed_work_sync(&extron->work_update_edid); + for (p = 0; p < extron->num_ports; p++) { + struct extron_port *port = extron->ports[p]; + + if (!port) + continue; + + if (port->cec_was_registered) { + if (cec_is_registered(port->adap)) + cec_unregister_adapter(port->adap); + /* + * After registering the adapter, the + * extron_setup_thread() function took an extra + * reference to the device. We call the corresponding + * put here. + */ + cec_put_device(port->adap); + } else { + cec_delete_adapter(port->adap); + } + video_unregister_device(&port->vdev); + } + + complete(&extron->edid_completion); + + for (p = 0; p < extron->num_ports; p++) { + struct extron_port *port = extron->ports[p]; + + if (!port) + continue; + v4l2_ctrl_handler_free(&port->hdl); + mutex_destroy(&port->video_lock); + kfree(port); + } + mutex_destroy(&extron->edid_lock); + mutex_destroy(&extron->serio_lock); + extron->serio = NULL; + serio_set_drvdata(serio, NULL); + serio_close(serio); +} + +static int extron_setup(struct extron *extron) +{ + struct serio *serio = extron->serio; + struct extron_port *port; + u8 *reply = extron->reply; + unsigned int p; + unsigned int major, minor; + int err; + + /* + * Attempt to disable CEC: avoid received CEC messages + * from interfering with the other serial port traffic. + */ + extron_send_and_wait(extron, NULL, "WI1*0CCEC", NULL); + extron_send_and_wait(extron, NULL, "WO0*CCEC", NULL); + + /* Obtain unit part number */ + err = extron_send_and_wait(extron, NULL, "N", "Pno"); + if (err) + return err; + dev_info(extron->dev, "Unit part number: %s\n", reply + 3); + if (strcmp(reply + 3, "60-1607-01") && + strcmp(reply + 3, "60-1608-01") && + strcmp(reply + 3, "60-1609-01")) { + dev_err(extron->dev, "Unsupported model\n"); + return -ENODEV; + } + /* Up to 6 output ports and one input port */ + extron->num_out_ports = 2 * (reply[9] - '6'); + extron->splitter.num_out_ports = extron->num_out_ports; + extron->splitter.ports = extron->splitter_ports; + extron->splitter.dev = extron->dev; + extron->num_in_ports = 1; + extron->num_ports = extron->num_out_ports + extron->num_in_ports; + dev_info(extron->dev, "Unit output ports: %d\n", extron->num_out_ports); + dev_info(extron->dev, "Unit input ports: %d\n", extron->num_in_ports); + + err = extron_send_and_wait(extron, NULL, "W CN", "Ipn "); + if (err) + return err; + dev_info(extron->dev, "Unit name: %s\n", reply + 4); + strscpy(extron->unit_name, reply + 4, sizeof(extron->unit_name)); + + err = extron_send_and_wait(extron, NULL, "*Q", "Bld"); + if (err) + return err; + dev_info(extron->dev, "Unit FW Version: %s\n", reply + 3); + strscpy(extron->unit_fw_version, reply + 3, + sizeof(extron->unit_fw_version)); + if (sscanf(reply + 3, "%u.%u.", &major, &minor) < 2 || + major < 1 || minor < 2) { + dev_err(extron->dev, + "Unsupported FW version (only 1.02 or up is supported)\n"); + return -ENODEV; + } + + err = extron_send_and_wait(extron, NULL, "2i", "Inf02*"); + if (err) + return err; + dev_info(extron->dev, "Unit Type: %s\n", reply + 6); + strscpy(extron->unit_type, reply + 6, sizeof(extron->unit_type)); + + err = extron_send_and_wait(extron, NULL, "39Q", "Ver39*"); + if (err) + return err; + dev_info(extron->dev, "CEC Engine Version: %s\n", reply + 6); + strscpy(extron->unit_cec_engine_version, reply + 6, + sizeof(extron->unit_cec_engine_version)); + + /* Disable CEC */ + err = extron_send_and_wait(extron, NULL, "WI1*0CCEC", "CcecI1*"); + if (err) + return err; + err = extron_send_and_wait(extron, NULL, "WO0*CCEC", "CcecO0"); + if (err) + return err; + + extron->hpd_never_low = hpd_never_low; + + /* Pull input port HPD low if all output ports also have a low HPD */ + if (hpd_never_low) { + dev_info(extron->dev, "Always keep input HPD high\n"); + } else { + dev_info(extron->dev, "Pull input HPD low if all output HPDs are low\n"); + extron_send_and_wait(extron, NULL, "W1ihpd", "Ihpd1"); + } + + for (p = 0; p < extron->num_ports; p++) { + u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL; + + if (vendor_id) + caps &= ~CEC_CAP_LOG_ADDRS; + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + INIT_WORK(&port->irq_work, extron_irq_work_handler); + spin_lock_init(&port->msg_lock); + mutex_init(&port->video_lock); + port->extron = extron; + port->is_input = p >= extron->num_out_ports; + port->direction = port->is_input ? 'I' : 'O'; + port->port.port = 1 + (port->is_input ? p - extron->num_out_ports : p); + port->port.splitter = &extron->splitter; + port->phys_addr = CEC_PHYS_ADDR_INVALID; + snprintf(port->name, sizeof(port->name), "%s-%s-%u", + dev_name(&serio->dev), port->is_input ? "in" : "out", + port->port.port); + + port->dev = extron->dev; + port->adap = cec_allocate_adapter(&extron_cec_adap_ops, port, + port->name, caps, 1); + err = PTR_ERR_OR_ZERO(port->adap); + if (err < 0) { + kfree(port); + return err; + } + + port->adap->xfer_timeout_ms = EXTRON_TIMEOUT_SECS * 1000; + port->port.adap = port->adap; + port->vdev = extron_videodev; + port->vdev.lock = &port->video_lock; + port->vdev.v4l2_dev = &extron->v4l2_dev; + port->vdev.ctrl_handler = &port->hdl; + port->vdev.device_caps = V4L2_CAP_EDID; + video_set_drvdata(&port->vdev, port); + + v4l2_ctrl_handler_init(&port->hdl, 2); + + if (port->is_input) { + port->vdev.vfl_dir = VFL_DIR_RX; + port->ctrl_rx_power_present = + v4l2_ctrl_new_std(&port->hdl, NULL, + V4L2_CID_DV_RX_POWER_PRESENT, + 0, 1, 0, 0); + port->has_edid = true; + } else { + port->vdev.vfl_dir = VFL_DIR_TX; + port->ctrl_tx_hotplug = + v4l2_ctrl_new_std(&port->hdl, NULL, + V4L2_CID_DV_TX_HOTPLUG, + 0, 1, 0, 0); + port->ctrl_tx_edid_present = + v4l2_ctrl_new_std(&port->hdl, NULL, + V4L2_CID_DV_TX_EDID_PRESENT, + 0, 1, 0, 0); + } + + err = port->hdl.error; + if (err < 0) { + cec_delete_adapter(port->adap); + kfree(port); + return err; + } + extron->ports[p] = port; + extron->splitter_ports[p] = &port->port; + if (port->is_input && manufacturer_name[0]) + extron_write_edid(port, hdmi_edid, 2); + } + + /* Enable CEC (manual mode, i.e. controlled by the driver) */ + err = extron_send_and_wait(extron, NULL, "WI1*20CCEC", "CcecI1*"); + if (err) + return err; + + err = extron_send_and_wait(extron, NULL, "WO20*CCEC", "CcecO20"); + if (err) + return err; + + /* Set logical addresses to 15 */ + err = extron_send_and_wait(extron, NULL, "WI1*15LCEC", "LcecI1*15"); + if (err) + return err; + + for (p = 0; p < extron->num_out_ports; p++) { + char cmd[20]; + char resp[20]; + + snprintf(cmd, sizeof(cmd), "WO%u*15LCEC", p + 1); + snprintf(resp, sizeof(resp), "LcecO%u*15", p + 1); + err = extron_send_and_wait(extron, extron->ports[p], cmd, resp); + if (err) + return err; + } + + /* + * The Extron is now ready for operation. Specifically it is now + * possible to retrieve EDIDs. + */ + extron->is_ready = true; + + /* Query HDCP and Signal states, used to update the initial state */ + err = extron_send_and_wait(extron, NULL, "WHDCP", "Hdcp"); + if (err) + return err; + + return extron_send_and_wait(extron, NULL, "WLS", "Sig"); +} + +static int extron_setup_thread(void *_extron) +{ + struct extron *extron = _extron; + struct extron_port *port; + unsigned int p; + bool poll_splitter = false; + bool was_connected = true; + int err; + + while (1) { + if (kthread_should_stop()) + return 0; + err = extron_send_and_wait(extron, NULL, "W3CV", "Vrb3"); + // that should make it possible to detect a serio disconnect + // here by stopping the workqueue + if (err >= 0) + break; + was_connected = false; + ssleep(1); + } + + /* + * If the Extron was not connected at probe() time, i.e. it just got + * powered up and while the serial port is working, the firmware is + * still booting up, then wait 10 seconds for the firmware to settle. + * + * Trying to continue too soon means that some commands will not + * work yet. + */ + if (!was_connected) + ssleep(10); + + err = extron_setup(extron); + if (err) + goto disable_ports; + + for (p = 0; p < extron->num_ports; p++) { + struct cec_log_addrs log_addrs = {}; + + port = extron->ports[p]; + if (port->is_input && manufacturer_name[0]) + v4l2_disable_ioctl(&port->vdev, VIDIOC_S_EDID); + err = video_register_device(&port->vdev, VFL_TYPE_VIDEO, -1); + if (err) { + v4l2_err(&extron->v4l2_dev, "Failed to register video device\n"); + goto disable_ports; + } + + err = cec_register_adapter(port->adap, extron->dev); + if (err < 0) + goto disable_ports; + port->dev = &port->adap->devnode.dev; + port->cec_was_registered = true; + /* + * This driver is unusual in that the whole setup takes place + * in a thread since it can take such a long time before the + * Extron Splitter boots up, and you do not want to block the + * probe function on this driver. In addition, as soon as + * CEC adapters come online, they can be used, and you cannot + * just unregister them again if an error occurs, since that + * can delete the underlying CEC adapter, which might already + * be in use. + * + * So we take an additional reference to the adapter. This + * allows us to unregister the device node if needed, without + * deleting the actual adapter. + * + * In the disconnect function we will do the corresponding + * put call to ensure the adapter is deleted. + */ + cec_get_device(port->adap); + + /* + * If vendor_id wasn't set, then userspace configures the + * CEC devices. Otherwise the driver configures the CEC + * devices as TV (input) and Playback (outputs) devices + * and the driver processes all CEC messages. + */ + if (!vendor_id) + continue; + + log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0; + log_addrs.num_log_addrs = 1; + log_addrs.vendor_id = vendor_id; + if (port->is_input) { + strscpy(log_addrs.osd_name, "Splitter In", + sizeof(log_addrs.osd_name)); + log_addrs.log_addr_type[0] = CEC_LOG_ADDR_TYPE_TV; + log_addrs.primary_device_type[0] = CEC_OP_PRIM_DEVTYPE_TV; + log_addrs.all_device_types[0] = CEC_OP_ALL_DEVTYPE_TV; + } else { + snprintf(log_addrs.osd_name, sizeof(log_addrs.osd_name), + "Splitter Out%u", port->port.port); + log_addrs.log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK; + log_addrs.primary_device_type[0] = CEC_OP_PRIM_DEVTYPE_PLAYBACK; + log_addrs.all_device_types[0] = CEC_OP_ALL_DEVTYPE_PLAYBACK; + } + err = cec_s_log_addrs(port->adap, &log_addrs, false); + if (err < 0) + goto disable_ports; + } + poll_splitter = true; + + port = extron->ports[extron->num_out_ports]; + while (!kthread_should_stop()) { + ssleep(1); + if (hpd_never_low != extron->hpd_never_low) { + /* + * Keep input port HPD high at all times, or pull it low + * if all output ports also have a low HPD + */ + if (hpd_never_low) { + dev_info(extron->dev, "Always keep input HPD high\n"); + extron_send_and_wait(extron, NULL, "W0ihpd", "Ihpd0"); + } else { + dev_info(extron->dev, "Pull input HPD low if all output HPDs are low\n"); + extron_send_and_wait(extron, NULL, "W1ihpd", "Ihpd1"); + } + extron->hpd_never_low = hpd_never_low; + } + if (poll_splitter && + cec_splitter_poll(&extron->splitter, port->adap, debug) && + manufacturer_name[0]) { + /* + * Sinks were lost, so see if the input edid needs to + * be updated. + */ + cancel_delayed_work_sync(&extron->work_update_edid); + schedule_delayed_work(&extron->work_update_edid, + msecs_to_jiffies(1000)); + } + } + return 0; + +disable_ports: + extron->is_ready = false; + for (p = 0; p < extron->num_ports; p++) { + struct extron_port *port = extron->ports[p]; + + if (!port) + continue; + port->disconnected = true; + cancel_work_sync(&port->irq_work); + video_unregister_device(&port->vdev); + if (port->cec_was_registered) + cec_unregister_adapter(port->adap); + } + cancel_delayed_work_sync(&extron->work_update_edid); + complete(&extron->edid_completion); + dev_err(extron->dev, "Setup failed with error %d\n", err); + while (!kthread_should_stop()) + ssleep(1); + return err; +} + +static int extron_connect(struct serio *serio, struct serio_driver *drv) +{ + struct extron *extron; + int err = -ENOMEM; + + if (manufacturer_name[0] && + (!isupper(manufacturer_name[0]) || + !isupper(manufacturer_name[1]) || + !isupper(manufacturer_name[2]))) { + dev_warn(&serio->dev, "ignoring invalid manufacturer name\n"); + manufacturer_name[0] = 0; + } + + extron = kzalloc(sizeof(*extron), GFP_KERNEL); + + if (!extron) + return -ENOMEM; + + extron->serio = serio; + extron->dev = &serio->dev; + mutex_init(&extron->serio_lock); + mutex_init(&extron->edid_lock); + INIT_DELAYED_WORK(&extron->work_update_edid, update_edid_work); + + err = v4l2_device_register(extron->dev, &extron->v4l2_dev); + if (err) + goto free_device; + + err = serio_open(serio, drv); + if (err) + goto unreg_v4l2_dev; + + serio_set_drvdata(serio, extron); + init_completion(&extron->edid_completion); + + extron->kthread_setup = kthread_run(extron_setup_thread, extron, + "extron-da-hd-4k-plus-cec-%s", dev_name(&serio->dev)); + if (!IS_ERR(extron->kthread_setup)) + return 0; + + dev_err(extron->dev, "kthread_run() failed\n"); + err = PTR_ERR(extron->kthread_setup); + + extron->serio = NULL; + serio_set_drvdata(serio, NULL); + serio_close(serio); +unreg_v4l2_dev: + v4l2_device_unregister(&extron->v4l2_dev); +free_device: + mutex_destroy(&extron->edid_lock); + mutex_destroy(&extron->serio_lock); + kfree(extron); + return err; +} + +static const struct serio_device_id extron_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_EXTRON_DA_HD_4K_PLUS, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, extron_serio_ids); + +static struct serio_driver extron_drv = { + .driver = { + .name = "extron-da-hd-4k-plus-cec", + }, + .description = "Extron DA HD 4K PLUS HDMI CEC driver", + .id_table = extron_serio_ids, + .interrupt = extron_interrupt, + .connect = extron_connect, + .disconnect = extron_disconnect, +}; + +module_serio_driver(extron_drv); diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.h b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.h new file mode 100644 index 000000000000..b79f1253ab5d --- /dev/null +++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Copyright 2021-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + */ + +#ifndef _EXTRON_DA_HD_4K_PLUS_H_ +#define _EXTRON_DA_HD_4K_PLUS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cec-splitter.h" + +#define DATA_SIZE 256 + +#define PING_PERIOD (15 * HZ) + +#define NUM_MSGS CEC_MAX_MSG_RX_QUEUE_SZ + +#define MAX_PORTS (1 + 6) + +#define MAX_EDID_BLOCKS 2 + +struct extron; + +struct extron_port { + struct cec_splitter_port port; + struct device *dev; + struct cec_adapter *adap; + struct video_device vdev; + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *ctrl_rx_power_present; + struct v4l2_ctrl *ctrl_tx_hotplug; + struct v4l2_ctrl *ctrl_tx_edid_present; + bool is_input; + char direction; + char name[26]; + unsigned char edid[MAX_EDID_BLOCKS * 128]; + unsigned char edid_tmp[MAX_EDID_BLOCKS * 128]; + unsigned int edid_blocks; + bool read_edid; + struct extron *extron; + struct work_struct irq_work; + struct completion cmd_done; + const char *response; + unsigned int cmd_error; + struct cec_msg rx_msg[NUM_MSGS]; + unsigned int rx_msg_cur_idx, rx_msg_num; + /* protect rx_msg_cur_idx and rx_msg_num */ + spinlock_t msg_lock; + u32 tx_done_status; + bool update_phys_addr; + u16 phys_addr; + bool cec_was_registered; + bool disconnected; + bool update_has_signal; + bool has_signal; + bool update_has_edid; + bool has_edid; + bool has_4kp30; + bool has_4kp60; + bool has_qy; + bool has_qs; + u8 est_i, est_ii; + + /* locks access to the video_device */ + struct mutex video_lock; +}; + +struct extron { + struct cec_splitter splitter; + struct device *dev; + struct serio *serio; + /* locks access to serio */ + struct mutex serio_lock; + unsigned int num_ports; + unsigned int num_in_ports; + unsigned int num_out_ports; + char unit_name[32]; + char unit_type[64]; + char unit_fw_version[32]; + char unit_cec_engine_version[32]; + struct extron_port *ports[MAX_PORTS]; + struct cec_splitter_port *splitter_ports[MAX_PORTS]; + struct v4l2_device v4l2_dev; + bool hpd_never_low; + struct task_struct *kthread_setup; + struct delayed_work work_update_edid; + + /* serializes EDID reading */ + struct mutex edid_lock; + unsigned int edid_bytes_read; + struct extron_port *edid_port; + struct completion edid_completion; + bool edid_reading; + bool is_ready; + + struct completion cmd_done; + const char *response; + unsigned int cmd_error; + char data[DATA_SIZE]; + unsigned int len; + char reply[DATA_SIZE]; + char buf[DATA_SIZE]; + unsigned int idx; +}; + +#endif -- cgit v1.2.3-70-g09d2 From b5a9c249bd9fd4b67739714f6eb9587c45d1f5d2 Mon Sep 17 00:00:00 2001 From: Roshan Khatri Date: Thu, 2 May 2024 06:34:15 +0200 Subject: media: atomisp: Fix spelling mistake in csi_rx_public.h codespell reported misspelled "register" in csi_rx_public.h, fix misspellings. Signed-off-by: Roshan Khatri Link: https://lore.kernel.org/r/20240502043415.88434-1-topofeverest8848@gmail.com Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../media/atomisp/pci/hive_isp_css_include/host/csi_rx_public.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/csi_rx_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/csi_rx_public.h index 693154e8ec2f..7e37f0809034 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/csi_rx_public.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/csi_rx_public.h @@ -94,7 +94,7 @@ hrt_data csi_rx_fe_ctrl_reg_load( const hrt_address reg); /** * @brief Store a value to the register. - * Store a value to the registe of the csi rx fe. + * Store a value to the register of the csi rx fe. * * @param[in] ID The global unique ID for the ibuf-controller instance. * @param[in] reg The offset address of the register. @@ -119,7 +119,7 @@ hrt_data csi_rx_be_ctrl_reg_load( const hrt_address reg); /** * @brief Store a value to the register. - * Store a value to the registe of the csi rx be. + * Store a value to the register of the csi rx be. * * @param[in] ID The global unique ID for the ibuf-controller instance. * @param[in] reg The offset address of the register. -- cgit v1.2.3-70-g09d2 From 5943fc104dc89cb2cd7e82b0bcf543efe6d03785 Mon Sep 17 00:00:00 2001 From: Roshan Khatri Date: Fri, 10 May 2024 15:40:00 +0200 Subject: media: atomisp: Fix spelling mistakes in atomisp_platform.h Codespell reported misspelled "streams" and "corresponding" in atomisp_platform.h. This patch fixes the misspellings. Signed-off-by: Roshan Khatri Link: https://lore.kernel.org/r/20240510134000.23848-1-topofeverest8848@gmail.com Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/include/linux/atomisp_platform.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h index fdeb247036b0..e27dc12b8759 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h @@ -116,7 +116,7 @@ struct intel_v4l2_subdev_table { }; /* - * Sensor of external ISP can send multiple steams with different mipi data + * Sensor of external ISP can send multiple streams with different mipi data * type in the same virtual channel. This information needs to come from the * sensor or external ISP */ @@ -138,7 +138,7 @@ struct atomisp_input_stream_info { /* * if more isys_configs is more than 0, sensor needs to configure the * input format differently. width and height can be 0. If width and - * height is not zero, then the corresponsing data needs to be set + * height is not zero, then the corresponding data needs to be set */ struct atomisp_isys_config_info isys_info[MAX_STREAMS_PER_CHANNEL]; }; -- cgit v1.2.3-70-g09d2 From 83e80dc8fe1442f7ffd931c21a15eb57f46f21e2 Mon Sep 17 00:00:00 2001 From: Roshan Khatri Date: Wed, 22 May 2024 18:18:30 +0200 Subject: media: atomisp: Fix spelling mistakes in atomisp.h codespell reported misspelled coefficients and vector in atomisp.h. This patch corrects the spellings to increase readability and searching. Signed-off-by: Roshan Khatri Link: https://lore.kernel.org/r/20240522161830.57434-1-topofeverest8848@gmail.com Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/include/linux/atomisp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/include/linux/atomisp.h b/drivers/staging/media/atomisp/include/linux/atomisp.h index 16c9da172c03..4f440ea2fcc1 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp.h @@ -201,7 +201,7 @@ struct atomisp_dis_vector { }; /* DVS 2.0 Coefficient types. This structure contains 4 pointers to - * arrays that contain the coeffients for each type. + * arrays that contain the coefficients for each type. */ struct atomisp_dvs2_coef_types { short __user *odd_real; /** real part of the odd coefficients*/ @@ -699,7 +699,7 @@ enum atomisp_burst_capture_options { /* Digital Image Stabilization: * 1. get dis statistics: reads DIS statistics from ISP (every frame) * 2. set dis coefficients: set DIS filter coefficients (one time) - * 3. set dis motion vecotr: set motion vector (result of DIS, every frame) + * 3. set dis motion vector: set motion vector (result of DIS, every frame) */ #define ATOMISP_IOC_G_DIS_STAT \ _IOWR('v', BASE_VIDIOC_PRIVATE + 6, struct atomisp_dis_statistics) -- cgit v1.2.3-70-g09d2 From f59ba45273ce37f28502306a3ed0e7a0eea3ff59 Mon Sep 17 00:00:00 2001 From: Kathara Sasikumar Date: Tue, 30 Jul 2024 10:27:25 +0200 Subject: media: atomisp: Fix trailing statement in ia_css_de.host.c Fix checkpatch diagostic of Error : Trailing statements should be on the next line Signed-off-by: Kathara Sasikumar Link: https://lore.kernel.org/r/20240730082725.2524-1-katharasasikumar007@gmail.com Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/atomisp/pci/isp/kernels/de/de_1.0/ia_css_de.host.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/de/de_1.0/ia_css_de.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/de/de_1.0/ia_css_de.host.c index 25e3f0822fb8..e66faeda3613 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/de/de_1.0/ia_css_de.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/de/de_1.0/ia_css_de.host.c @@ -47,7 +47,8 @@ ia_css_de_dump( const struct sh_css_isp_de_params *de, unsigned int level) { - if (!de) return; + if (!de) + return; ia_css_debug_dtrace(level, "Demosaic:\n"); ia_css_debug_dtrace(level, "\t%-32s = %d\n", "de_pixelnoise", de->pixelnoise); -- cgit v1.2.3-70-g09d2 From 456b86d420fcaf2410949bea183d4c6dd0ba49e8 Mon Sep 17 00:00:00 2001 From: Sakirnth Nagarasa Date: Tue, 30 Jul 2024 10:42:20 +0200 Subject: media: atomisp: move trailing statement to next line. Fix checkpath error "ERROR: trailing statements should be on next line" in ia_css_fpn.host.c:46. Signed-off-by: Sakirnth Nagarasa Link: https://lore.kernel.org/r/20240730084220.38204-1-sakirnth@gmail.com Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.c index 57b5e11e1cfe..8ccfa99c61ef 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.c @@ -43,7 +43,8 @@ ia_css_fpn_dump( const struct sh_css_isp_fpn_params *fpn, unsigned int level) { - if (!fpn) return; + if (!fpn) + return; ia_css_debug_dtrace(level, "Fixed Pattern Noise Reduction:\n"); ia_css_debug_dtrace(level, "\t%-32s = %d\n", "fpn_shift", fpn->shift); -- cgit v1.2.3-70-g09d2 From 5788a2d2e2c733809660d884946d3e453a41c6d9 Mon Sep 17 00:00:00 2001 From: Sergio de Almeida Cipriano Junior Date: Tue, 30 Jul 2024 09:19:04 +0200 Subject: media: atomisp: move trailing */ to separate lines Fix checkpatch diagnostic "WARNING: Block comments use a trailing */ on a separate line" in assert_support.h file. Signed-off-by: Sergio de Almeida Cipriano Junior Link: https://lore.kernel.org/r/20240730071904.1047-1-sergiosacj@riseup.net Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/atomisp/pci/hive_isp_css_include/assert_support.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/assert_support.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/assert_support.h index d294ac402de8..c5ab13511db8 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/assert_support.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/assert_support.h @@ -27,7 +27,8 @@ * #define assert(cnd) BUG_ON(cnd) * but that causes many compiler warnings (==errors) under Android * because it seems that the BUG_ON() macro is not seen as a check by - * gcc like the BUG() macro is. */ + * gcc like the BUG() macro is. + */ #define assert(cnd) \ do { \ if (!(cnd)) \ @@ -37,7 +38,8 @@ #ifndef PIPE_GENERATION /* Deprecated OP___assert, this is still used in ~1000 places * in the code. This will be removed over time. - * The implementation for the pipe generation tool is in see support.isp.h */ + * The implementation for the pipe generation tool is in see support.isp.h + */ #define OP___assert(cnd) assert(cnd) static inline void compile_time_assert(unsigned int cond) -- cgit v1.2.3-70-g09d2 From cfc0ac5b9c3c99500ad56dfe440ca2db79d564fd Mon Sep 17 00:00:00 2001 From: Kartik Kulkarni Date: Wed, 31 Jul 2024 12:33:53 +0200 Subject: media: atomisp: bnr: fix trailing statement Fix checkpatch error trailing statements should be on next line in ia_css_bnr.host.c:48 Signed-off-by: Kartik Kulkarni Link: https://lore.kernel.org/r/20240731103353.39245-1-kartik.koolks@gmail.com Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../media/atomisp/pci/isp/kernels/bnr/bnr_1.0/ia_css_bnr.host.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/bnr/bnr_1.0/ia_css_bnr.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/bnr/bnr_1.0/ia_css_bnr.host.c index 457a004e194d..b75cfd3096d8 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/bnr/bnr_1.0/ia_css_bnr.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/bnr/bnr_1.0/ia_css_bnr.host.c @@ -45,7 +45,8 @@ ia_css_bnr_dump( const struct sh_css_isp_bnr_params *bnr, unsigned int level) { - if (!bnr) return; + if (!bnr) + return; ia_css_debug_dtrace(level, "Bayer Noise Reduction:\n"); ia_css_debug_dtrace(level, "\t%-32s = %d\n", "bnr_gain_all", bnr->gain_all); -- cgit v1.2.3-70-g09d2 From 9e3513a58f71195d239426c347645ad44b0fe1ac Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 13 Aug 2024 10:54:58 +0200 Subject: media: atomisp: Remove duplicated leftover, i.e. sh_css_dvs_info.h The contents of sh_css_dvs_info.h are already included in the solely user of this header, drop the former for good. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240813085458.1545949-1-andriy.shevchenko@linux.intel.com Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/atomisp/pci/sh_css_dvs_info.h | 37 ---------------------- .../staging/media/atomisp/pci/sh_css_param_dvs.h | 1 - 2 files changed, 38 deletions(-) delete mode 100644 drivers/staging/media/atomisp/pci/sh_css_dvs_info.h (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/sh_css_dvs_info.h b/drivers/staging/media/atomisp/pci/sh_css_dvs_info.h deleted file mode 100644 index 6f058f132300..000000000000 --- a/drivers/staging/media/atomisp/pci/sh_css_dvs_info.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/** -Support for Intel Camera Imaging ISP subsystem. -Copyright (c) 2010 - 2015, Intel Corporation. - -This program is free software; you can redistribute it and/or modify it -under the terms and conditions of the GNU General Public License, -version 2, as published by the Free Software Foundation. - -This program is distributed in the hope it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -more details. -*/ - -#ifndef __SH_CSS_DVS_INFO_H__ -#define __SH_CSS_DVS_INFO_H__ - -#include - -/* horizontal 64x64 blocks round up to DVS_BLOCKDIM_X, make even */ -#define DVS_NUM_BLOCKS_X(X) (CEIL_MUL(CEIL_DIV((X), DVS_BLOCKDIM_X), 2)) - -/* vertical 64x64 blocks round up to DVS_BLOCKDIM_Y */ -#define DVS_NUM_BLOCKS_Y(X) (CEIL_DIV((X), DVS_BLOCKDIM_Y_LUMA)) - -/* Bilinear interpolation (HRT_GDC_BLI_MODE) is the supported method currently. - * Bicubic interpolation (HRT_GDC_BCI_MODE) is not supported yet */ -#define DVS_GDC_INTERP_METHOD HRT_GDC_BLI_MODE - -#define DVS_INPUT_BYTES_PER_PIXEL (1) - -#define DVS_NUM_BLOCKS_X_CHROMA(X) (CEIL_DIV((X), DVS_BLOCKDIM_X)) - -#define DVS_NUM_BLOCKS_Y_CHROMA(X) (CEIL_DIV((X), DVS_BLOCKDIM_Y_CHROMA)) - -#endif /* __SH_CSS_DVS_INFO_H__ */ diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.h b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.h index 7782f76b9f97..25e5b4570f7d 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.h +++ b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.h @@ -18,7 +18,6 @@ #include #include -#include #include "gdc_global.h" /* gdc_warp_param_mem_t */ #define DVS_ENV_MIN_X (12) -- cgit v1.2.3-70-g09d2 From 1f24d0b3234b39b3404c965545d470c6022bcffd Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 13 Aug 2024 15:12:25 +0200 Subject: media: atomisp: Replace rarely used macro from math_support.h Replace rarely used macro by generic ones from Linux kernel headers. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240813131225.2232817-1-andriy.shevchenko@linux.intel.com Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../media/atomisp/pci/hive_isp_css_include/math_support.h | 6 ------ .../isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c | 9 +++++---- .../isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c | 9 +++++---- drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c | 3 ++- .../staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c | 8 ++++---- 5 files changed, 16 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h index 160c496784b7..907f9ebcc60d 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h @@ -28,12 +28,6 @@ #define CEIL_SHIFT(a, b) (((a) + (1 << (b)) - 1) >> (b)) #define CEIL_SHIFT_MUL(a, b) (CEIL_SHIFT(a, b) << (b)) -#if !defined(PIPE_GENERATION) - -#define ceil_div(a, b) (CEIL_DIV(a, b)) - -#endif /* !defined(PIPE_GENERATION) */ - /* * For SP and ISP, SDK provides the definition of OP_std_modadd. * We need it only for host diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c index 0091e2a3da52..c32659894c29 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c @@ -13,9 +13,11 @@ * more details. */ +#include +#include + #include "ia_css_bayer_io.host.h" #include "dma.h" -#include "math_support.h" #ifndef IA_CSS_NO_DEBUG #include "ia_css_debug.h" #endif @@ -29,9 +31,8 @@ int ia_css_bayer_io_config(const struct ia_css_binary *binary, const struct ia_css_frame **out_frames = (const struct ia_css_frame **) &args->out_frame; const struct ia_css_frame_info *in_frame_info = ia_css_frame_get_info(in_frame); - const unsigned int ddr_bits_per_element = sizeof(short) * 8; - const unsigned int ddr_elems_per_word = ceil_div(HIVE_ISP_DDR_WORD_BITS, - ddr_bits_per_element); + const unsigned int ddr_elems_per_word = + DIV_ROUND_UP(HIVE_ISP_DDR_WORD_BITS, BITS_PER_TYPE(short)); unsigned int size_get = 0, size_put = 0; unsigned int offset = 0; int ret; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c index 32c504a950ce..5b2d5023b5ee 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c @@ -13,9 +13,11 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ +#include +#include + #include "ia_css_yuv444_io.host.h" #include "dma.h" -#include "math_support.h" #ifndef IA_CSS_NO_DEBUG #include "ia_css_debug.h" #endif @@ -29,9 +31,8 @@ int ia_css_yuv444_io_config(const struct ia_css_binary *binary, const struct ia_css_frame **out_frames = (const struct ia_css_frame **) &args->out_frame; const struct ia_css_frame_info *in_frame_info = ia_css_frame_get_info(in_frame); - const unsigned int ddr_bits_per_element = sizeof(short) * 8; - const unsigned int ddr_elems_per_word = ceil_div(HIVE_ISP_DDR_WORD_BITS, - ddr_bits_per_element); + const unsigned int ddr_elems_per_word = + DIV_ROUND_UP(HIVE_ISP_DDR_WORD_BITS, BITS_PER_TYPE(short)); unsigned int size_get = 0, size_put = 0; unsigned int offset = 0; int ret; diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c index b0f904a5e442..2ff522f7dec8 100644 --- a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c +++ b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c @@ -328,7 +328,8 @@ ia_css_binary_dvs_grid_info(const struct ia_css_binary *binary, dvs_info = &info->dvs_grid.dvs_grid_info; - /* for DIS, we use a division instead of a ceil_div. If this is smaller + /* + * For DIS, we use a division instead of a DIV_ROUND_UP(). If this is smaller * than the 3a grid size, it indicates that the outer values are not * valid for DIS. */ diff --git a/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c b/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c index 52483498239d..2e0193671f4b 100644 --- a/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c +++ b/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c @@ -13,6 +13,8 @@ * more details. */ +#include +#include #include /* for memcpy() */ #include "system_global.h" @@ -20,7 +22,6 @@ #include "ia_css_isys.h" #include "ia_css_debug.h" -#include "math_support.h" #include "virtual_isys.h" #include "isp.h" #include "sh_css_defs.h" @@ -558,7 +559,7 @@ static int32_t calculate_stride( bits_per_pixel = CEIL_MUL(bits_per_pixel, 8); pixels_per_word = HIVE_ISP_DDR_WORD_BITS / bits_per_pixel; - words_per_line = ceil_div(pixels_per_line_padded, pixels_per_word); + words_per_line = DIV_ROUND_UP(pixels_per_line_padded, pixels_per_word); bytes_per_line = HIVE_ISP_DDR_WORD_BYTES * words_per_line; return bytes_per_line; @@ -690,7 +691,6 @@ static bool calculate_ibuf_ctrl_cfg( const isp2401_input_system_cfg_t *isys_cfg, ibuf_ctrl_cfg_t *cfg) { - const s32 bits_per_byte = 8; s32 bits_per_pixel; s32 bytes_per_pixel; s32 left_padding; @@ -698,7 +698,7 @@ static bool calculate_ibuf_ctrl_cfg( (void)input_port; bits_per_pixel = isys_cfg->input_port_resolution.bits_per_pixel; - bytes_per_pixel = ceil_div(bits_per_pixel, bits_per_byte); + bytes_per_pixel = BITS_TO_BYTES(bits_per_pixel); left_padding = CEIL_MUL(isys_cfg->output_port_attr.left_padding, ISP_VEC_NELEMS) * bytes_per_pixel; -- cgit v1.2.3-70-g09d2 From 7483ce8fc72f8c56b3dbc14424d2ade15233ac69 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 13 Aug 2024 10:56:15 +0200 Subject: media: atomisp: Simplify ia_css_pipe_create_cas_scaler_desc_single_output() Make ia_css_pipe_create_cas_scaler_desc_single_output() easier to read by shortening parameter names. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240813085615.1546111-1-andriy.shevchenko@linux.intel.com Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/sh_css.c | 44 ++++++++++++++---------------- 1 file changed, 20 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index 01f0b8a33c99..ca97ea082cf4 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -5826,20 +5826,19 @@ need_yuv_scaler_stage(const struct ia_css_pipe *pipe) * Later, merge this with ia_css_pipe_create_cas_scaler_desc */ static int ia_css_pipe_create_cas_scaler_desc_single_output( - struct ia_css_frame_info *cas_scaler_in_info, - struct ia_css_frame_info *cas_scaler_out_info, - struct ia_css_frame_info *cas_scaler_vf_info, + struct ia_css_frame_info *in_info, + struct ia_css_frame_info *out_info, + struct ia_css_frame_info *vf_info, struct ia_css_cas_binary_descr *descr) { unsigned int i; unsigned int hor_ds_factor = 0, ver_ds_factor = 0; int err = 0; struct ia_css_frame_info tmp_in_info; - unsigned int max_scale_factor_per_stage = MAX_PREFERRED_YUV_DS_PER_STEP; - assert(cas_scaler_in_info); - assert(cas_scaler_out_info); + assert(in_info); + assert(out_info); ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "ia_css_pipe_create_cas_scaler_desc() enter:\n"); @@ -5847,10 +5846,8 @@ static int ia_css_pipe_create_cas_scaler_desc_single_output( /* We assume that this function is used only for single output port case. */ descr->num_output_stage = 1; - hor_ds_factor = CEIL_DIV(cas_scaler_in_info->res.width, - cas_scaler_out_info->res.width); - ver_ds_factor = CEIL_DIV(cas_scaler_in_info->res.height, - cas_scaler_out_info->res.height); + hor_ds_factor = CEIL_DIV(in_info->res.width, out_info->res.width); + ver_ds_factor = CEIL_DIV(in_info->res.height, out_info->res.height); /* use the same horizontal and vertical downscaling factor for simplicity */ assert(hor_ds_factor == ver_ds_factor); @@ -5895,30 +5892,29 @@ static int ia_css_pipe_create_cas_scaler_desc_single_output( goto ERR; } - tmp_in_info = *cas_scaler_in_info; + tmp_in_info = *in_info; for (i = 0; i < descr->num_stage; i++) { descr->in_info[i] = tmp_in_info; - if ((tmp_in_info.res.width / max_scale_factor_per_stage) <= - cas_scaler_out_info->res.width) { + if ((tmp_in_info.res.width / max_scale_factor_per_stage) <= out_info->res.width) { descr->is_output_stage[i] = true; if ((descr->num_output_stage > 1) && (i != (descr->num_stage - 1))) { - descr->internal_out_info[i].res.width = cas_scaler_out_info->res.width; - descr->internal_out_info[i].res.height = cas_scaler_out_info->res.height; - descr->internal_out_info[i].padded_width = cas_scaler_out_info->padded_width; + descr->internal_out_info[i].res.width = out_info->res.width; + descr->internal_out_info[i].res.height = out_info->res.height; + descr->internal_out_info[i].padded_width = out_info->padded_width; descr->internal_out_info[i].format = IA_CSS_FRAME_FORMAT_YUV420; } else { assert(i == (descr->num_stage - 1)); descr->internal_out_info[i].res.width = 0; descr->internal_out_info[i].res.height = 0; } - descr->out_info[i].res.width = cas_scaler_out_info->res.width; - descr->out_info[i].res.height = cas_scaler_out_info->res.height; - descr->out_info[i].padded_width = cas_scaler_out_info->padded_width; - descr->out_info[i].format = cas_scaler_out_info->format; - if (cas_scaler_vf_info) { - descr->vf_info[i].res.width = cas_scaler_vf_info->res.width; - descr->vf_info[i].res.height = cas_scaler_vf_info->res.height; - descr->vf_info[i].padded_width = cas_scaler_vf_info->padded_width; + descr->out_info[i].res.width = out_info->res.width; + descr->out_info[i].res.height = out_info->res.height; + descr->out_info[i].padded_width = out_info->padded_width; + descr->out_info[i].format = out_info->format; + if (vf_info) { + descr->vf_info[i].res.width = vf_info->res.width; + descr->vf_info[i].res.height = vf_info->res.height; + descr->vf_info[i].padded_width = vf_info->padded_width; ia_css_frame_info_set_format(&descr->vf_info[i], IA_CSS_FRAME_FORMAT_YUV_LINE); } else { descr->vf_info[i].res.width = 0; -- cgit v1.2.3-70-g09d2 From 7adc7193537470839ec66ecbdb54f4dc9092e6f6 Mon Sep 17 00:00:00 2001 From: Li Zetao Date: Fri, 30 Aug 2024 03:17:51 +0200 Subject: media: atomisp: use clamp() in ia_css_eed1_8_encode() When it needs to get a value within a certain interval, using clamp() makes the code easier to understand than min(max()). Signed-off-by: Li Zetao Link: https://lore.kernel.org/r/20240830011752.603433-2-lizetao1@huawei.com Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c index e4fc90f88e24..55d102b479ac 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c @@ -276,7 +276,7 @@ ia_css_eed1_8_encode( for (i = 0; i < (IA_CSS_NUMBER_OF_DEW_ENHANCE_SEGMENTS - 1); i++) { min_exp = max(min_exp, from->dew_enhance_seg_exp[i]); } - to->e_dew_enh_asr = 13 - min(max(min_exp, 0), 13); + to->e_dew_enh_asr = 13 - clamp(min_exp, 0, 13); to->dedgew_max = from->dedgew_max; } -- cgit v1.2.3-70-g09d2 From 9a5e69c030608aec545201398be626102272bf72 Mon Sep 17 00:00:00 2001 From: Li Zetao Date: Fri, 30 Aug 2024 03:17:52 +0200 Subject: media: atomisp: use clamp() in compute_coring() When it needs to get a value within a certain interval, using clamp() makes the code easier to understand than min(max()). Signed-off-by: Li Zetao Link: https://lore.kernel.org/r/20240830011752.603433-3-lizetao1@huawei.com Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3.host.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3.host.c index 70132d955e9b..def2c8fb4b38 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3.host.c @@ -108,7 +108,7 @@ compute_coring(int coring) * factor. Clip to [0, isp_scale-1). */ isp_coring = ((coring * isp_scale) + offset) / host_scale; - return min(max(isp_coring, 0), isp_scale - 1); + return clamp(isp_coring, 0, isp_scale - 1); } /* @@ -168,15 +168,15 @@ ia_css_xnr3_encode( to->alpha.y0 = alpha_y0; to->alpha.u0 = alpha_u0; to->alpha.v0 = alpha_v0; - to->alpha.ydiff = min(max(alpha_ydiff, min_diff), max_diff); - to->alpha.udiff = min(max(alpha_udiff, min_diff), max_diff); - to->alpha.vdiff = min(max(alpha_vdiff, min_diff), max_diff); + to->alpha.ydiff = clamp(alpha_ydiff, min_diff, max_diff); + to->alpha.udiff = clamp(alpha_udiff, min_diff, max_diff); + to->alpha.vdiff = clamp(alpha_vdiff, min_diff, max_diff); /* coring parameters are expressed in q1.NN format */ to->coring.u0 = coring_u0; to->coring.v0 = coring_v0; - to->coring.udiff = min(max(coring_udiff, min_diff), max_diff); - to->coring.vdiff = min(max(coring_vdiff, min_diff), max_diff); + to->coring.udiff = clamp(coring_udiff, min_diff, max_diff); + to->coring.vdiff = clamp(coring_vdiff, min_diff, max_diff); /* blending strength is expressed in q1.NN format */ to->blending.strength = blending; -- cgit v1.2.3-70-g09d2 From d579ef05daa759612546728d9ec1e1996b288f82 Mon Sep 17 00:00:00 2001 From: Zhang Zekun Date: Fri, 30 Aug 2024 05:00:35 +0200 Subject: media: atomisp: Remove unused declaration v4l2_get_acpi_sensor_info() has been removed since commit d80be6a10cd3 ("media: atomisp: Drop v4l2_get_acpi_sensor_info() function"), remain the declaration untouched in the header file. So, let's remove this unused declartion. Signed-off-by: Zhang Zekun Reviewed-by: Changhuang Liang Link: https://lore.kernel.org/r/20240830030035.15686-1-zhangzekun11@huawei.com Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/include/linux/atomisp_platform.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h index e27dc12b8759..064449fd51af 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h @@ -175,8 +175,6 @@ int atomisp_register_sensor_no_gmin(struct v4l2_subdev *subdev, u32 lanes, enum atomisp_bayer_order bayer_order); void atomisp_unregister_subdev(struct v4l2_subdev *subdev); -int v4l2_get_acpi_sensor_info(struct device *dev, char **module_id_str); - /* API from old platform_camera.h, new CPUID implementation */ #define __IS_SOC(x) (boot_cpu_data.x86_vfm == x) #define __IS_SOCS(x, y) (boot_cpu_data.x86_vfm == x || boot_cpu_data.x86_vfm == y) -- cgit v1.2.3-70-g09d2 From cf738cc7cfe07dda024778e7778f57f06fb042a2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Sep 2024 10:31:21 +0200 Subject: media: atomisp: add missing wait_prepare/finish ops Without these ops the v4l2-compliance blocking wait test will fail. These ops are required to ensure that when VIDIOC_DQBUF has to wait for buffers to arrive, the queue lock is correctly released and retaken. Otherwise the wait for a buffer would block all other queue ioctls. Signed-off-by: Hans Verkuil Link: https://lore.kernel.org/r/9f401f3732dd728e3d2ca508002c97b80a2eae30.1725265884.git.hverkuil-cisco@xs4all.nl Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp_fops.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/atomisp_fops.c b/drivers/staging/media/atomisp/pci/atomisp_fops.c index 50c4123ba006..b180fcbea9b1 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_fops.c +++ b/drivers/staging/media/atomisp/pci/atomisp_fops.c @@ -441,6 +441,8 @@ const struct vb2_ops atomisp_vb2_ops = { .buf_queue = atomisp_buf_queue, .start_streaming = atomisp_start_streaming, .stop_streaming = atomisp_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static void atomisp_dev_init_struct(struct atomisp_device *isp) -- cgit v1.2.3-70-g09d2 From 41bcaff3164cf4737b032010150f0da01ead80de Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 2 Sep 2024 11:52:27 +0200 Subject: media: atomisp: csi2-bridge: Add DMI quirk for t4ka3 on Xiaomi Mipad2 The t4ka3 sensor on the Xiaomi Mipad2 is used as a back facing sensor, it uses 4 CSI lanes, but the _DSM has CsiLanes set to 2. Extend the existing Xiaomi Mipad2 DMI quirk to override the wrong _DSM setting. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240902095229.59059-1-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c index d789d38ef689..6abda358a72f 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c @@ -109,6 +109,8 @@ static struct gmin_cfg_var lenovo_ideapad_miix_310_vars[] = { static struct gmin_cfg_var xiaomi_mipad2_vars[] = { /* _DSM contains the wrong CsiPort for the front facing OV5693 sensor */ { "INT33BE:00", "CsiPort", "0" }, + /* _DSM contains the wrong CsiLanes for the back facing T4KA3 sensor */ + { "XMCC0003:00", "CsiLanes", "4" }, {} }; -- cgit v1.2.3-70-g09d2 From b8b2383c66602700919a276599ceefe84ab5d4c2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 2 Sep 2024 11:52:28 +0200 Subject: media: atomisp: Drop dev_dbg() calls from hmm_[alloc|free]() Debug logging every alloc + free just polutes the debug logs without adding much value, remove the alloc + free dev_dbg() calls. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240902095229.59059-2-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/hmm/hmm.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm.c b/drivers/staging/media/atomisp/pci/hmm/hmm.c index 3e2899ad8517..e8c5d728fd55 100644 --- a/drivers/staging/media/atomisp/pci/hmm/hmm.c +++ b/drivers/staging/media/atomisp/pci/hmm/hmm.c @@ -204,9 +204,6 @@ static ia_css_ptr __hmm_alloc(size_t bytes, enum hmm_bo_type type, goto bind_err; } - dev_dbg(atomisp_dev, "pages: 0x%08x (%zu bytes), type: %d, vmalloc %p\n", - bo->start, bytes, type, vmalloc_noprof); - return bo->start; bind_err: @@ -231,8 +228,6 @@ void hmm_free(ia_css_ptr virt) { struct hmm_buffer_object *bo; - dev_dbg(atomisp_dev, "%s: free 0x%08x\n", __func__, virt); - if (WARN_ON(virt == mmgr_EXCEPTION)) return; -- cgit v1.2.3-70-g09d2 From 92eb52260b8c00b4a31cb1eac48b2f3073905432 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 2 Sep 2024 11:52:29 +0200 Subject: media: atomisp: Improve binary finding debug logging The atomisp firmware contains a number of different pipeline binaries inside its firmware file and the driver selects the right one depending on the selected pipeline configuration. Sometimes (e.g. when the selected output resolution is too big) it fails to find a binary. This happens especially when adding support for new sensors. Improve the logging when this happens to make debugging easier: 1. Replace ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, ...) with standard dev_dbg() calls so that the logs can be enabled with dyndbg 2. Do not dump_stack() when this fails, doing so adds no useful extra info 3. With the dump_stack() call gone, remove the wrapper and rename __ia_css_binary_find() to ia_css_binary_find() 4. On error use dev_err() instead of dev_dbg() so that when things fail it is clear why they fail without needing to enable dyndbg Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20240902095229.59059-3-hdegoede@redhat.com Signed-off-by: Mauro Carvalho Chehab --- .../media/atomisp/pci/runtime/binary/src/binary.c | 256 ++++++++------------- 1 file changed, 95 insertions(+), 161 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c index 2ff522f7dec8..7ce2b2d6da11 100644 --- a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c +++ b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c @@ -924,8 +924,8 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo, return 0; } -static int __ia_css_binary_find(struct ia_css_binary_descr *descr, - struct ia_css_binary *binary) { +int ia_css_binary_find(struct ia_css_binary_descr *descr, struct ia_css_binary *binary) +{ int mode; bool online; bool two_ppc; @@ -954,10 +954,8 @@ static int __ia_css_binary_find(struct ia_css_binary_descr *descr, /* MW: used after an error check, may accept NULL, but doubtfull */ assert(binary); - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() enter: descr=%p, (mode=%d), binary=%p\n", - descr, descr->mode, - binary); + dev_dbg(atomisp_dev, "ia_css_binary_find() enter: descr=%p, (mode=%d), binary=%p\n", + descr, descr->mode, binary); mode = descr->mode; online = descr->online; @@ -1002,15 +1000,15 @@ static int __ia_css_binary_find(struct ia_css_binary_descr *descr, } /* print a map of the binary file */ - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "BINARY INFO:\n"); + dev_dbg(atomisp_dev, "BINARY INFO:\n"); for (i = 0; i < IA_CSS_BINARY_NUM_MODES; i++) { xcandidate = binary_infos[i]; if (xcandidate) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "%d:\n", i); + dev_dbg(atomisp_dev, "%d:\n", i); while (xcandidate) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, " Name:%s Type:%d Cont:%d\n", - xcandidate->blob->name, xcandidate->type, - xcandidate->sp.enable.continuous); + dev_dbg(atomisp_dev, " Name:%s Type:%d Cont:%d\n", + xcandidate->blob->name, xcandidate->type, + xcandidate->sp.enable.continuous); xcandidate = xcandidate->next; } } @@ -1022,9 +1020,9 @@ static int __ia_css_binary_find(struct ia_css_binary_descr *descr, struct ia_css_binary_info *candidate = &xcandidate->sp; /* printf("sh_css_binary_find: evaluating candidate: * %d\n",candidate->id); */ - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() candidate = %p, mode = %d ID = %d\n", - candidate, candidate->pipeline.mode, candidate->id); + dev_dbg(atomisp_dev, + "ia_css_binary_find() candidate = %p, mode = %d ID = %d\n", + candidate, candidate->pipeline.mode, candidate->id); /* * MW: Only a limited set of jointly configured binaries can @@ -1033,17 +1031,16 @@ static int __ia_css_binary_find(struct ia_css_binary_descr *descr, */ if (!candidate->enable.continuous && continuous && (mode != IA_CSS_BINARY_MODE_COPY)) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: !%d && %d && (%d != %d)\n", - __LINE__, candidate->enable.continuous, - continuous, mode, - IA_CSS_BINARY_MODE_COPY); + dev_dbg(atomisp_dev, + "ia_css_binary_find() [%d] continue: !%d && %d && (%d != %d)\n", + __LINE__, candidate->enable.continuous, + continuous, mode, IA_CSS_BINARY_MODE_COPY); continue; } if (striped && candidate->iterator.num_stripes == 1) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: binary is not striped\n", - __LINE__); + dev_dbg(atomisp_dev, + "ia_css_binary_find() [%d] continue: binary is not striped\n", + __LINE__); continue; } @@ -1051,58 +1048,38 @@ static int __ia_css_binary_find(struct ia_css_binary_descr *descr, (mode != IA_CSS_BINARY_MODE_COPY) && (mode != IA_CSS_BINARY_MODE_CAPTURE_PP) && (mode != IA_CSS_BINARY_MODE_VF_PP)) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: (%d != %d)\n", - __LINE__, - candidate->pipeline.isp_pipe_version, isp_pipe_version); + dev_dbg(atomisp_dev, "ia_css_binary_find() [%d] continue: (%d != %d)\n", + __LINE__, candidate->pipeline.isp_pipe_version, isp_pipe_version); continue; } if (!candidate->enable.reduced_pipe && enable_reduced_pipe) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: !%d && %d\n", - __LINE__, - candidate->enable.reduced_pipe, - enable_reduced_pipe); + dev_dbg(atomisp_dev, "ia_css_binary_find() [%d] continue: !%d && %d\n", + __LINE__, candidate->enable.reduced_pipe, enable_reduced_pipe); continue; } if (!candidate->enable.dvs_6axis && enable_dvs_6axis) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: !%d && %d\n", - __LINE__, - candidate->enable.dvs_6axis, - enable_dvs_6axis); + dev_dbg(atomisp_dev, "ia_css_binary_find() [%d] continue: !%d && %d\n", + __LINE__, candidate->enable.dvs_6axis, enable_dvs_6axis); continue; } if (candidate->enable.high_speed && !enable_high_speed) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: %d && !%d\n", - __LINE__, - candidate->enable.high_speed, - enable_high_speed); + dev_dbg(atomisp_dev, "ia_css_binary_find() [%d] continue: %d && !%d\n", + __LINE__, candidate->enable.high_speed, enable_high_speed); continue; } if (!candidate->enable.xnr && need_xnr) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: %d && !%d\n", - __LINE__, - candidate->enable.xnr, - need_xnr); + dev_dbg(atomisp_dev, "ia_css_binary_find() [%d] continue: %d && !%d\n", + __LINE__, candidate->enable.xnr, need_xnr); continue; } if (!(candidate->enable.ds & 2) && enable_yuv_ds) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: !%d && %d\n", - __LINE__, - ((candidate->enable.ds & 2) != 0), - enable_yuv_ds); + dev_dbg(atomisp_dev, "ia_css_binary_find() [%d] continue: !%d && %d\n", + __LINE__, ((candidate->enable.ds & 2) != 0), enable_yuv_ds); continue; } if ((candidate->enable.ds & 2) && !enable_yuv_ds) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: %d && !%d\n", - __LINE__, - ((candidate->enable.ds & 2) != 0), - enable_yuv_ds); + dev_dbg(atomisp_dev, "ia_css_binary_find() [%d] continue: %d && !%d\n", + __LINE__, ((candidate->enable.ds & 2) != 0), enable_yuv_ds); continue; } @@ -1116,100 +1093,85 @@ static int __ia_css_binary_find(struct ia_css_binary_descr *descr, candidate->vf_dec.is_variable || /* or more than one output pin. */ xcandidate->num_output_pins > 1)) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: (%p != NULL) && !(%d || %d || (%d >%d))\n", - __LINE__, req_vf_info, - candidate->enable.vf_veceven, - candidate->vf_dec.is_variable, - xcandidate->num_output_pins, 1); + dev_dbg(atomisp_dev, + "ia_css_binary_find() [%d] continue: (%p != NULL) && !(%d || %d || (%d >%d))\n", + __LINE__, req_vf_info, candidate->enable.vf_veceven, + candidate->vf_dec.is_variable, xcandidate->num_output_pins, 1); continue; } if (!candidate->enable.dvs_envelope && need_dvs) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: !%d && %d\n", - __LINE__, - candidate->enable.dvs_envelope, (int)need_dvs); + dev_dbg(atomisp_dev, "ia_css_binary_find() [%d] continue: !%d && %d\n", + __LINE__, candidate->enable.dvs_envelope, (int)need_dvs); continue; } /* internal_res check considers input, output, and dvs envelope sizes */ ia_css_binary_internal_res(req_in_info, req_bds_out_info, req_bin_out_info, &dvs_env, candidate, &internal_res); if (internal_res.width > candidate->internal.max_width) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: (%d > %d)\n", - __LINE__, internal_res.width, - candidate->internal.max_width); + dev_dbg(atomisp_dev, "ia_css_binary_find() [%d] continue: (%d > %d)\n", + __LINE__, internal_res.width, candidate->internal.max_width); continue; } if (internal_res.height > candidate->internal.max_height) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: (%d > %d)\n", - __LINE__, internal_res.height, - candidate->internal.max_height); + dev_dbg(atomisp_dev, "ia_css_binary_find() [%d] continue: (%d > %d)\n", + __LINE__, internal_res.height, candidate->internal.max_height); continue; } if (!candidate->enable.ds && need_ds && !(xcandidate->num_output_pins > 1)) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: !%d && %d\n", - __LINE__, candidate->enable.ds, (int)need_ds); + dev_dbg(atomisp_dev, "ia_css_binary_find() [%d] continue: !%d && %d\n", + __LINE__, candidate->enable.ds, (int)need_ds); continue; } if (!candidate->enable.uds && !candidate->enable.dvs_6axis && need_dz) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: !%d && !%d && %d\n", - __LINE__, candidate->enable.uds, - candidate->enable.dvs_6axis, (int)need_dz); + dev_dbg(atomisp_dev, + "ia_css_binary_find() [%d] continue: !%d && !%d && %d\n", + __LINE__, candidate->enable.uds, candidate->enable.dvs_6axis, + (int)need_dz); continue; } if (online && candidate->input.source == IA_CSS_BINARY_INPUT_MEMORY) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: %d && (%d == %d)\n", - __LINE__, online, candidate->input.source, - IA_CSS_BINARY_INPUT_MEMORY); + dev_dbg(atomisp_dev, + "ia_css_binary_find() [%d] continue: %d && (%d == %d)\n", + __LINE__, online, candidate->input.source, + IA_CSS_BINARY_INPUT_MEMORY); continue; } if (!online && candidate->input.source == IA_CSS_BINARY_INPUT_SENSOR) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: !%d && (%d == %d)\n", - __LINE__, online, candidate->input.source, - IA_CSS_BINARY_INPUT_SENSOR); + dev_dbg(atomisp_dev, + "ia_css_binary_find() [%d] continue: !%d && (%d == %d)\n", + __LINE__, online, candidate->input.source, + IA_CSS_BINARY_INPUT_SENSOR); continue; } if (req_bin_out_info->res.width < candidate->output.min_width || req_bin_out_info->res.width > candidate->output.max_width) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: (%d > %d) || (%d < %d)\n", - __LINE__, - req_bin_out_info->padded_width, - candidate->output.min_width, - req_bin_out_info->padded_width, - candidate->output.max_width); + dev_dbg(atomisp_dev, + "ia_css_binary_find() [%d] continue: (%d > %d) || (%d < %d)\n", + __LINE__, req_bin_out_info->padded_width, + candidate->output.min_width, req_bin_out_info->padded_width, + candidate->output.max_width); continue; } if (xcandidate->num_output_pins > 1 && /* in case we have a second output pin, */ req_vf_info) { /* and we need vf output. */ if (req_vf_info->res.width > candidate->output.max_width) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: (%d < %d)\n", - __LINE__, - req_vf_info->res.width, - candidate->output.max_width); + dev_dbg(atomisp_dev, + "ia_css_binary_find() [%d] continue: (%d < %d)\n", + __LINE__, req_vf_info->res.width, + candidate->output.max_width); continue; } } if (req_in_info->padded_width > candidate->input.max_width) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: (%d > %d)\n", - __LINE__, req_in_info->padded_width, - candidate->input.max_width); + dev_dbg(atomisp_dev, "ia_css_binary_find() [%d] continue: (%d > %d)\n", + __LINE__, req_in_info->padded_width, candidate->input.max_width); continue; } if (!binary_supports_output_format(xcandidate, req_bin_out_info->format)) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: !%d\n", - __LINE__, - binary_supports_output_format(xcandidate, req_bin_out_info->format)); + dev_dbg(atomisp_dev, "ia_css_binary_find() [%d] continue: !%d\n", + __LINE__, binary_supports_output_format(xcandidate, + req_bin_out_info->format)); continue; } if (xcandidate->num_output_pins > 1 && @@ -1218,11 +1180,10 @@ static int __ia_css_binary_find(struct ia_css_binary_descr *descr, /* check if the required vf format is supported. */ !binary_supports_output_format(xcandidate, req_vf_info->format)) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: (%d > %d) && (%p != NULL) && !%d\n", - __LINE__, xcandidate->num_output_pins, 1, - req_vf_info, - binary_supports_output_format(xcandidate, req_vf_info->format)); + dev_dbg(atomisp_dev, + "ia_css_binary_find() [%d] continue: (%d > %d) && (%p != NULL) && !%d\n", + __LINE__, xcandidate->num_output_pins, 1, req_vf_info, + binary_supports_output_format(xcandidate, req_vf_info->format)); continue; } @@ -1230,11 +1191,11 @@ static int __ia_css_binary_find(struct ia_css_binary_descr *descr, if (xcandidate->num_output_pins == 1 && req_vf_info && candidate->enable.vf_veceven && !binary_supports_vf_format(xcandidate, req_vf_info->format)) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: (%d == %d) && (%p != NULL) && %d && !%d\n", - __LINE__, xcandidate->num_output_pins, 1, - req_vf_info, candidate->enable.vf_veceven, - binary_supports_vf_format(xcandidate, req_vf_info->format)); + dev_dbg(atomisp_dev, + "ia_css_binary_find() [%d] continue: (%d == %d) && (%p != NULL) && %d && !%d\n", + __LINE__, xcandidate->num_output_pins, 1, + req_vf_info, candidate->enable.vf_veceven, + binary_supports_vf_format(xcandidate, req_vf_info->format)); continue; } @@ -1242,37 +1203,31 @@ static int __ia_css_binary_find(struct ia_css_binary_descr *descr, if (xcandidate->num_output_pins == 1 && req_vf_info && candidate->enable.vf_veceven) { /* and we need vf output. */ if (req_vf_info->res.width > candidate->output.max_width) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: (%d < %d)\n", - __LINE__, - req_vf_info->res.width, - candidate->output.max_width); + dev_dbg(atomisp_dev, + "ia_css_binary_find() [%d] continue: (%d < %d)\n", + __LINE__, req_vf_info->res.width, + candidate->output.max_width); continue; } } if (!supports_bds_factor(candidate->bds.supported_bds_factors, descr->required_bds_factor)) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: 0x%x & 0x%x)\n", - __LINE__, candidate->bds.supported_bds_factors, - descr->required_bds_factor); + dev_dbg(atomisp_dev, "ia_css_binary_find() [%d] continue: 0x%x & 0x%x)\n", + __LINE__, candidate->bds.supported_bds_factors, + descr->required_bds_factor); continue; } if (!candidate->enable.dpc && need_dpc) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: 0x%x & 0x%x)\n", - __LINE__, candidate->enable.dpc, - descr->enable_dpc); + dev_dbg(atomisp_dev, "ia_css_binary_find() [%d] continue: 0x%x & 0x%x)\n", + __LINE__, candidate->enable.dpc, descr->enable_dpc); continue; } if (candidate->uds.use_bci && enable_capture_pp_bli) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() [%d] continue: 0x%x & 0x%x)\n", - __LINE__, candidate->uds.use_bci, - descr->enable_capture_pp_bli); + dev_dbg(atomisp_dev, "ia_css_binary_find() [%d] continue: 0x%x & 0x%x)\n", + __LINE__, candidate->uds.use_bci, descr->enable_capture_pp_bli); continue; } @@ -1291,39 +1246,18 @@ static int __ia_css_binary_find(struct ia_css_binary_descr *descr, break; } - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() selected = %p, mode = %d ID = %d\n", - xcandidate, xcandidate ? xcandidate->sp.pipeline.mode : 0, xcandidate ? xcandidate->sp.id : 0); - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_binary_find() leave: return_err=%d\n", err); - if (!err && xcandidate) - dev_dbg(atomisp_dev, - "Using binary %s (id %d), type %d, mode %d, continuous %s\n", - xcandidate->blob->name, - xcandidate->sp.id, - xcandidate->type, + dev_dbg(atomisp_dev, "Using binary %s (id %d), type %d, mode %d, continuous %s\n", + xcandidate->blob->name, xcandidate->sp.id, xcandidate->type, xcandidate->sp.pipeline.mode, xcandidate->sp.enable.continuous ? "true" : "false"); + if (err) + dev_err(atomisp_dev, "Failed to find a firmware binary matching the pipeline parameters\n"); return err; } -int ia_css_binary_find(struct ia_css_binary_descr *descr, - struct ia_css_binary *binary) -{ - int ret = __ia_css_binary_find(descr, binary); - - if (unlikely(ret)) { - dev_dbg(atomisp_dev, "Seeking for binary failed at:"); - dump_stack(); - } - - return ret; -} - unsigned ia_css_binary_max_vf_width(void) { -- cgit v1.2.3-70-g09d2 From b36c41c51e9d763393634359b90f02414ef470a3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Sep 2024 16:04:47 +0200 Subject: media: atomisp: set lock before calling vb2_queue_init() The vb2_queue_init() will expect the vb2_queue lock pointer to be set in the future. So for those drivers that set the lock later, move it up to before the vb2_queue_init() call. Signed-off-by: Hans Verkuil Link: https://lore.kernel.org/r/90b8e7a40c3ed306cbeb96c2f4dad97eb7e53bfd.1725285495.git.hverkuil-cisco@xs4all.nl Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/atomisp/pci/atomisp_subdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c index 3a3e84a035e2..202497695e46 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c @@ -797,12 +797,12 @@ static int atomisp_init_subdev_pipe(struct atomisp_sub_device *asd, pipe->vb_queue.ops = &atomisp_vb2_ops; pipe->vb_queue.mem_ops = &vb2_vmalloc_memops; pipe->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + pipe->vb_queue.lock = &pipe->vb_queue_mutex; ret = vb2_queue_init(&pipe->vb_queue); if (ret) return ret; pipe->vdev.queue = &pipe->vb_queue; - pipe->vdev.queue->lock = &pipe->vb_queue_mutex; INIT_LIST_HEAD(&pipe->buffers_in_css); INIT_LIST_HEAD(&pipe->activeq); -- cgit v1.2.3-70-g09d2 From ffe3dc5acaa2477276011d6eb61373220217cd9d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 7 Sep 2024 13:17:01 +0200 Subject: media: atomisp: Fix eed1_8 code assigning signed values to an unsigned variable ia_css_eed1_8_vmem_encode() is assigning values with a range of -8192 - 8191 to e_dew_enh_y and e_dew_enh_a both of which are of the VMEM_ARRAY type which maps to u16. This causes the following smatch warnings: drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c: drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c:177 ia_css_eed1_8_vmem_encode() warn: assigning (-8192) to unsigned variable 'to->e_dew_enh_y[0][base + j]' drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c: drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c:182 ia_css_eed1_8_vmem_encode() warn: assigning (-8192) to unsigned variable 'to->e_dew_enh_a[0][base + j]' Convert the e_dew_enh_y and e_dew_enh_a arrays to a new SVMEM_ARRAY type which maps to s16 to fix this. Link: https://lore.kernel.org/linux-media/20240907111701.8493-1-hdegoede@redhat.com Reported-by: Mauro Carvalho Chehab Closes: https://lore.kernel.org/linux-media/20240906081542.5cb0c142@foz.lan/ Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/atomisp/pci/hive_isp_css_common/host/vmem_local.h | 4 +++- .../media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_param.h | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem_local.h b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem_local.h index d0ba59cedc92..6f0a8fe868bd 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem_local.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem_local.h @@ -20,8 +20,10 @@ #include "vmem_global.h" typedef u16 t_vmem_elem; +typedef s16 t_svmem_elem; -#define VMEM_ARRAY(x, s) t_vmem_elem x[s / ISP_NWAY][ISP_NWAY] +#define VMEM_ARRAY(x, s) t_vmem_elem x[(s) / ISP_NWAY][ISP_NWAY] +#define SVMEM_ARRAY(x, s) t_svmem_elem x[(s) / ISP_NWAY][ISP_NWAY] void isp_vmem_load( const isp_ID_t ID, diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_param.h b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_param.h index 6fb3b38f49e7..b9eeeb592ec8 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_param.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_param.h @@ -94,8 +94,8 @@ struct eed1_8_vmem_params { VMEM_ARRAY(e_dew_enh_x, ISP_VEC_NELEMS); - VMEM_ARRAY(e_dew_enh_y, ISP_VEC_NELEMS); - VMEM_ARRAY(e_dew_enh_a, ISP_VEC_NELEMS); + SVMEM_ARRAY(e_dew_enh_y, ISP_VEC_NELEMS); + SVMEM_ARRAY(e_dew_enh_a, ISP_VEC_NELEMS); VMEM_ARRAY(e_dew_enh_f, ISP_VEC_NELEMS); VMEM_ARRAY(chgrinv_x, ISP_VEC_NELEMS); VMEM_ARRAY(chgrinv_a, ISP_VEC_NELEMS); -- cgit v1.2.3-70-g09d2 From 81ee62e8d09ee3c7107d11c8bbfd64073ab601ad Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 27 Jul 2024 14:51:56 +0200 Subject: media: atomisp: Use clamp() in ia_css_eed1_8_vmem_encode() Using clamp() instead of min_t(max_t()) is easier to read. It also reduces the size of the preprocessed files by ~ 193 ko. (see [1] for a discussion about it) $ ls -l ia_css_eed1_8.host*.i 4829993 27 juil. 14:36 ia_css_eed1_8.host.old.i 4636649 27 juil. 14:42 ia_css_eed1_8.host.new.i [1]: https://lore.kernel.org/all/23bdb6fc8d884ceebeb6e8b8653b8cfe@AcuMS.aculab.com/ Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/155aba6ab759e98f66349e6bb4f69e2410486c09.1722084704.git.christophe.jaillet@wanadoo.fr Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c index 55d102b479ac..b79d78e5b77f 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c @@ -172,25 +172,21 @@ ia_css_eed1_8_vmem_encode( base = shuffle_block * i; for (j = 0; j < IA_CSS_NUMBER_OF_DEW_ENHANCE_SEGMENTS; j++) { - to->e_dew_enh_x[0][base + j] = min_t(int, max_t(int, - from->dew_enhance_seg_x[j], 0), - 8191); - to->e_dew_enh_y[0][base + j] = min_t(int, max_t(int, - from->dew_enhance_seg_y[j], -8192), - 8191); + to->e_dew_enh_x[0][base + j] = clamp(from->dew_enhance_seg_x[j], + 0, 8191); + to->e_dew_enh_y[0][base + j] = clamp(from->dew_enhance_seg_y[j], + -8192, 8191); } for (j = 0; j < (IA_CSS_NUMBER_OF_DEW_ENHANCE_SEGMENTS - 1); j++) { - to->e_dew_enh_a[0][base + j] = min_t(int, max_t(int, - from->dew_enhance_seg_slope[j], - -8192), 8191); + to->e_dew_enh_a[0][base + j] = clamp(from->dew_enhance_seg_slope[j], + -8192, 8191); /* Convert dew_enhance_seg_exp to flag: * 0 -> 0 * 1...13 -> 1 */ - to->e_dew_enh_f[0][base + j] = (min_t(int, max_t(int, - from->dew_enhance_seg_exp[j], - 0), 13) > 0); + to->e_dew_enh_f[0][base + j] = clamp(from->dew_enhance_seg_exp[j], + 0, 13) > 0; } /* Hard-coded to 0, in order to be able to handle out of -- cgit v1.2.3-70-g09d2