|
@@ -590,12 +590,99 @@ static void dvb_frontend_wakeup(struct dvb_frontend *fe)
|
|
|
wake_up_interruptible(&fepriv->wait_queue);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * dvb_enable_media_tuner() - tries to enable the DVB tuner
|
|
|
+ *
|
|
|
+ * @fe: struct dvb_frontend pointer
|
|
|
+ *
|
|
|
+ * This function ensures that just one media tuner is enabled for a given
|
|
|
+ * frontend. It has two different behaviors:
|
|
|
+ * - For trivial devices with just one tuner:
|
|
|
+ * it just enables the existing tuner->fe link
|
|
|
+ * - For devices with more than one tuner:
|
|
|
+ * It is up to the driver to implement the logic that will enable one tuner
|
|
|
+ * and disable the other ones. However, if more than one tuner is enabled for
|
|
|
+ * the same frontend, it will print an error message and return -EINVAL.
|
|
|
+ *
|
|
|
+ * At return, it will return the error code returned by media_entity_setup_link,
|
|
|
+ * or 0 if everything is OK, if no tuner is linked to the frontend or if the
|
|
|
+ * mdev is NULL.
|
|
|
+ */
|
|
|
+static int dvb_enable_media_tuner(struct dvb_frontend *fe)
|
|
|
+{
|
|
|
+#ifdef CONFIG_MEDIA_CONTROLLER_DVB
|
|
|
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
|
|
|
+ struct dvb_adapter *adapter = fe->dvb;
|
|
|
+ struct media_device *mdev = adapter->mdev;
|
|
|
+ struct media_entity *entity, *source;
|
|
|
+ struct media_link *link, *found_link = NULL;
|
|
|
+ int i, ret, n_links = 0, active_links = 0;
|
|
|
+
|
|
|
+ if (!mdev)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ entity = fepriv->dvbdev->entity;
|
|
|
+ for (i = 0; i < entity->num_links; i++) {
|
|
|
+ link = &entity->links[i];
|
|
|
+ if (link->sink->entity == entity) {
|
|
|
+ found_link = link;
|
|
|
+ n_links++;
|
|
|
+ if (link->flags & MEDIA_LNK_FL_ENABLED)
|
|
|
+ active_links++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!n_links || active_links == 1 || !found_link)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If a frontend has more than one tuner linked, it is up to the driver
|
|
|
+ * to select with one will be the active one, as the frontend core can't
|
|
|
+ * guess. If the driver doesn't do that, it is a bug.
|
|
|
+ */
|
|
|
+ if (n_links > 1 && active_links != 1) {
|
|
|
+ dev_err(fe->dvb->device,
|
|
|
+ "WARNING: there are %d active links among %d tuners. This is a driver's bug!\n",
|
|
|
+ active_links, n_links);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ source = found_link->source->entity;
|
|
|
+ for (i = 0; i < source->num_links; i++) {
|
|
|
+ struct media_entity *sink;
|
|
|
+ int flags = 0;
|
|
|
+
|
|
|
+ link = &source->links[i];
|
|
|
+ sink = link->sink->entity;
|
|
|
+
|
|
|
+ if (sink == entity)
|
|
|
+ flags = MEDIA_LNK_FL_ENABLED;
|
|
|
+
|
|
|
+ ret = media_entity_setup_link(link, flags);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(fe->dvb->device,
|
|
|
+ "Couldn't change link %s->%s to %s. Error %d\n",
|
|
|
+ source->name, sink->name,
|
|
|
+ flags ? "enabled" : "disabled",
|
|
|
+ ret);
|
|
|
+ return ret;
|
|
|
+ } else
|
|
|
+ dev_dbg(fe->dvb->device,
|
|
|
+ "link %s->%s was %s\n",
|
|
|
+ source->name, sink->name,
|
|
|
+ flags ? "ENABLED" : "disabled");
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int dvb_frontend_thread(void *data)
|
|
|
{
|
|
|
struct dvb_frontend *fe = data;
|
|
|
struct dvb_frontend_private *fepriv = fe->frontend_priv;
|
|
|
fe_status_t s;
|
|
|
enum dvbfe_algo algo;
|
|
|
+ int ret;
|
|
|
|
|
|
bool re_tune = false;
|
|
|
bool semheld = false;
|
|
@@ -609,6 +696,13 @@ static int dvb_frontend_thread(void *data)
|
|
|
fepriv->wakeup = 0;
|
|
|
fepriv->reinitialise = 0;
|
|
|
|
|
|
+ ret = dvb_enable_media_tuner(fe);
|
|
|
+ if (ret) {
|
|
|
+ /* FIXME: return an error if it fails */
|
|
|
+ dev_info(fe->dvb->device,
|
|
|
+ "proceeding with FE task\n");
|
|
|
+ }
|
|
|
+
|
|
|
dvb_frontend_init(fe);
|
|
|
|
|
|
set_freezable();
|