]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
media: vivid: loopback based on 'Connected To' controls
authorHans Verkuil <hverkuil-cisco@xs4all.nl>
Mon, 24 Jun 2024 09:53:04 +0000 (12:53 +0300)
committerHans Verkuil <hverkuil-cisco@xs4all.nl>
Fri, 28 Jun 2024 06:00:29 +0000 (08:00 +0200)
Instead of using hardwired video loopback limited to a single vivid
instance, use the new 'Connected To' controls to only loopback if an
HDMI or S-Video input is connected to another output, which can be
in another vivid instance. Effectively this emulates connecting and
disconnecting an HDMI/S-Video cable.

The Loop Video control is dropped since it has now been replaced by
the new 'Connected To' controls. The Display Present has also been
dropped since it no longer fits.

Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Co-developed-by: Dorcas Anono Litunya <anonolitunya@gmail.com>
Signed-off-by: Dorcas Anono Litunya <anonolitunya@gmail.com>
12 files changed:
Documentation/admin-guide/media/vivid.rst
drivers/media/test-drivers/vivid/vivid-cec.c
drivers/media/test-drivers/vivid/vivid-core.c
drivers/media/test-drivers/vivid/vivid-core.h
drivers/media/test-drivers/vivid/vivid-ctrls.c
drivers/media/test-drivers/vivid/vivid-kthread-cap.c
drivers/media/test-drivers/vivid/vivid-vbi-cap.c
drivers/media/test-drivers/vivid/vivid-vid-cap.c
drivers/media/test-drivers/vivid/vivid-vid-cap.h
drivers/media/test-drivers/vivid/vivid-vid-common.c
drivers/media/test-drivers/vivid/vivid-vid-common.h
drivers/media/test-drivers/vivid/vivid-vid-out.c

index 31fb86030249c2c8dc9998bff6a992153b9dbcc8..29481241d7cbc658d797f8acf70d66fcdfb20171 100644 (file)
@@ -1020,11 +1020,6 @@ Digital Video Controls
        affects the reported colorspace since DVI_D outputs will always use
        sRGB.
 
