linux/drivers/media/platform/vivid/vivid-cec.c
<<
>>
Prefs
   1/*
   2 * vivid-cec.c - A Virtual Video Test Driver, cec emulation
   3 *
   4 * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
   5 *
   6 * This program is free software; you may redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; version 2 of the License.
   9 *
  10 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  11 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  12 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  13 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  14 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  15 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  16 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  17 * SOFTWARE.
  18 */
  19
  20#include <media/cec.h>
  21
  22#include "vivid-core.h"
  23#include "vivid-cec.h"
  24
  25void vivid_cec_bus_free_work(struct vivid_dev *dev)
  26{
  27        spin_lock(&dev->cec_slock);
  28        while (!list_empty(&dev->cec_work_list)) {
  29                struct vivid_cec_work *cw =
  30                        list_first_entry(&dev->cec_work_list,
  31                                         struct vivid_cec_work, list);
  32
  33                spin_unlock(&dev->cec_slock);
  34                cancel_delayed_work_sync(&cw->work);
  35                spin_lock(&dev->cec_slock);
  36                list_del(&cw->list);
  37                cec_transmit_attempt_done(cw->adap, CEC_TX_STATUS_LOW_DRIVE);
  38                kfree(cw);
  39        }
  40        spin_unlock(&dev->cec_slock);
  41}
  42
  43static bool vivid_cec_find_dest_adap(struct vivid_dev *dev,
  44                                     struct cec_adapter *adap, u8 dest)
  45{
  46        unsigned int i;
  47
  48        if (dest >= 0xf)
  49                return false;
  50
  51        if (adap != dev->cec_rx_adap && dev->cec_rx_adap &&
  52            dev->cec_rx_adap->is_configured &&
  53            cec_has_log_addr(dev->cec_rx_adap, dest))
  54                return true;
  55
  56        for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) {
  57                if (adap == dev->cec_tx_adap[i])
  58                        continue;
  59                if (!dev->cec_tx_adap[i]->is_configured)
  60                        continue;
  61                if (cec_has_log_addr(dev->cec_tx_adap[i], dest))
  62                        return true;
  63        }
  64        return false;
  65}
  66
  67static void vivid_cec_xfer_done_worker(struct work_struct *work)
  68{
  69        struct vivid_cec_work *cw =
  70                container_of(work, struct vivid_cec_work, work.work);
  71        struct vivid_dev *dev = cw->dev;
  72        struct cec_adapter *adap = cw->adap;
  73        u8 dest = cec_msg_destination(&cw->msg);
  74        bool valid_dest;
  75        unsigned int i;
  76
  77        valid_dest = cec_msg_is_broadcast(&cw->msg);
  78        if (!valid_dest)
  79                valid_dest = vivid_cec_find_dest_adap(dev, adap, dest);
  80
  81        cw->tx_status = valid_dest ? CEC_TX_STATUS_OK : CEC_TX_STATUS_NACK;
  82        spin_lock(&dev->cec_slock);
  83        dev->cec_xfer_time_jiffies = 0;
  84        dev->cec_xfer_start_jiffies = 0;
  85        list_del(&cw->list);
  86        spin_unlock(&dev->cec_slock);
  87        cec_transmit_attempt_done(cw->adap, cw->tx_status);
  88
  89        /* Broadcast message */
  90        if (adap != dev->cec_rx_adap)
  91                cec_received_msg(dev->cec_rx_adap, &cw->msg);
  92        for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++)
  93                if (adap != dev->cec_tx_adap[i])
  94                        cec_received_msg(dev->cec_tx_adap[i], &cw->msg);
  95        kfree(cw);
  96}
  97
  98static void vivid_cec_xfer_try_worker(struct work_struct *work)
  99{
 100        struct vivid_cec_work *cw =
 101                container_of(work, struct vivid_cec_work, work.work);
 102        struct vivid_dev *dev = cw->dev;
 103
 104        spin_lock(&dev->cec_slock);
 105        if (dev->cec_xfer_time_jiffies) {
 106                list_del(&cw->list);
 107                spin_unlock(&dev->cec_slock);
 108                cec_transmit_attempt_done(cw->adap, CEC_TX_STATUS_ARB_LOST);
 109                kfree(cw);
 110        } else {
 111                INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_done_worker);
 112                dev->cec_xfer_start_jiffies = jiffies;
 113                dev->cec_xfer_time_jiffies = usecs_to_jiffies(cw->usecs);
 114                spin_unlock(&dev->cec_slock);
 115                schedule_delayed_work(&cw->work, dev->cec_xfer_time_jiffies);
 116        }
 117}
 118
 119static int vivid_cec_adap_enable(struct cec_adapter *adap, bool enable)
 120{
 121        return 0;
 122}
 123
 124static int vivid_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
 125{
 126        return 0;
 127}
 128
 129/*
 130 * One data bit takes 2400 us, each byte needs 10 bits so that's 24000 us
 131 * per byte.
 132 */
 133#define USECS_PER_BYTE 24000
 134
 135static int vivid_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
 136                                   u32 signal_free_time, struct cec_msg *msg)
 137{
 138        struct vivid_dev *dev = cec_get_drvdata(adap);
 139        struct vivid_cec_work *cw = kzalloc(sizeof(*cw), GFP_KERNEL);
 140        long delta_jiffies = 0;
 141
 142        if (cw == NULL)
 143                return -ENOMEM;
 144        cw->dev = dev;
 145        cw->adap = adap;
 146        cw->usecs = CEC_FREE_TIME_TO_USEC(signal_free_time) +
 147                    msg->len * USECS_PER_BYTE;
 148        cw->msg = *msg;
 149
 150        spin_lock(&dev->cec_slock);
 151        list_add(&cw->list, &dev->cec_work_list);
 152        if (dev->cec_xfer_time_jiffies == 0) {
 153                INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_done_worker);
 154                dev->cec_xfer_start_jiffies = jiffies;
 155                dev->cec_xfer_time_jiffies = usecs_to_jiffies(cw->usecs);
 156                delta_jiffies = dev->cec_xfer_time_jiffies;
 157        } else {
 158                INIT_DELAYED_WORK(&cw->work, vivid_cec_xfer_try_worker);
 159                delta_jiffies = dev->cec_xfer_start_jiffies +
 160                        dev->cec_xfer_time_jiffies - jiffies;
 161        }
 162        spin_unlock(&dev->cec_slock);
 163        schedule_delayed_work(&cw->work, delta_jiffies < 0 ? 0 : delta_jiffies);
 164        return 0;
 165}
 166
 167static int vivid_received(struct cec_adapter *adap, struct cec_msg *msg)
 168{
 169        struct vivid_dev *dev = cec_get_drvdata(adap);
 170        struct cec_msg reply;
 171        u8 dest = cec_msg_destination(msg);
 172        u8 disp_ctl;
 173        char osd[14];
 174
 175        if (cec_msg_is_broadcast(msg))
 176                dest = adap->log_addrs.log_addr[0];
 177        cec_msg_init(&reply, dest, cec_msg_initiator(msg));
 178
 179        switch (cec_msg_opcode(msg)) {
 180        case CEC_MSG_SET_OSD_STRING:
 181                if (!cec_is_sink(adap))
 182                        return -ENOMSG;
 183                cec_ops_set_osd_string(msg, &disp_ctl, osd);
 184                switch (disp_ctl) {
 185                case CEC_OP_DISP_CTL_DEFAULT:
 186                        strcpy(dev->osd, osd);
 187                        dev->osd_jiffies = jiffies;
 188                        break;
 189                case CEC_OP_DISP_CTL_UNTIL_CLEARED:
 190                        strcpy(dev->osd, osd);
 191                        dev->osd_jiffies = 0;
 192                        break;
 193                case CEC_OP_DISP_CTL_CLEAR:
 194                        dev->osd[0] = 0;
 195                        dev->osd_jiffies = 0;
 196                        break;
 197                default:
 198                        cec_msg_feature_abort(&reply, cec_msg_opcode(msg),
 199                                              CEC_OP_ABORT_INVALID_OP);
 200                        cec_transmit_msg(adap, &reply, false);
 201                        break;
 202                }
 203                break;
 204        default:
 205                return -ENOMSG;
 206        }
 207        return 0;
 208}
 209
 210static const struct cec_adap_ops vivid_cec_adap_ops = {
 211        .adap_enable = vivid_cec_adap_enable,
 212        .adap_log_addr = vivid_cec_adap_log_addr,
 213        .adap_transmit = vivid_cec_adap_transmit,
 214        .received = vivid_received,
 215};
 216
 217struct cec_adapter *vivid_cec_alloc_adap(struct vivid_dev *dev,
 218                                         unsigned int idx,
 219                                         bool is_source)
 220{
 221        char name[sizeof(dev->vid_out_dev.name) + 2];
 222        u32 caps = CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
 223                CEC_CAP_PASSTHROUGH | CEC_CAP_RC | CEC_CAP_MONITOR_ALL;
 224
 225        snprintf(name, sizeof(name), "%s%d",
 226                 is_source ? dev->vid_out_dev.name : dev->vid_cap_dev.name,
 227                 idx);
 228        return cec_allocate_adapter(&vivid_cec_adap_ops, dev,
 229                name, caps, 1);
 230}
 231