From 9447082ae666fbf3adbe9c9152117daa31a8b737 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 16 Nov 2016 12:21:19 -0200 Subject: [media] smiapp: Implement power-on and power-off sequences without runtime PM Power on the sensor when the module is loaded and power it off when it is removed. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 59872b31f832..620f8ce8185e 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2741,8 +2741,6 @@ static const struct v4l2_subdev_internal_ops smiapp_internal_ops = { * I2C Driver */ -#ifdef CONFIG_PM - static int smiapp_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -2783,13 +2781,6 @@ static int smiapp_resume(struct device *dev) return rval; } -#else - -#define smiapp_suspend NULL -#define smiapp_resume NULL - -#endif /* CONFIG_PM */ - static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev) { struct smiapp_hwconfig *hwcfg; @@ -2913,13 +2904,9 @@ static int smiapp_probe(struct i2c_client *client, if (IS_ERR(sensor->xshutdown)) return PTR_ERR(sensor->xshutdown); - pm_runtime_enable(&client->dev); - - rval = pm_runtime_get_sync(&client->dev); - if (rval < 0) { - rval = -ENODEV; - goto out_power_off; - } + rval = smiapp_power_on(&client->dev); + if (rval < 0) + return rval; rval = smiapp_identify_module(sensor); if (rval) { @@ -3100,6 +3087,9 @@ static int smiapp_probe(struct i2c_client *client, if (rval < 0) goto out_media_entity_cleanup; + pm_runtime_set_active(&client->dev); + pm_runtime_get_noresume(&client->dev); + pm_runtime_enable(&client->dev); pm_runtime_set_autosuspend_delay(&client->dev, 1000); pm_runtime_use_autosuspend(&client->dev); pm_runtime_put_autosuspend(&client->dev); @@ -3113,8 +3103,7 @@ out_cleanup: smiapp_cleanup(sensor); out_power_off: - pm_runtime_put(&client->dev); - pm_runtime_disable(&client->dev); + smiapp_power_off(&client->dev); return rval; } @@ -3127,8 +3116,10 @@ static int smiapp_remove(struct i2c_client *client) v4l2_async_unregister_subdev(subdev); - pm_runtime_suspend(&client->dev); pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + smiapp_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); for (i = 0; i < sensor->ssds_used; i++) { v4l2_device_unregister_subdev(&sensor->ssds[i].sd); -- cgit v1.2.3 From 4bfb934b0067b7f6a24470682c5f7254fd4d8282 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 19 Nov 2016 19:50:10 -0200 Subject: [media] smiapp: Make suspend and resume functions __maybe_unused The smiapp_suspend() and smiapp_resume() functions will end up being unused if CONFIG_PM is enabled but CONFIG_PM_SLEEP is disabled, causing a compiler warning from both of the function definitions. Fix this by marking the functions with __maybe_unused. Suggested-by: Arnd Bergmann Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 620f8ce8185e..f4e92bdfe192 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2741,7 +2741,7 @@ static const struct v4l2_subdev_internal_ops smiapp_internal_ops = { * I2C Driver */ -static int smiapp_suspend(struct device *dev) +static int __maybe_unused smiapp_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct v4l2_subdev *subdev = i2c_get_clientdata(client); @@ -2766,7 +2766,7 @@ static int smiapp_suspend(struct device *dev) return 0; } -static int smiapp_resume(struct device *dev) +static int __maybe_unused smiapp_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct v4l2_subdev *subdev = i2c_get_clientdata(client); -- cgit v1.2.3 From f3854973f196baad5be6b62d8f5ea24b0346b63f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 6 Dec 2016 11:17:12 -0200 Subject: [media] cec: fix report_current_latency In the (very) small print of the REPORT_CURRENT_LATENCY message there is a line that says that the last byte of the message (audio out delay) is only present if the 'audio out compensated' value is 3. I missed this, and so if this message was sent with a total length of 6 (i.e. without the audio out delay byte), then it was rejected by the framework since a minimum length of 7 was expected. Fix this minimum length check and update the wrappers in cec-funcs.h to do the right thing based on the message length. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-adap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media') diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index 0ea4efb3de66..f15f6ffd75b2 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -851,7 +851,7 @@ static const u8 cec_msg_size[256] = { [CEC_MSG_REQUEST_ARC_TERMINATION] = 2 | DIRECTED, [CEC_MSG_TERMINATE_ARC] = 2 | DIRECTED, [CEC_MSG_REQUEST_CURRENT_LATENCY] = 4 | BCAST, - [CEC_MSG_REPORT_CURRENT_LATENCY] = 7 | BCAST, + [CEC_MSG_REPORT_CURRENT_LATENCY] = 6 | BCAST, [CEC_MSG_CDC_MESSAGE] = 2 | BCAST, }; -- cgit v1.2.3 From 120476123646ba3619c90db7bcbc6f8eea53c990 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 9 Dec 2016 11:14:32 -0200 Subject: [media] cec: when canceling a message, don't overwrite old status info When a pending message was canceled (e.g. due to a timeout), then the old tx_status info was overwritten instead of ORed. The same happened with the tx_error_cnt field. So just modify them instead of overwriting them. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-adap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index f15f6ffd75b2..3191c0c5eae1 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -288,10 +288,10 @@ static void cec_data_cancel(struct cec_data *data) /* Mark it as an error */ data->msg.tx_ts = ktime_get_ns(); - data->msg.tx_status = CEC_TX_STATUS_ERROR | - CEC_TX_STATUS_MAX_RETRIES; + data->msg.tx_status |= CEC_TX_STATUS_ERROR | + CEC_TX_STATUS_MAX_RETRIES; + data->msg.tx_error_cnt++; data->attempts = 0; - data->msg.tx_error_cnt = 1; /* Queue transmitted message for monitoring purposes */ cec_queue_msg_monitor(data->adap, &data->msg, 1); -- cgit v1.2.3 From a24f56d47930492c94ef6875bf45adf7607ca1a4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 9 Dec 2016 11:28:19 -0200 Subject: [media] cec: CEC_MSG_GIVE_FEATURES should abort for CEC version < 2 This is a 2.0 only message, so it should return Feature Abort if the adapter is configured for CEC version 1.4. Right now it does nothing, which means that the sender will time out. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-adap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index 3191c0c5eae1..c05956fdd500 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -1777,9 +1777,9 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, } case CEC_MSG_GIVE_FEATURES: - if (adap->log_addrs.cec_version >= CEC_OP_CEC_VERSION_2_0) - return cec_report_features(adap, la_idx); - return 0; + if (adap->log_addrs.cec_version < CEC_OP_CEC_VERSION_2_0) + return cec_feature_abort(adap, msg); + return cec_report_features(adap, la_idx); default: /* -- cgit v1.2.3 From 7af26f889eb67db272021a939f7d4a57e96dd961 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 9 Dec 2016 11:54:06 -0200 Subject: [media] cec: update log_addr[] before finishing configuration The loop that sets the unused logical addresses to INVALID should be done before 'configured' is set to true. This ensures that cec_log_addrs is consistent before it will be used. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-adap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index c05956fdd500..f3fef487f28d 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -1250,6 +1250,8 @@ configured: for (i = 1; i < las->num_log_addrs; i++) las->log_addr[i] = CEC_LOG_ADDR_INVALID; } + for (i = las->num_log_addrs; i < CEC_MAX_LOG_ADDRS; i++) + las->log_addr[i] = CEC_LOG_ADDR_INVALID; adap->is_configured = true; adap->is_configuring = false; cec_post_state_event(adap); @@ -1268,8 +1270,6 @@ configured: cec_report_features(adap, i); cec_report_phys_addr(adap, i); } - for (i = las->num_log_addrs; i < CEC_MAX_LOG_ADDRS; i++) - las->log_addr[i] = CEC_LOG_ADDR_INVALID; mutex_lock(&adap->lock); adap->kthread_config = NULL; mutex_unlock(&adap->lock); -- cgit v1.2.3 From 52bc30fda9622f492427d484bd4dd8ee42cc4667 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 9 Dec 2016 11:36:03 -0200 Subject: [media] cec: replace cec_report_features by cec_fill_msg_report_features The fill function just fills in the cec_msg struct, it doesn't transmit the message. This is now done explicitly. This makes it possible to switch to transmitting this message with adap->lock held. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-adap.c | 48 ++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 22 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index f3fef487f28d..2b668510ca36 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -30,8 +30,10 @@ #include "cec-priv.h" -static int cec_report_features(struct cec_adapter *adap, unsigned int la_idx); static int cec_report_phys_addr(struct cec_adapter *adap, unsigned int la_idx); +static void cec_fill_msg_report_features(struct cec_adapter *adap, + struct cec_msg *msg, + unsigned int la_idx); /* * 400 ms is the time it takes for one 16 byte message to be @@ -1258,16 +1260,21 @@ configured: mutex_unlock(&adap->lock); for (i = 0; i < las->num_log_addrs; i++) { + struct cec_msg msg = {}; + if (las->log_addr[i] == CEC_LOG_ADDR_INVALID || (las->flags & CEC_LOG_ADDRS_FL_CDC_ONLY)) continue; - /* - * Report Features must come first according - * to CEC 2.0 - */ - if (las->log_addr[i] != CEC_LOG_ADDR_UNREGISTERED) - cec_report_features(adap, i); + msg.msg[0] = (las->log_addr[i] << 4) | 0x0f; + + /* Report Features must come first according to CEC 2.0 */ + if (las->log_addr[i] != CEC_LOG_ADDR_UNREGISTERED && + adap->log_addrs.cec_version >= CEC_OP_CEC_VERSION_2_0) { + cec_fill_msg_report_features(adap, &msg, i); + cec_transmit_msg(adap, &msg, false); + } + cec_report_phys_addr(adap, i); } mutex_lock(&adap->lock); @@ -1526,36 +1533,32 @@ EXPORT_SYMBOL_GPL(cec_s_log_addrs); /* High-level core CEC message handling */ -/* Transmit the Report Features message */ -static int cec_report_features(struct cec_adapter *adap, unsigned int la_idx) +/* Fill in the Report Features message */ +static void cec_fill_msg_report_features(struct cec_adapter *adap, + struct cec_msg *msg, + unsigned int la_idx) { - struct cec_msg msg = { }; const struct cec_log_addrs *las = &adap->log_addrs; const u8 *features = las->features[la_idx]; bool op_is_dev_features = false; unsigned int idx; - /* This is 2.0 and up only */ - if (adap->log_addrs.cec_version < CEC_OP_CEC_VERSION_2_0) - return 0; - /* Report Features */ - msg.msg[0] = (las->log_addr[la_idx] << 4) | 0x0f; - msg.len = 4; - msg.msg[1] = CEC_MSG_REPORT_FEATURES; - msg.msg[2] = adap->log_addrs.cec_version; - msg.msg[3] = las->all_device_types[la_idx]; + msg->msg[0] = (las->log_addr[la_idx] << 4) | 0x0f; + msg->len = 4; + msg->msg[1] = CEC_MSG_REPORT_FEATURES; + msg->msg[2] = adap->log_addrs.cec_version; + msg->msg[3] = las->all_device_types[la_idx]; /* Write RC Profiles first, then Device Features */ for (idx = 0; idx < ARRAY_SIZE(las->features[0]); idx++) { - msg.msg[msg.len++] = features[idx]; + msg->msg[msg->len++] = features[idx]; if ((features[idx] & CEC_OP_FEAT_EXT) == 0) { if (op_is_dev_features) break; op_is_dev_features = true; } } - return cec_transmit_msg(adap, &msg, false); } /* Transmit the Report Physical Address message */ @@ -1779,7 +1782,8 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, case CEC_MSG_GIVE_FEATURES: if (adap->log_addrs.cec_version < CEC_OP_CEC_VERSION_2_0) return cec_feature_abort(adap, msg); - return cec_report_features(adap, la_idx); + cec_fill_msg_report_features(adap, &tx_cec_msg, la_idx); + return cec_transmit_msg(adap, &tx_cec_msg, false); default: /* -- cgit v1.2.3 From d3d64bc7408f1ff0b0ff8354056e2a48eda5886d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 9 Dec 2016 11:48:34 -0200 Subject: [media] cec: move cec_report_phys_addr into cec_config_thread_func It's only a small function and this makes it easier to switch to transmitting the message with adap->lock held in the next patch. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-adap.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index 2b668510ca36..f3d495654a53 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -30,7 +30,6 @@ #include "cec-priv.h" -static int cec_report_phys_addr(struct cec_adapter *adap, unsigned int la_idx); static void cec_fill_msg_report_features(struct cec_adapter *adap, struct cec_msg *msg, unsigned int la_idx); @@ -1275,7 +1274,13 @@ configured: cec_transmit_msg(adap, &msg, false); } - cec_report_phys_addr(adap, i); + /* Report Physical Address */ + cec_msg_report_physical_addr(&msg, adap->phys_addr, + las->primary_device_type[i]); + dprintk(2, "config: la %d pa %x.%x.%x.%x\n", + las->log_addr[i], + cec_phys_addr_exp(adap->phys_addr)); + cec_transmit_msg(adap, &msg, false); } mutex_lock(&adap->lock); adap->kthread_config = NULL; @@ -1561,22 +1566,6 @@ static void cec_fill_msg_report_features(struct cec_adapter *adap, } } -/* Transmit the Report Physical Address message */ -static int cec_report_phys_addr(struct cec_adapter *adap, unsigned int la_idx) -{ - const struct cec_log_addrs *las = &adap->log_addrs; - struct cec_msg msg = { }; - - /* Report Physical Address */ - msg.msg[0] = (las->log_addr[la_idx] << 4) | 0x0f; - cec_msg_report_physical_addr(&msg, adap->phys_addr, - las->primary_device_type[la_idx]); - dprintk(2, "config: la %d pa %x.%x.%x.%x\n", - las->log_addr[la_idx], - cec_phys_addr_exp(adap->phys_addr)); - return cec_transmit_msg(adap, &msg, false); -} - /* Transmit the Feature Abort message */ static int cec_feature_abort_reason(struct cec_adapter *adap, struct cec_msg *msg, u8 reason) -- cgit v1.2.3 From f60f35609f89ef4fee73776bc1ef697923251995 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 9 Dec 2016 12:00:49 -0200 Subject: [media] cec: fix race between configuring and unconfiguring This race was discovered by running cec-compliance -A with the cec module debug parameter set to 2: suddenly the test would fail. It turns out that this happens when the test configures the adapter in non-blocking mode, then it waits for the CEC_EVENT_STATE_CHANGE event and once the event is received it unconfigures the adapter. What happened was that the unconfigure was executed while the configure was still transmitting the Report Features and Report Physical Address messages. This messed up the internal state of the cec_adapter. The fix is to transmit those messages with the adap->lock mutex held (this will just queue them up in the internal transmit queue, and not actually transmit anything yet). Only unlock the mutex once everything is done. The main thread will dequeue the messages from the internal transmit queue and transmit them one by one, unless an unconfigure was done, and in that case any messages are just dropped. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-adap.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index f3d495654a53..ebb5e391b800 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -1256,8 +1256,17 @@ configured: adap->is_configured = true; adap->is_configuring = false; cec_post_state_event(adap); - mutex_unlock(&adap->lock); + /* + * Now post the Report Features and Report Physical Address broadcast + * messages. Note that these are non-blocking transmits, meaning that + * they are just queued up and once adap->lock is unlocked the main + * thread will kick in and start transmitting these. + * + * If after this function is done (but before one or more of these + * messages are actually transmitted) the CEC adapter is unconfigured, + * then any remaining messages will be dropped by the main thread. + */ for (i = 0; i < las->num_log_addrs; i++) { struct cec_msg msg = {}; @@ -1271,7 +1280,7 @@ configured: if (las->log_addr[i] != CEC_LOG_ADDR_UNREGISTERED && adap->log_addrs.cec_version >= CEC_OP_CEC_VERSION_2_0) { cec_fill_msg_report_features(adap, &msg, i); - cec_transmit_msg(adap, &msg, false); + cec_transmit_msg_fh(adap, &msg, NULL, false); } /* Report Physical Address */ @@ -1280,12 +1289,11 @@ configured: dprintk(2, "config: la %d pa %x.%x.%x.%x\n", las->log_addr[i], cec_phys_addr_exp(adap->phys_addr)); - cec_transmit_msg(adap, &msg, false); + cec_transmit_msg_fh(adap, &msg, NULL, false); } - mutex_lock(&adap->lock); adap->kthread_config = NULL; - mutex_unlock(&adap->lock); complete(&adap->config_completion); + mutex_unlock(&adap->lock); return 0; unconfigure: -- cgit v1.2.3 From 78ccbf9ff89bd7a20d36be039cb3eab71081648c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 11 Sep 2016 10:31:28 -0300 Subject: [media] media/cobalt: use pci_irq_allocate_vectors Simply the interrupt setup by using the new PCI layer helpers. Despite using pci_enable_msi_range, this driver was only requesting a single MSI vector anyway. Signed-off-by: Christoph Hellwig Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cobalt/cobalt-driver.c | 8 ++------ drivers/media/pci/cobalt/cobalt-driver.h | 2 -- 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/pci/cobalt/cobalt-driver.c b/drivers/media/pci/cobalt/cobalt-driver.c index 979634000597..d5c911c09e2b 100644 --- a/drivers/media/pci/cobalt/cobalt-driver.c +++ b/drivers/media/pci/cobalt/cobalt-driver.c @@ -308,9 +308,7 @@ static void cobalt_pci_iounmap(struct cobalt *cobalt, struct pci_dev *pci_dev) static void cobalt_free_msi(struct cobalt *cobalt, struct pci_dev *pci_dev) { free_irq(pci_dev->irq, (void *)cobalt); - - if (cobalt->msi_enabled) - pci_disable_msi(pci_dev); + pci_free_irq_vectors(pci_dev); } static int cobalt_setup_pci(struct cobalt *cobalt, struct pci_dev *pci_dev, @@ -387,14 +385,12 @@ static int cobalt_setup_pci(struct cobalt *cobalt, struct pci_dev *pci_dev, from being generated. */ cobalt_set_interrupt(cobalt, false); - if (pci_enable_msi_range(pci_dev, 1, 1) < 1) { + if (pci_alloc_irq_vectors(pci_dev, 1, 1, PCI_IRQ_MSI) < 1) { cobalt_err("Could not enable MSI\n"); - cobalt->msi_enabled = false; ret = -EIO; goto err_release; } msi_config_show(cobalt, pci_dev); - cobalt->msi_enabled = true; /* Register IRQ */ if (request_irq(pci_dev->irq, cobalt_irq_handler, IRQF_SHARED, diff --git a/drivers/media/pci/cobalt/cobalt-driver.h b/drivers/media/pci/cobalt/cobalt-driver.h index ed00dc9d9399..00f773ec359a 100644 --- a/drivers/media/pci/cobalt/cobalt-driver.h +++ b/drivers/media/pci/cobalt/cobalt-driver.h @@ -287,8 +287,6 @@ struct cobalt { u32 irq_none; u32 irq_full_fifo; - bool msi_enabled; - /* omnitek dma */ int dma_channels; int first_fifo_channel; -- cgit v1.2.3 From 48775cb73c2e26b7ca9d679875a6e570c8b8e124 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Thu, 15 Dec 2016 19:51:07 -0200 Subject: [media] pctv452e: move buffer to heap, no mutex commit 73d5c5c864f4 ("[media] pctv452e: don't do DMA on stack") caused a NULL pointer dereference which occurs when dvb_usb_init() calls dvb_usb_device_power_ctrl() for the first time, before the frontend has been attached. It also caused a recursive deadlock because tt3650_ci_msg_locked() has already locked the mutex. So, partially revert it, but move the buffer to the heap (DMA capable), not to the stack (may not be DMA capable). Instead of sharing one buffer which needs mutex protection, do a new heap allocation for each call. Fixes: commit 73d5c5c864f4 ("[media] pctv452e: don't do DMA on stack") Cc: stable@vger.kernel.org # For Kernel 4.9 Signed-off-by: Max Kellermann Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/pctv452e.c | 133 +++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 61 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/usb/dvb-usb/pctv452e.c b/drivers/media/usb/dvb-usb/pctv452e.c index 07fa08be9e99..d54ebe7e0215 100644 --- a/drivers/media/usb/dvb-usb/pctv452e.c +++ b/drivers/media/usb/dvb-usb/pctv452e.c @@ -97,14 +97,13 @@ struct pctv452e_state { u8 c; /* transaction counter, wraps around... */ u8 initialized; /* set to 1 if 0x15 has been sent */ u16 last_rc_key; - - unsigned char data[80]; }; static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len) { struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + u8 *buf; u8 id; unsigned int rlen; int ret; @@ -114,36 +113,39 @@ static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data, return -EIO; } - mutex_lock(&state->ca_mutex); + buf = kmalloc(64, GFP_KERNEL); + if (!buf) + return -ENOMEM; + id = state->c++; - state->data[0] = SYNC_BYTE_OUT; - state->data[1] = id; - state->data[2] = cmd; - state->data[3] = write_len; + buf[0] = SYNC_BYTE_OUT; + buf[1] = id; + buf[2] = cmd; + buf[3] = write_len; - memcpy(state->data + 4, data, write_len); + memcpy(buf + 4, data, write_len); rlen = (read_len > 0) ? 64 : 0; - ret = dvb_usb_generic_rw(d, state->data, 4 + write_len, - state->data, rlen, /* delay_ms */ 0); + ret = dvb_usb_generic_rw(d, buf, 4 + write_len, + buf, rlen, /* delay_ms */ 0); if (0 != ret) goto failed; ret = -EIO; - if (SYNC_BYTE_IN != state->data[0] || id != state->data[1]) + if (SYNC_BYTE_IN != buf[0] || id != buf[1]) goto failed; - memcpy(data, state->data + 4, read_len); + memcpy(data, buf + 4, read_len); - mutex_unlock(&state->ca_mutex); + kfree(buf); return 0; failed: err("CI error %d; %02X %02X %02X -> %*ph.", - ret, SYNC_BYTE_OUT, id, cmd, 3, state->data); + ret, SYNC_BYTE_OUT, id, cmd, 3, buf); - mutex_unlock(&state->ca_mutex); + kfree(buf); return ret; } @@ -410,53 +412,57 @@ static int pctv452e_i2c_msg(struct dvb_usb_device *d, u8 addr, u8 *rcv_buf, u8 rcv_len) { struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + u8 *buf; u8 id; int ret; - mutex_lock(&state->ca_mutex); + buf = kmalloc(64, GFP_KERNEL); + if (!buf) + return -ENOMEM; + id = state->c++; ret = -EINVAL; if (snd_len > 64 - 7 || rcv_len > 64 - 7) goto failed; - state->data[0] = SYNC_BYTE_OUT; - state->data[1] = id; - state->data[2] = PCTV_CMD_I2C; - state->data[3] = snd_len + 3; - state->data[4] = addr << 1; - state->data[5] = snd_len; - state->data[6] = rcv_len; + buf[0] = SYNC_BYTE_OUT; + buf[1] = id; + buf[2] = PCTV_CMD_I2C; + buf[3] = snd_len + 3; + buf[4] = addr << 1; + buf[5] = snd_len; + buf[6] = rcv_len; - memcpy(state->data + 7, snd_buf, snd_len); + memcpy(buf + 7, snd_buf, snd_len); - ret = dvb_usb_generic_rw(d, state->data, 7 + snd_len, - state->data, /* rcv_len */ 64, + ret = dvb_usb_generic_rw(d, buf, 7 + snd_len, + buf, /* rcv_len */ 64, /* delay_ms */ 0); if (ret < 0) goto failed; /* TT USB protocol error. */ ret = -EIO; - if (SYNC_BYTE_IN != state->data[0] || id != state->data[1]) + if (SYNC_BYTE_IN != buf[0] || id != buf[1]) goto failed; /* I2C device didn't respond as expected. */ ret = -EREMOTEIO; - if (state->data[5] < snd_len || state->data[6] < rcv_len) + if (buf[5] < snd_len || buf[6] < rcv_len) goto failed; - memcpy(rcv_buf, state->data + 7, rcv_len); - mutex_unlock(&state->ca_mutex); + memcpy(rcv_buf, buf + 7, rcv_len); + kfree(buf); return rcv_len; failed: err("I2C error %d; %02X %02X %02X %02X %02X -> %*ph", ret, SYNC_BYTE_OUT, id, addr << 1, snd_len, rcv_len, - 7, state->data); + 7, buf); - mutex_unlock(&state->ca_mutex); + kfree(buf); return ret; } @@ -505,7 +511,7 @@ static u32 pctv452e_i2c_func(struct i2c_adapter *adapter) static int pctv452e_power_ctrl(struct dvb_usb_device *d, int i) { struct pctv452e_state *state = (struct pctv452e_state *)d->priv; - u8 *rx; + u8 *b0, *rx; int ret; info("%s: %d\n", __func__, i); @@ -516,11 +522,12 @@ static int pctv452e_power_ctrl(struct dvb_usb_device *d, int i) if (state->initialized) return 0; - rx = kmalloc(PCTV_ANSWER_LEN, GFP_KERNEL); - if (!rx) + b0 = kmalloc(5 + PCTV_ANSWER_LEN, GFP_KERNEL); + if (!b0) return -ENOMEM; - mutex_lock(&state->ca_mutex); + rx = b0 + 5; + /* hmm where shoud this should go? */ ret = usb_set_interface(d->udev, 0, ISOC_INTERFACE_ALTERNATIVE); if (ret != 0) @@ -528,66 +535,70 @@ static int pctv452e_power_ctrl(struct dvb_usb_device *d, int i) __func__, ret); /* this is a one-time initialization, dont know where to put */ - state->data[0] = 0xaa; - state->data[1] = state->c++; - state->data[2] = PCTV_CMD_RESET; - state->data[3] = 1; - state->data[4] = 0; + b0[0] = 0xaa; + b0[1] = state->c++; + b0[2] = PCTV_CMD_RESET; + b0[3] = 1; + b0[4] = 0; /* reset board */ - ret = dvb_usb_generic_rw(d, state->data, 5, rx, PCTV_ANSWER_LEN, 0); + ret = dvb_usb_generic_rw(d, b0, 5, rx, PCTV_ANSWER_LEN, 0); if (ret) goto ret; - state->data[1] = state->c++; - state->data[4] = 1; + b0[1] = state->c++; + b0[4] = 1; /* reset board (again?) */ - ret = dvb_usb_generic_rw(d, state->data, 5, rx, PCTV_ANSWER_LEN, 0); + ret = dvb_usb_generic_rw(d, b0, 5, rx, PCTV_ANSWER_LEN, 0); if (ret) goto ret; state->initialized = 1; ret: - mutex_unlock(&state->ca_mutex); - kfree(rx); + kfree(b0); return ret; } static int pctv452e_rc_query(struct dvb_usb_device *d) { struct pctv452e_state *state = (struct pctv452e_state *)d->priv; + u8 *b, *rx; int ret, i; u8 id; - mutex_lock(&state->ca_mutex); + b = kmalloc(CMD_BUFFER_SIZE + PCTV_ANSWER_LEN, GFP_KERNEL); + if (!b) + return -ENOMEM; + + rx = b + CMD_BUFFER_SIZE; + id = state->c++; /* prepare command header */ - state->data[0] = SYNC_BYTE_OUT; - state->data[1] = id; - state->data[2] = PCTV_CMD_IR; - state->data[3] = 0; + b[0] = SYNC_BYTE_OUT; + b[1] = id; + b[2] = PCTV_CMD_IR; + b[3] = 0; /* send ir request */ - ret = dvb_usb_generic_rw(d, state->data, 4, - state->data, PCTV_ANSWER_LEN, 0); + ret = dvb_usb_generic_rw(d, b, 4, rx, PCTV_ANSWER_LEN, 0); if (ret != 0) goto ret; if (debug > 3) { - info("%s: read: %2d: %*ph: ", __func__, ret, 3, state->data); - for (i = 0; (i < state->data[3]) && ((i + 3) < PCTV_ANSWER_LEN); i++) - info(" %02x", state->data[i + 3]); + info("%s: read: %2d: %*ph: ", __func__, ret, 3, rx); + for (i = 0; (i < rx[3]) && ((i+3) < PCTV_ANSWER_LEN); i++) + info(" %02x", rx[i+3]); info("\n"); } - if ((state->data[3] == 9) && (state->data[12] & 0x01)) { + if ((rx[3] == 9) && (rx[12] & 0x01)) { /* got a "press" event */ - state->last_rc_key = RC_SCANCODE_RC5(state->data[7], state->data[6]); + state->last_rc_key = RC_SCANCODE_RC5(rx[7], rx[6]); if (debug > 2) info("%s: cmd=0x%02x sys=0x%02x\n", - __func__, state->data[6], state->data[7]); + __func__, rx[6], rx[7]); rc_keydown(d->rc_dev, RC_TYPE_RC5, state->last_rc_key, 0); } else if (state->last_rc_key) { @@ -595,7 +606,7 @@ static int pctv452e_rc_query(struct dvb_usb_device *d) state->last_rc_key = 0; } ret: - mutex_unlock(&state->ca_mutex); + kfree(b); return ret; } -- cgit v1.2.3 From aff808e813fc2d311137754165cf53d4ee6ddcc2 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 9 Dec 2016 09:47:17 -0200 Subject: [media] v4l: tvp5150: Reset device at probe time, not in get/set format handlers The tvp5150 doesn't support format setting through the subdev pad API and thus implements the set format handler as a get format operation. The single handler, tvp5150_fill_fmt(), resets the device by calling tvp5150_reset(). This causes malfunction as the device can be reset at will, possibly from userspace when the subdev userspace API is enabled. The reset call was added in commit ec2c4f3f93cb ("[media] media: tvp5150: Add mbus_fmt callbacks"), probably as an attempt to set the device to a known state before detecting the current TV standard. However, the get format handler doesn't access the hardware to get the TV standard since commit 963ddc63e20d ("[media] media: tvp5150: Add cropping support"). There is thus no need to reset the device when getting the format. However, removing the tvp5150_reset() from the get/set format handlers results in the function not being called at all if the bridge driver doesn't use the .reset() operation. The operation is nowadays abused and shouldn't be used, so shouldn't expect bridge drivers to call it. To make sure the device is properly initialize, move the reset call from the format handlers to the probe function. Cc: stable@vger.kernel.org # For Kernel 4.5 and upper Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp5150.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 3a0fe8cc64e9..a30bfcb4eec6 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -861,8 +861,6 @@ static int tvp5150_fill_fmt(struct v4l2_subdev *sd, f = &format->format; - tvp5150_reset(sd, 0); - f->width = decoder->rect.width; f->height = decoder->rect.height / 2; @@ -1524,7 +1522,6 @@ static int tvp5150_probe(struct i2c_client *c, res = core->hdl.error; goto err; } - v4l2_ctrl_handler_setup(&core->hdl); /* Default is no cropping */ core->rect.top = 0; @@ -1535,6 +1532,8 @@ static int tvp5150_probe(struct i2c_client *c, core->rect.left = 0; core->rect.width = TVP5150_H_MAX; + tvp5150_reset(sd, 0); /* Calls v4l2_ctrl_handler_setup() */ + res = v4l2_async_register_subdev(sd); if (res < 0) goto err; -- cgit v1.2.3 From b4b2de386bbb6589d81596999d4a924928dc119b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 9 Dec 2016 09:47:18 -0200 Subject: [media] v4l: tvp5150: Fix comment regarding output pin muxing The FID/GLCO/VLK/HVLK and INTREQ/GPCL/VBLK pins are muxed differently depending on whether the input is an S-Video or composite signal. The comment that explains the logic doesn't reflect the code. It appears that the comment is incorrect, as disabling the output data bus in composite mode makes no sense. Update the comment to match the code. While at it define macros for the MISC_CTL register bits, the code is too confusing with numerical values. Cc: stable@vger.kernel.org # For Kernel 4.5 and upper Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp5150.c | 24 +++++++++++++++++------- drivers/media/i2c/tvp5150_reg.h | 9 +++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index a30bfcb4eec6..8852fa8c957b 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -291,8 +291,12 @@ static void tvp5150_selmux(struct v4l2_subdev *sd) tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode); tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input); - /* Svideo should enable YCrCb output and disable GPCL output - * For Composite and TV, it should be the reverse + /* + * Setup the FID/GLCO/VLK/HVLK and INTREQ/GPCL/VBLK output signals. For + * S-Video we output the vertical lock (VLK) signal on FID/GLCO/VLK/HVLK + * and set INTREQ/GPCL/VBLK to logic 0. For composite we output the + * field indicator (FID) signal on FID/GLCO/VLK/HVLK and set + * INTREQ/GPCL/VBLK to logic 1. */ val = tvp5150_read(sd, TVP5150_MISC_CTL); if (val < 0) { @@ -301,9 +305,9 @@ static void tvp5150_selmux(struct v4l2_subdev *sd) } if (decoder->input == TVP5150_SVIDEO) - val = (val & ~0x40) | 0x10; + val = (val & ~TVP5150_MISC_CTL_GPCL) | TVP5150_MISC_CTL_HVLK; else - val = (val & ~0x10) | 0x40; + val = (val & ~TVP5150_MISC_CTL_HVLK) | TVP5150_MISC_CTL_GPCL; tvp5150_write(sd, TVP5150_MISC_CTL, val); }; @@ -455,7 +459,12 @@ static const struct i2c_reg_value tvp5150_init_enable[] = { },{ /* Automatic offset and AGC enabled */ TVP5150_ANAL_CHL_CTL, 0x15 },{ /* Activate YCrCb output 0x9 or 0xd ? */ - TVP5150_MISC_CTL, 0x6f + TVP5150_MISC_CTL, TVP5150_MISC_CTL_GPCL | + TVP5150_MISC_CTL_INTREQ_OE | + TVP5150_MISC_CTL_YCBCR_OE | + TVP5150_MISC_CTL_SYNC_OE | + TVP5150_MISC_CTL_VBLANK | + TVP5150_MISC_CTL_CLOCK_OE, },{ /* Activates video std autodetection for all standards */ TVP5150_AUTOSW_MSK, 0x0 },{ /* Default format: 0x47. For 4:2:2: 0x40 */ @@ -1050,11 +1059,12 @@ static int tvp5150_s_stream(struct v4l2_subdev *sd, int enable) { struct tvp5150 *decoder = to_tvp5150(sd); /* Output format: 8-bit ITU-R BT.656 with embedded syncs */ - int val = 0x09; + int val = TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_CLOCK_OE; /* Output format: 8-bit 4:2:2 YUV with discrete sync */ if (decoder->mbus_type == V4L2_MBUS_PARALLEL) - val = 0x0d; + val = TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_SYNC_OE + | TVP5150_MISC_CTL_CLOCK_OE; /* Initializes TVP5150 to its default values */ /* # set PCLK (27MHz) */ diff --git a/drivers/media/i2c/tvp5150_reg.h b/drivers/media/i2c/tvp5150_reg.h index 25a994944918..30a48c28d05a 100644 --- a/drivers/media/i2c/tvp5150_reg.h +++ b/drivers/media/i2c/tvp5150_reg.h @@ -9,6 +9,15 @@ #define TVP5150_ANAL_CHL_CTL 0x01 /* Analog channel controls */ #define TVP5150_OP_MODE_CTL 0x02 /* Operation mode controls */ #define TVP5150_MISC_CTL 0x03 /* Miscellaneous controls */ +#define TVP5150_MISC_CTL_VBLK_GPCL BIT(7) +#define TVP5150_MISC_CTL_GPCL BIT(6) +#define TVP5150_MISC_CTL_INTREQ_OE BIT(5) +#define TVP5150_MISC_CTL_HVLK BIT(4) +#define TVP5150_MISC_CTL_YCBCR_OE BIT(3) +#define TVP5150_MISC_CTL_SYNC_OE BIT(2) +#define TVP5150_MISC_CTL_VBLANK BIT(1) +#define TVP5150_MISC_CTL_CLOCK_OE BIT(0) + #define TVP5150_AUTOSW_MSK 0x04 /* Autoswitch mask: TVP5150A / TVP5150AM */ /* Reserved 05h */ -- cgit v1.2.3 From 79d6205a3f741c9fb89cfc47dfa0eddb1526726d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 9 Dec 2016 09:47:19 -0200 Subject: [media] v4l: tvp5150: Don't override output pinmuxing at stream on/off time The s_stream() handler incorrectly writes the whole MISC_CTL register to enable or disable the outputs, overriding the output pinmuxing configuration. Fix it to only touch the output enable bits. The CONF_SHARED_PIN register is also written by the same function, resulting in muxing the INTREQ signal instead of the VBLK/GPCL signal on the INTREQ/GPCL/VBLK pin. As the driver doesn't support interrupts this is obviously incorrect, and breaks operation on other devices. Fix it by removing the write. Cc: stable@vger.kernel.org # For Kernel 4.5 and upper Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp5150.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 8852fa8c957b..48646a7f3fb0 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1058,22 +1058,27 @@ static const struct media_entity_operations tvp5150_sd_media_ops = { static int tvp5150_s_stream(struct v4l2_subdev *sd, int enable) { struct tvp5150 *decoder = to_tvp5150(sd); - /* Output format: 8-bit ITU-R BT.656 with embedded syncs */ - int val = TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_CLOCK_OE; - - /* Output format: 8-bit 4:2:2 YUV with discrete sync */ - if (decoder->mbus_type == V4L2_MBUS_PARALLEL) - val = TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_SYNC_OE - | TVP5150_MISC_CTL_CLOCK_OE; + int val; - /* Initializes TVP5150 to its default values */ - /* # set PCLK (27MHz) */ - tvp5150_write(sd, TVP5150_CONF_SHARED_PIN, 0x00); + /* Enable or disable the video output signals. */ + val = tvp5150_read(sd, TVP5150_MISC_CTL); + if (val < 0) + return val; + + val &= ~(TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_SYNC_OE | + TVP5150_MISC_CTL_CLOCK_OE); + + if (enable) { + /* + * Enable the YCbCr and clock outputs. In discrete sync mode + * (non-BT.656) additionally enable the the sync outputs. + */ + val |= TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_CLOCK_OE; + if (decoder->mbus_type == V4L2_MBUS_PARALLEL) + val |= TVP5150_MISC_CTL_SYNC_OE; + } - if (enable) - tvp5150_write(sd, TVP5150_MISC_CTL, val); - else - tvp5150_write(sd, TVP5150_MISC_CTL, 0x00); + tvp5150_write(sd, TVP5150_MISC_CTL, val); return 0; } -- cgit v1.2.3 From 4dd19196c5539c377beaa9850fac30c18318c7a1 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 9 Dec 2016 09:36:29 -0200 Subject: [media] dvb: avoid warning in dvb_net With gcc-5 or higher on x86, we can get a bogus warning in the dvb-net code: drivers/media/dvb-core/dvb_net.c: In function 'dvb_net_ule': arch/x86/include/asm/string_32.h:78:22: error: '*((void *)&dest_addr+4)' may be used uninitialized in this function [-Werror=maybe-uninitialized] The problem here is that gcc doesn't track all of the conditions to prove it can't end up copying uninitialized data. This changes the logic around so we zero out the destination address earlier when we determine that it is not set here. This allows the compiler to figure it out. Signed-off-by: Arnd Bergmann Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_net.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers/media') diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c index bd833b0824c6..eb60cb1442f2 100644 --- a/drivers/media/dvb-core/dvb_net.c +++ b/drivers/media/dvb-core/dvb_net.c @@ -719,6 +719,9 @@ static void dvb_net_ule_check_crc(struct dvb_net_ule_handle *h, skb_copy_from_linear_data(h->priv->ule_skb, dest_addr, ETH_ALEN); skb_pull(h->priv->ule_skb, ETH_ALEN); + } else { + /* dest_addr buffer is only valid if h->priv->ule_dbit == 0 */ + eth_zero_addr(dest_addr); } /* Handle ULE Extension Headers. */ @@ -750,16 +753,8 @@ static void dvb_net_ule_check_crc(struct dvb_net_ule_handle *h, if (!h->priv->ule_bridged) { skb_push(h->priv->ule_skb, ETH_HLEN); h->ethh = (struct ethhdr *)h->priv->ule_skb->data; - if (!h->priv->ule_dbit) { - /* - * dest_addr buffer is only valid if - * h->priv->ule_dbit == 0 - */ - memcpy(h->ethh->h_dest, dest_addr, ETH_ALEN); - eth_zero_addr(h->ethh->h_source); - } else /* zeroize source and dest */ - memset(h->ethh, 0, ETH_ALEN * 2); - + memcpy(h->ethh->h_dest, dest_addr, ETH_ALEN); + eth_zero_addr(h->ethh->h_source); h->ethh->h_proto = htons(h->priv->ule_sndu_type); } /* else: skb is in correct state; nothing to do. */ -- cgit v1.2.3 From c739c0a7c3c2472d7562b8f802cdce44d2597c8b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 9 Dec 2016 09:41:29 -0200 Subject: [media] s5k4ecgx: select CRC32 helper A rare randconfig build failure shows up in this driver when the CRC32 helper is not there: drivers/media/built-in.o: In function `s5k4ecgx_s_power': s5k4ecgx.c:(.text+0x9eb4): undefined reference to `crc32_le' This adds the 'select' that all other users of this function have. Fixes: 8b99312b7214 ("[media] Add v4l2 subdev driver for S5K4ECGX sensor") Signed-off-by: Arnd Bergmann Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index b31fa6fae009..b979ea148251 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -655,6 +655,7 @@ config VIDEO_S5K6A3 config VIDEO_S5K4ECGX tristate "Samsung S5K4ECGX sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select CRC32 ---help--- This is a V4L2 sensor-level driver for Samsung S5K4ECGX 5M camera sensor with an embedded SoC image signal processor. -- cgit v1.2.3