-- Display Present:
-
-       sets the presence of a "display" on the HDMI output. This affects
-       the tx_edid_present, tx_hotplug and tx_rxsense controls.
-
 
 FM Radio Receiver Controls
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
index 1f7469ff04d5a745ab46091952a73338a91468c0..941ef42632142595257bba5d5836674ec41b32b4 100644 (file)
@@ -23,7 +23,7 @@ struct xfer_on_bus {
 static bool find_dest_adap(struct vivid_dev *dev,
                           struct cec_adapter *adap, u8 dest)
 {
-       unsigned int i;
+       unsigned int i, j;
 
        if (dest >= 0xf)
                return false;
@@ -33,12 +33,29 @@ static bool find_dest_adap(struct vivid_dev *dev,
            cec_has_log_addr(dev->cec_rx_adap, dest))
                return true;
 
-       for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) {
-               if (adap == dev->cec_tx_adap[i])
+       for (i = 0, j = 0; i < dev->num_inputs; i++) {
+               unsigned int menu_idx =
+                       dev->input_is_connected_to_output[i];
+
+               if (dev->input_type[i] != HDMI)
+                       continue;
+               j++;
+               if (menu_idx < FIXED_MENU_ITEMS)
+                       continue;
+
+               struct vivid_dev *dev_tx = vivid_ctrl_hdmi_to_output_instance[menu_idx];
+               unsigned int output = vivid_ctrl_hdmi_to_output_index[menu_idx];
+
+               if (!dev_tx)
                        continue;
-               if (!dev->cec_tx_adap[i]->is_configured)
+
+               unsigned int hdmi_output = dev_tx->output_to_iface_index[output];
+
+               if (adap == dev_tx->cec_tx_adap[hdmi_output])
+                       continue;
+               if (!dev_tx->cec_tx_adap[hdmi_output]->is_configured)
                        continue;
-               if (cec_has_log_addr(dev->cec_tx_adap[i], dest))
+               if (cec_has_log_addr(dev_tx->cec_tx_adap[hdmi_output], dest))
                        return true;
        }
        return false;
@@ -96,7 +113,7 @@ static void adjust_sfts(struct vivid_dev *dev)
 int vivid_cec_bus_thread(void *_dev)
 {
        u32 last_sft;
-       unsigned int i;
+       unsigned int i, j;
        unsigned int dest;
        ktime_t start, end;
        s64 delta_us, retry_us;
@@ -193,9 +210,27 @@ int vivid_cec_bus_thread(void *_dev)
                if (first_status == CEC_TX_STATUS_OK) {
                        if (xfers_on_bus[first_idx].adap != dev->cec_rx_adap)
                                cec_received_msg(dev->cec_rx_adap, &first_msg);
-                       for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++)
-                               if (xfers_on_bus[first_idx].adap != dev->cec_tx_adap[i])
-                                       cec_received_msg(dev->cec_tx_adap[i], &first_msg);
+                       for (i = 0, j = 0; i < dev->num_inputs; i++) {
+                               unsigned int menu_idx =
+                                       dev->input_is_connected_to_output[i];
+
+                               if (dev->input_type[i] != HDMI)
+                                       continue;
+                               j++;
+                               if (menu_idx < FIXED_MENU_ITEMS)
+                                       continue;
+
+                               struct vivid_dev *dev_tx = vivid_ctrl_hdmi_to_output_instance[menu_idx];
+                               unsigned int output = vivid_ctrl_hdmi_to_output_index[menu_idx];
+
+                               if (!dev_tx)
+                                       continue;
+
+                               unsigned int hdmi_output = dev_tx->output_to_iface_index[output];
+
+                               if (xfers_on_bus[first_idx].adap != dev_tx->cec_tx_adap[hdmi_output])
+                                       cec_received_msg(dev_tx->cec_tx_adap[hdmi_output], &first_msg);
+                       }
                }
                end = ktime_get();
                /*
@@ -242,21 +277,36 @@ static int vivid_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
                                   u32 signal_free_time, struct cec_msg *msg)
 {
        struct vivid_dev *dev = cec_get_drvdata(adap);
+       struct vivid_dev *dev_rx = dev;
        u8 idx = cec_msg_initiator(msg);
+       u8 output = 0;
 
-       spin_lock(&dev->cec_xfers_slock);
-       dev->xfers[idx].adap = adap;
-       memcpy(dev->xfers[idx].msg, msg->msg, CEC_MAX_MSG_SIZE);
-       dev->xfers[idx].len = msg->len;
-       dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_RETRY;
+       if (dev->cec_rx_adap != adap) {
+               int i;
+
+               for (i = 0; i < dev->num_hdmi_outputs; i++)
+                       if (dev->cec_tx_adap[i] == adap)
+                               break;
+               if (i == dev->num_hdmi_outputs)
+                       return -ENONET;
+               output = dev->hdmi_index_to_output_index[i];
+               dev_rx = dev->output_to_input_instance[output];
+               if (!dev_rx)
+                       return -ENONET;
+       }
+       spin_lock(&dev_rx->cec_xfers_slock);
+       dev_rx->xfers[idx].adap = adap;
+       memcpy(dev_rx->xfers[idx].msg, msg->msg, CEC_MAX_MSG_SIZE);
+       dev_rx->xfers[idx].len = msg->len;
+       dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_RETRY;
        if (signal_free_time > CEC_SIGNAL_FREE_TIME_RETRY) {
-               if (idx == dev->last_initiator)
-                       dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER;
+               if (idx == dev_rx->last_initiator)
+                       dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER;
                else
-                       dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
+                       dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR;
        }
-       spin_unlock(&dev->cec_xfers_slock);
-       wake_up_interruptible(&dev->kthread_waitq_cec);
+       spin_unlock(&dev_rx->cec_xfers_slock);
+       wake_up_interruptible(&dev_rx->kthread_waitq_cec);
 
        return 0;
 }
index 7eb8a0a5d4d35efca908c83a13461e474f018fb8..00e0d08af3573bdd7687cd00baff76fed37340cb 100644 (file)
@@ -190,6 +190,7 @@ struct vivid_dev *vivid_devs[VIVID_MAX_DEVS];
 DEFINE_SPINLOCK(hdmi_output_skip_mask_lock);
 struct workqueue_struct *update_hdmi_ctrls_workqueue;
 u64 hdmi_to_output_menu_skip_mask;
+u64 hdmi_input_update_outputs_mask;
 
 struct vivid_dev *vivid_ctrl_hdmi_to_output_instance[MAX_MENU_ITEMS];
 unsigned int vivid_ctrl_hdmi_to_output_index[MAX_MENU_ITEMS];
@@ -985,7 +986,6 @@ static int vivid_detect_feature_set(struct vivid_dev *dev, int inst,
        for (i = 0; i < dev->num_outputs; i++) {
                dev->output_type[i] = ((output_types[inst] >> i) & 1) ? HDMI : SVID;
                dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++;
-               dev->display_present[i] = true;
        }
        dev->has_audio_outputs = out_type_counter[SVID];
        if (out_type_counter[HDMI] == 16) {
@@ -1418,7 +1418,6 @@ static int vivid_create_queues(struct vivid_dev *dev)
 
 static int vivid_create_devnodes(struct platform_device *pdev,
                                 struct vivid_dev *dev, int inst,
-                                unsigned int cec_tx_bus_cnt,
                                 v4l2_std_id tvnorms_cap,
                                 v4l2_std_id tvnorms_out,
                                 unsigned in_type_counter[4],
@@ -1462,7 +1461,7 @@ static int vivid_create_devnodes(struct platform_device *pdev,
                                return ret;
                        }
                        cec_s_phys_addr(dev->cec_rx_adap, 0, false);
-                       v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI input 0\n",
+                       v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI input\n",
                                  dev_name(&dev->cec_rx_adap->devnode.dev));
                }
 #endif
@@ -1505,10 +1504,10 @@ static int vivid_create_devnodes(struct platform_device *pdev,
 #endif
 
 #ifdef CONFIG_VIDEO_VIVID_CEC
-               for (i = 0; i < cec_tx_bus_cnt; i++) {
+               for (i = 0; i < dev->num_hdmi_outputs; i++) {
                        ret = cec_register_adapter(dev->cec_tx_adap[i], &pdev->dev);
                        if (ret < 0) {
-                               for (; i < cec_tx_bus_cnt; i++) {
+                               for (; i >= 0; i--) {
                                        cec_delete_adapter(dev->cec_tx_adap[i]);
                                        dev->cec_tx_adap[i] = NULL;
                                }
@@ -1516,10 +1515,6 @@ static int vivid_create_devnodes(struct platform_device *pdev,
                        }
                        v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI output %d\n",
                                  dev_name(&dev->cec_tx_adap[i]->devnode.dev), i);
-                       if (i < out_type_counter[HDMI])
-                               cec_s_phys_addr(dev->cec_tx_adap[i], (i + 1) << 12, false);
-                       else
-                               cec_s_phys_addr(dev->cec_tx_adap[i], 0x1000, false);
                }
 #endif
 
@@ -1762,11 +1757,16 @@ static int vivid_create_devnodes(struct platform_device *pdev,
 static void update_hdmi_ctrls_work_handler(struct work_struct *work)
 {
        u64 skip_mask;
+       u64 update_mask;
 
        spin_lock(&hdmi_output_skip_mask_lock);
        skip_mask = hdmi_to_output_menu_skip_mask;
+       update_mask = hdmi_input_update_outputs_mask;
+       hdmi_input_update_outputs_mask = 0;
        spin_unlock(&hdmi_output_skip_mask_lock);
        for (int i = 0; i < n_devs && vivid_devs[i]; i++) {
+               if (update_mask & (1 << i))
+                       vivid_update_connected_outputs(vivid_devs[i]);
                for (int j = 0; j < vivid_devs[i]->num_hdmi_inputs; j++) {
                        struct v4l2_ctrl *c = vivid_devs[i]->ctrl_hdmi_to_output[j];
 
@@ -1808,7 +1808,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
        struct vivid_dev *dev;
        unsigned node_type = node_types[inst];
        v4l2_std_id tvnorms_cap = 0, tvnorms_out = 0;
-       unsigned int cec_tx_bus_cnt = 0;
        int ret;
        int i;
 
@@ -1937,8 +1936,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
                goto unreg_dev;
 
        /* enable/disable interface specific controls */
-       if (dev->num_outputs && dev->output_type[0] != HDMI)
-               v4l2_ctrl_activate(dev->ctrl_display_present, false);
        if (dev->num_inputs && dev->input_type[0] != HDMI) {
                v4l2_ctrl_activate(dev->ctrl_dv_timings_signal_mode, false);
                v4l2_ctrl_activate(dev->ctrl_dv_timings, false);
@@ -1994,27 +1991,27 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
        }
 
        if (dev->has_vid_out) {
-               for (i = 0; i < dev->num_outputs; i++) {
+               int j;
+
+               for (i = j = 0; i < dev->num_outputs; i++) {
                        struct cec_adapter *adap;
 
                        if (dev->output_type[i] != HDMI)
                                continue;
 
-                       dev->cec_output2bus_map[i] = cec_tx_bus_cnt;
-                       adap = vivid_cec_alloc_adap(dev, cec_tx_bus_cnt, true);
+                       adap = vivid_cec_alloc_adap(dev, j, true);
                        ret = PTR_ERR_OR_ZERO(adap);
                        if (ret < 0) {
-                               for (i = 0; i < dev->num_outputs; i++)
-                                       cec_delete_adapter(dev->cec_tx_adap[i]);
+                               while (j--)
+                                       cec_delete_adapter(dev->cec_tx_adap[j]);
                                goto unreg_dev;
                        }
 
-                       dev->cec_tx_adap[cec_tx_bus_cnt] = adap;
-                       cec_tx_bus_cnt++;
+                       dev->cec_tx_adap[j++] = adap;
                }
        }
 
-       if (dev->cec_rx_adap || cec_tx_bus_cnt) {
+       if (dev->cec_rx_adap || dev->num_hdmi_outputs) {
                init_waitqueue_head(&dev->kthread_waitq_cec);
                dev->kthread_cec = kthread_run(vivid_cec_bus_thread, dev,
                                               "vivid_cec-%s", dev->v4l2_dev.name);
@@ -2040,7 +2037,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
        v4l2_ctrl_handler_setup(&dev->ctrl_hdl_touch_cap);
 
        /* finally start creating the device nodes */
-       ret = vivid_create_devnodes(pdev, dev, inst, cec_tx_bus_cnt,
+       ret = vivid_create_devnodes(pdev, dev, inst,
                                    tvnorms_cap, tvnorms_out,
                                    in_type_counter, out_type_counter);
        if (ret)
index b0c5b7369de577006dbf7702bda6324c3a5c58eb..cc18a3bc6dc0b2cf5dddead26e2cdcb0d392f26c 100644 (file)
@@ -83,7 +83,15 @@ extern unsigned vivid_debug;
 extern char *vivid_ctrl_hdmi_to_output_strings[1 + MAX_MENU_ITEMS];
 /* Menu control skip mask of all HDMI outputs that are in use */
 extern u64 hdmi_to_output_menu_skip_mask;
-/* Spinlock for access to hdmi_to_output_menu_skip_mask */
+/*
+ * Bitmask of which vivid instances need to update any connected
+ * HDMI outputs.
+ */
+extern u64 hdmi_input_update_outputs_mask;
+/*
+ * Spinlock for access to hdmi_to_output_menu_skip_mask and
+ * hdmi_input_update_outputs_mask.
+ */
 extern spinlock_t hdmi_output_skip_mask_lock;
 /*
  * Workqueue that updates the menu controls whenever the HDMI menu skip mask
@@ -283,7 +291,6 @@ struct vivid_dev {
        bool                            has_tv_tuner;
        bool                            has_touch_cap;
 
-       bool                            can_loop_video;
        /* Output index (0-MAX_OUTPUTS) to vivid instance of connected input */
        struct vivid_dev                *output_to_input_instance[MAX_OUTPUTS];
        /* Output index (0-MAX_OUTPUTS) to input index (0-MAX_INPUTS) of connected input */
@@ -336,7 +343,6 @@ struct vivid_dev {
                struct v4l2_ctrl        *ctrl_dv_timings_signal_mode;
                struct v4l2_ctrl        *ctrl_dv_timings;
        };
-       struct v4l2_ctrl                *ctrl_display_present;
        struct v4l2_ctrl                *ctrl_has_crop_cap;
        struct v4l2_ctrl                *ctrl_has_compose_cap;
        struct v4l2_ctrl                *ctrl_has_scaler_cap;
@@ -463,7 +469,6 @@ struct vivid_dev {
        u8                              *scaled_line;
        u8                              *blended_line;
        unsigned                        cur_scaled_line;
-       bool                            display_present[MAX_OUTPUTS];
 
        /* Output Overlay */
        void                            *fb_vbase_out;
@@ -643,11 +648,10 @@ struct vivid_dev {
 
        /* CEC */
        struct cec_adapter              *cec_rx_adap;
-       struct cec_adapter              *cec_tx_adap[MAX_OUTPUTS];
-       u8                              cec_output2bus_map[MAX_OUTPUTS];
+       struct cec_adapter              *cec_tx_adap[MAX_HDMI_OUTPUTS];
        struct task_struct              *kthread_cec;
        wait_queue_head_t               kthread_waitq_cec;
-       struct vivid_cec_xfer   xfers[MAX_OUTPUTS];
+       struct vivid_cec_xfer           xfers[MAX_OUTPUTS];
        spinlock_t                      cec_xfers_slock; /* read and write cec messages */
        u32                             cec_sft; /* bus signal free time, in bit periods */
        u8                              last_initiator;
index f713f10349b70fb5e82aa45d90fc367bb262c982..8bb38bc7b8cc27439cab353ef5cc3e562e97dbbd 100644 (file)
@@ -18,7 +18,6 @@
 #include "vivid-radio-common.h"
 #include "vivid-osd.h"
 #include "vivid-ctrls.h"
-#include "vivid-cec.h"
 
 #define VIVID_CID_CUSTOM_BASE          (V4L2_CID_USER_BASE | 0xf000)
 #define VIVID_CID_BUTTON               (VIVID_CID_CUSTOM_BASE + 0)
 #define VIVID_CID_HAS_CROP_OUT         (VIVID_CID_VIVID_BASE + 34)
 #define VIVID_CID_HAS_COMPOSE_OUT      (VIVID_CID_VIVID_BASE + 35)
 #define VIVID_CID_HAS_SCALER_OUT       (VIVID_CID_VIVID_BASE + 36)
-#define VIVID_CID_LOOP_VIDEO           (VIVID_CID_VIVID_BASE + 37)
 #define VIVID_CID_SEQ_WRAP             (VIVID_CID_VIVID_BASE + 38)
 #define VIVID_CID_TIME_WRAP            (VIVID_CID_VIVID_BASE + 39)
 #define VIVID_CID_MAX_EDID_BLOCKS      (VIVID_CID_VIVID_BASE + 40)
 #define VIVID_CID_PERCENTAGE_FILL      (VIVID_CID_VIVID_BASE + 41)
 #define VIVID_CID_REDUCED_FPS          (VIVID_CID_VIVID_BASE + 42)
 #define VIVID_CID_HSV_ENC              (VIVID_CID_VIVID_BASE + 43)
-#define VIVID_CID_DISPLAY_PRESENT      (VIVID_CID_VIVID_BASE + 44)
 
 #define VIVID_CID_STD_SIGNAL_MODE      (VIVID_CID_VIVID_BASE + 60)
 #define VIVID_CID_STANDARD             (VIVID_CID_VIVID_BASE + 61)
@@ -445,6 +442,33 @@ static const struct v4l2_ctrl_ops vivid_user_vid_ctrl_ops = {
 
 /* Video Capture Controls */
 
+static void vivid_update_power_present(struct vivid_dev *dev)
+{
+       unsigned int i, j;
+
+       dev->power_present = 0;
+       for (i = 0, j = 0;
+            i < ARRAY_SIZE(dev->dv_timings_signal_mode); i++) {
+               if (dev->input_type[i] != HDMI)
+                       continue;
+               /*
+                * If connected to TPG or HDMI output, and the signal
+                * mode is not NO_SIGNAL, then there is power present.
+                */
+               if (dev->input_is_connected_to_output[i] != 1 &&
+                   dev->dv_timings_signal_mode[i] != NO_SIGNAL)
+                       dev->power_present |= (1 << j);
+               j++;
+       }
+
+       __v4l2_ctrl_s_ctrl(dev->ctrl_rx_power_present,
+                          dev->power_present);
+
+       v4l2_ctrl_activate(dev->ctrl_dv_timings,
+                          dev->dv_timings_signal_mode[dev->input] ==
+                          SELECTED_DV_TIMINGS);
+}
+
 static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl)
 {
        static const u32 colorspaces[] = {
@@ -459,7 +483,7 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl)
                V4L2_COLORSPACE_470_SYSTEM_BG,
        };
        struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_cap);
-       unsigned int i, j;
+       unsigned int i;
        struct vivid_dev *output_inst = NULL;
        int index = 0;
        int hdmi_index, svid_index;
@@ -579,25 +603,9 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl)
                dev->dv_timings_signal_mode[dev->input] =
                        dev->ctrl_dv_timings_signal_mode->val;
                dev->query_dv_timings[dev->input] = dev->ctrl_dv_timings->val;
-
-               dev->power_present = 0;
-               for (i = 0, j = 0;
-                    i < ARRAY_SIZE(dev->dv_timings_signal_mode);
-                    i++)
-                       if (dev->input_type[i] == HDMI) {
-                               if (dev->dv_timings_signal_mode[i] != NO_SIGNAL)
-                                       dev->power_present |= (1 << j);
-                               j++;
-                       }
-               __v4l2_ctrl_s_ctrl(dev->ctrl_rx_power_present,
-                                  dev->power_present);
-
-               v4l2_ctrl_activate(dev->ctrl_dv_timings,
-                       dev->dv_timings_signal_mode[dev->input] ==
-                               SELECTED_DV_TIMINGS);
-
+               vivid_update_power_present(dev);
                vivid_update_quality(dev);
-               vivid_send_source_change(dev, HDMI);
+               vivid_send_input_source_change(dev, dev->input);
                break;
        case VIVID_CID_DV_TIMINGS_ASPECT_RATIO:
                dev->dv_timings_aspect_ratio[dev->input] = ctrl->val;
@@ -621,8 +629,11 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl)
                input_index = dev->hdmi_index_to_input_index[hdmi_index];
                dev->input_is_connected_to_output[input_index] = ctrl->val;
 
-               if (output_inst)
+               if (output_inst) {
                        output_inst->output_to_input_instance[index] = NULL;
+                       vivid_update_outputs(output_inst);
+                       cec_phys_addr_invalidate(output_inst->cec_tx_adap[index]);
+               }
                if (ctrl->val >= FIXED_MENU_ITEMS) {
                        output_inst = vivid_ctrl_hdmi_to_output_instance[ctrl->val];
                        index = vivid_ctrl_hdmi_to_output_index[ctrl->val];
@@ -635,8 +646,14 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl)
                if (ctrl->val >= FIXED_MENU_ITEMS)
                        hdmi_to_output_menu_skip_mask |= 1ULL << ctrl->val;
                spin_unlock(&hdmi_output_skip_mask_lock);
+               vivid_update_power_present(dev);
+               vivid_update_quality(dev);
+               vivid_send_input_source_change(dev, dev->hdmi_index_to_input_index[hdmi_index]);
                if (ctrl->val < FIXED_MENU_ITEMS && ctrl->cur.val < FIXED_MENU_ITEMS)
                        break;
+               spin_lock(&hdmi_output_skip_mask_lock);
+               hdmi_input_update_outputs_mask |= 1 << dev->inst;
+               spin_unlock(&hdmi_output_skip_mask_lock);
                queue_work(update_hdmi_ctrls_workqueue, &dev->update_hdmi_ctrl_work);
                break;
        case VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(0) ... VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(15):
@@ -660,6 +677,8 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl)
                if (ctrl->val >= FIXED_MENU_ITEMS)
                        svid_to_output_menu_skip_mask |= 1ULL << ctrl->val;
                spin_unlock(&svid_output_skip_mask_lock);
+               vivid_update_quality(dev);
+               vivid_send_input_source_change(dev, dev->svid_index_to_input_index[svid_index]);
                if (ctrl->val < FIXED_MENU_ITEMS && ctrl->cur.val < FIXED_MENU_ITEMS)
                        break;
                queue_work(update_svid_ctrls_workqueue, &dev->update_svid_ctrl_work);
@@ -1026,37 +1045,6 @@ static const struct v4l2_ctrl_config vivid_ctrl_limited_rgb_range = {
 };
 
 
-/* Video Loop Control */
-
-static int vivid_loop_cap_s_ctrl(struct v4l2_ctrl *ctrl)
-{
-       struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_loop_cap);
-
-       switch (ctrl->id) {
-       case VIVID_CID_LOOP_VIDEO:
-               dev->loop_video = ctrl->val;
-               vivid_update_quality(dev);
-               vivid_send_source_change(dev, SVID);
-               vivid_send_source_change(dev, HDMI);
-               break;
-       }
-       return 0;
-}
-
-static const struct v4l2_ctrl_ops vivid_loop_cap_ctrl_ops = {
-       .s_ctrl = vivid_loop_cap_s_ctrl,
-};
-
-static const struct v4l2_ctrl_config vivid_ctrl_loop_video = {
-       .ops = &vivid_loop_cap_ctrl_ops,
-       .id = VIVID_CID_LOOP_VIDEO,
-       .name = "Loop Video",
-       .type = V4L2_CTRL_TYPE_BOOLEAN,
-       .max = 1,
-       .step = 1,
-};
-
-
 /* VBI Capture Control */
 
 static int vivid_vbi_cap_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -1091,8 +1079,6 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl)
 {
        struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_out);
        struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt;
-       u32 display_present = 0;
-       unsigned int i, j, bus_idx;
 
        switch (ctrl->id) {
        case VIVID_CID_HAS_CROP_OUT:
@@ -1123,39 +1109,11 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl)
                                        V4L2_QUANTIZATION_LIM_RANGE :
                                        V4L2_QUANTIZATION_DEFAULT;
                }
-               if (dev->loop_video)
-                       vivid_send_source_change(dev, HDMI);
-               break;
-       case VIVID_CID_DISPLAY_PRESENT:
-               if (dev->output_type[dev->output] != HDMI)
-                       break;
+               if (vivid_output_is_connected_to(dev)) {
+                       struct vivid_dev *dev_rx = vivid_output_is_connected_to(dev);
 
-               dev->display_present[dev->output] = ctrl->val;
-               for (i = 0, j = 0; i < dev->num_outputs; i++)
-                       if (dev->output_type[i] == HDMI)
-                               display_present |=
-                                       dev->display_present[i] << j++;
-
-               __v4l2_ctrl_s_ctrl(dev->ctrl_tx_rxsense, display_present);
-
-               if (dev->edid_blocks) {
-                       __v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present,
-                                          display_present);
-                       __v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug,
-                                          display_present);
+                       vivid_send_source_change(dev_rx, HDMI);
                }
-
-               bus_idx = dev->cec_output2bus_map[dev->output];
-               if (!dev->cec_tx_adap[bus_idx])
-                       break;
-
-               if (ctrl->val && dev->edid_blocks)
-                       cec_s_phys_addr(dev->cec_tx_adap[bus_idx],
-                                       dev->cec_tx_adap[bus_idx]->phys_addr,
-                                       false);
-               else
-                       cec_phys_addr_invalidate(dev->cec_tx_adap[bus_idx]);
-
                break;
        }
        return 0;
@@ -1195,16 +1153,6 @@ static const struct v4l2_ctrl_config vivid_ctrl_has_scaler_out = {
        .step = 1,
 };
 
-static const struct v4l2_ctrl_config vivid_ctrl_display_present = {
-       .ops = &vivid_vid_out_ctrl_ops,
-       .id = VIVID_CID_DISPLAY_PRESENT,
-       .name = "Display Present",
-       .type = V4L2_CTRL_TYPE_BOOLEAN,
-       .max = 1,
-       .def = 1,
-       .step = 1,
-};
-
 /* Streaming Controls */
 
 static int vivid_streaming_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -1914,21 +1862,13 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
                dev->ctrl_tx_mode = v4l2_ctrl_new_std_menu(hdl_vid_out, NULL,
                        V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI,
                        0, V4L2_DV_TX_MODE_HDMI);
-               dev->ctrl_display_present = v4l2_ctrl_new_custom(hdl_vid_out,
-                       &vivid_ctrl_display_present, NULL);
-               dev->ctrl_tx_hotplug = v4l2_ctrl_new_std(hdl_vid_out,
-                       NULL, V4L2_CID_DV_TX_HOTPLUG, 0, hdmi_output_mask,
-                       0, hdmi_output_mask);
-               dev->ctrl_tx_rxsense = v4l2_ctrl_new_std(hdl_vid_out,
-                       NULL, V4L2_CID_DV_TX_RXSENSE, 0, hdmi_output_mask,
-                       0, hdmi_output_mask);
-               dev->ctrl_tx_edid_present = v4l2_ctrl_new_std(hdl_vid_out,
-                       NULL, V4L2_CID_DV_TX_EDID_PRESENT, 0, hdmi_output_mask,
-                       0, hdmi_output_mask);
+               dev->ctrl_tx_hotplug = v4l2_ctrl_new_std(hdl_vid_out, NULL,
+                       V4L2_CID_DV_TX_HOTPLUG, 0, hdmi_output_mask, 0, 0);
+               dev->ctrl_tx_rxsense = v4l2_ctrl_new_std(hdl_vid_out, NULL,
+                       V4L2_CID_DV_TX_RXSENSE, 0, hdmi_output_mask, 0, 0);
+               dev->ctrl_tx_edid_present = v4l2_ctrl_new_std(hdl_vid_out, NULL,
+                       V4L2_CID_DV_TX_EDID_PRESENT, 0, hdmi_output_mask, 0, 0);
        }
-       if ((dev->has_vid_cap && dev->has_vid_out) ||
-           (dev->has_vbi_cap && dev->has_vbi_out))
-               v4l2_ctrl_new_custom(hdl_loop_cap, &vivid_ctrl_loop_video, NULL);
 
        if (dev->has_fb)
                v4l2_ctrl_new_custom(hdl_fb, &vivid_ctrl_clear_fb, NULL);
index 42048727d7ff359bdf7769cfba549b72f47b6283..669bd96da4c7956788b52efec6c6e2371c7493d5 100644 (file)
@@ -142,7 +142,7 @@ static void scale_line(const u8 *src, u8 *dst, unsigned srcw, unsigned dstw, uns
  * (loop_vid_overlay). Finally calculate the part of the capture buffer that
  * will receive that overlaid video.
  */
-static void vivid_precalc_copy_rects(struct vivid_dev *dev)
+static void vivid_precalc_copy_rects(struct vivid_dev *dev, struct vivid_dev *out_dev)
 {
        /* Framebuffer rectangle */
        struct v4l2_rect r_fb = {
@@ -150,16 +150,16 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev)
        };
        /* Overlay window rectangle in framebuffer coordinates */
        struct v4l2_rect r_overlay = {
-               dev->overlay_out_left, dev->overlay_out_top,
-               dev->compose_out.width, dev->compose_out.height
+               out_dev->overlay_out_left, out_dev->overlay_out_top,
+               out_dev->compose_out.width, out_dev->compose_out.height
        };
 
-       v4l2_rect_intersect(&dev->loop_vid_copy, &dev->crop_cap, &dev->compose_out);
+       v4l2_rect_intersect(&dev->loop_vid_copy, &dev->crop_cap, &out_dev->compose_out);
 
        dev->loop_vid_out = dev->loop_vid_copy;
-       v4l2_rect_scale(&dev->loop_vid_out, &dev->compose_out, &dev->crop_out);
-       dev->loop_vid_out.left += dev->crop_out.left;
-       dev->loop_vid_out.top += dev->crop_out.top;
+       v4l2_rect_scale(&dev->loop_vid_out, &out_dev->compose_out, &out_dev->crop_out);
+       dev->loop_vid_out.left += out_dev->crop_out.left;
+       dev->loop_vid_out.top += out_dev->crop_out.top;
 
        dev->loop_vid_cap = dev->loop_vid_copy;
        v4l2_rect_scale(&dev->loop_vid_cap, &dev->crop_cap, &dev->compose_cap);
@@ -176,15 +176,15 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev)
        v4l2_rect_intersect(&r_overlay, &r_fb, &r_overlay);
 
        /* shift r_overlay to the same origin as compose_out */
-       r_overlay.left += dev->compose_out.left - dev->overlay_out_left;
-       r_overlay.top += dev->compose_out.top - dev->overlay_out_top;
+       r_overlay.left += out_dev->compose_out.left - out_dev->overlay_out_left;
+       r_overlay.top += out_dev->compose_out.top - out_dev->overlay_out_top;
 
        v4l2_rect_intersect(&dev->loop_vid_overlay, &r_overlay, &dev->loop_vid_copy);
        dev->loop_fb_copy = dev->loop_vid_overlay;
 
        /* shift dev->loop_fb_copy back again to the fb origin */
-       dev->loop_fb_copy.left -= dev->compose_out.left - dev->overlay_out_left;
-       dev->loop_fb_copy.top -= dev->compose_out.top - dev->overlay_out_top;
+       dev->loop_fb_copy.left -= out_dev->compose_out.left - out_dev->overlay_out_left;
+       dev->loop_fb_copy.top -= out_dev->compose_out.top - out_dev->overlay_out_top;
 
        dev->loop_vid_overlay_cap = dev->loop_vid_overlay;
        v4l2_rect_scale(&dev->loop_vid_overlay_cap, &dev->crop_cap, &dev->compose_cap);
@@ -213,24 +213,25 @@ static void *plane_vaddr(struct tpg_data *tpg, struct vivid_buffer *buf,
        return vbuf;
 }
 
-static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned p,
-               u8 *vcapbuf, struct vivid_buffer *vid_cap_buf)
+static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev,
+                                               struct vivid_dev *out_dev, unsigned p,
+                                               u8 *vcapbuf, struct vivid_buffer *vid_cap_buf)
 {
        bool blank = dev->must_blank[vid_cap_buf->vb.vb2_buf.index];
        struct tpg_data *tpg = &dev->tpg;
        struct vivid_buffer *vid_out_buf = NULL;
-       unsigned vdiv = dev->fmt_out->vdownsampling[p];
+       unsigned vdiv = out_dev->fmt_out->vdownsampling[p];
        unsigned twopixsize = tpg_g_twopixelsize(tpg, p);
        unsigned img_width = tpg_hdiv(tpg, p, dev->compose_cap.width);
        unsigned img_height = dev->compose_cap.height;
        unsigned stride_cap = tpg->bytesperline[p];
-       unsigned stride_out = dev->bytesperline_out[p];
+       unsigned stride_out = out_dev->bytesperline_out[p];
        unsigned stride_osd = dev->display_byte_stride;
        unsigned hmax = (img_height * tpg->perc_fill) / 100;
        u8 *voutbuf;
        u8 *vosdbuf = NULL;
        unsigned y;
-       bool blend = dev->fbuf_out_flags;
+       bool blend = out_dev->fbuf_out_flags;
        /* Coarse scaling with Bresenham */
        unsigned vid_out_int_part;
        unsigned vid_out_fract_part;
@@ -247,8 +248,8 @@ static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned
        vid_out_int_part = dev->loop_vid_out.height / dev->loop_vid_cap.height;
        vid_out_fract_part = dev->loop_vid_out.height % dev->loop_vid_cap.height;
 
-       if (!list_empty(&dev->vid_out_active))
-               vid_out_buf = list_entry(dev->vid_out_active.next,
+       if (!list_empty(&out_dev->vid_out_active))
+               vid_out_buf = list_entry(out_dev->vid_out_active.next,
                                         struct vivid_buffer, list);
        if (vid_out_buf == NULL)
                return -ENODATA;
@@ -256,8 +257,8 @@ static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned
        vid_cap_buf->vb.field = vid_out_buf->vb.field;
 
        voutbuf = plane_vaddr(tpg, vid_out_buf, p,
-                             dev->bytesperline_out, dev->fmt_out_rect.height);
-       if (p < dev->fmt_out->buffers)
+                             out_dev->bytesperline_out, out_dev->fmt_out_rect.height);
+       if (p < out_dev->fmt_out->buffers)
                voutbuf += vid_out_buf->vb.vb2_buf.planes[p].data_offset;
        voutbuf += tpg_hdiv(tpg, p, dev->loop_vid_out.left) +
                (dev->loop_vid_out.top / vdiv) * stride_out;
@@ -274,7 +275,7 @@ static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned
                return 0;
        }
 
-       if (dev->overlay_out_enabled &&
+       if (out_dev->overlay_out_enabled &&
            dev->loop_vid_overlay.width && dev->loop_vid_overlay.height) {
                vosdbuf = dev->video_vbase;
                vosdbuf += (dev->loop_fb_copy.left * twopixsize) / 2 +
@@ -385,6 +386,7 @@ update_vid_out_y:
 
 static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
 {
+       struct vivid_dev *out_dev = NULL;
        struct tpg_data *tpg = &dev->tpg;
        unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_cap) ? 2 : 1;
        unsigned line_height = 16 / factor;
@@ -396,14 +398,6 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
        unsigned ms;
        char str[100];
        s32 gain;
-       bool is_loop = false;
-
-       if (dev->loop_video && dev->can_loop_video &&
-               ((vivid_is_svid_cap(dev) &&
-               !VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) ||
-               (vivid_is_hdmi_cap(dev) &&
-               !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode[dev->input]))))
-               is_loop = true;
 
        buf->vb.sequence = dev->vid_cap_seq_count;
        v4l2_ctrl_s_ctrl(dev->ro_int32, buf->vb.sequence & 0xff);
@@ -428,7 +422,34 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
                    dev->field_cap == V4L2_FIELD_ALTERNATE);
        tpg_s_perc_fill_blank(tpg, dev->must_blank[buf->vb.vb2_buf.index]);
 
-       vivid_precalc_copy_rects(dev);
+       if (vivid_vid_can_loop(dev) &&
+           ((vivid_is_svid_cap(dev) &&
+           !VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) ||
+           (vivid_is_hdmi_cap(dev) &&
+           !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode[dev->input])))) {
+               out_dev = vivid_input_is_connected_to(dev);
+               /*
+                * If the vivid instance of the output device is different
+                * from the vivid instance of this input device, then we
+                * must take care to properly serialize the output device to
+                * prevent that the buffer we are copying from is being freed.
+                *
+                * If the output device is part of the same instance, then the
+                * lock is already taken and there is no need to take the mutex.
+                *
+                * The problem with taking the mutex is that you can get
+                * deadlocked if instance A locks instance B and vice versa.
+                * It is not really worth trying to be very smart about this,
+                * so just try to take the lock, and if you can't, then just
+                * set out_dev to NULL and you will end up with a single frame
+                * of Noise (the default test pattern in this case).
+                */
+               if (out_dev && dev != out_dev && !mutex_trylock(&out_dev->mutex))
+                       out_dev = NULL;
+       }
+
+       if (out_dev)
+               vivid_precalc_copy_rects(dev, out_dev);
 
        for (p = 0; p < tpg_g_planes(tpg); p++) {
                void *vbuf = plane_vaddr(tpg, buf, p,
@@ -445,10 +466,13 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf)
                        vbuf += dev->fmt_cap->data_offset[p];
                }
                tpg_calc_text_basep(tpg, basep, p, vbuf);
-               if (!is_loop || vivid_copy_buffer(dev, p, vbuf, buf))
+               if (!out_dev || vivid_copy_buffer(dev, out_dev, p, vbuf, buf))
                        tpg_fill_plane_buffer(tpg, vivid_get_std_cap(dev),
                                        p, vbuf);
        }
+       if (out_dev && dev != out_dev)
+               mutex_unlock(&out_dev->mutex);
+
        dev->must_blank[buf->vb.vb2_buf.index] = false;
 
        /* Updates stream time, only update at the start of a new frame. */
index 95387d57eb938050f9007f308b01663862d3a6ab..99138f63585c7146cb3289c4e9ac8180ed66966d 100644 (file)
@@ -14,6 +14,7 @@
 #include "vivid-kthread-cap.h"
 #include "vivid-vbi-cap.h"
 #include "vivid-vbi-gen.h"
+#include "vivid-vid-common.h"
 
 static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr)
 {
@@ -23,7 +24,7 @@ static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr)
        vivid_vbi_gen_sliced(vbi_gen, is_60hz, seqnr);
 
        if (!is_60hz) {
-               if (dev->loop_video) {
+               if (vivid_vid_can_loop(dev)) {
                        if (dev->vbi_out_have_wss) {
                                vbi_gen->data[12].data[0] = dev->vbi_out_wss[0];
                                vbi_gen->data[12].data[1] = dev->vbi_out_wss[1];
@@ -47,7 +48,7 @@ static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr)
                                break;
                        }
                }
-       } else if (dev->loop_video && is_60hz) {
+       } else if (vivid_vid_can_loop(dev) && is_60hz) {
                if (dev->vbi_out_have_cc[0]) {
                        vbi_gen->data[0].data[0] = dev->vbi_out_cc[0][0];
                        vbi_gen->data[0].data[1] = dev->vbi_out_cc[0][1];
index b41a7c87aaa7173206074c2e4d6200ef5a9cd5e3..69620e0a35a02fb210529a1d652abf915b4445af 100644 (file)
@@ -211,9 +211,6 @@ static int vid_cap_start_streaming(struct vb2_queue *vq, unsigned count)
        unsigned i;
        int err;
 
-       if (vb2_is_streaming(&dev->vb_vid_out_q))
-               dev->can_loop_video = vivid_vid_can_loop(dev);
-
        dev->vid_cap_seq_count = 0;
        dprintk(dev, 1, "%s\n", __func__);
        for (i = 0; i < VIDEO_MAX_FRAME; i++)
@@ -243,7 +240,6 @@ static void vid_cap_stop_streaming(struct vb2_queue *vq)
 
        dprintk(dev, 1, "%s\n", __func__);
        vivid_stop_generating_vid_cap(dev, &dev->vid_cap_streaming);
-       dev->can_loop_video = false;
 }
 
 static void vid_cap_buf_request_complete(struct vb2_buffer *vb)
@@ -274,7 +270,7 @@ void vivid_update_quality(struct vivid_dev *dev)
 {
        unsigned freq_modulus;
 
-       if (dev->loop_video && (vivid_is_svid_cap(dev) || vivid_is_hdmi_cap(dev))) {
+       if (dev->input_is_connected_to_output[dev->input]) {
                /*
                 * The 'noise' will only be replaced by the actual video
                 * if the output video matches the input video settings.
@@ -488,35 +484,35 @@ static enum v4l2_field vivid_field_cap(struct vivid_dev *dev, enum v4l2_field fi
 
 static unsigned vivid_colorspace_cap(struct vivid_dev *dev)
 {
-       if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
+       if (!vivid_input_is_connected_to(dev))
                return tpg_g_colorspace(&dev->tpg);
        return dev->colorspace_out;
 }
 
 static unsigned vivid_xfer_func_cap(struct vivid_dev *dev)
 {
-       if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
+       if (!vivid_input_is_connected_to(dev))
                return tpg_g_xfer_func(&dev->tpg);
        return dev->xfer_func_out;
 }
 
 static unsigned vivid_ycbcr_enc_cap(struct vivid_dev *dev)
 {
-       if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
+       if (!vivid_input_is_connected_to(dev))
                return tpg_g_ycbcr_enc(&dev->tpg);
        return dev->ycbcr_enc_out;
 }
 
 static unsigned int vivid_hsv_enc_cap(struct vivid_dev *dev)
 {
-       if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
+       if (!vivid_input_is_connected_to(dev))
                return tpg_g_hsv_enc(&dev->tpg);
        return dev->hsv_enc_out;
 }
 
 static unsigned vivid_quantization_cap(struct vivid_dev *dev)
 {
-       if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
+       if (!vivid_input_is_connected_to(dev))
                return tpg_g_quantization(&dev->tpg);
        return dev->quantization_out;
 }
@@ -1538,13 +1534,65 @@ int vidioc_query_dv_timings(struct file *file, void *_fh,
        return 0;
 }
 
+void vivid_update_outputs(struct vivid_dev *dev)
+{
+       u32 edid_present = 0;
+
+       if (!dev || !dev->num_outputs)
+               return;
+       for (unsigned int i = 0, j = 0; i < dev->num_outputs; i++) {
+               if (dev->output_type[i] != HDMI)
+                       continue;
+
+               struct vivid_dev *dev_rx = dev->output_to_input_instance[i];
+
+               if (dev_rx && dev_rx->edid_blocks)
+                       edid_present |= 1 << j;
+               j++;
+       }
+       v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, edid_present);
+       v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, edid_present);
+       v4l2_ctrl_s_ctrl(dev->ctrl_tx_rxsense, edid_present);
+}
+
+void vivid_update_connected_outputs(struct vivid_dev *dev)
+{
+       u16 phys_addr = cec_get_edid_phys_addr(dev->edid, dev->edid_blocks * 128, NULL);
+
+       for (unsigned int i = 0, j = 0; i < dev->num_inputs; i++) {
+               unsigned int menu_idx =
+                       dev->input_is_connected_to_output[i];
+
+               if (dev->input_type[i] != HDMI)
+                       continue;
+               j++;
+               if (menu_idx < FIXED_MENU_ITEMS)
+                       continue;
+
+               struct vivid_dev *dev_tx = vivid_ctrl_hdmi_to_output_instance[menu_idx];
+               unsigned int output = vivid_ctrl_hdmi_to_output_index[menu_idx];
+
+               if (!dev_tx)
+                       continue;
+
+               unsigned int hdmi_output = dev_tx->output_to_iface_index[output];
+
+               vivid_update_outputs(dev_tx);
+               if (dev->edid_blocks) {
+                       cec_s_phys_addr(dev_tx->cec_tx_adap[hdmi_output],
+                                       v4l2_phys_addr_for_input(phys_addr, j),
+                                       false);
+               } else {
+                       cec_phys_addr_invalidate(dev_tx->cec_tx_adap[hdmi_output]);
+               }
+       }
+}
+
 int vidioc_s_edid(struct file *file, void *_fh,
                         struct v4l2_edid *edid)
 {
        struct vivid_dev *dev = video_drvdata(file);
        u16 phys_addr;
-       u32 display_present = 0;
-       unsigned int i, j;
        int ret;
 
        memset(edid->reserved, 0, sizeof(edid->reserved));
@@ -1553,13 +1601,11 @@ int vidioc_s_edid(struct file *file, void *_fh,
        if (dev->input_type[edid->pad] != HDMI || edid->start_block)
                return -EINVAL;
        if (edid->blocks == 0) {
+               if (vb2_is_busy(&dev->vb_vid_cap_q))
+                       return -EBUSY;
                dev->edid_blocks = 0;
-               if (dev->num_outputs) {
-                       v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, 0);
-                       v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, 0);
-               }
-               phys_addr = CEC_PHYS_ADDR_INVALID;
-               goto set_phys_addr;
+               vivid_update_connected_outputs(dev);
+               return 0;
        }
        if (edid->blocks > dev->edid_max_blocks) {
                edid->blocks = dev->edid_max_blocks;
@@ -1576,26 +1622,7 @@ int vidioc_s_edid(struct file *file, void *_fh,
        dev->edid_blocks = edid->blocks;
        memcpy(dev->edid, edid->edid, edid->blocks * 128);
 
-       for (i = 0, j = 0; i < dev->num_outputs; i++)
-               if (dev->output_type[i] == HDMI)
-                       display_present |=
-                               dev->display_present[i] << j++;
-
-       if (dev->num_outputs) {
-               v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, display_present);
-               v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, display_present);
-       }
-
-set_phys_addr:
-       /* TODO: a proper hotplug detect cycle should be emulated here */
-       cec_s_phys_addr(dev->cec_rx_adap, phys_addr, false);
-
-       for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++)
-               cec_s_phys_addr(dev->cec_tx_adap[i],
-                               dev->display_present[i] ?
-                               v4l2_phys_addr_for_input(phys_addr, i + 1) :
-                               CEC_PHYS_ADDR_INVALID,
-                               false);
+       vivid_update_connected_outputs(dev);
        return 0;
 }
 
index 949768652d38222f3dea13592ffa34a52ef6e7ea..7a8daf0af2ca5a21353cbde83d309d1dd803fa2c 100644 (file)
@@ -10,6 +10,8 @@
 
 void vivid_update_quality(struct vivid_dev *dev);
 void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls);
+void vivid_update_outputs(struct vivid_dev *dev);
+void vivid_update_connected_outputs(struct vivid_dev *dev);
 enum tpg_video_aspect vivid_get_video_aspect(const struct vivid_dev *dev);
 
 extern const v4l2_std_id vivid_standard[];
index a3e8eb90f11b1acfd911677b6b0b817e5ac8aef0..df7678db67fbfad42139050aee27726a1e55d061 100644 (file)
@@ -769,14 +769,55 @@ const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat)
        return NULL;
 }
 
+struct vivid_dev *vivid_output_is_connected_to(struct vivid_dev *dev)
+{
+       struct vivid_dev *input_inst = dev->output_to_input_instance[dev->output];
+
+       if (!input_inst)
+               return NULL;
+       if (input_inst->input != dev->output_to_input_index[dev->output])
+               return NULL;
+       return input_inst;
+}
+
+struct vivid_dev *vivid_input_is_connected_to(struct vivid_dev *dev)
+{
+       s32 connected_output = dev->input_is_connected_to_output[dev->input];
+
+       if (connected_output < FIXED_MENU_ITEMS)
+               return NULL;
+       struct vivid_dev *output_inst = NULL;
+
+       if (vivid_is_hdmi_cap(dev)) {
+               output_inst = vivid_ctrl_hdmi_to_output_instance[connected_output];
+               if (vivid_ctrl_hdmi_to_output_index[connected_output] != output_inst->output)
+                       return NULL;
+               return output_inst;
+       } else if (vivid_is_svid_cap(dev)) {
+               output_inst = vivid_ctrl_svid_to_output_instance[connected_output];
+               if (vivid_ctrl_svid_to_output_index[connected_output] != output_inst->output)
+                       return NULL;
+               return output_inst;
+       } else {
+               return NULL;
+       }
+       return output_inst;
+}
+
 bool vivid_vid_can_loop(struct vivid_dev *dev)
 {
-       if (dev->src_rect.width != dev->sink_rect.width ||
-           dev->src_rect.height != dev->sink_rect.height)
+       struct vivid_dev *output_inst = vivid_input_is_connected_to(dev);
+
+       if (!output_inst)
                return false;
-       if (dev->fmt_cap->fourcc != dev->fmt_out->fourcc)
+       if (!vb2_is_streaming(&output_inst->vb_vid_out_q))
                return false;
-       if (dev->field_cap != dev->field_out)
+       if (dev->src_rect.width != output_inst->sink_rect.width ||
+           dev->src_rect.height != output_inst->sink_rect.height)
+               return false;
+       if (dev->fmt_cap->fourcc != output_inst->fmt_out->fourcc)
+               return false;
+       if (dev->field_cap != output_inst->field_out)
                return false;
        /*
         * While this can be supported, it is just too much work
@@ -785,34 +826,34 @@ bool vivid_vid_can_loop(struct vivid_dev *dev)
        if (dev->field_cap == V4L2_FIELD_SEQ_TB ||
            dev->field_cap == V4L2_FIELD_SEQ_BT)
                return false;
-       if (vivid_is_svid_cap(dev) && vivid_is_svid_out(dev)) {
-               if (!(dev->std_cap[dev->input] & V4L2_STD_525_60) !=
-                   !(dev->std_out & V4L2_STD_525_60))
-                       return false;
-               return true;
-       }
-       if (vivid_is_hdmi_cap(dev) && vivid_is_hdmi_out(dev))
+       if (vivid_is_hdmi_cap(dev))
                return true;
-       return false;
+       if (!(dev->std_cap[dev->input] & V4L2_STD_525_60) !=
+           !(output_inst->std_out & V4L2_STD_525_60))
+               return false;
+       return true;
 }
 
-void vivid_send_source_change(struct vivid_dev *dev, unsigned type)
+void vivid_send_input_source_change(struct vivid_dev *dev, unsigned int input_index)
 {
        struct v4l2_event ev = {
                .type = V4L2_EVENT_SOURCE_CHANGE,
                .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
        };
-       unsigned i;
+       ev.id = input_index;
 
-       for (i = 0; i < dev->num_inputs; i++) {
-               ev.id = i;
-               if (dev->input_type[i] == type) {
-                       if (video_is_registered(&dev->vid_cap_dev) && dev->has_vid_cap)
-                               v4l2_event_queue(&dev->vid_cap_dev, &ev);
-                       if (video_is_registered(&dev->vbi_cap_dev) && dev->has_vbi_cap)
-                               v4l2_event_queue(&dev->vbi_cap_dev, &ev);
-               }
-       }
+       if (video_is_registered(&dev->vid_cap_dev) && dev->has_vid_cap)
+               v4l2_event_queue(&dev->vid_cap_dev, &ev);
+       if (dev->input_type[input_index] == TV || dev->input_type[input_index] == SVID)
+               if (video_is_registered(&dev->vbi_cap_dev) && dev->has_vbi_cap)
+                       v4l2_event_queue(&dev->vbi_cap_dev, &ev);
+}
+
+void vivid_send_source_change(struct vivid_dev *dev, unsigned int type)
+{
+       for (int i = 0; i < dev->num_inputs; i++)
+               if (dev->input_type[i] == type)
+                       vivid_send_input_source_change(dev, i);
 }
 
 /*
@@ -1036,6 +1077,7 @@ int vidioc_g_edid(struct file *file, void *_fh,
                         struct v4l2_edid *edid)
 {
        struct vivid_dev *dev = video_drvdata(file);
+       struct vivid_dev *dev_rx = dev;
        struct video_device *vdev = video_devdata(file);
        struct cec_adapter *adap;
        unsigned int loc;
@@ -1048,31 +1090,33 @@ int vidioc_g_edid(struct file *file, void *_fh,
                        return -EINVAL;
                adap = dev->cec_rx_adap;
        } else {
-               unsigned int bus_idx;
-
                if (edid->pad >= dev->num_outputs)
                        return -EINVAL;
                if (dev->output_type[edid->pad] != HDMI)
                        return -EINVAL;
-               if (!dev->display_present[edid->pad])
+               dev_rx = dev->output_to_input_instance[edid->pad];
+               if (!dev_rx)
                        return -ENODATA;
-               bus_idx = dev->cec_output2bus_map[edid->pad];
-               adap = dev->cec_tx_adap[bus_idx];
+
+               unsigned int hdmi_output = dev->output_to_iface_index[edid->pad];
+
+               adap = dev->cec_tx_adap[hdmi_output];
        }
        if (edid->start_block == 0 && edid->blocks == 0) {
-               edid->blocks = dev->edid_blocks;
+               edid->blocks = dev_rx->edid_blocks;
                return 0;
        }
-       if (dev->edid_blocks == 0)
+       if (dev_rx->edid_blocks == 0)
                return -ENODATA;
-       if (edid->start_block >= dev->edid_blocks)
+       if (edid->start_block >= dev_rx->edid_blocks)
                return -EINVAL;
-       if (edid->blocks > dev->edid_blocks - edid->start_block)
-               edid->blocks = dev->edid_blocks - edid->start_block;
+       if (edid->blocks > dev_rx->edid_blocks - edid->start_block)
+               edid->blocks = dev_rx->edid_blocks - edid->start_block;
 
-       memcpy(edid->edid, dev->edid + edid->start_block * 128, edid->blocks * 128);
+       memcpy(edid->edid, dev_rx->edid + edid->start_block * 128, edid->blocks * 128);
 
-       loc = cec_get_edid_spa_location(dev->edid, dev->edid_blocks * 128);
+       loc = cec_get_edid_spa_location(dev_rx->edid,
+                                       dev_rx->edid_blocks * 128);
        if (vdev->vfl_dir == VFL_DIR_TX && adap && loc &&
            loc >= edid->start_block * 128 &&
            loc < (edid->start_block + edid->blocks) * 128) {
index d908d97252830923c2322f2639fd87309ebefe2d..c49ac85abaede7f4832c82ef5251db51acb3aed2 100644 (file)
@@ -22,8 +22,11 @@ extern const struct v4l2_dv_timings_cap vivid_dv_timings_cap;
 
 const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat);
 
+struct vivid_dev *vivid_input_is_connected_to(struct vivid_dev *dev);
+struct vivid_dev *vivid_output_is_connected_to(struct vivid_dev *dev);
 bool vivid_vid_can_loop(struct vivid_dev *dev);
-void vivid_send_source_change(struct vivid_dev *dev, unsigned type);
+void vivid_send_source_change(struct vivid_dev *dev, unsigned int type);
+void vivid_send_input_source_change(struct vivid_dev *dev, unsigned int input_index);
 
 int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r);
 
index 5e47bdcb9923ecef7301c7e6f192b700ea8ec17e..60327f3612af40f7ebd6dedf8e23a113d8d6ac43 100644 (file)
@@ -157,9 +157,6 @@ static int vid_out_start_streaming(struct vb2_queue *vq, unsigned count)
        struct vivid_dev *dev = vb2_get_drv_priv(vq);
        int err;
 
-       if (vb2_is_streaming(&dev->vb_vid_cap_q))
-               dev->can_loop_video = vivid_vid_can_loop(dev);
-
        dev->vid_out_seq_count = 0;
        dprintk(dev, 1, "%s\n", __func__);
        if (dev->start_streaming_error) {
@@ -187,7 +184,6 @@ static void vid_out_stop_streaming(struct vb2_queue *vq)
 
        dprintk(dev, 1, "%s\n", __func__);
        vivid_stop_generating_vid_out(dev, &dev->vid_out_streaming);
-       dev->can_loop_video = false;
 }
 
 static void vid_out_buf_request_complete(struct vb2_buffer *vb)
@@ -564,9 +560,11 @@ set_colorspace:
        dev->xfer_func_out = mp->xfer_func;
        dev->ycbcr_enc_out = mp->ycbcr_enc;
        dev->quantization_out = mp->quantization;
-       if (dev->loop_video) {
-               vivid_send_source_change(dev, SVID);
-               vivid_send_source_change(dev, HDMI);
+       struct vivid_dev *in_dev = vivid_output_is_connected_to(dev);
+
+       if (in_dev) {
+               vivid_send_source_change(in_dev, SVID);
+               vivid_send_source_change(in_dev, HDMI);
        }
        return 0;
 }
@@ -1016,11 +1014,6 @@ int vidioc_s_output(struct file *file, void *priv, unsigned o)
        dev->meta_out_dev.tvnorms = dev->vid_out_dev.tvnorms;
        vivid_update_format_out(dev);
 
-       v4l2_ctrl_activate(dev->ctrl_display_present, vivid_is_hdmi_out(dev));
-       if (vivid_is_hdmi_out(dev))
-               v4l2_ctrl_s_ctrl(dev->ctrl_display_present,
-                                dev->display_present[dev->output]);
-
        return 0;
 }