diff options
Diffstat (limited to 'drivers/media')
346 files changed, 11902 insertions, 3341 deletions
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 4efe8014445e..d5d5d28d0b36 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -751,6 +751,9 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, struct cec_data *data; bool is_raw = msg_is_raw(msg); + if (adap->devnode.unregistered) + return -ENODEV; + msg->rx_ts = 0; msg->tx_ts = 0; msg->rx_status = 0; @@ -1049,6 +1052,9 @@ void cec_received_msg_ts(struct cec_adapter *adap, if (WARN_ON(!msg->len || msg->len > CEC_MAX_MSG_SIZE)) return; + if (adap->devnode.unregistered) + return; + /* * Some CEC adapters will receive the messages that they transmitted. * This test filters out those messages by checking if we are the @@ -1199,7 +1205,7 @@ void cec_received_msg_ts(struct cec_adapter *adap, /* Cancel the pending timeout work */ if (!cancel_delayed_work(&data->work)) { mutex_unlock(&adap->lock); - flush_scheduled_work(); + cancel_delayed_work_sync(&data->work); mutex_lock(&adap->lock); } /* @@ -1928,7 +1934,7 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, */ if (!adap->passthrough && from_unregistered) return 0; - /* Fall through */ + fallthrough; case CEC_MSG_GIVE_DEVICE_VENDOR_ID: case CEC_MSG_GIVE_FEATURES: case CEC_MSG_GIVE_PHYSICAL_ADDR: diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index c599cd94dd62..ece236291f35 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -309,7 +309,7 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, adap->rc->allowed_protocols = RC_PROTO_BIT_CEC; adap->rc->priv = adap; adap->rc->map_name = RC_MAP_CEC; - adap->rc->timeout = MS_TO_NS(550); + adap->rc->timeout = MS_TO_US(550); #endif return adap; } @@ -359,27 +359,16 @@ int cec_register_adapter(struct cec_adapter *adap, if (!top_cec_dir) return 0; - adap->cec_dir = debugfs_create_dir(dev_name(&adap->devnode.dev), top_cec_dir); - if (IS_ERR_OR_NULL(adap->cec_dir)) { - pr_warn("cec-%s: Failed to create debugfs dir\n", adap->name); - return 0; - } - adap->status_file = debugfs_create_devm_seqfile(&adap->devnode.dev, - "status", adap->cec_dir, cec_adap_status); - if (IS_ERR_OR_NULL(adap->status_file)) { - pr_warn("cec-%s: Failed to create status file\n", adap->name); - debugfs_remove_recursive(adap->cec_dir); - adap->cec_dir = NULL; - return 0; - } + adap->cec_dir = debugfs_create_dir(dev_name(&adap->devnode.dev), + top_cec_dir); + + debugfs_create_devm_seqfile(&adap->devnode.dev, "status", adap->cec_dir, + cec_adap_status); + if (!adap->ops->error_inj_show || !adap->ops->error_inj_parse_line) return 0; - adap->error_inj_file = debugfs_create_file("error-inj", 0644, - adap->cec_dir, adap, - &cec_error_inj_fops); - if (IS_ERR_OR_NULL(adap->error_inj_file)) - pr_warn("cec-%s: Failed to create error-inj file\n", - adap->name); + debugfs_create_file("error-inj", 0644, adap->cec_dir, adap, + &cec_error_inj_fops); #endif return 0; } @@ -407,9 +396,9 @@ void cec_delete_adapter(struct cec_adapter *adap) { if (IS_ERR_OR_NULL(adap)) return; - kthread_stop(adap->kthread); if (adap->kthread_config) kthread_stop(adap->kthread_config); + kthread_stop(adap->kthread); if (adap->ops->adap_free) adap->ops->adap_free(adap); #ifdef CONFIG_MEDIA_CEC_RC diff --git a/drivers/media/cec/core/cec-pin.c b/drivers/media/cec/core/cec-pin.c index 660fe111f540..f006bd8eec63 100644 --- a/drivers/media/cec/core/cec-pin.c +++ b/drivers/media/cec/core/cec-pin.c @@ -417,7 +417,7 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts) wake_up_interruptible(&pin->kthread_waitq); break; } - /* fall through */ + fallthrough; case CEC_ST_TX_DATA_BIT_0_HIGH: case CEC_ST_TX_DATA_BIT_0_HIGH_SHORT: case CEC_ST_TX_DATA_BIT_0_HIGH_LONG: @@ -445,7 +445,7 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts) wake_up_interruptible(&pin->kthread_waitq); break; } - /* fall through */ + fallthrough; case CEC_ST_TX_DATA_BIT_HIGH_CUSTOM: if (tx_last_bit(pin)) { /* Error Injection: just stop sending after this bit */ @@ -459,7 +459,7 @@ static void cec_pin_tx_states(struct cec_pin *pin, ktime_t ts) break; } pin->tx_bit++; - /* fall through */ + fallthrough; case CEC_ST_TX_START_BIT_HIGH: case CEC_ST_TX_START_BIT_HIGH_SHORT: case CEC_ST_TX_START_BIT_HIGH_LONG: diff --git a/drivers/media/cec/platform/seco/seco-cec.c b/drivers/media/cec/platform/seco/seco-cec.c index 075dd79beb6f..ae138cc253fd 100644 --- a/drivers/media/cec/platform/seco/seco-cec.c +++ b/drivers/media/cec/platform/seco/seco-cec.c @@ -369,7 +369,7 @@ static int secocec_ir_probe(void *priv) cec->ir->allowed_protocols = RC_PROTO_BIT_RC5; cec->ir->priv = cec; cec->ir->map_name = RC_MAP_HAUPPAUGE; - cec->ir->timeout = MS_TO_NS(100); + cec->ir->timeout = MS_TO_US(100); /* Clear the status register */ status = smb_rd16(SECOCEC_STATUS_REG_1, &val); diff --git a/drivers/media/cec/usb/pulse8/pulse8-cec.c b/drivers/media/cec/usb/pulse8/pulse8-cec.c index beae6aa12638..e4d8446b87da 100644 --- a/drivers/media/cec/usb/pulse8/pulse8-cec.c +++ b/drivers/media/cec/usb/pulse8/pulse8-cec.c @@ -389,7 +389,7 @@ static irqreturn_t pulse8_interrupt(struct serio *serio, unsigned char data, pulse8->new_rx_msg[0] = pulse8->buf[1]; break; } - /* fall through */ + fallthrough; case MSGCODE_FRAME_DATA: if (pulse8->new_rx_msg_len < CEC_MAX_MSG_SIZE) pulse8->new_rx_msg[pulse8->new_rx_msg_len++] = diff --git a/drivers/media/common/saa7146/saa7146_core.c b/drivers/media/common/saa7146/saa7146_core.c index 6b06ea590074..21fb16cc5ca1 100644 --- a/drivers/media/common/saa7146/saa7146_core.c +++ b/drivers/media/common/saa7146/saa7146_core.c @@ -140,7 +140,7 @@ static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages) struct page *pg; int i; - sglist = kcalloc(nr_pages, sizeof(struct scatterlist), GFP_KERNEL); + sglist = kmalloc_array(nr_pages, sizeof(struct scatterlist), GFP_KERNEL); if (NULL == sglist) return NULL; sg_init_table(sglist, nr_pages); diff --git a/drivers/media/common/siano/sms-cards.c b/drivers/media/common/siano/sms-cards.c index e67ee3d55488..d4a116ab6c88 100644 --- a/drivers/media/common/siano/sms-cards.c +++ b/drivers/media/common/siano/sms-cards.c @@ -79,7 +79,7 @@ static struct sms_board sms_boards[] = { .board_cfg.rf_switch_uhf = 17, }, [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2] = { - .name = "Hauppauge WinTV MiniCard", + .name = "Hauppauge WinTV MiniCard Rev 2", .type = SMS_NOVA_B0, .fw[DEVICE_MODE_DVBT_BDA] = SMS_FW_DVBT_HCW_55XXX, .default_mode = DEVICE_MODE_DVBT_BDA, diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c index 79bd627f84b8..d85c78c104b9 100644 --- a/drivers/media/common/siano/smsir.c +++ b/drivers/media/common/siano/smsir.c @@ -27,7 +27,7 @@ void sms_ir_event(struct smscore_device_t *coredev, const char *buf, int len) for (i = 0; i < len >> 2; i++) { struct ir_raw_event ev = { - .duration = abs(samples[i]) * 1000, /* Convert to ns */ + .duration = abs(samples[i]), .pulse = (samples[i] > 0) ? false : true }; @@ -48,7 +48,7 @@ int sms_ir_init(struct smscore_device_t *coredev) return -ENOMEM; coredev->ir.controller = 0; /* Todo: vega/nova SPI number */ - coredev->ir.timeout = IR_DEFAULT_TIMEOUT; + coredev->ir.timeout = US_TO_NS(IR_DEFAULT_TIMEOUT); pr_debug("IR port %d, timeout %d ms\n", coredev->ir.controller, coredev->ir.timeout); diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index f544d3393e9d..4eab6d81cce1 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -721,39 +721,14 @@ int vb2_verify_memory_type(struct vb2_queue *q, } EXPORT_SYMBOL(vb2_verify_memory_type); -static void set_queue_consistency(struct vb2_queue *q, bool consistent_mem) -{ - q->dma_attrs &= ~DMA_ATTR_NON_CONSISTENT; - - if (!vb2_queue_allows_cache_hints(q)) - return; - if (!consistent_mem) - q->dma_attrs |= DMA_ATTR_NON_CONSISTENT; -} - -static bool verify_consistency_attr(struct vb2_queue *q, bool consistent_mem) -{ - bool queue_is_consistent = !(q->dma_attrs & DMA_ATTR_NON_CONSISTENT); - - if (consistent_mem != queue_is_consistent) { - dprintk(q, 1, "memory consistency model mismatch\n"); - return false; - } - return true; -} - int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, - unsigned int flags, unsigned int *count) + unsigned int *count) { unsigned int num_buffers, allocated_buffers, num_planes = 0; unsigned plane_sizes[VB2_MAX_PLANES] = { }; - bool consistent_mem = true; unsigned int i; int ret; - if (flags & V4L2_FLAG_MEMORY_NON_CONSISTENT) - consistent_mem = false; - if (q->streaming) { dprintk(q, 1, "streaming active\n"); return -EBUSY; @@ -765,8 +740,7 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, } if (*count == 0 || q->num_buffers != 0 || - (q->memory != VB2_MEMORY_UNKNOWN && q->memory != memory) || - !verify_consistency_attr(q, consistent_mem)) { + (q->memory != VB2_MEMORY_UNKNOWN && q->memory != memory)) { /* * We already have buffers allocated, so first check if they * are not in use and can be freed. @@ -803,7 +777,6 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, num_buffers = min_t(unsigned int, num_buffers, VB2_MAX_FRAME); memset(q->alloc_devs, 0, sizeof(q->alloc_devs)); q->memory = memory; - set_queue_consistency(q, consistent_mem); /* * Ask the driver how many buffers and planes per buffer it requires. @@ -888,18 +861,14 @@ int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, EXPORT_SYMBOL_GPL(vb2_core_reqbufs); int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, - unsigned int flags, unsigned int *count, + unsigned int *count, unsigned int requested_planes, const unsigned int requested_sizes[]) { unsigned int num_planes = 0, num_buffers, allocated_buffers; unsigned plane_sizes[VB2_MAX_PLANES] = { }; - bool consistent_mem = true; int ret; - if (flags & V4L2_FLAG_MEMORY_NON_CONSISTENT) - consistent_mem = false; - if (q->num_buffers == VB2_MAX_FRAME) { dprintk(q, 1, "maximum number of buffers already allocated\n"); return -ENOBUFS; @@ -912,15 +881,12 @@ int vb2_core_create_bufs(struct vb2_queue *q, enum vb2_memory memory, } memset(q->alloc_devs, 0, sizeof(q->alloc_devs)); q->memory = memory; - set_queue_consistency(q, consistent_mem); q->waiting_for_buffers = !q->is_output; } else { if (q->memory != memory) { dprintk(q, 1, "memory model mismatch\n"); return -EINVAL; } - if (!verify_consistency_attr(q, consistent_mem)) - return -EINVAL; } num_buffers = min(*count, VB2_MAX_FRAME - q->num_buffers); @@ -2581,7 +2547,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) fileio->memory = VB2_MEMORY_MMAP; fileio->type = q->type; q->fileio = fileio; - ret = vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); + ret = vb2_core_reqbufs(q, fileio->memory, &fileio->count); if (ret) goto err_kfree; @@ -2638,7 +2604,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) err_reqbufs: fileio->count = 0; - vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); + vb2_core_reqbufs(q, fileio->memory, &fileio->count); err_kfree: q->fileio = NULL; @@ -2658,7 +2624,7 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q) vb2_core_streamoff(q, q->type); q->fileio = NULL; fileio->count = 0; - vb2_core_reqbufs(q, fileio->memory, 0, &fileio->count); + vb2_core_reqbufs(q, fileio->memory, &fileio->count); kfree(fileio); dprintk(q, 3, "file io emulator closed\n"); } diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c index ec3446cc45b8..2f3a5996d3fc 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c @@ -42,11 +42,6 @@ struct vb2_dc_buf { struct dma_buf_attachment *db_attach; }; -static inline bool vb2_dc_buffer_consistent(unsigned long attr) -{ - return !(attr & DMA_ATTR_NON_CONSISTENT); -} - /*********************************************/ /* scatterlist table functions */ /*********************************************/ @@ -58,10 +53,10 @@ static unsigned long vb2_dc_get_contiguous_size(struct sg_table *sgt) unsigned int i; unsigned long size = 0; - for_each_sg(sgt->sgl, s, sgt->nents, i) { + for_each_sgtable_dma_sg(sgt, s, i) { if (sg_dma_address(s) != expected) break; - expected = sg_dma_address(s) + sg_dma_len(s); + expected += sg_dma_len(s); size += sg_dma_len(s); } return size; @@ -103,8 +98,7 @@ static void vb2_dc_prepare(void *buf_priv) if (!sgt) return; - dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->orig_nents, - buf->dma_dir); + dma_sync_sgtable_for_device(buf->dev, sgt, buf->dma_dir); } static void vb2_dc_finish(void *buf_priv) @@ -115,7 +109,7 @@ static void vb2_dc_finish(void *buf_priv) if (!sgt) return; - dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir); + dma_sync_sgtable_for_cpu(buf->dev, sgt, buf->dma_dir); } /*********************************************/ @@ -275,8 +269,8 @@ static void vb2_dc_dmabuf_ops_detach(struct dma_buf *dbuf, * memory locations do not require any explicit cache * maintenance prior or after being used by the device. */ - dma_unmap_sg_attrs(db_attach->dev, sgt->sgl, sgt->orig_nents, - attach->dma_dir, DMA_ATTR_SKIP_CPU_SYNC); + dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir, + DMA_ATTR_SKIP_CPU_SYNC); sg_free_table(sgt); kfree(attach); db_attach->priv = NULL; @@ -301,8 +295,8 @@ static struct sg_table *vb2_dc_dmabuf_ops_map( /* release any previous cache */ if (attach->dma_dir != DMA_NONE) { - dma_unmap_sg_attrs(db_attach->dev, sgt->sgl, sgt->orig_nents, - attach->dma_dir, DMA_ATTR_SKIP_CPU_SYNC); + dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir, + DMA_ATTR_SKIP_CPU_SYNC); attach->dma_dir = DMA_NONE; } @@ -310,9 +304,8 @@ static struct sg_table *vb2_dc_dmabuf_ops_map( * mapping to the client with new direction, no cache sync * required see comment in vb2_dc_dmabuf_ops_detach() */ - sgt->nents = dma_map_sg_attrs(db_attach->dev, sgt->sgl, sgt->orig_nents, - dma_dir, DMA_ATTR_SKIP_CPU_SYNC); - if (!sgt->nents) { + if (dma_map_sgtable(db_attach->dev, sgt, dma_dir, + DMA_ATTR_SKIP_CPU_SYNC)) { pr_err("failed to map scatterlist\n"); mutex_unlock(lock); return ERR_PTR(-EIO); @@ -341,13 +334,6 @@ static int vb2_dc_dmabuf_ops_begin_cpu_access(struct dma_buf *dbuf, enum dma_data_direction direction) { - struct vb2_dc_buf *buf = dbuf->priv; - struct sg_table *sgt = buf->dma_sgt; - - if (vb2_dc_buffer_consistent(buf->attrs)) - return 0; - - dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); return 0; } @@ -355,13 +341,6 @@ static int vb2_dc_dmabuf_ops_end_cpu_access(struct dma_buf *dbuf, enum dma_data_direction direction) { - struct vb2_dc_buf *buf = dbuf->priv; - struct sg_table *sgt = buf->dma_sgt; - - if (vb2_dc_buffer_consistent(buf->attrs)) - return 0; - - dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); return 0; } @@ -455,8 +434,8 @@ static void vb2_dc_put_userptr(void *buf_priv) * No need to sync to CPU, it's already synced to the CPU * since the finish() memop will have been called before this. */ - dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents, - buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC); + dma_unmap_sgtable(buf->dev, sgt, buf->dma_dir, + DMA_ATTR_SKIP_CPU_SYNC); pages = frame_vector_pages(buf->vec); /* sgt should exist only if vector contains pages... */ BUG_ON(IS_ERR(pages)); @@ -553,9 +532,8 @@ static void *vb2_dc_get_userptr(struct device *dev, unsigned long vaddr, * No need to sync to the device, this will happen later when the * prepare() memop is called. */ - sgt->nents = dma_map_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents, - buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC); - if (sgt->nents <= 0) { + if (dma_map_sgtable(buf->dev, sgt, buf->dma_dir, + DMA_ATTR_SKIP_CPU_SYNC)) { pr_err("failed to map scatterlist\n"); ret = -EIO; goto fail_sgt_init; @@ -577,8 +555,7 @@ out: return buf; fail_map_sg: - dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents, - buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC); + dma_unmap_sgtable(buf->dev, sgt, buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC); fail_sgt_init: sg_free_table(sgt); diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c index 0a40e00f0d7e..748131151c49 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c @@ -123,8 +123,7 @@ static void *vb2_dma_sg_alloc(struct device *dev, unsigned long dma_attrs, /* * NOTE: dma-sg allocates memory using the page allocator directly, so * there is no memory consistency guarantee, hence dma-sg ignores DMA - * attributes passed from the upper layer. That means that - * V4L2_FLAG_MEMORY_NON_CONSISTENT has no effect on dma-sg buffers. + * attributes passed from the upper layer. */ buf->pages = kvmalloc_array(buf->num_pages, sizeof(struct page *), GFP_KERNEL | __GFP_ZERO); @@ -148,9 +147,8 @@ static void *vb2_dma_sg_alloc(struct device *dev, unsigned long dma_attrs, * No need to sync to the device, this will happen later when the * prepare() memop is called. */ - sgt->nents = dma_map_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents, - buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC); - if (!sgt->nents) + if (dma_map_sgtable(buf->dev, sgt, buf->dma_dir, + DMA_ATTR_SKIP_CPU_SYNC)) goto fail_map; buf->handler.refcount = &buf->refcount; @@ -186,8 +184,8 @@ static void vb2_dma_sg_put(void *buf_priv) if (refcount_dec_and_test(&buf->refcount)) { dprintk(1, "%s: Freeing buffer of %d pages\n", __func__, buf->num_pages); - dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents, - buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC); + dma_unmap_sgtable(buf->dev, sgt, buf->dma_dir, + DMA_ATTR_SKIP_CPU_SYNC); if (buf->vaddr) vm_unmap_ram(buf->vaddr, buf->num_pages); sg_free_table(buf->dma_sgt); @@ -204,8 +202,7 @@ static void vb2_dma_sg_prepare(void *buf_priv) struct vb2_dma_sg_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; - dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->orig_nents, - buf->dma_dir); + dma_sync_sgtable_for_device(buf->dev, sgt, buf->dma_dir); } static void vb2_dma_sg_finish(void *buf_priv) @@ -213,7 +210,7 @@ static void vb2_dma_sg_finish(void *buf_priv) struct vb2_dma_sg_buf *buf = buf_priv; struct sg_table *sgt = buf->dma_sgt; - dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir); + dma_sync_sgtable_for_cpu(buf->dev, sgt, buf->dma_dir); } static void *vb2_dma_sg_get_userptr(struct device *dev, unsigned long vaddr, @@ -256,9 +253,8 @@ static void *vb2_dma_sg_get_userptr(struct device *dev, unsigned long vaddr, * No need to sync to the device, this will happen later when the * prepare() memop is called. */ - sgt->nents = dma_map_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents, - buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC); - if (!sgt->nents) + if (dma_map_sgtable(buf->dev, sgt, buf->dma_dir, + DMA_ATTR_SKIP_CPU_SYNC)) goto userptr_fail_map; return buf; @@ -284,8 +280,7 @@ static void vb2_dma_sg_put_userptr(void *buf_priv) dprintk(1, "%s: Releasing userspace buffer of %d pages\n", __func__, buf->num_pages); - dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir, - DMA_ATTR_SKIP_CPU_SYNC); + dma_unmap_sgtable(buf->dev, sgt, buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC); if (buf->vaddr) vm_unmap_ram(buf->vaddr, buf->num_pages); sg_free_table(buf->dma_sgt); @@ -408,8 +403,7 @@ static void vb2_dma_sg_dmabuf_ops_detach(struct dma_buf *dbuf, /* release the scatterlist cache */ if (attach->dma_dir != DMA_NONE) - dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, - attach->dma_dir); + dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir, 0); sg_free_table(sgt); kfree(attach); db_attach->priv = NULL; @@ -434,15 +428,12 @@ static struct sg_table *vb2_dma_sg_dmabuf_ops_map( /* release any previous cache */ if (attach->dma_dir != DMA_NONE) { - dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, - attach->dma_dir); + dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir, 0); attach->dma_dir = DMA_NONE; } /* mapping to the client with new direction */ - sgt->nents = dma_map_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, - dma_dir); - if (!sgt->nents) { + if (dma_map_sgtable(db_attach->dev, sgt, dma_dir, 0)) { pr_err("failed to map scatterlist\n"); mutex_unlock(lock); return ERR_PTR(-EIO); diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 30caad27281e..96d3b2b2aa31 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -14,21 +14,22 @@ * the Free Software Foundation. */ +#include <linux/device.h> #include <linux/err.h> +#include <linux/freezer.h> #include <linux/kernel.h> -#include <linux/module.h> +#include <linux/kthread.h> #include <linux/mm.h> +#include <linux/module.h> #include <linux/poll.h> -#include <linux/slab.h> #include <linux/sched.h> -#include <linux/freezer.h> -#include <linux/kthread.h> +#include <linux/slab.h> +#include <media/v4l2-common.h> #include <media/v4l2-dev.h> #include <media/v4l2-device.h> -#include <media/v4l2-fh.h> #include <media/v4l2-event.h> -#include <media/v4l2-common.h> +#include <media/v4l2-fh.h> #include <media/videobuf2-v4l2.h> @@ -600,7 +601,7 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb) break; case VB2_BUF_STATE_ERROR: b->flags |= V4L2_BUF_FLAG_ERROR; - /* fall through */ + fallthrough; case VB2_BUF_STATE_DONE: b->flags |= V4L2_BUF_FLAG_DONE; break; @@ -722,22 +723,12 @@ static void fill_buf_caps(struct vb2_queue *q, u32 *caps) #endif } -static void clear_consistency_attr(struct vb2_queue *q, - int memory, - unsigned int *flags) -{ - if (!q->allow_cache_hints || memory != V4L2_MEMORY_MMAP) - *flags &= ~V4L2_FLAG_MEMORY_NON_CONSISTENT; -} - int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) { int ret = vb2_verify_memory_type(q, req->memory, req->type); fill_buf_caps(q, &req->capabilities); - clear_consistency_attr(q, req->memory, &req->flags); - return ret ? ret : vb2_core_reqbufs(q, req->memory, - req->flags, &req->count); + return ret ? ret : vb2_core_reqbufs(q, req->memory, &req->count); } EXPORT_SYMBOL_GPL(vb2_reqbufs); @@ -769,7 +760,6 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) unsigned i; fill_buf_caps(q, &create->capabilities); - clear_consistency_attr(q, create->memory, &create->flags); create->index = q->num_buffers; if (create->count == 0) return ret != -EBUSY ? ret : 0; @@ -813,7 +803,6 @@ int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) if (requested_sizes[i] == 0) return -EINVAL; return ret ? ret : vb2_core_create_bufs(q, create->memory, - create->flags, &create->count, requested_planes, requested_sizes); @@ -998,12 +987,11 @@ int vb2_ioctl_reqbufs(struct file *file, void *priv, int res = vb2_verify_memory_type(vdev->queue, p->memory, p->type); fill_buf_caps(vdev->queue, &p->capabilities); - clear_consistency_attr(vdev->queue, p->memory, &p->flags); if (res) return res; if (vb2_queue_is_busy(vdev, file)) return -EBUSY; - res = vb2_core_reqbufs(vdev->queue, p->memory, p->flags, &p->count); + res = vb2_core_reqbufs(vdev->queue, p->memory, &p->count); /* If count == 0, then the owner has released all buffers and he is no longer owner of the queue. Otherwise we have a new owner. */ if (res == 0) @@ -1021,7 +1009,6 @@ int vb2_ioctl_create_bufs(struct file *file, void *priv, p->index = vdev->queue->num_buffers; fill_buf_caps(vdev->queue, &p->capabilities); - clear_consistency_attr(vdev->queue, p->memory, &p->flags); /* * If count == 0, then just check if memory and type are valid. * Any -EBUSY result from vb2_verify_memory_type can be mapped to 0. @@ -1234,6 +1221,44 @@ unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr, EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area); #endif +void vb2_video_unregister_device(struct video_device *vdev) +{ + /* Check if vdev was ever registered at all */ + if (!vdev || !video_is_registered(vdev)) + return; + + /* + * Calling this function only makes sense if vdev->queue is set. + * If it is NULL, then just call video_unregister_device() instead. + */ + WARN_ON(!vdev->queue); + + /* + * Take a reference to the device since video_unregister_device() + * calls device_unregister(), but we don't want that to release + * the device since we want to clean up the queue first. + */ + get_device(&vdev->dev); + video_unregister_device(vdev); + if (vdev->queue && vdev->queue->owner) { + struct mutex *lock = vdev->queue->lock ? + vdev->queue->lock : vdev->lock; + + if (lock) + mutex_lock(lock); + vb2_queue_release(vdev->queue); + vdev->queue->owner = NULL; + if (lock) + mutex_unlock(lock); + } + /* + * Now we put the device, and in most cases this will release + * everything. + */ + put_device(&vdev->dev); +} +EXPORT_SYMBOL_GPL(vb2_video_unregister_device); + /* vb2_ops helpers. Only use if vq->lock is non-NULL. */ void vb2_ops_wait_prepare(struct vb2_queue *vq) diff --git a/drivers/media/common/videobuf2/videobuf2-vmalloc.c b/drivers/media/common/videobuf2/videobuf2-vmalloc.c index c66fda4a65e4..bf5ac63a5742 100644 --- a/drivers/media/common/videobuf2/videobuf2-vmalloc.c +++ b/drivers/media/common/videobuf2/videobuf2-vmalloc.c @@ -229,7 +229,7 @@ static int vb2_vmalloc_dmabuf_ops_attach(struct dma_buf *dbuf, kfree(attach); return ret; } - for_each_sg(sgt->sgl, sg, sgt->nents, i) { + for_each_sgtable_sg(sgt, sg, i) { struct page *page = vmalloc_to_page(vaddr); if (!page) { @@ -259,8 +259,7 @@ static void vb2_vmalloc_dmabuf_ops_detach(struct dma_buf *dbuf, /* release the scatterlist cache */ if (attach->dma_dir != DMA_NONE) - dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, - attach->dma_dir); + dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir, 0); sg_free_table(sgt); kfree(attach); db_attach->priv = NULL; @@ -285,15 +284,12 @@ static struct sg_table *vb2_vmalloc_dmabuf_ops_map( /* release any previous cache */ if (attach->dma_dir != DMA_NONE) { - dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, - attach->dma_dir); + dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir, 0); attach->dma_dir = DMA_NONE; } /* mapping to the client with new direction */ - sgt->nents = dma_map_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, - dma_dir); - if (!sgt->nents) { + if (dma_map_sgtable(db_attach->dev, sgt, dma_dir, 0)) { pr_err("failed to map scatterlist\n"); mutex_unlock(lock); return ERR_PTR(-EIO); diff --git a/drivers/media/dvb-core/dvb_vb2.c b/drivers/media/dvb-core/dvb_vb2.c index 959d110407a4..6974f1731529 100644 --- a/drivers/media/dvb-core/dvb_vb2.c +++ b/drivers/media/dvb-core/dvb_vb2.c @@ -342,7 +342,7 @@ int dvb_vb2_reqbufs(struct dvb_vb2_ctx *ctx, struct dmx_requestbuffers *req) ctx->buf_siz = req->size; ctx->buf_cnt = req->count; - ret = vb2_core_reqbufs(&ctx->vb_q, VB2_MEMORY_MMAP, 0, &req->count); + ret = vb2_core_reqbufs(&ctx->vb_q, VB2_MEMORY_MMAP, &req->count); if (ret) { ctx->state = DVB_VB2_STATE_NONE; dprintk(1, "[%s] count=%d size=%d errno=%d\n", ctx->name, diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c index 7281899bd7ae..7d7c341b2bd8 100644 --- a/drivers/media/dvb-frontends/af9013.c +++ b/drivers/media/dvb-frontends/af9013.c @@ -597,7 +597,7 @@ static int af9013_read_status(struct dvb_frontend *fe, enum fe_status *status) state->strength_en = 2; break; } - /* Fall through */ + fallthrough; case 1: if (time_is_after_jiffies(state->strength_jiffies + msecs_to_jiffies(2000))) break; diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index 32f9346deb3e..a57470bf71bf 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -1011,8 +1011,7 @@ static int hi_command(struct drxk_state *state, u16 cmd, u16 *p_result) retry_count += 1; status = read16(state, SIO_HI_RA_RAM_CMD__A, &wait_cmd); - } while ((status < 0) && (retry_count < DRXK_MAX_RETRIES) - && (wait_cmd != 0)); + } while ((status < 0 || wait_cmd) && (retry_count < DRXK_MAX_RETRIES)); if (status < 0) goto error; status = read16(state, SIO_HI_RA_RAM_RES__A, p_result); diff --git a/drivers/media/dvb-frontends/lg2160.c b/drivers/media/dvb-frontends/lg2160.c index 10c152f461dd..f343066c297e 100644 --- a/drivers/media/dvb-frontends/lg2160.c +++ b/drivers/media/dvb-frontends/lg2160.c @@ -1408,7 +1408,7 @@ struct dvb_frontend *lg2160_attach(const struct lg2160_config *config, switch (config->lg_chip) { default: lg_warn("invalid chip requested, defaulting to LG2160"); - /* fall-thru */ + fallthrough; case LG2160: memcpy(&state->frontend.ops, &lg2160_ops, sizeof(struct dvb_frontend_ops)); diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index f204e715bc59..ad6d9d564a87 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -906,7 +906,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) if (ret) goto err; } - /* fall through */ + fallthrough; default: u16tmp = DIV_ROUND_UP(target_mclk, dev->cfg->ts_clk); u8tmp1 = u16tmp / 2 - 1; diff --git a/drivers/media/dvb-frontends/mb86a16.c b/drivers/media/dvb-frontends/mb86a16.c index 3843181bba16..2505f1e5794e 100644 --- a/drivers/media/dvb-frontends/mb86a16.c +++ b/drivers/media/dvb-frontends/mb86a16.c @@ -1452,11 +1452,8 @@ static int mb86a16_set_fe(struct mb86a16_state *state) wait_t = (786432 + state->srate / 2) / state->srate; else wait_t = (1572864 + state->srate / 2) / state->srate; - if (state->srate < 5000) - /* FIXME ! , should be a long wait ! */ - msleep_interruptible(wait_t); - else - msleep_interruptible(wait_t); + + msleep_interruptible(wait_t); if (sync_chk(state, &junk) == 0) { iq_vt_set(state, 1); diff --git a/drivers/media/dvb-frontends/mxl5xx.c b/drivers/media/dvb-frontends/mxl5xx.c index 4404ace82981..0b00a23436ed 100644 --- a/drivers/media/dvb-frontends/mxl5xx.c +++ b/drivers/media/dvb-frontends/mxl5xx.c @@ -27,7 +27,6 @@ #include <linux/delay.h> #include <linux/firmware.h> #include <linux/i2c.h> -#include <linux/version.h> #include <linux/mutex.h> #include <linux/vmalloc.h> #include <asm/div64.h> diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 720756728f2d..ef6feb299d46 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -1411,6 +1411,7 @@ static int rtl2832_sdr_probe(struct platform_device *pdev) default: v4l2_ctrl_handler_init(&dev->hdl, 0); dev_err(&pdev->dev, "Unsupported tuner\n"); + ret = -ENODEV; goto err_v4l2_ctrl_handler_free; } if (dev->hdl.error) { diff --git a/drivers/media/dvb-frontends/tda10021.c b/drivers/media/dvb-frontends/tda10021.c index 9fb207b41576..faa6e54b3372 100644 --- a/drivers/media/dvb-frontends/tda10021.c +++ b/drivers/media/dvb-frontends/tda10021.c @@ -137,26 +137,36 @@ static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate { s32 BDR; s32 BDRI; - s16 SFIL=0; + s16 SFIL = 0; u16 NDEC = 0; u32 tmp, ratio; - if (symbolrate > XIN/2) - symbolrate = XIN/2; - if (symbolrate < 500000) + if (symbolrate > XIN / 2) + symbolrate = XIN / 2; + else if (symbolrate < 500000) symbolrate = 500000; - if (symbolrate < XIN/16) NDEC = 1; - if (symbolrate < XIN/32) NDEC = 2; - if (symbolrate < XIN/64) NDEC = 3; - - if (symbolrate < (u32)(XIN/12.3)) SFIL = 1; - if (symbolrate < (u32)(XIN/16)) SFIL = 0; - if (symbolrate < (u32)(XIN/24.6)) SFIL = 1; - if (symbolrate < (u32)(XIN/32)) SFIL = 0; - if (symbolrate < (u32)(XIN/49.2)) SFIL = 1; - if (symbolrate < (u32)(XIN/64)) SFIL = 0; - if (symbolrate < (u32)(XIN/98.4)) SFIL = 1; + if (symbolrate < XIN / 16) + NDEC = 1; + if (symbolrate < XIN / 32) + NDEC = 2; + if (symbolrate < XIN / 64) + NDEC = 3; + + if (symbolrate < XIN * 10 / 123) + SFIL = 1; + if (symbolrate < XIN * 10 / 160) + SFIL = 0; + if (symbolrate < XIN * 10 / 246) + SFIL = 1; + if (symbolrate < XIN * 10 / 320) + SFIL = 0; + if (symbolrate < XIN * 10 / 492) + SFIL = 1; + if (symbolrate < XIN * 10 / 640) + SFIL = 0; + if (symbolrate < XIN * 10 / 984) + SFIL = 1; symbolrate <<= NDEC; ratio = (symbolrate << 4) / FIN; diff --git a/drivers/media/dvb-frontends/tda10086.c b/drivers/media/dvb-frontends/tda10086.c index be6b40138f6e..cdcf97664bba 100644 --- a/drivers/media/dvb-frontends/tda10086.c +++ b/drivers/media/dvb-frontends/tda10086.c @@ -17,7 +17,7 @@ #include <media/dvb_frontend.h> #include "tda10086.h" -#define SACLK 96000000 +#define SACLK 96000000U struct tda10086_state { struct i2c_adapter* i2c; @@ -297,34 +297,34 @@ static int tda10086_set_symbol_rate(struct tda10086_state *state, dprintk ("%s %i\n", __func__, symbol_rate); /* setup the decimation and anti-aliasing filters.. */ - if (symbol_rate < (u32) (SACLK * 0.0137)) { + if (symbol_rate < SACLK / 10000 * 137) { dfn=4; afs=1; - } else if (symbol_rate < (u32) (SACLK * 0.0208)) { + } else if (symbol_rate < SACLK / 10000 * 208) { dfn=4; afs=0; - } else if (symbol_rate < (u32) (SACLK * 0.0270)) { + } else if (symbol_rate < SACLK / 10000 * 270) { dfn=3; afs=1; - } else if (symbol_rate < (u32) (SACLK * 0.0416)) { + } else if (symbol_rate < SACLK / 10000 * 416) { dfn=3; afs=0; - } else if (symbol_rate < (u32) (SACLK * 0.0550)) { + } else if (symbol_rate < SACLK / 10000 * 550) { dfn=2; afs=1; - } else if (symbol_rate < (u32) (SACLK * 0.0833)) { + } else if (symbol_rate < SACLK / 10000 * 833) { dfn=2; afs=0; - } else if (symbol_rate < (u32) (SACLK * 0.1100)) { + } else if (symbol_rate < SACLK / 10000 * 1100) { dfn=1; afs=1; - } else if (symbol_rate < (u32) (SACLK * 0.1666)) { + } else if (symbol_rate < SACLK / 10000 * 1666) { dfn=1; afs=0; - } else if (symbol_rate < (u32) (SACLK * 0.2200)) { + } else if (symbol_rate < SACLK / 10000 * 2200) { dfn=0; afs=1; - } else if (symbol_rate < (u32) (SACLK * 0.3333)) { + } else if (symbol_rate < SACLK / 10000 * 3333) { dfn=0; afs=0; } else { diff --git a/drivers/media/dvb-frontends/tda18271c2dd.c b/drivers/media/dvb-frontends/tda18271c2dd.c index 43312bba1aec..a34834487943 100644 --- a/drivers/media/dvb-frontends/tda18271c2dd.c +++ b/drivers/media/dvb-frontends/tda18271c2dd.c @@ -198,58 +198,55 @@ static void reset(struct tda_state *state) state->m_bFMInput = (ulFMInput == 2); } -static bool SearchMap1(struct SMap Map[], - u32 Frequency, u8 *pParam) +static bool SearchMap1(const struct SMap map[], u32 frequency, u8 *param) { int i = 0; - while ((Map[i].m_Frequency != 0) && (Frequency > Map[i].m_Frequency)) + while ((map[i].m_Frequency != 0) && (frequency > map[i].m_Frequency)) i += 1; - if (Map[i].m_Frequency == 0) + if (map[i].m_Frequency == 0) return false; - *pParam = Map[i].m_Param; + *param = map[i].m_Param; return true; } -static bool SearchMap2(struct SMapI Map[], - u32 Frequency, s32 *pParam) +static bool SearchMap2(const struct SMapI map[], u32 frequency, s32 *param) { int i = 0; - while ((Map[i].m_Frequency != 0) && - (Frequency > Map[i].m_Frequency)) + while ((map[i].m_Frequency != 0) && + (frequency > map[i].m_Frequency)) i += 1; - if (Map[i].m_Frequency == 0) + if (map[i].m_Frequency == 0) return false; - *pParam = Map[i].m_Param; + *param = map[i].m_Param; return true; } -static bool SearchMap3(struct SMap2 Map[], u32 Frequency, - u8 *pParam1, u8 *pParam2) +static bool SearchMap3(const struct SMap2 map[], u32 frequency, u8 *param1, + u8 *param2) { int i = 0; - while ((Map[i].m_Frequency != 0) && - (Frequency > Map[i].m_Frequency)) + while ((map[i].m_Frequency != 0) && + (frequency > map[i].m_Frequency)) i += 1; - if (Map[i].m_Frequency == 0) + if (map[i].m_Frequency == 0) return false; - *pParam1 = Map[i].m_Param1; - *pParam2 = Map[i].m_Param2; + *param1 = map[i].m_Param1; + *param2 = map[i].m_Param2; return true; } -static bool SearchMap4(struct SRFBandMap Map[], - u32 Frequency, u8 *pRFBand) +static bool SearchMap4(const struct SRFBandMap map[], u32 frequency, u8 *rfband) { int i = 0; - while (i < 7 && (Frequency > Map[i].m_RF_max)) + while (i < 7 && (frequency > map[i].m_RF_max)) i += 1; if (i == 7) return false; - *pRFBand = i; + *rfband = i; return true; } diff --git a/drivers/media/dvb-frontends/tda18271c2dd_maps.h b/drivers/media/dvb-frontends/tda18271c2dd_maps.h index 5f75516bc0cb..82218e02d77d 100644 --- a/drivers/media/dvb-frontends/tda18271c2dd_maps.h +++ b/drivers/media/dvb-frontends/tda18271c2dd_maps.h @@ -6,7 +6,7 @@ enum HF_S { HF_DVBC_8MHZ, HF_DVBC }; -static struct SStandardParam m_StandardTable[] = { +static const struct SStandardParam m_StandardTable[] = { { 0, 0, 0x00, 0x00 }, /* HF_None */ { 6000000, 7000000, 0x1D, 0x2C }, /* HF_B, */ { 6900000, 8000000, 0x1E, 0x2C }, /* HF_DK, */ @@ -28,7 +28,7 @@ static struct SStandardParam m_StandardTable[] = { { 0, 0, 0x00, 0x00 }, /* HF_DVBC (Unused) */ }; -static struct SMap m_BP_Filter_Map[] = { +static const struct SMap m_BP_Filter_Map[] = { { 62000000, 0x00 }, { 84000000, 0x01 }, { 100000000, 0x02 }, @@ -39,7 +39,7 @@ static struct SMap m_BP_Filter_Map[] = { { 0, 0x00 }, /* Table End */ }; -static struct SMapI m_RF_Cal_Map[] = { +static const struct SMapI m_RF_Cal_Map[] = { { 41000000, 0x0F }, { 43000000, 0x1C }, { 45000000, 0x2F }, @@ -481,7 +481,7 @@ static struct SMapI m_RF_Cal_Map[] = { }; -static struct SMap2 m_KM_Map[] = { +static const struct SMap2 m_KM_Map[] = { { 47900000, 3, 2 }, { 61100000, 3, 1 }, { 350000000, 3, 0 }, @@ -490,7 +490,7 @@ static struct SMap2 m_KM_Map[] = { { 0, 0x00 }, /* Table End */ }; -static struct SMap2 m_Main_PLL_Map[] = { +static const struct SMap2 m_Main_PLL_Map[] = { { 33125000, 0x57, 0xF0 }, { 35500000, 0x56, 0xE0 }, { 38188000, 0x55, 0xD0 }, @@ -534,7 +534,7 @@ static struct SMap2 m_Main_PLL_Map[] = { { 0, 0x00, 0x00 }, /* Table End */ }; -static struct SMap2 m_Cal_PLL_Map[] = { +static const struct SMap2 m_Cal_PLL_Map[] = { { 33813000, 0xDD, 0xD0 }, { 36625000, 0xDC, 0xC0 }, { 39938000, 0xDB, 0xB0 }, @@ -572,7 +572,7 @@ static struct SMap2 m_Cal_PLL_Map[] = { { 0, 0x00, 0x00 }, /* Table End */ }; -static struct SMap m_GainTaper_Map[] = { +static const struct SMap m_GainTaper_Map[] = { { 45400000, 0x1F }, { 45800000, 0x1E }, { 46200000, 0x1D }, @@ -661,7 +661,7 @@ static struct SMap m_GainTaper_Map[] = { { 0, 0x00 }, /* Table End */ }; -static struct SMap m_RF_Cal_DC_Over_DT_Map[] = { +static const struct SMap m_RF_Cal_DC_Over_DT_Map[] = { { 47900000, 0x00 }, { 55000000, 0x00 }, { 61100000, 0x0A }, @@ -767,14 +767,14 @@ static struct SMap m_RF_Cal_DC_Over_DT_Map[] = { }; -static struct SMap m_IR_Meas_Map[] = { +static const struct SMap m_IR_Meas_Map[] = { { 200000000, 0x05 }, { 400000000, 0x06 }, { 865000000, 0x07 }, { 0, 0x00 }, /* Table End */ }; -static struct SMap2 m_CID_Target_Map[] = { +static const struct SMap2 m_CID_Target_Map[] = { { 46000000, 0x04, 18 }, { 52200000, 0x0A, 15 }, { 70100000, 0x01, 40 }, @@ -790,7 +790,7 @@ static struct SMap2 m_CID_Target_Map[] = { { 0, 0x00, 0 }, /* Table End */ }; -static struct SRFBandMap m_RF_Band_Map[7] = { +static const struct SRFBandMap m_RF_Band_Map[7] = { { 47900000, 46000000, 0, 0}, { 61100000, 52200000, 0, 0}, { 152600000, 70100000, 136800000, 0}, diff --git a/drivers/media/dvb-frontends/zd1301_demod.h b/drivers/media/dvb-frontends/zd1301_demod.h index d56196f5c801..01eaacf76a13 100644 --- a/drivers/media/dvb-frontends/zd1301_demod.h +++ b/drivers/media/dvb-frontends/zd1301_demod.h @@ -43,12 +43,6 @@ struct i2c_adapter *zd1301_demod_get_i2c_adapter(struct platform_device *pdev); #else -/** - * zd1301_demod_get_dvb_frontend() - Attach a zd1301 frontend - * @dev: Pointer to platform device - * - * Return: Pointer to %struct dvb_frontend or NULL if attach fails. - */ static inline struct dvb_frontend *zd1301_demod_get_dvb_frontend(struct platform_device *dev) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); diff --git a/drivers/media/firewire/firedtv-fw.c b/drivers/media/firewire/firedtv-fw.c index 3f1ca40b9b98..8a8585261bb8 100644 --- a/drivers/media/firewire/firedtv-fw.c +++ b/drivers/media/firewire/firedtv-fw.c @@ -272,8 +272,10 @@ static int node_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) name_len = fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)); - if (name_len < 0) - return name_len; + if (name_len < 0) { + err = name_len; + goto fail_free; + } for (i = ARRAY_SIZE(model_names); --i; ) if (strlen(model_names[i]) <= name_len && strncmp(name, model_names[i], name_len) == 0) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index c7ba76fee599..878f66ef2719 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1015,7 +1015,7 @@ config VIDEO_OV7670 config VIDEO_OV7740 tristate "OmniVision OV7740 sensor support" depends on I2C && VIDEO_V4L2 - select REGMAP_I2C + select REGMAP_SCCB help This is a Video4Linux2 sensor driver for the OmniVision OV7740 VGA camera sensor. diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 00159daa6fcd..4498d14d3429 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -726,7 +726,7 @@ static int adv7180_set_pad_format(struct v4l2_subdev *sd, case V4L2_FIELD_NONE: if (state->chip_info->flags & ADV7180_FLAG_I2P) break; - /* fall through */ + fallthrough; default: format->format.field = V4L2_FIELD_ALTERNATE; break; @@ -760,8 +760,9 @@ static int adv7180_init_cfg(struct v4l2_subdev *sd, return adv7180_set_pad_format(sd, cfg, &fmt); } -static int adv7180_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) +static int adv7180_get_mbus_config(struct v4l2_subdev *sd, + unsigned int pad, + struct v4l2_mbus_config *cfg) { struct adv7180_state *state = to_state(sd); @@ -852,7 +853,6 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = { .querystd = adv7180_querystd, .g_input_status = adv7180_g_input_status, .s_routing = adv7180_s_routing, - .g_mbus_config = adv7180_g_mbus_config, .g_pixelaspect = adv7180_g_pixelaspect, .g_tvnorms = adv7180_g_tvnorms, .s_stream = adv7180_s_stream, @@ -869,6 +869,7 @@ static const struct v4l2_subdev_pad_ops adv7180_pad_ops = { .enum_mbus_code = adv7180_enum_mbus_code, .set_fmt = adv7180_set_pad_format, .get_fmt = adv7180_get_pad_format, + .get_mbus_config = adv7180_get_mbus_config, }; static const struct v4l2_subdev_sensor_ops adv7180_sensor_ops = { diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index 23e02ff27b17..1fe7f97c6d52 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -241,10 +241,10 @@ static int adv748x_power_up_tx(struct adv748x_csi2 *tx) int ret = 0; /* Enable n-lane MIPI */ - adv748x_write_check(state, page, 0x00, 0x80 | tx->num_lanes, &ret); + adv748x_write_check(state, page, 0x00, 0x80 | tx->active_lanes, &ret); /* Set Auto DPHY Timing */ - adv748x_write_check(state, page, 0x00, 0xa0 | tx->num_lanes, &ret); + adv748x_write_check(state, page, 0x00, 0xa0 | tx->active_lanes, &ret); /* ADI Required Write */ if (tx->src == &state->hdmi.sd) { @@ -270,7 +270,7 @@ static int adv748x_power_up_tx(struct adv748x_csi2 *tx) usleep_range(2000, 2500); /* Power-up CSI-TX */ - adv748x_write_check(state, page, 0x00, 0x20 | tx->num_lanes, &ret); + adv748x_write_check(state, page, 0x00, 0x20 | tx->active_lanes, &ret); usleep_range(1000, 1500); /* ADI Required Writes */ @@ -292,7 +292,7 @@ static int adv748x_power_down_tx(struct adv748x_csi2 *tx) adv748x_write_check(state, page, 0x1e, 0x00, &ret); /* Enable n-lane MIPI */ - adv748x_write_check(state, page, 0x00, 0x80 | tx->num_lanes, &ret); + adv748x_write_check(state, page, 0x00, 0x80 | tx->active_lanes, &ret); /* i2c_mipi_pll_en - 1'b1 */ adv748x_write_check(state, page, 0xda, 0x01, &ret); @@ -357,14 +357,29 @@ static int adv748x_link_setup(struct media_entity *entity, if (state->afe.tx) { /* AFE Requires TXA enabled, even when output to TXB */ io10 |= ADV748X_IO_10_CSI4_EN; - if (is_txa(tx)) + if (is_txa(tx)) { + /* + * Output from the SD-core (480i and 576i) from the TXA + * interface requires reducing the number of enabled + * data lanes in order to guarantee a valid link + * frequency. + */ + tx->active_lanes = min(tx->num_lanes, 2U); io10 |= ADV748X_IO_10_CSI4_IN_SEL_AFE; - else + } else { + /* TXB has a single data lane, no need to adjust. */ io10 |= ADV748X_IO_10_CSI1_EN; + } } - if (state->hdmi.tx) + if (state->hdmi.tx) { + /* + * Restore the number of active lanes, in case we have gone + * through an AFE->TXA streaming sessions. + */ + tx->active_lanes = tx->num_lanes; io10 |= ADV748X_IO_10_CSI4_EN; + } return io_clrset(state, ADV748X_IO_10, io10_mask, io10); } @@ -596,6 +611,7 @@ static int adv748x_parse_csi2_lanes(struct adv748x_state *state, } state->txa.num_lanes = num_lanes; + state->txa.active_lanes = num_lanes; adv_dbg(state, "TXA: using %u lanes\n", state->txa.num_lanes); } @@ -607,6 +623,7 @@ static int adv748x_parse_csi2_lanes(struct adv748x_state *state, } state->txb.num_lanes = num_lanes; + state->txb.active_lanes = num_lanes; adv_dbg(state, "TXB: using %u lanes\n", state->txb.num_lanes); } diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 2091cda50935..99bb63d05eef 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -214,9 +214,40 @@ unlock: return ret; } +static int adv748x_csi2_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_config *config) +{ + struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); + + if (pad != ADV748X_CSI2_SOURCE) + return -EINVAL; + + config->type = V4L2_MBUS_CSI2_DPHY; + switch (tx->active_lanes) { + case 1: + config->flags = V4L2_MBUS_CSI2_1_LANE; + break; + + case 2: + config->flags = V4L2_MBUS_CSI2_2_LANE; + break; + + case 3: + config->flags = V4L2_MBUS_CSI2_3_LANE; + break; + + case 4: + config->flags = V4L2_MBUS_CSI2_4_LANE; + break; + } + + return 0; +} + static const struct v4l2_subdev_pad_ops adv748x_csi2_pad_ops = { .get_fmt = adv748x_csi2_get_format, .set_fmt = adv748x_csi2_set_format, + .get_mbus_config = adv748x_csi2_get_mbus_config, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index fccb388ce179..1061f425ece5 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -79,6 +79,7 @@ struct adv748x_csi2 { unsigned int page; unsigned int port; unsigned int num_lanes; + unsigned int active_lanes; struct media_pad pads[ADV748X_CSI2_NR_PADS]; struct v4l2_ctrl_handler ctrl_hdl; diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index 62763ec4cd07..a3161d709015 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -470,7 +470,7 @@ static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * reg->val = adv7511_cec_read(sd, reg->reg & 0xff); break; } - /* fall through */ + fallthrough; default: v4l2_info(sd, "Register %03llx not supported\n", reg->reg); adv7511_inv_register(sd); @@ -492,7 +492,7 @@ static int adv7511_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_regi adv7511_cec_write(sd, reg->reg & 0xff, reg->val & 0xff); break; } - /* fall through */ + fallthrough; default: v4l2_info(sd, "Register %03llx not supported\n", reg->reg); adv7511_inv_register(sd); diff --git a/drivers/media/i2c/cx25840/cx25840-ir.c b/drivers/media/i2c/cx25840/cx25840-ir.c index 2181c8a347fc..2cf3e6a1f9e1 100644 --- a/drivers/media/i2c/cx25840/cx25840-ir.c +++ b/drivers/media/i2c/cx25840/cx25840-ir.c @@ -688,7 +688,7 @@ static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, } v = (unsigned) pulse_width_count_to_ns( - (u16) (p->hw_fifo_data & FIFO_RXTX), divider); + (u16)(p->hw_fifo_data & FIFO_RXTX), divider) / 1000; if (v > IR_MAX_DURATION) v = IR_MAX_DURATION; diff --git a/drivers/media/i2c/dw9807-vcm.c b/drivers/media/i2c/dw9807-vcm.c index b38a4e6d270d..438a44b76da8 100644 --- a/drivers/media/i2c/dw9807-vcm.c +++ b/drivers/media/i2c/dw9807-vcm.c @@ -324,6 +324,6 @@ static struct i2c_driver dw9807_i2c_driver = { module_i2c_driver(dw9807_i2c_driver); -MODULE_AUTHOR("Chiang, Alan <alanx.chiang@intel.com>"); +MODULE_AUTHOR("Chiang, Alan"); MODULE_DESCRIPTION("DW9807 VCM driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index f64c0ef7a897..1cee45e35355 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -1188,7 +1188,7 @@ static int __maybe_unused imx219_resume(struct device *dev) error: imx219_stop_streaming(imx219); - imx219->streaming = 0; + imx219->streaming = false; return ret; } diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index f86ae18bc104..ccb55fd1d506 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -1304,7 +1304,7 @@ static struct i2c_driver imx258_i2c_driver = { module_i2c_driver(imx258_i2c_driver); MODULE_AUTHOR("Yeh, Andy <andy.yeh@intel.com>"); -MODULE_AUTHOR("Chiang, Alan <alanx.chiang@intel.com>"); +MODULE_AUTHOR("Chiang, Alan"); MODULE_AUTHOR("Chen, Jason <jasonx.z.chen@intel.com>"); MODULE_DESCRIPTION("Sony IMX258 sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 6011cec5e351..e6aa9f32b6a8 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -1235,6 +1235,8 @@ static int imx274_s_frame_interval(struct v4l2_subdev *sd, ret = imx274_set_frame_interval(imx274, fi->interval); if (!ret) { + fi->interval = imx274->frame_interval; + /* * exposure time range is decided by frame interval * need to update it after frame interval changes @@ -1730,9 +1732,9 @@ static int imx274_set_frame_interval(struct stimx274 *priv, __func__, frame_interval.numerator, frame_interval.denominator); - if (frame_interval.numerator == 0) { - err = -EINVAL; - goto fail; + if (frame_interval.numerator == 0 || frame_interval.denominator == 0) { + frame_interval.denominator = IMX274_DEF_FRAME_RATE; + frame_interval.numerator = 1; } req_frame_rate = (u32)(frame_interval.denominator diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c index de295114ca48..21666d705e37 100644 --- a/drivers/media/i2c/m5mols/m5mols_core.c +++ b/drivers/media/i2c/m5mols/m5mols_core.c @@ -764,7 +764,8 @@ static int m5mols_sensor_power(struct m5mols_info *info, bool enable) ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); if (ret) { - info->set_power(&client->dev, 0); + if (info->set_power) + info->set_power(&client->dev, 0); return ret; } diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index 47f280518fdb..c82c1493e099 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -135,13 +135,19 @@ #define MAX9286_SRC_PAD 4 struct max9286_source { - struct v4l2_async_subdev asd; struct v4l2_subdev *sd; struct fwnode_handle *fwnode; }; -#define asd_to_max9286_source(_asd) \ - container_of(_asd, struct max9286_source, asd) +struct max9286_asd { + struct v4l2_async_subdev base; + struct max9286_source *source; +}; + +static inline struct max9286_asd *to_max9286_asd(struct v4l2_async_subdev *asd) +{ + return container_of(asd, struct max9286_asd, base); +} struct max9286_priv { struct i2c_client *client; @@ -405,10 +411,11 @@ static int max9286_check_config_link(struct max9286_priv *priv, * to 5 milliseconds. */ for (i = 0; i < 10; i++) { - ret = max9286_read(priv, 0x49) & 0xf0; + ret = max9286_read(priv, 0x49); if (ret < 0) return -EIO; + ret &= 0xf0; if (ret == conflink_mask) break; @@ -480,7 +487,7 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_async_subdev *asd) { struct max9286_priv *priv = sd_to_max9286(notifier->sd); - struct max9286_source *source = asd_to_max9286_source(asd); + struct max9286_source *source = to_max9286_asd(asd)->source; unsigned int index = to_index(priv, source); unsigned int src_pad; int ret; @@ -544,7 +551,7 @@ static void max9286_notify_unbind(struct v4l2_async_notifier *notifier, struct v4l2_async_subdev *asd) { struct max9286_priv *priv = sd_to_max9286(notifier->sd); - struct max9286_source *source = asd_to_max9286_source(asd); + struct max9286_source *source = to_max9286_asd(asd)->source; unsigned int index = to_index(priv, source); source->sd = NULL; @@ -569,23 +576,19 @@ static int max9286_v4l2_notifier_register(struct max9286_priv *priv) for_each_source(priv, source) { unsigned int i = to_index(priv, source); - - source->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - source->asd.match.fwnode = source->fwnode; - - ret = v4l2_async_notifier_add_subdev(&priv->notifier, - &source->asd); - if (ret) { - dev_err(dev, "Failed to add subdev for source %d", i); + struct v4l2_async_subdev *asd; + + asd = v4l2_async_notifier_add_fwnode_subdev(&priv->notifier, + source->fwnode, + sizeof(*asd)); + if (IS_ERR(asd)) { + dev_err(dev, "Failed to add subdev for source %u: %ld", + i, PTR_ERR(asd)); v4l2_async_notifier_cleanup(&priv->notifier); - return ret; + return PTR_ERR(asd); } - /* - * Balance the reference counting handled through - * v4l2_async_notifier_cleanup() - */ - fwnode_handle_get(source->fwnode); + to_max9286_asd(asd)->source = source; } priv->notifier.ops = &max9286_notify_ops; diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c index c444bd6a0658..ff212335326a 100644 --- a/drivers/media/i2c/ml86v7667.c +++ b/drivers/media/i2c/ml86v7667.c @@ -219,8 +219,9 @@ static int ml86v7667_fill_fmt(struct v4l2_subdev *sd, return 0; } -static int ml86v7667_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) +static int ml86v7667_get_mbus_config(struct v4l2_subdev *sd, + unsigned int pad, + struct v4l2_mbus_config *cfg) { cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_DATA_ACTIVE_HIGH; @@ -291,13 +292,13 @@ static const struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = { .s_std = ml86v7667_s_std, .querystd = ml86v7667_querystd, .g_input_status = ml86v7667_g_input_status, - .g_mbus_config = ml86v7667_g_mbus_config, }; static const struct v4l2_subdev_pad_ops ml86v7667_subdev_pad_ops = { .enum_mbus_code = ml86v7667_enum_mbus_code, .get_fmt = ml86v7667_fill_fmt, .set_fmt = ml86v7667_fill_fmt, + .get_mbus_config = ml86v7667_get_mbus_config, }; static const struct v4l2_subdev_core_ops ml86v7667_subdev_core_ops = { diff --git a/drivers/media/i2c/msp3400-kthreads.c b/drivers/media/i2c/msp3400-kthreads.c index d3b0d1c18efd..52e506f86de5 100644 --- a/drivers/media/i2c/msp3400-kthreads.c +++ b/drivers/media/i2c/msp3400-kthreads.c @@ -646,7 +646,7 @@ restart: break; case 0: /* 4.5 */ state->detected_std = V4L2_STD_MN; - /* fall-through */ + fallthrough; default: no_second: state->second = msp3400c_carrier_detect_main[max1].cdo; diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c index 210ea76adb53..3b0ba8ed5233 100644 --- a/drivers/media/i2c/mt9m001.c +++ b/drivers/media/i2c/mt9m001.c @@ -689,8 +689,9 @@ static int mt9m001_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static int mt9m001_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) +static int mt9m001_get_mbus_config(struct v4l2_subdev *sd, + unsigned int pad, + struct v4l2_mbus_config *cfg) { /* MT9M001 has all capture_format parameters fixed */ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | @@ -703,7 +704,6 @@ static int mt9m001_g_mbus_config(struct v4l2_subdev *sd, static const struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { .s_stream = mt9m001_s_stream, - .g_mbus_config = mt9m001_g_mbus_config, }; static const struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = { @@ -717,6 +717,7 @@ static const struct v4l2_subdev_pad_ops mt9m001_subdev_pad_ops = { .set_selection = mt9m001_set_selection, .get_fmt = mt9m001_get_fmt, .set_fmt = mt9m001_set_fmt, + .get_mbus_config = mt9m001_get_mbus_config, }; static const struct v4l2_subdev_ops mt9m001_subdev_ops = { diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 17e8253f5748..69697386ffcd 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -1137,8 +1137,9 @@ static int mt9m111_init_cfg(struct v4l2_subdev *sd, return 0; } -static int mt9m111_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) +static int mt9m111_get_mbus_config(struct v4l2_subdev *sd, + unsigned int pad, + struct v4l2_mbus_config *cfg) { struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); @@ -1155,7 +1156,6 @@ static int mt9m111_g_mbus_config(struct v4l2_subdev *sd, } static const struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = { - .g_mbus_config = mt9m111_g_mbus_config, .s_stream = mt9m111_s_stream, .g_frame_interval = mt9m111_g_frame_interval, .s_frame_interval = mt9m111_s_frame_interval, @@ -1168,6 +1168,7 @@ static const struct v4l2_subdev_pad_ops mt9m111_subdev_pad_ops = { .set_selection = mt9m111_set_selection, .get_fmt = mt9m111_get_fmt, .set_fmt = mt9m111_set_fmt, + .get_mbus_config = mt9m111_get_mbus_config, }; static const struct v4l2_subdev_ops mt9m111_subdev_ops = { diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index fd0b6a903ec1..bd0d45b0d43f 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -1018,6 +1018,10 @@ static int ov2740_register_nvmem(struct i2c_client *client) if (!nvm) return -ENOMEM; + nvm->nvm_buffer = devm_kzalloc(dev, CUSTOMER_USE_OTP_SIZE, GFP_KERNEL); + if (!nvm->nvm_buffer) + return -ENOMEM; + regmap_config.val_bits = 8; regmap_config.reg_bits = 16; regmap_config.disable_locking = true; @@ -1027,6 +1031,12 @@ static int ov2740_register_nvmem(struct i2c_client *client) nvm->regmap = regmap; + ret = ov2740_load_otp_data(client, nvm); + if (ret) { + dev_err(dev, "failed to load OTP data, ret %d\n", ret); + return ret; + } + nvmem_config.name = dev_name(dev); nvmem_config.dev = dev; nvmem_config.read_only = true; @@ -1042,18 +1052,8 @@ static int ov2740_register_nvmem(struct i2c_client *client) nvmem_config.size = CUSTOMER_USE_OTP_SIZE; nvm->nvmem = devm_nvmem_register(dev, &nvmem_config); - if (IS_ERR(nvm->nvmem)) - return PTR_ERR(nvm->nvmem); - nvm->nvm_buffer = devm_kzalloc(dev, CUSTOMER_USE_OTP_SIZE, GFP_KERNEL); - if (!nvm->nvm_buffer) - return -ENOMEM; - - ret = ov2740_load_otp_data(client, nvm); - if (ret) - dev_err(dev, "failed to load OTP data, ret %d\n", ret); - - return ret; + return PTR_ERR_OR_ZERO(nvm->nvmem); } static int ov2740_probe(struct i2c_client *client) @@ -1107,7 +1107,7 @@ static int ov2740_probe(struct i2c_client *client) ret = ov2740_register_nvmem(client); if (ret) - dev_err(&client->dev, "register nvmem failed, ret %d\n", ret); + dev_warn(&client->dev, "register nvmem failed, ret %d\n", ret); /* * Device is already turned on by i2c-core with ACPI domain PM. diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 2fe4a7ac0592..8d0254d0e5ea 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -34,6 +34,8 @@ #define OV5640_REG_SYS_RESET02 0x3002 #define OV5640_REG_SYS_CLOCK_ENABLE02 0x3006 #define OV5640_REG_SYS_CTRL0 0x3008 +#define OV5640_REG_SYS_CTRL0_SW_PWDN 0x42 +#define OV5640_REG_SYS_CTRL0_SW_PWUP 0x02 #define OV5640_REG_CHIP_ID 0x300a #define OV5640_REG_IO_MIPI_CTRL00 0x300e #define OV5640_REG_PAD_OUTPUT_ENABLE01 0x3017 @@ -82,6 +84,7 @@ #define OV5640_REG_VFIFO_HSIZE 0x4602 #define OV5640_REG_VFIFO_VSIZE 0x4604 #define OV5640_REG_JPG_MODE_SELECT 0x4713 +#define OV5640_REG_CCIR656_CTRL00 0x4730 #define OV5640_REG_POLARITY_CTRL00 0x4740 #define OV5640_REG_MIPI_CTRL00 0x4800 #define OV5640_REG_DEBUG_MODE 0x4814 @@ -274,8 +277,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) /* YUV422 UYVY VGA@30fps */ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0}, - {0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0}, - {0x3630, 0x36, 0, 0}, + {0x3103, 0x03, 0, 0}, {0x3630, 0x36, 0, 0}, {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0}, {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0}, {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0}, @@ -751,7 +753,7 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg, * +->| PLL Root Div | - reg 0x3037, bit 4 * +-+------------+ * | +---------+ - * +->| Bit Div | - reg 0x3035, bits 0-3 + * +->| Bit Div | - reg 0x3034, bits 0-3 * +-+-------+ * | +-------------+ * +->| SCLK Div | - reg 0x3108, bits 0-1 @@ -1120,6 +1122,12 @@ static int ov5640_load_regs(struct ov5640_dev *sensor, val = regs->val; mask = regs->mask; + /* remain in power down mode for DVP */ + if (regs->reg_addr == OV5640_REG_SYS_CTRL0 && + val == OV5640_REG_SYS_CTRL0_SW_PWUP && + sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY) + continue; + if (mask) ret = ov5640_mod_reg(sensor, reg_addr, mask, val); else @@ -1208,98 +1216,25 @@ static int ov5640_set_autogain(struct ov5640_dev *sensor, bool on) BIT(1), on ? 0 : BIT(1)); } -static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on) +static int ov5640_set_stream_bt656(struct ov5640_dev *sensor, bool on) { int ret; - unsigned int flags = sensor->ep.bus.parallel.flags; - u8 pclk_pol = 0; - u8 hsync_pol = 0; - u8 vsync_pol = 0; - - /* - * Note about parallel port configuration. - * - * When configured in parallel mode, the OV5640 will - * output 10 bits data on DVP data lines [9:0]. - * If only 8 bits data are wanted, the 8 bits data lines - * of the camera interface must be physically connected - * on the DVP data lines [9:2]. - * - * Control lines polarity can be configured through - * devicetree endpoint control lines properties. - * If no endpoint control lines properties are set, - * polarity will be as below: - * - VSYNC: active high - * - HREF: active low - * - PCLK: active low - */ - if (on) { - /* - * configure parallel port control lines polarity - * - * POLARITY CTRL0 - * - [5]: PCLK polarity (0: active low, 1: active high) - * - [1]: HREF polarity (0: active low, 1: active high) - * - [0]: VSYNC polarity (mismatch here between - * datasheet and hardware, 0 is active high - * and 1 is active low...) - */ - if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) - pclk_pol = 1; - if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) - hsync_pol = 1; - if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - vsync_pol = 1; - - ret = ov5640_write_reg(sensor, - OV5640_REG_POLARITY_CTRL00, - (pclk_pol << 5) | - (hsync_pol << 1) | - vsync_pol); - - if (ret) - return ret; - } - - /* - * powerdown MIPI TX/RX PHY & disable MIPI - * - * MIPI CONTROL 00 - * 4: PWDN PHY TX - * 3: PWDN PHY RX - * 2: MIPI enable - */ - ret = ov5640_write_reg(sensor, - OV5640_REG_IO_MIPI_CTRL00, on ? 0x18 : 0); + ret = ov5640_write_reg(sensor, OV5640_REG_CCIR656_CTRL00, + on ? 0x1 : 0x00); if (ret) return ret; - /* - * enable VSYNC/HREF/PCLK DVP control lines - * & D[9:6] DVP data lines - * - * PAD OUTPUT ENABLE 01 - * - 6: VSYNC output enable - * - 5: HREF output enable - * - 4: PCLK output enable - * - [3:0]: D[9:6] output enable - */ - ret = ov5640_write_reg(sensor, - OV5640_REG_PAD_OUTPUT_ENABLE01, - on ? 0x7f : 0); - if (ret) - return ret; + return ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0, on ? + OV5640_REG_SYS_CTRL0_SW_PWUP : + OV5640_REG_SYS_CTRL0_SW_PWDN); +} - /* - * enable D[5:0] DVP data lines - * - * PAD OUTPUT ENABLE 02 - * - [7:2]: D[5:0] output enable - */ - return ov5640_write_reg(sensor, - OV5640_REG_PAD_OUTPUT_ENABLE02, - on ? 0xfc : 0); +static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on) +{ + return ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0, on ? + OV5640_REG_SYS_CTRL0_SW_PWUP : + OV5640_REG_SYS_CTRL0_SW_PWDN); } static int ov5640_set_stream_mipi(struct ov5640_dev *sensor, bool on) @@ -2001,79 +1936,181 @@ static void ov5640_set_power_off(struct ov5640_dev *sensor) clk_disable_unprepare(sensor->xclk); } -static int ov5640_set_power(struct ov5640_dev *sensor, bool on) +static int ov5640_set_power_mipi(struct ov5640_dev *sensor, bool on) { - int ret = 0; + int ret; + + if (!on) { + /* Reset MIPI bus settings to their default values. */ + ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x58); + ov5640_write_reg(sensor, OV5640_REG_MIPI_CTRL00, 0x04); + ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00, 0x00); + return 0; + } + + /* + * Power up MIPI HS Tx and LS Rx; 2 data lanes mode + * + * 0x300e = 0x40 + * [7:5] = 010 : 2 data lanes mode (see FIXME note in + * "ov5640_set_stream_mipi()") + * [4] = 0 : Power up MIPI HS Tx + * [3] = 0 : Power up MIPI LS Rx + * [2] = 0 : MIPI interface disabled + */ + ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x40); + if (ret) + return ret; + + /* + * Gate clock and set LP11 in 'no packets mode' (idle) + * + * 0x4800 = 0x24 + * [5] = 1 : Gate clock when 'no packets' + * [2] = 1 : MIPI bus in LP11 when 'no packets' + */ + ret = ov5640_write_reg(sensor, OV5640_REG_MIPI_CTRL00, 0x24); + if (ret) + return ret; + + /* + * Set data lanes and clock in LP11 when 'sleeping' + * + * 0x3019 = 0x70 + * [6] = 1 : MIPI data lane 2 in LP11 when 'sleeping' + * [5] = 1 : MIPI data lane 1 in LP11 when 'sleeping' + * [4] = 1 : MIPI clock lane in LP11 when 'sleeping' + */ + ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00, 0x70); + if (ret) + return ret; + + /* Give lanes some time to coax into LP11 state. */ + usleep_range(500, 1000); + + return 0; +} + +static int ov5640_set_power_dvp(struct ov5640_dev *sensor, bool on) +{ + unsigned int flags = sensor->ep.bus.parallel.flags; + u8 pclk_pol = 0; + u8 hsync_pol = 0; + u8 vsync_pol = 0; + int ret; + + if (!on) { + /* Reset settings to their default values. */ + ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x58); + ov5640_write_reg(sensor, OV5640_REG_POLARITY_CTRL00, 0x20); + ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE01, 0x00); + ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE02, 0x00); + return 0; + } + + /* + * Note about parallel port configuration. + * + * When configured in parallel mode, the OV5640 will + * output 10 bits data on DVP data lines [9:0]. + * If only 8 bits data are wanted, the 8 bits data lines + * of the camera interface must be physically connected + * on the DVP data lines [9:2]. + * + * Control lines polarity can be configured through + * devicetree endpoint control lines properties. + * If no endpoint control lines properties are set, + * polarity will be as below: + * - VSYNC: active high + * - HREF: active low + * - PCLK: active low + */ + /* + * configure parallel port control lines polarity + * + * POLARITY CTRL0 + * - [5]: PCLK polarity (0: active low, 1: active high) + * - [1]: HREF polarity (0: active low, 1: active high) + * - [0]: VSYNC polarity (mismatch here between + * datasheet and hardware, 0 is active high + * and 1 is active low...) + */ + if (sensor->ep.bus_type == V4L2_MBUS_PARALLEL) { + if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + pclk_pol = 1; + if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + hsync_pol = 1; + if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + vsync_pol = 1; + + ret = ov5640_write_reg(sensor, OV5640_REG_POLARITY_CTRL00, + (pclk_pol << 5) | (hsync_pol << 1) | + vsync_pol); - if (on) { - ret = ov5640_set_power_on(sensor); if (ret) return ret; + } - ret = ov5640_restore_mode(sensor); - if (ret) - goto power_off; + /* + * powerdown MIPI TX/RX PHY & disable MIPI + * + * MIPI CONTROL 00 + * 4: PWDN PHY TX + * 3: PWDN PHY RX + * 2: MIPI enable + */ + ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, 0x18); + if (ret) + return ret; - /* We're done here for DVP bus, while CSI-2 needs setup. */ - if (sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY) - return 0; + /* + * enable VSYNC/HREF/PCLK DVP control lines + * & D[9:6] DVP data lines + * + * PAD OUTPUT ENABLE 01 + * - 6: VSYNC output enable + * - 5: HREF output enable + * - 4: PCLK output enable + * - [3:0]: D[9:6] output enable + */ + ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE01, + sensor->ep.bus_type == V4L2_MBUS_PARALLEL ? + 0x7f : 0x1f); + if (ret) + return ret; - /* - * Power up MIPI HS Tx and LS Rx; 2 data lanes mode - * - * 0x300e = 0x40 - * [7:5] = 010 : 2 data lanes mode (see FIXME note in - * "ov5640_set_stream_mipi()") - * [4] = 0 : Power up MIPI HS Tx - * [3] = 0 : Power up MIPI LS Rx - * [2] = 0 : MIPI interface disabled - */ - ret = ov5640_write_reg(sensor, - OV5640_REG_IO_MIPI_CTRL00, 0x40); - if (ret) - goto power_off; + /* + * enable D[5:0] DVP data lines + * + * PAD OUTPUT ENABLE 02 + * - [7:2]: D[5:0] output enable + */ + return ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT_ENABLE02, 0xfc); +} - /* - * Gate clock and set LP11 in 'no packets mode' (idle) - * - * 0x4800 = 0x24 - * [5] = 1 : Gate clock when 'no packets' - * [2] = 1 : MIPI bus in LP11 when 'no packets' - */ - ret = ov5640_write_reg(sensor, - OV5640_REG_MIPI_CTRL00, 0x24); +static int ov5640_set_power(struct ov5640_dev *sensor, bool on) +{ + int ret = 0; + + if (on) { + ret = ov5640_set_power_on(sensor); if (ret) - goto power_off; + return ret; - /* - * Set data lanes and clock in LP11 when 'sleeping' - * - * 0x3019 = 0x70 - * [6] = 1 : MIPI data lane 2 in LP11 when 'sleeping' - * [5] = 1 : MIPI data lane 1 in LP11 when 'sleeping' - * [4] = 1 : MIPI clock lane in LP11 when 'sleeping' - */ - ret = ov5640_write_reg(sensor, - OV5640_REG_PAD_OUTPUT00, 0x70); + ret = ov5640_restore_mode(sensor); if (ret) goto power_off; + } - /* Give lanes some time to coax into LP11 state. */ - usleep_range(500, 1000); - - } else { - if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) { - /* Reset MIPI bus settings to their default values. */ - ov5640_write_reg(sensor, - OV5640_REG_IO_MIPI_CTRL00, 0x58); - ov5640_write_reg(sensor, - OV5640_REG_MIPI_CTRL00, 0x04); - ov5640_write_reg(sensor, - OV5640_REG_PAD_OUTPUT00, 0x00); - } + if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) + ret = ov5640_set_power_mipi(sensor, on); + else + ret = ov5640_set_power_dvp(sensor, on); + if (ret) + goto power_off; + if (!on) ov5640_set_power_off(sensor); - } return 0; @@ -2888,6 +2925,8 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable) if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) ret = ov5640_set_stream_mipi(sensor, enable); + else if (sensor->ep.bus_type == V4L2_MBUS_BT656) + ret = ov5640_set_stream_bt656(sensor, enable); else ret = ov5640_set_stream_dvp(sensor, enable); @@ -3010,7 +3049,7 @@ static int ov5640_probe(struct i2c_client *client) switch (rotation) { case 180: sensor->upside_down = true; - /* fall through */ + fallthrough; case 0: break; default: @@ -3033,6 +3072,13 @@ static int ov5640_probe(struct i2c_client *client) return ret; } + if (sensor->ep.bus_type != V4L2_MBUS_PARALLEL && + sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY && + sensor->ep.bus_type != V4L2_MBUS_BT656) { + dev_err(dev, "Unsupported bus type %d\n", sensor->ep.bus_type); + return -EINVAL; + } + /* get system clock (xclk) */ sensor->xclk = devm_clk_get(dev, "xclk"); if (IS_ERR(sensor->xclk)) { diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c index 8537cc4ca108..9540ce8918f0 100644 --- a/drivers/media/i2c/ov5675.c +++ b/drivers/media/i2c/ov5675.c @@ -666,8 +666,8 @@ static int ov5675_set_ctrl(struct v4l2_ctrl *ctrl) /* Propagate change of current control to all related controls */ if (ctrl->id == V4L2_CID_VBLANK) { /* Update max exposure while meeting expected vblanking */ - exposure_max = (ov5675->cur_mode->height + ctrl->val - - OV5675_EXPOSURE_MAX_MARGIN) / 2; + exposure_max = ov5675->cur_mode->height + ctrl->val - + OV5675_EXPOSURE_MAX_MARGIN; __v4l2_ctrl_modify_range(ov5675->exposure, ov5675->exposure->minimum, exposure_max, ov5675->exposure->step, @@ -689,7 +689,13 @@ static int ov5675_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_EXPOSURE: - /* 3 least significant bits of expsoure are fractional part */ + /* 4 least significant bits of expsoure are fractional part + * val = val << 4 + * for ov5675, the unit of exposure is differnt from other + * OmniVision sensors, its exposure value is twice of the + * register value, the exposure should be divided by 2 before + * set register, e.g. val << 3. + */ ret = ov5675_write_reg(ov5675, OV5675_REG_EXPOSURE, OV5675_REG_VALUE_24BIT, ctrl->val << 3); break; @@ -770,8 +776,7 @@ static int ov5675_init_controls(struct ov5675 *ov5675) v4l2_ctrl_new_std(ctrl_hdlr, &ov5675_ctrl_ops, V4L2_CID_DIGITAL_GAIN, OV5675_DGTL_GAIN_MIN, OV5675_DGTL_GAIN_MAX, OV5675_DGTL_GAIN_STEP, OV5675_DGTL_GAIN_DEFAULT); - exposure_max = (ov5675->cur_mode->vts_def - - OV5675_EXPOSURE_MAX_MARGIN) / 2; + exposure_max = (ov5675->cur_mode->vts_def - OV5675_EXPOSURE_MAX_MARGIN); ov5675->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov5675_ctrl_ops, V4L2_CID_EXPOSURE, OV5675_EXPOSURE_MIN, exposure_max, diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 91906b94f978..d73f9f540932 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -685,7 +685,7 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, switch (mf->code) { case MEDIA_BUS_FMT_Y10_1X10: mf->code = MEDIA_BUS_FMT_Y8_1X8; - /* fall through */ + fallthrough; case MEDIA_BUS_FMT_Y8_1X8: case MEDIA_BUS_FMT_YVYU8_2X8: case MEDIA_BUS_FMT_YUYV8_2X8: @@ -694,7 +694,7 @@ static int ov6650_set_fmt(struct v4l2_subdev *sd, break; default: mf->code = MEDIA_BUS_FMT_SBGGR8_1X8; - /* fall through */ + fallthrough; case MEDIA_BUS_FMT_SBGGR8_1X8: break; } @@ -921,55 +921,74 @@ static const struct v4l2_subdev_core_ops ov6650_core_ops = { }; /* Request bus settings on camera side */ -static int ov6650_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) +static int ov6650_get_mbus_config(struct v4l2_subdev *sd, + unsigned int pad, + struct v4l2_mbus_config *cfg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 comj, comf; + int ret; + + ret = ov6650_reg_read(client, REG_COMJ, &comj); + if (ret) + return ret; - cfg->flags = V4L2_MBUS_MASTER | - V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | - V4L2_MBUS_DATA_ACTIVE_HIGH; + ret = ov6650_reg_read(client, REG_COMF, &comf); + if (ret) + return ret; + + cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH + | ((comj & COMJ_VSYNC_HIGH) ? V4L2_MBUS_VSYNC_ACTIVE_HIGH + : V4L2_MBUS_VSYNC_ACTIVE_LOW) + | ((comf & COMF_HREF_LOW) ? V4L2_MBUS_HSYNC_ACTIVE_LOW + : V4L2_MBUS_HSYNC_ACTIVE_HIGH) + | ((comj & COMJ_PCLK_RISING) ? V4L2_MBUS_PCLK_SAMPLE_RISING + : V4L2_MBUS_PCLK_SAMPLE_FALLING); cfg->type = V4L2_MBUS_PARALLEL; return 0; } /* Alter bus settings on camera side */ -static int ov6650_s_mbus_config(struct v4l2_subdev *sd, - const struct v4l2_mbus_config *cfg) +static int ov6650_set_mbus_config(struct v4l2_subdev *sd, + unsigned int pad, + struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; + int ret = 0; if (cfg->flags & V4L2_MBUS_PCLK_SAMPLE_RISING) ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0); - else + else if (cfg->flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING); if (ret) return ret; if (cfg->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0); - else + else if (cfg->flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW); if (ret) return ret; if (cfg->flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0); - else + else if (cfg->flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH); + if (ret) + return ret; - return ret; + /* + * Update the configuration to report what is actually applied to + * the hardware. + */ + return ov6650_get_mbus_config(sd, pad, cfg); } static const struct v4l2_subdev_video_ops ov6650_video_ops = { .s_stream = ov6650_s_stream, .g_frame_interval = ov6650_g_frame_interval, .s_frame_interval = ov6650_s_frame_interval, - .g_mbus_config = ov6650_g_mbus_config, - .s_mbus_config = ov6650_s_mbus_config, }; static const struct v4l2_subdev_pad_ops ov6650_pad_ops = { @@ -978,6 +997,8 @@ static const struct v4l2_subdev_pad_ops ov6650_pad_ops = { .set_selection = ov6650_set_selection, .get_fmt = ov6650_get_fmt, .set_fmt = ov6650_set_fmt, + .get_mbus_config = ov6650_get_mbus_config, + .set_mbus_config = ov6650_set_mbus_config, }; static const struct v4l2_subdev_ops ov6650_subdev_ops = { diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 732655fe4ba3..5832461c032d 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1068,13 +1068,6 @@ static int ov7740_probe(struct i2c_client *client) struct v4l2_subdev *sd; int ret; - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_err(&client->dev, - "OV7740: I2C-Adapter doesn't support SMBUS\n"); - return -EIO; - } - ov7740 = devm_kzalloc(&client->dev, sizeof(*ov7740), GFP_KERNEL); if (!ov7740) return -ENOMEM; @@ -1091,7 +1084,7 @@ static int ov7740_probe(struct i2c_client *client) if (ret) return ret; - ov7740->regmap = devm_regmap_init_i2c(client, &ov7740_regmap_config); + ov7740->regmap = devm_regmap_init_sccb(client, &ov7740_regmap_config); if (IS_ERR(ov7740->regmap)) { ret = PTR_ERR(ov7740->regmap); dev_err(&client->dev, "Failed to allocate register map: %d\n", @@ -1100,7 +1093,6 @@ static int ov7740_probe(struct i2c_client *client) } sd = &ov7740->subdev; - client->flags |= I2C_CLIENT_SCCB; v4l2_i2c_subdev_init(sd, client, &ov7740_subdev_ops); #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index 4ca27675cc5a..2f4ceaa80593 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -338,6 +338,209 @@ static const struct ov8856_reg mode_3280x2464_regs[] = { {0x5e00, 0x00} }; +static const struct ov8856_reg mode_3264x2448_regs[] = { + {0x0103, 0x01}, + {0x0302, 0x3c}, + {0x0303, 0x01}, + {0x031e, 0x0c}, + {0x3000, 0x20}, + {0x3003, 0x08}, + {0x300e, 0x20}, + {0x3010, 0x00}, + {0x3015, 0x84}, + {0x3018, 0x72}, + {0x3021, 0x23}, + {0x3033, 0x24}, + {0x3500, 0x00}, + {0x3501, 0x9a}, + {0x3502, 0x20}, + {0x3503, 0x08}, + {0x3505, 0x83}, + {0x3508, 0x01}, + {0x3509, 0x80}, + {0x350c, 0x00}, + {0x350d, 0x80}, + {0x350e, 0x04}, + {0x350f, 0x00}, + {0x3510, 0x00}, + {0x3511, 0x02}, + {0x3512, 0x00}, + {0x3600, 0x72}, + {0x3601, 0x40}, + {0x3602, 0x30}, + {0x3610, 0xc5}, + {0x3611, 0x58}, + {0x3612, 0x5c}, + {0x3613, 0xca}, + {0x3614, 0x60}, + {0x3628, 0xff}, + {0x3629, 0xff}, + {0x362a, 0xff}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x10}, + {0x3636, 0x10}, + {0x3663, 0x08}, + {0x3669, 0x34}, + {0x366d, 0x00}, + {0x366e, 0x10}, + {0x3706, 0x86}, + {0x370b, 0x7e}, + {0x3714, 0x23}, + {0x3730, 0x12}, + {0x3733, 0x10}, + {0x3764, 0x00}, + {0x3765, 0x00}, + {0x3769, 0x62}, + {0x376a, 0x2a}, + {0x376b, 0x30}, + {0x3780, 0x00}, + {0x3781, 0x24}, + {0x3782, 0x00}, + {0x3783, 0x23}, + {0x3798, 0x2f}, + {0x37a1, 0x60}, + {0x37a8, 0x6a}, + {0x37ab, 0x3f}, + {0x37c2, 0x04}, + {0x37c3, 0xf1}, + {0x37c9, 0x80}, + {0x37cb, 0x16}, + {0x37cc, 0x16}, + {0x37cd, 0x16}, + {0x37ce, 0x16}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x0c}, + {0x3804, 0x0c}, + {0x3805, 0xdf}, + {0x3806, 0x09}, + {0x3807, 0xa3}, + {0x3808, 0x0c}, + {0x3809, 0xc0}, + {0x380a, 0x09}, + {0x380b, 0x90}, + {0x380c, 0x07}, + {0x380d, 0x8c}, + {0x380e, 0x09}, + {0x380f, 0xb2}, + {0x3810, 0x00}, + {0x3811, 0x04}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3814, 0x01}, + {0x3815, 0x01}, + {0x3816, 0x00}, + {0x3817, 0x00}, + {0x3818, 0x00}, + {0x3819, 0x10}, + {0x3820, 0x80}, + {0x3821, 0x46}, + {0x382a, 0x01}, + {0x382b, 0x01}, + {0x3830, 0x06}, + {0x3836, 0x02}, + {0x3862, 0x04}, + {0x3863, 0x08}, + {0x3cc0, 0x33}, + {0x3d85, 0x17}, + {0x3d8c, 0x73}, + {0x3d8d, 0xde}, + {0x4001, 0xe0}, + {0x4003, 0x40}, + {0x4008, 0x00}, + {0x4009, 0x0b}, + {0x400a, 0x00}, + {0x400b, 0x84}, + {0x400f, 0x80}, + {0x4010, 0xf0}, + {0x4011, 0xff}, + {0x4012, 0x02}, + {0x4013, 0x01}, + {0x4014, 0x01}, + {0x4015, 0x01}, + {0x4042, 0x00}, + {0x4043, 0x80}, + {0x4044, 0x00}, + {0x4045, 0x80}, + {0x4046, 0x00}, + {0x4047, 0x80}, + {0x4048, 0x00}, + {0x4049, 0x80}, + {0x4041, 0x03}, + {0x404c, 0x20}, + {0x404d, 0x00}, + {0x404e, 0x20}, + {0x4203, 0x80}, + {0x4307, 0x30}, + {0x4317, 0x00}, + {0x4502, 0x50}, + {0x4503, 0x08}, + {0x4601, 0x80}, + {0x4800, 0x44}, + {0x4816, 0x53}, + {0x481b, 0x50}, + {0x481f, 0x27}, + {0x4823, 0x3c}, + {0x482b, 0x00}, + {0x4831, 0x66}, + {0x4837, 0x16}, + {0x483c, 0x0f}, + {0x484b, 0x05}, + {0x5000, 0x77}, + {0x5001, 0x0a}, + {0x5003, 0xc8}, + {0x5004, 0x04}, + {0x5006, 0x00}, + {0x5007, 0x00}, + {0x502e, 0x03}, + {0x5030, 0x41}, + {0x5780, 0x14}, + {0x5781, 0x0f}, + {0x5782, 0x44}, + {0x5783, 0x02}, + {0x5784, 0x01}, + {0x5785, 0x01}, + {0x5786, 0x00}, + {0x5787, 0x04}, + {0x5788, 0x02}, + {0x5789, 0x0f}, + {0x578a, 0xfd}, + {0x578b, 0xf5}, + {0x578c, 0xf5}, + {0x578d, 0x03}, + {0x578e, 0x08}, + {0x578f, 0x0c}, + {0x5790, 0x08}, + {0x5791, 0x04}, + {0x5792, 0x00}, + {0x5793, 0x52}, + {0x5794, 0xa3}, + {0x5795, 0x02}, + {0x5796, 0x20}, + {0x5797, 0x20}, + {0x5798, 0xd5}, + {0x5799, 0xd5}, + {0x579a, 0x00}, + {0x579b, 0x50}, + {0x579c, 0x00}, + {0x579d, 0x2c}, + {0x579e, 0x0c}, + {0x579f, 0x40}, + {0x57a0, 0x09}, + {0x57a1, 0x40}, + {0x59f8, 0x3d}, + {0x5a08, 0x02}, + {0x5b00, 0x02}, + {0x5b01, 0x10}, + {0x5b02, 0x03}, + {0x5b03, 0xcf}, + {0x5b05, 0x6c}, + {0x5e00, 0x00}, + {0x5e10, 0xfc} +}; + static const struct ov8856_reg mode_1640x1232_regs[] = { {0x3000, 0x20}, {0x3003, 0x08}, @@ -528,6 +731,209 @@ static const struct ov8856_reg mode_1640x1232_regs[] = { {0x5e00, 0x00} }; +static const struct ov8856_reg mode_1632x1224_regs[] = { + {0x0103, 0x01}, + {0x0302, 0x3c}, + {0x0303, 0x01}, + {0x031e, 0x0c}, + {0x3000, 0x20}, + {0x3003, 0x08}, + {0x300e, 0x20}, + {0x3010, 0x00}, + {0x3015, 0x84}, + {0x3018, 0x72}, + {0x3021, 0x23}, + {0x3033, 0x24}, + {0x3500, 0x00}, + {0x3501, 0x4c}, + {0x3502, 0xe0}, + {0x3503, 0x08}, + {0x3505, 0x83}, + {0x3508, 0x01}, + {0x3509, 0x80}, + {0x350c, 0x00}, + {0x350d, 0x80}, + {0x350e, 0x04}, + {0x350f, 0x00}, + {0x3510, 0x00}, + {0x3511, 0x02}, + {0x3512, 0x00}, + {0x3600, 0x72}, + {0x3601, 0x40}, + {0x3602, 0x30}, + {0x3610, 0xc5}, + {0x3611, 0x58}, + {0x3612, 0x5c}, + {0x3613, 0xca}, + {0x3614, 0x60}, + {0x3628, 0xff}, + {0x3629, 0xff}, + {0x362a, 0xff}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x10}, + {0x3636, 0x10}, + {0x3663, 0x08}, + {0x3669, 0x34}, + {0x366d, 0x00}, + {0x366e, 0x08}, + {0x3706, 0x86}, + {0x370b, 0x7e}, + {0x3714, 0x27}, + {0x3730, 0x12}, + {0x3733, 0x10}, + {0x3764, 0x00}, + {0x3765, 0x00}, + {0x3769, 0x62}, + {0x376a, 0x2a}, + {0x376b, 0x30}, + {0x3780, 0x00}, + {0x3781, 0x24}, + {0x3782, 0x00}, + {0x3783, 0x23}, + {0x3798, 0x2f}, + {0x37a1, 0x60}, + {0x37a8, 0x6a}, + {0x37ab, 0x3f}, + {0x37c2, 0x14}, + {0x37c3, 0xf1}, + {0x37c9, 0x80}, + {0x37cb, 0x16}, + {0x37cc, 0x16}, + {0x37cd, 0x16}, + {0x37ce, 0x16}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x0c}, + {0x3804, 0x0c}, + {0x3805, 0xdf}, + {0x3806, 0x09}, + {0x3807, 0xa3}, + {0x3808, 0x06}, + {0x3809, 0x60}, + {0x380a, 0x04}, + {0x380b, 0xc8}, + {0x380c, 0x07}, + {0x380d, 0x8c}, + {0x380e, 0x09}, + {0x380f, 0xb2}, + {0x3810, 0x00}, + {0x3811, 0x02}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3814, 0x03}, + {0x3815, 0x01}, + {0x3816, 0x00}, + {0x3817, 0x00}, + {0x3818, 0x00}, + {0x3819, 0x10}, + {0x3820, 0x80}, + {0x3821, 0x47}, + {0x382a, 0x03}, + {0x382b, 0x01}, + {0x3830, 0x06}, + {0x3836, 0x02}, + {0x3862, 0x04}, + {0x3863, 0x08}, + {0x3cc0, 0x33}, + {0x3d85, 0x17}, + {0x3d8c, 0x73}, + {0x3d8d, 0xde}, + {0x4001, 0xe0}, + {0x4003, 0x40}, + {0x4008, 0x00}, + {0x4009, 0x05}, + {0x400a, 0x00}, + {0x400b, 0x84}, + {0x400f, 0x80}, + {0x4010, 0xf0}, + {0x4011, 0xff}, + {0x4012, 0x02}, + {0x4013, 0x01}, + {0x4014, 0x01}, + {0x4015, 0x01}, + {0x4042, 0x00}, + {0x4043, 0x80}, + {0x4044, 0x00}, + {0x4045, 0x80}, + {0x4046, 0x00}, + {0x4047, 0x80}, + {0x4048, 0x00}, + {0x4049, 0x80}, + {0x4041, 0x03}, + {0x404c, 0x20}, + {0x404d, 0x00}, + {0x404e, 0x20}, + {0x4203, 0x80}, + {0x4307, 0x30}, + {0x4317, 0x00}, + {0x4502, 0x50}, + {0x4503, 0x08}, + {0x4601, 0x80}, + {0x4800, 0x44}, + {0x4816, 0x53}, + {0x481b, 0x50}, + {0x481f, 0x27}, + {0x4823, 0x3c}, + {0x482b, 0x00}, + {0x4831, 0x66}, + {0x4837, 0x16}, + {0x483c, 0x0f}, + {0x484b, 0x05}, + {0x5000, 0x77}, + {0x5001, 0x0a}, + {0x5003, 0xc8}, + {0x5004, 0x04}, + {0x5006, 0x00}, + {0x5007, 0x00}, + {0x502e, 0x03}, + {0x5030, 0x41}, + {0x5795, 0x00}, + {0x5796, 0x10}, + {0x5797, 0x10}, + {0x5798, 0x73}, + {0x5799, 0x73}, + {0x579a, 0x00}, + {0x579b, 0x28}, + {0x579c, 0x00}, + {0x579d, 0x16}, + {0x579e, 0x06}, + {0x579f, 0x20}, + {0x57a0, 0x04}, + {0x57a1, 0xa0}, + {0x5780, 0x14}, + {0x5781, 0x0f}, + {0x5782, 0x44}, + {0x5783, 0x02}, + {0x5784, 0x01}, + {0x5785, 0x01}, + {0x5786, 0x00}, + {0x5787, 0x04}, + {0x5788, 0x02}, + {0x5789, 0x0f}, + {0x578a, 0xfd}, + {0x578b, 0xf5}, + {0x578c, 0xf5}, + {0x578d, 0x03}, + {0x578e, 0x08}, + {0x578f, 0x0c}, + {0x5790, 0x08}, + {0x5791, 0x04}, + {0x5792, 0x00}, + {0x5793, 0x52}, + {0x5794, 0xa3}, + {0x59f8, 0x3d}, + {0x5a08, 0x02}, + {0x5b00, 0x02}, + {0x5b01, 0x10}, + {0x5b02, 0x03}, + {0x5b03, 0xcf}, + {0x5b05, 0x6c}, + {0x5e00, 0x00}, + {0x5e10, 0xfc} +}; + static const char * const ov8856_test_pattern_menu[] = { "Disabled", "Standard Color Bar", @@ -570,6 +976,18 @@ static const struct ov8856_mode supported_modes[] = { .link_freq_index = OV8856_LINK_FREQ_720MBPS, }, { + .width = 3264, + .height = 2448, + .hts = 1932, + .vts_def = 2482, + .vts_min = 2482, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3264x2448_regs), + .regs = mode_3264x2448_regs, + }, + .link_freq_index = OV8856_LINK_FREQ_720MBPS, + }, + { .width = 1640, .height = 1232, .hts = 3820, @@ -580,6 +998,18 @@ static const struct ov8856_mode supported_modes[] = { .regs = mode_1640x1232_regs, }, .link_freq_index = OV8856_LINK_FREQ_360MBPS, + }, + { + .width = 1632, + .height = 1224, + .hts = 1932, + .vts_def = 2482, + .vts_min = 2482, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1632x1224_regs), + .regs = mode_1632x1224_regs, + }, + .link_freq_index = OV8856_LINK_FREQ_360MBPS, } }; diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 3a21f51d9325..e2a25240fc85 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -538,7 +538,7 @@ static int ov9640_set_fmt(struct v4l2_subdev *sd, break; default: mf->code = MEDIA_BUS_FMT_UYVY8_2X8; - /* fall through */ + fallthrough; case MEDIA_BUS_FMT_UYVY8_2X8: mf->colorspace = V4L2_COLORSPACE_JPEG; break; @@ -648,8 +648,9 @@ static const struct v4l2_subdev_core_ops ov9640_core_ops = { }; /* Request bus settings on camera side */ -static int ov9640_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) +static int ov9640_get_mbus_config(struct v4l2_subdev *sd, + unsigned int pad, + struct v4l2_mbus_config *cfg) { cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | @@ -661,13 +662,13 @@ static int ov9640_g_mbus_config(struct v4l2_subdev *sd, static const struct v4l2_subdev_video_ops ov9640_video_ops = { .s_stream = ov9640_s_stream, - .g_mbus_config = ov9640_g_mbus_config, }; static const struct v4l2_subdev_pad_ops ov9640_pad_ops = { .enum_mbus_code = ov9640_enum_mbus_code, .get_selection = ov9640_get_selection, .set_fmt = ov9640_set_fmt, + .get_mbus_config = ov9640_get_mbus_config, }; static const struct v4l2_subdev_ops ov9640_subdev_ops = { diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c index 71cf68a95bb2..141ad0ba7f5a 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c @@ -46,7 +46,7 @@ static int s5c73m3_get_af_status(struct s5c73m3 *state, struct v4l2_ctrl *ctrl) break; default: v4l2_info(&state->sensor_sd, "Unknown AF status %#x\n", reg); - /* Fall through */ + fallthrough; case REG_CAF_STATUS_UNFOCUSED: case REG_AF_STATUS_UNFOCUSED: case REG_AF_STATUS_INVALID: diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index 42584a088273..ec6f22efe19a 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -280,8 +280,7 @@ struct s5k5baf_fw { struct { u16 id; u16 offset; - } seq[0]; - u16 data[]; + } seq[]; }; struct s5k5baf { @@ -563,7 +562,7 @@ static u16 *s5k5baf_fw_get_seq(struct s5k5baf *state, u16 seq_id) if (fw == NULL) return NULL; - data = fw->data + 2 * fw->count; + data = &fw->seq[0].id + 2 * fw->count; for (i = 0; i < fw->count; ++i) { if (fw->seq[i].id == seq_id) diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 8a9c7de0c056..6fc0680a93d0 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -1721,7 +1721,7 @@ static void smiapp_propagate(struct v4l2_subdev *subdev, sensor->binning_vertical = 1; } } - /* Fall through */ + fallthrough; case V4L2_SEL_TGT_COMPOSE: *crops[SMIAPP_PAD_SRC] = *comp; break; @@ -2120,7 +2120,7 @@ static int __smiapp_sel_supported(struct v4l2_subdev *subdev, && SMIA_LIM(sensor, SCALING_CAPABILITY) != SMIAPP_SCALING_CAPABILITY_NONE) return 0; - /* Fall through */ + fallthrough; default: return -EINVAL; } @@ -2795,7 +2795,7 @@ static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev) case 180: hwcfg->module_board_orient = SMIAPP_MODULE_BOARD_ORIENT_180; - /* Fall through */ + fallthrough; case 0: break; default: diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index dbbab75f135e..831b5b54fd78 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -919,8 +919,8 @@ static const struct cec_adap_ops tc358743_cec_adap_ops = { .adap_monitor_all_enable = tc358743_cec_adap_monitor_all_enable, }; -static void tc358743_cec_isr(struct v4l2_subdev *sd, u16 intstatus, - bool *handled) +static void tc358743_cec_handler(struct v4l2_subdev *sd, u16 intstatus, + bool *handled) { struct tc358743_state *state = to_state(sd); unsigned int cec_rxint, cec_txint; @@ -953,7 +953,8 @@ static void tc358743_cec_isr(struct v4l2_subdev *sd, u16 intstatus, cec_transmit_attempt_done(state->cec_adap, CEC_TX_STATUS_ERROR); } - *handled = true; + if (handled) + *handled = true; } if ((intstatus & MASK_CEC_RINT) && (cec_rxint & MASK_CECRIEND)) { @@ -968,7 +969,8 @@ static void tc358743_cec_isr(struct v4l2_subdev *sd, u16 intstatus, msg.msg[i] = v & 0xff; } cec_received_msg(state->cec_adap, &msg); - *handled = true; + if (handled) + *handled = true; } i2c_wr16(sd, INTSTATUS, intstatus & (MASK_CEC_RINT | MASK_CEC_TINT)); @@ -1432,7 +1434,7 @@ static int tc358743_isr(struct v4l2_subdev *sd, u32 status, bool *handled) #ifdef CONFIG_VIDEO_TC358743_CEC if (intstatus & (MASK_CEC_RINT | MASK_CEC_TINT)) { - tc358743_cec_isr(sd, intstatus, handled); + tc358743_cec_handler(sd, intstatus, handled); i2c_wr16(sd, INTSTATUS, intstatus & (MASK_CEC_RINT | MASK_CEC_TINT)); intstatus &= ~(MASK_CEC_RINT | MASK_CEC_TINT); @@ -1461,7 +1463,7 @@ static int tc358743_isr(struct v4l2_subdev *sd, u32 status, bool *handled) static irqreturn_t tc358743_irq_handler(int irq, void *dev_id) { struct tc358743_state *state = dev_id; - bool handled; + bool handled = false; tc358743_isr(&state->sd, 0, &handled); @@ -1602,8 +1604,9 @@ static int tc358743_dv_timings_cap(struct v4l2_subdev *sd, return 0; } -static int tc358743_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) +static int tc358743_get_mbus_config(struct v4l2_subdev *sd, + unsigned int pad, + struct v4l2_mbus_config *cfg) { struct tc358743_state *state = to_state(sd); @@ -1836,7 +1839,6 @@ static const struct v4l2_subdev_video_ops tc358743_video_ops = { .s_dv_timings = tc358743_s_dv_timings, .g_dv_timings = tc358743_g_dv_timings, .query_dv_timings = tc358743_query_dv_timings, - .g_mbus_config = tc358743_g_mbus_config, .s_stream = tc358743_s_stream, }; @@ -1848,6 +1850,7 @@ static const struct v4l2_subdev_pad_ops tc358743_pad_ops = { .set_edid = tc358743_s_edid, .enum_dv_timings = tc358743_enum_dv_timings, .dv_timings_cap = tc358743_dv_timings_cap, + .get_mbus_config = tc358743_get_mbus_config, }; static const struct v4l2_subdev_ops tc358743_ops = { diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 5e68182001ec..a09bf0a39d05 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -908,7 +908,7 @@ tda1997x_configure_audout(struct v4l2_subdev *sd, u8 channel_assignment) { struct tda1997x_state *state = to_state(sd); struct tda1997x_platform_data *pdata = &state->pdata; - bool sp_used_by_fifo = 1; + bool sp_used_by_fifo = true; u8 reg; if (!pdata->audout_format) @@ -936,7 +936,7 @@ tda1997x_configure_audout(struct v4l2_subdev *sd, u8 channel_assignment) break; case AUDCFG_TYPE_DST: reg |= AUDCFG_TYPE_DST << AUDCFG_TYPE_SHIFT; - sp_used_by_fifo = 0; + sp_used_by_fifo = false; break; case AUDCFG_TYPE_HBR: reg |= AUDCFG_TYPE_HBR << AUDCFG_TYPE_SHIFT; @@ -944,7 +944,7 @@ tda1997x_configure_audout(struct v4l2_subdev *sd, u8 channel_assignment) /* demuxed via AP0:AP3 */ reg |= AUDCFG_HBR_DEMUX << AUDCFG_HBR_SHIFT; if (pdata->audout_format == AUDFMT_TYPE_SPDIF) - sp_used_by_fifo = 0; + sp_used_by_fifo = false; } else { /* straight via AP0 */ reg |= AUDCFG_HBR_STRAIGHT << AUDCFG_HBR_SHIFT; @@ -2588,7 +2588,7 @@ static int tda1997x_probe(struct i2c_client *client, case 36: mbus_codes[i++] = MEDIA_BUS_FMT_RGB121212_1X36; mbus_codes[i++] = MEDIA_BUS_FMT_YUV12_1X36; - /* fall-through */ + fallthrough; case 24: mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_1X24; break; @@ -2617,10 +2617,10 @@ static int tda1997x_probe(struct i2c_client *client, mbus_codes[i++] = MEDIA_BUS_FMT_RGB888_1X24; mbus_codes[i++] = MEDIA_BUS_FMT_YUV8_1X24; mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_1X24; - /* fall through */ + fallthrough; case 20: mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_1X20; - /* fall through */ + fallthrough; case 16: mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_1X16; break; @@ -2633,10 +2633,10 @@ static int tda1997x_probe(struct i2c_client *client, case 16: case 12: mbus_codes[i++] = MEDIA_BUS_FMT_UYVY12_2X12; - /* fall through */ + fallthrough; case 10: mbus_codes[i++] = MEDIA_BUS_FMT_UYVY10_2X10; - /* fall through */ + fallthrough; case 8: mbus_codes[i++] = MEDIA_BUS_FMT_UYVY8_2X8; break; diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 9df575238952..7d9401219a3a 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -293,7 +293,7 @@ static void tvp5150_selmux(struct v4l2_subdev *sd) switch (decoder->input) { case TVP5150_COMPOSITE1: input |= 2; - /* fall through */ + fallthrough; case TVP5150_COMPOSITE0: break; case TVP5150_SVIDEO: @@ -1191,8 +1191,9 @@ static int tvp5150_get_selection(struct v4l2_subdev *sd, } } -static int tvp5150_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) +static int tvp5150_get_mbus_config(struct v4l2_subdev *sd, + unsigned int pad, + struct v4l2_mbus_config *cfg) { struct tvp5150 *decoder = to_tvp5150(sd); @@ -1721,7 +1722,6 @@ static const struct v4l2_subdev_video_ops tvp5150_video_ops = { .querystd = tvp5150_querystd, .s_stream = tvp5150_s_stream, .s_routing = tvp5150_s_routing, - .g_mbus_config = tvp5150_g_mbus_config, }; static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { @@ -1739,6 +1739,7 @@ static const struct v4l2_subdev_pad_ops tvp5150_pad_ops = { .get_fmt = tvp5150_fill_fmt, .get_selection = tvp5150_get_selection, .set_selection = tvp5150_set_selection, + .get_mbus_config = tvp5150_get_mbus_config, }; static const struct v4l2_subdev_ops tvp5150_ops = { diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index de313b1306da..ada4ec5ef782 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -688,9 +688,11 @@ static int tvp7002_g_register(struct v4l2_subdev *sd, int ret; ret = tvp7002_read(sd, reg->reg & 0xff, &val); + if (ret < 0) + return ret; reg->val = val; reg->size = 1; - return ret; + return 0; } /* diff --git a/drivers/media/mc/mc-device.c b/drivers/media/mc/mc-device.c index da8088351135..9e56d2ad6b94 100644 --- a/drivers/media/mc/mc-device.c +++ b/drivers/media/mc/mc-device.c @@ -370,10 +370,11 @@ static long media_device_get_topology(struct media_device *mdev, void *arg) return ret; } -static long media_device_request_alloc(struct media_device *mdev, - int *alloc_fd) +static long media_device_request_alloc(struct media_device *mdev, void *arg) { #ifdef CONFIG_MEDIA_CONTROLLER_REQUEST_API + int *alloc_fd = arg; + if (!mdev->ops || !mdev->ops->req_validate || !mdev->ops->req_queue) return -ENOTTY; @@ -407,7 +408,7 @@ static long copy_arg_to_user(void __user *uarg, void *karg, unsigned int cmd) #define MEDIA_IOC_ARG(__cmd, func, fl, from_user, to_user) \ [_IOC_NR(MEDIA_IOC_##__cmd)] = { \ .cmd = MEDIA_IOC_##__cmd, \ - .fn = (long (*)(struct media_device *, void *))func, \ + .fn = func, \ .flags = fl, \ .arg_from_user = from_user, \ .arg_to_user = to_user, \ diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 9144f795fb93..8824dd0fb331 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -2332,7 +2332,7 @@ static int bttv_try_fmt_vid_cap(struct file *file, void *priv, field = V4L2_FIELD_SEQ_TB; break; } - /* fall through */ + fallthrough; default: /* FIELD_ANY case */ height2 = btv->crop[!!fh->do_crop].rect.height >> 1; field = (f->fmt.pix.height > height2) @@ -4013,11 +4013,13 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) btv->id = dev->device; if (pci_enable_device(dev)) { pr_warn("%d: Can't enable device\n", btv->c.nr); - return -EIO; + result = -EIO; + goto free_mem; } if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) { pr_warn("%d: No suitable DMA available\n", btv->c.nr); - return -EIO; + result = -EIO; + goto free_mem; } if (!request_mem_region(pci_resource_start(dev,0), pci_resource_len(dev,0), @@ -4025,7 +4027,8 @@ static int bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) pr_warn("%d: can't request iomem (0x%llx)\n", btv->c.nr, (unsigned long long)pci_resource_start(dev, 0)); - return -EBUSY; + result = -EBUSY; + goto free_mem; } pci_set_master(dev); pci_set_command(dev); @@ -4211,6 +4214,10 @@ fail0: release_mem_region(pci_resource_start(btv->c.pci,0), pci_resource_len(btv->c.pci,0)); pci_disable_device(btv->c.pci); + +free_mem: + bttvs[btv->c.nr] = NULL; + kfree(btv); return result; } diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.c b/drivers/media/pci/bt8xx/dvb-bt8xx.c index 02ebd43e672e..4cb890b949c3 100644 --- a/drivers/media/pci/bt8xx/dvb-bt8xx.c +++ b/drivers/media/pci/bt8xx/dvb-bt8xx.c @@ -39,9 +39,10 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ -static void dvb_bt8xx_task(unsigned long data) +static void dvb_bt8xx_task(struct tasklet_struct *t) { - struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *)data; + struct bt878 *bt = from_tasklet(bt, t, tasklet); + struct dvb_bt8xx_card *card = dev_get_drvdata(&bt->adapter->dev); dprintk("%d\n", card->bt->finished_block); @@ -777,7 +778,7 @@ static int dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type) goto err_disconnect_frontend; } - tasklet_init(&card->bt->tasklet, dvb_bt8xx_task, (unsigned long) card); + tasklet_setup(&card->bt->tasklet, dvb_bt8xx_task); frontend_init(card, type); diff --git a/drivers/media/pci/cobalt/cobalt-i2c.c b/drivers/media/pci/cobalt/cobalt-i2c.c index c374dae78bf7..10c9ee33f73e 100644 --- a/drivers/media/pci/cobalt/cobalt-i2c.c +++ b/drivers/media/pci/cobalt/cobalt-i2c.c @@ -118,11 +118,11 @@ static int cobalt_tx_bytes(struct cobalt_i2c_regs __iomem *regs, iowrite8(data[i], ®s->txr_rxr); /* Setup command */ - if (i == 0 && start != 0) { + if (i == 0 && start) { /* Write + Start */ cmd = M00018_CR_BITMAP_WR_MSK | M00018_CR_BITMAP_STA_MSK; - } else if (i == len - 1 && stop != 0) { + } else if (i == len - 1 && stop) { /* Write + Stop */ cmd = M00018_CR_BITMAP_WR_MSK | M00018_CR_BITMAP_STO_MSK; @@ -173,11 +173,11 @@ static int cobalt_rx_bytes(struct cobalt_i2c_regs __iomem *regs, for (i = 0; i < len; i++) { /* Setup command */ - if (i == 0 && start != 0) { + if (i == 0 && start) { /* Read + Start */ cmd = M00018_CR_BITMAP_RD_MSK | M00018_CR_BITMAP_STA_MSK; - } else if (i == len - 1 && stop != 0) { + } else if (i == len - 1 && stop) { /* Read + Stop */ cmd = M00018_CR_BITMAP_RD_MSK | M00018_CR_BITMAP_STO_MSK; diff --git a/drivers/media/pci/cobalt/cobalt-omnitek.c b/drivers/media/pci/cobalt/cobalt-omnitek.c index 4c137453e679..01b82a2e8d33 100644 --- a/drivers/media/pci/cobalt/cobalt-omnitek.c +++ b/drivers/media/pci/cobalt/cobalt-omnitek.c @@ -116,7 +116,7 @@ void omni_sg_dma_abort_channel(struct cobalt_stream *s) { struct cobalt *cobalt = s->cobalt; - if (is_dma_done(s) == false) + if (!is_dma_done(s)) iowrite32(ABORT, CS_REG(s->dma_channel)); } diff --git a/drivers/media/pci/cx23885/cx23885-alsa.c b/drivers/media/pci/cx23885/cx23885-alsa.c index df44ed7393a0..13689c5dd47f 100644 --- a/drivers/media/pci/cx23885/cx23885-alsa.c +++ b/drivers/media/pci/cx23885/cx23885-alsa.c @@ -68,7 +68,8 @@ MODULE_PARM_DESC(audio_debug, "enable debug messages [analog audio]"); #define AUD_INT_MCHG_IRQ (1 << 21) #define GP_COUNT_CONTROL_RESET 0x3 -static int cx23885_alsa_dma_init(struct cx23885_audio_dev *chip, int nr_pages) +static int cx23885_alsa_dma_init(struct cx23885_audio_dev *chip, + unsigned long nr_pages) { struct cx23885_audio_buffer *buf = chip->buf; struct page *pg; @@ -76,11 +77,11 @@ static int cx23885_alsa_dma_init(struct cx23885_audio_dev *chip, int nr_pages) buf->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); if (NULL == buf->vaddr) { - dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); + dprintk(1, "vmalloc_32(%lu pages) failed\n", nr_pages); return -ENOMEM; } - dprintk(1, "vmalloc is at addr %p, size=%d\n", + dprintk(1, "vmalloc is at addr %p, size=%lu\n", buf->vaddr, nr_pages << PAGE_SHIFT); memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT); @@ -113,7 +114,7 @@ static int cx23885_alsa_dma_map(struct cx23885_audio_dev *dev) struct cx23885_audio_buffer *buf = dev->buf; buf->sglen = dma_map_sg(&dev->pci->dev, buf->sglist, - buf->nr_pages, PCI_DMA_FROMDEVICE); + buf->nr_pages, DMA_FROM_DEVICE); if (0 == buf->sglen) { pr_warn("%s: cx23885_alsa_map_sg failed\n", __func__); @@ -129,7 +130,7 @@ static int cx23885_alsa_dma_unmap(struct cx23885_audio_dev *dev) if (!buf->sglen) return 0; - dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->sglen, PCI_DMA_FROMDEVICE); + dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->nr_pages, DMA_FROM_DEVICE); buf->sglen = 0; return 0; } diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 440d108b7ddd..a380e0920a21 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -637,7 +637,7 @@ static int vidioc_querycap(struct file *file, void *priv, sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci)); cap->capabilities = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_AUDIO | V4L2_CAP_VBI_CAPTURE | - V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_DEVICE_CAPS; switch (dev->board) { /* i2c device tuners */ case CX23885_BOARD_HAUPPAUGE_HVR1265_K4: diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index c472498e57c4..349462ee2c48 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -325,8 +325,8 @@ struct cx23885_audio_buffer { struct cx23885_riscmem risc; void *vaddr; struct scatterlist *sglist; - int sglen; - int nr_pages; + int sglen; + unsigned long nr_pages; }; struct cx23885_audio_dev { diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c index ad7f8ccad526..ddfd2eb37484 100644 --- a/drivers/media/pci/cx23885/cx23888-ir.c +++ b/drivers/media/pci/cx23885/cx23888-ir.c @@ -663,7 +663,7 @@ static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, } v = (unsigned) pulse_width_count_to_ns( - (u16) (p->hw_fifo_data & FIFO_RXTX), divider); + (u16)(p->hw_fifo_data & FIFO_RXTX), divider) / 1000; if (v > IR_MAX_DURATION) v = IR_MAX_DURATION; diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c index 301616426d8a..608fbaf0f659 100644 --- a/drivers/media/pci/cx25821/cx25821-alsa.c +++ b/drivers/media/pci/cx25821/cx25821-alsa.c @@ -53,8 +53,8 @@ struct cx25821_audio_buffer { struct cx25821_riscmem risc; void *vaddr; struct scatterlist *sglist; - int sglen; - int nr_pages; + int sglen; + unsigned long nr_pages; }; struct cx25821_audio_dev { @@ -131,7 +131,8 @@ MODULE_PARM_DESC(debug, "enable debug messages"); #define PCI_MSK_AUD_EXT (1 << 4) #define PCI_MSK_AUD_INT (1 << 3) -static int cx25821_alsa_dma_init(struct cx25821_audio_dev *chip, int nr_pages) +static int cx25821_alsa_dma_init(struct cx25821_audio_dev *chip, + unsigned long nr_pages) { struct cx25821_audio_buffer *buf = chip->buf; struct page *pg; @@ -139,11 +140,11 @@ static int cx25821_alsa_dma_init(struct cx25821_audio_dev *chip, int nr_pages) buf->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); if (NULL == buf->vaddr) { - dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); + dprintk(1, "vmalloc_32(%lu pages) failed\n", nr_pages); return -ENOMEM; } - dprintk(1, "vmalloc is at addr 0x%p, size=%d\n", + dprintk(1, "vmalloc is at addr 0x%p, size=%lu\n", buf->vaddr, nr_pages << PAGE_SHIFT); @@ -177,7 +178,7 @@ static int cx25821_alsa_dma_map(struct cx25821_audio_dev *dev) struct cx25821_audio_buffer *buf = dev->buf; buf->sglen = dma_map_sg(&dev->pci->dev, buf->sglist, - buf->nr_pages, PCI_DMA_FROMDEVICE); + buf->nr_pages, DMA_FROM_DEVICE); if (0 == buf->sglen) { pr_warn("%s: cx25821_alsa_map_sg failed\n", __func__); @@ -193,7 +194,7 @@ static int cx25821_alsa_dma_unmap(struct cx25821_audio_dev *dev) if (!buf->sglen) return 0; - dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->sglen, PCI_DMA_FROMDEVICE); + dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->nr_pages, DMA_FROM_DEVICE); buf->sglen = 0; return 0; } diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c index 7d7aceecc985..95e0cbb1277d 100644 --- a/drivers/media/pci/cx88/cx88-alsa.c +++ b/drivers/media/pci/cx88/cx88-alsa.c @@ -42,12 +42,12 @@ */ struct cx88_audio_buffer { - unsigned int bpl; - struct cx88_riscmem risc; + unsigned int bpl; + struct cx88_riscmem risc; void *vaddr; struct scatterlist *sglist; int sglen; - int nr_pages; + unsigned long nr_pages; }; struct cx88_audio_dev { @@ -271,7 +271,8 @@ static irqreturn_t cx8801_irq(int irq, void *dev_id) return IRQ_RETVAL(handled); } -static int cx88_alsa_dma_init(struct cx88_audio_dev *chip, int nr_pages) +static int cx88_alsa_dma_init(struct cx88_audio_dev *chip, + unsigned long nr_pages) { struct cx88_audio_buffer *buf = chip->buf; struct page *pg; @@ -279,11 +280,11 @@ static int cx88_alsa_dma_init(struct cx88_audio_dev *chip, int nr_pages) buf->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); if (!buf->vaddr) { - dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); + dprintk(1, "vmalloc_32(%lu pages) failed\n", nr_pages); return -ENOMEM; } - dprintk(1, "vmalloc is at addr %p, size=%d\n", + dprintk(1, "vmalloc is at addr %p, size=%lu\n", buf->vaddr, nr_pages << PAGE_SHIFT); memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT); @@ -316,7 +317,7 @@ static int cx88_alsa_dma_map(struct cx88_audio_dev *dev) struct cx88_audio_buffer *buf = dev->buf; buf->sglen = dma_map_sg(&dev->pci->dev, buf->sglist, - buf->nr_pages, PCI_DMA_FROMDEVICE); + buf->nr_pages, DMA_FROM_DEVICE); if (buf->sglen == 0) { pr_warn("%s: cx88_alsa_map_sg failed\n", __func__); @@ -332,8 +333,8 @@ static int cx88_alsa_dma_unmap(struct cx88_audio_dev *dev) if (!buf->sglen) return 0; - dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->sglen, - PCI_DMA_FROMDEVICE); + dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->nr_pages, + DMA_FROM_DEVICE); buf->sglen = 0; return 0; } diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c index 9fa388626bae..8e224fc0474d 100644 --- a/drivers/media/pci/cx88/cx88-cards.c +++ b/drivers/media/pci/cx88/cx88-cards.c @@ -3499,7 +3499,7 @@ static void cx88_card_setup(struct cx88_core *core) cx_clear(MO_GP0_IO, 0x00000040); msleep(1000); cx_set(MO_GP0_IO, 0x00004040); - /* FALLTHROUGH */ + fallthrough; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1: case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID: diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c index 7e0fed9cd200..ce0ef0b8186f 100644 --- a/drivers/media/pci/cx88/cx88-input.c +++ b/drivers/media/pci/cx88/cx88-input.c @@ -479,7 +479,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) dev->scancode_mask = hardware_mask; if (ir->sampling) { - dev->timeout = 10 * 1000 * 1000; /* 10 ms */ + dev->timeout = MS_TO_US(10); /* 10 ms */ } else { dev->driver_type = RC_DRIVER_SCANCODE; dev->allowed_protocols = rc_proto; @@ -544,7 +544,7 @@ void cx88_ir_irq(struct cx88_core *core) for (todo = 32; todo > 0; todo -= bits) { ev.pulse = samples & 0x80000000 ? false : true; bits = min(todo, 32U - fls(ev.pulse ? samples : ~samples)); - ev.duration = (bits * (NSEC_PER_SEC / 1000)) / ir_samplerate; + ev.duration = (bits * (USEC_PER_SEC / 1000)) / ir_samplerate; ir_raw_event_store_with_filter(ir->dev, &ev); samples <<= bits; } diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index e7fd7516787c..8cffdacf6007 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -1385,7 +1385,7 @@ static int cx8800_initdev(struct pci_dev *pci_dev, request_module("rtc-isl1208"); core->i2c_rtc = i2c_new_client_device(&core->i2c_adap, &rtc_info); } - /* fall-through */ + fallthrough; case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: request_module("ir-kbd-i2c"); } diff --git a/drivers/media/pci/dt3155/dt3155.c b/drivers/media/pci/dt3155/dt3155.c index ef8d5c9cfffe..961f844de99c 100644 --- a/drivers/media/pci/dt3155/dt3155.c +++ b/drivers/media/pci/dt3155/dt3155.c @@ -575,9 +575,8 @@ static void dt3155_remove(struct pci_dev *pdev) struct dt3155_priv *pd = container_of(v4l2_dev, struct dt3155_priv, v4l2_dev); - video_unregister_device(&pd->vdev); + vb2_video_unregister_device(&pd->vdev); free_irq(pd->pdev->irq, pd); - vb2_queue_release(&pd->vidq); v4l2_device_unregister(&pd->v4l2_dev); pci_iounmap(pdev, pd->regs); pci_release_region(pdev, 0); diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index 92f5eadf2c99..4e598e937dfe 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2017 Intel Corporation + * Copyright (C) 2017,2020 Intel Corporation * * Based partially on Intel IPU4 driver written by * Sakari Ailus <sakari.ailus@linux.intel.com> @@ -9,13 +9,14 @@ * Jouni Ukkonen <jouni.ukkonen@intel.com> * Antti Laakso <antti.laakso@intel.com> * et al. - * */ #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/iopoll.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/pfn.h> #include <linux/pm_runtime.h> #include <linux/property.h> #include <linux/vmalloc.h> @@ -96,12 +97,12 @@ static inline u32 cio2_bytesperline(const unsigned int width) static void cio2_fbpt_exit_dummy(struct cio2_device *cio2) { if (cio2->dummy_lop) { - dma_free_coherent(&cio2->pci_dev->dev, CIO2_PAGE_SIZE, + dma_free_coherent(&cio2->pci_dev->dev, PAGE_SIZE, cio2->dummy_lop, cio2->dummy_lop_bus_addr); cio2->dummy_lop = NULL; } if (cio2->dummy_page) { - dma_free_coherent(&cio2->pci_dev->dev, CIO2_PAGE_SIZE, + dma_free_coherent(&cio2->pci_dev->dev, PAGE_SIZE, cio2->dummy_page, cio2->dummy_page_bus_addr); cio2->dummy_page = NULL; } @@ -111,12 +112,10 @@ static int cio2_fbpt_init_dummy(struct cio2_device *cio2) { unsigned int i; - cio2->dummy_page = dma_alloc_coherent(&cio2->pci_dev->dev, - CIO2_PAGE_SIZE, + cio2->dummy_page = dma_alloc_coherent(&cio2->pci_dev->dev, PAGE_SIZE, &cio2->dummy_page_bus_addr, GFP_KERNEL); - cio2->dummy_lop = dma_alloc_coherent(&cio2->pci_dev->dev, - CIO2_PAGE_SIZE, + cio2->dummy_lop = dma_alloc_coherent(&cio2->pci_dev->dev, PAGE_SIZE, &cio2->dummy_lop_bus_addr, GFP_KERNEL); if (!cio2->dummy_page || !cio2->dummy_lop) { @@ -127,8 +126,8 @@ static int cio2_fbpt_init_dummy(struct cio2_device *cio2) * List of Pointers(LOP) contains 1024x32b pointers to 4KB page each * Initialize each entry to dummy_page bus base address. */ - for (i = 0; i < CIO2_PAGE_SIZE / sizeof(*cio2->dummy_lop); i++) - cio2->dummy_lop[i] = cio2->dummy_page_bus_addr >> PAGE_SHIFT; + for (i = 0; i < CIO2_LOP_ENTRIES; i++) + cio2->dummy_lop[i] = PFN_DOWN(cio2->dummy_page_bus_addr); return 0; } @@ -160,12 +159,11 @@ static void cio2_fbpt_entry_init_dummy(struct cio2_device *cio2, unsigned int i; entry[0].first_entry.first_page_offset = 0; - entry[1].second_entry.num_of_pages = - CIO2_PAGE_SIZE / sizeof(u32) * CIO2_MAX_LOPS; - entry[1].second_entry.last_page_available_bytes = CIO2_PAGE_SIZE - 1; + entry[1].second_entry.num_of_pages = CIO2_LOP_ENTRIES * CIO2_MAX_LOPS; + entry[1].second_entry.last_page_available_bytes = PAGE_SIZE - 1; for (i = 0; i < CIO2_MAX_LOPS; i++) - entry[i].lop_page_addr = cio2->dummy_lop_bus_addr >> PAGE_SHIFT; + entry[i].lop_page_addr = PFN_DOWN(cio2->dummy_lop_bus_addr); cio2_fbpt_entry_enable(cio2, entry); } @@ -182,26 +180,24 @@ static void cio2_fbpt_entry_init_buf(struct cio2_device *cio2, entry[0].first_entry.first_page_offset = b->offset; remaining = length + entry[0].first_entry.first_page_offset; - entry[1].second_entry.num_of_pages = - DIV_ROUND_UP(remaining, CIO2_PAGE_SIZE); + entry[1].second_entry.num_of_pages = PFN_UP(remaining); /* * last_page_available_bytes has the offset of the last byte in the * last page which is still accessible by DMA. DMA cannot access * beyond this point. Valid range for this is from 0 to 4095. * 0 indicates 1st byte in the page is DMA accessible. - * 4095 (CIO2_PAGE_SIZE - 1) means every single byte in the last page + * 4095 (PAGE_SIZE - 1) means every single byte in the last page * is available for DMA transfer. */ entry[1].second_entry.last_page_available_bytes = (remaining & ~PAGE_MASK) ? - (remaining & ~PAGE_MASK) - 1 : - CIO2_PAGE_SIZE - 1; + (remaining & ~PAGE_MASK) - 1 : PAGE_SIZE - 1; /* Fill FBPT */ remaining = length; i = 0; while (remaining > 0) { - entry->lop_page_addr = b->lop_bus_addr[i] >> PAGE_SHIFT; - remaining -= CIO2_PAGE_SIZE / sizeof(u32) * CIO2_PAGE_SIZE; + entry->lop_page_addr = PFN_DOWN(b->lop_bus_addr[i]); + remaining -= CIO2_LOP_ENTRIES * PAGE_SIZE; entry++; i++; } @@ -209,7 +205,7 @@ static void cio2_fbpt_entry_init_buf(struct cio2_device *cio2, /* * The first not meaningful FBPT entry should point to a valid LOP */ - entry->lop_page_addr = cio2->dummy_lop_bus_addr >> PAGE_SHIFT; + entry->lop_page_addr = PFN_DOWN(cio2->dummy_lop_bus_addr); cio2_fbpt_entry_enable(cio2, entry); } @@ -295,7 +291,7 @@ static int cio2_csi2_calc_timing(struct cio2_device *cio2, struct cio2_queue *q, struct cio2_csi2_timing *timing) { struct device *dev = &cio2->pci_dev->dev; - struct v4l2_querymenu qm = {.id = V4L2_CID_LINK_FREQ, }; + struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ }; struct v4l2_ctrl *link_freq; s64 freq; int r; @@ -475,8 +471,7 @@ static int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q) } /* Enable DMA */ - writel(q->fbpt_bus_addr >> PAGE_SHIFT, - base + CIO2_REG_CDMABA(CIO2_DMA_CHAN)); + writel(PFN_DOWN(q->fbpt_bus_addr), base + CIO2_REG_CDMABA(CIO2_DMA_CHAN)); writel(num_buffers1 << CIO2_CDMAC0_FBPT_LEN_SHIFT | FBPT_WIDTH << CIO2_CDMAC0_FBPT_WIDTH_SHIFT | @@ -512,8 +507,10 @@ static int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q) static void cio2_hw_exit(struct cio2_device *cio2, struct cio2_queue *q) { - void __iomem *base = cio2->base; - unsigned int i, maxloops = 1000; + void __iomem *const base = cio2->base; + unsigned int i; + u32 value; + int ret; /* Disable CSI receiver and MIPI backend devices */ writel(0, q->csi_rx_base + CIO2_REG_IRQCTRL_MASK); @@ -523,13 +520,10 @@ static void cio2_hw_exit(struct cio2_device *cio2, struct cio2_queue *q) /* Halt DMA */ writel(0, base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN)); - do { - if (readl(base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN)) & - CIO2_CDMAC0_DMA_HALTED) - break; - usleep_range(1000, 2000); - } while (--maxloops); - if (!maxloops) + ret = readl_poll_timeout(base + CIO2_REG_CDMAC0(CIO2_DMA_CHAN), + value, value & CIO2_CDMAC0_DMA_HALTED, + 4000, 2000000); + if (ret) dev_err(&cio2->pci_dev->dev, "DMA %i can not be halted\n", CIO2_DMA_CHAN); @@ -545,7 +539,7 @@ static void cio2_buffer_done(struct cio2_device *cio2, unsigned int dma_chan) { struct device *dev = &cio2->pci_dev->dev; struct cio2_queue *q = cio2->cur_queue; - int buffers_found = 0; + struct cio2_fbpt_entry *entry; u64 ns = ktime_get_ns(); if (dma_chan >= CIO2_QUEUES) { @@ -553,15 +547,18 @@ static void cio2_buffer_done(struct cio2_device *cio2, unsigned int dma_chan) return; } + entry = &q->fbpt[q->bufs_first * CIO2_MAX_LOPS]; + if (entry->first_entry.ctrl & CIO2_FBPT_CTRL_VALID) { + dev_warn(&cio2->pci_dev->dev, + "no ready buffers found on DMA channel %u\n", + dma_chan); + return; + } + /* Find out which buffer(s) are ready */ do { - struct cio2_fbpt_entry *const entry = - &q->fbpt[q->bufs_first * CIO2_MAX_LOPS]; struct cio2_buffer *b; - if (entry->first_entry.ctrl & CIO2_FBPT_CTRL_VALID) - break; - b = q->bufs[q->bufs_first]; if (b) { unsigned int bytes = entry[1].second_entry.num_of_bytes; @@ -583,13 +580,8 @@ static void cio2_buffer_done(struct cio2_device *cio2, unsigned int dma_chan) atomic_inc(&q->frame_sequence); cio2_fbpt_entry_init_dummy(cio2, entry); q->bufs_first = (q->bufs_first + 1) % CIO2_MAX_BUFFERS; - buffers_found++; - } while (1); - - if (buffers_found == 0) - dev_warn(&cio2->pci_dev->dev, - "no ready buffers found on DMA channel %u\n", - dma_chan); + entry = &q->fbpt[q->bufs_first * CIO2_MAX_LOPS]; + } while (!(entry->first_entry.ctrl & CIO2_FBPT_CTRL_VALID)); } static void cio2_queue_event_sof(struct cio2_device *cio2, struct cio2_queue *q) @@ -841,13 +833,11 @@ static int cio2_vb2_buf_init(struct vb2_buffer *vb) struct device *dev = &cio2->pci_dev->dev; struct cio2_buffer *b = container_of(vb, struct cio2_buffer, vbb.vb2_buf); - static const unsigned int entries_per_page = - CIO2_PAGE_SIZE / sizeof(u32); - unsigned int pages = DIV_ROUND_UP(vb->planes[0].length, CIO2_PAGE_SIZE); - unsigned int lops = DIV_ROUND_UP(pages + 1, entries_per_page); + unsigned int pages = PFN_UP(vb->planes[0].length); + unsigned int lops = DIV_ROUND_UP(pages + 1, CIO2_LOP_ENTRIES); struct sg_table *sg; struct sg_dma_page_iter sg_iter; - int i, j; + unsigned int i, j; if (lops <= 0 || lops > CIO2_MAX_LOPS) { dev_err(dev, "%s: bad buffer size (%i)\n", __func__, @@ -858,7 +848,7 @@ static int cio2_vb2_buf_init(struct vb2_buffer *vb) memset(b->lop, 0, sizeof(b->lop)); /* Allocate LOP table */ for (i = 0; i < lops; i++) { - b->lop[i] = dma_alloc_coherent(dev, CIO2_PAGE_SIZE, + b->lop[i] = dma_alloc_coherent(dev, PAGE_SIZE, &b->lop_bus_addr[i], GFP_KERNEL); if (!b->lop[i]) goto fail; @@ -873,23 +863,22 @@ static int cio2_vb2_buf_init(struct vb2_buffer *vb) b->offset = sg->sgl->offset; i = j = 0; - for_each_sg_dma_page (sg->sgl, &sg_iter, sg->nents, 0) { + for_each_sg_dma_page(sg->sgl, &sg_iter, sg->nents, 0) { if (!pages--) break; - b->lop[i][j] = sg_page_iter_dma_address(&sg_iter) >> PAGE_SHIFT; + b->lop[i][j] = PFN_DOWN(sg_page_iter_dma_address(&sg_iter)); j++; - if (j == entries_per_page) { + if (j == CIO2_LOP_ENTRIES) { i++; j = 0; } } - b->lop[i][j] = cio2->dummy_page_bus_addr >> PAGE_SHIFT; + b->lop[i][j] = PFN_DOWN(cio2->dummy_page_bus_addr); return 0; fail: - for (i--; i >= 0; i--) - dma_free_coherent(dev, CIO2_PAGE_SIZE, - b->lop[i], b->lop_bus_addr[i]); + while (i--) + dma_free_coherent(dev, PAGE_SIZE, b->lop[i], b->lop_bus_addr[i]); return -ENOMEM; } @@ -979,7 +968,7 @@ static void cio2_vb2_buf_cleanup(struct vb2_buffer *vb) /* Free LOP table */ for (i = 0; i < CIO2_MAX_LOPS; i++) { if (b->lop[i]) - dma_free_coherent(&cio2->pci_dev->dev, CIO2_PAGE_SIZE, + dma_free_coherent(&cio2->pci_dev->dev, PAGE_SIZE, b->lop[i], b->lop_bus_addr[i]); } } @@ -1633,7 +1622,7 @@ static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q) if (r) { dev_err(&cio2->pci_dev->dev, "failed to initialize videobuf2 queue (%d)\n", r); - goto fail_vbq; + goto fail_subdev; } /* Initialize vdev */ @@ -1664,10 +1653,8 @@ static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q) return 0; fail_link: - video_unregister_device(&q->vdev); + vb2_video_unregister_device(&q->vdev); fail_vdev: - vb2_queue_release(vbq); -fail_vbq: v4l2_device_unregister_subdev(subdev); fail_subdev: media_entity_cleanup(&vdev->entity); @@ -1683,9 +1670,8 @@ fail_fbpt: static void cio2_queue_exit(struct cio2_device *cio2, struct cio2_queue *q) { - video_unregister_device(&q->vdev); + vb2_video_unregister_device(&q->vdev); media_entity_cleanup(&q->vdev.entity); - vb2_queue_release(&q->vbq); v4l2_device_unregister_subdev(&q->subdev); media_entity_cleanup(&q->subdev.entity); cio2_fbpt_exit(q, &cio2->pci_dev->dev); @@ -1721,29 +1707,10 @@ static void cio2_queues_exit(struct cio2_device *cio2) /**************** PCI interface ****************/ -static int cio2_pci_config_setup(struct pci_dev *dev) -{ - u16 pci_command; - int r = pci_enable_msi(dev); - - if (r) { - dev_err(&dev->dev, "failed to enable MSI (%d)\n", r); - return r; - } - - pci_read_config_word(dev, PCI_COMMAND, &pci_command); - pci_command |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | - PCI_COMMAND_INTX_DISABLE; - pci_write_config_word(dev, PCI_COMMAND, pci_command); - - return 0; -} - static int cio2_pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) { struct cio2_device *cio2; - void __iomem *const *iomap; int r; cio2 = devm_kzalloc(&pci_dev->dev, sizeof(*cio2), GFP_KERNEL); @@ -1766,13 +1733,7 @@ static int cio2_pci_probe(struct pci_dev *pci_dev, return -ENODEV; } - iomap = pcim_iomap_table(pci_dev); - if (!iomap) { - dev_err(&pci_dev->dev, "failed to iomap table\n"); - return -ENODEV; - } - - cio2->base = iomap[CIO2_PCI_BAR]; + cio2->base = pcim_iomap_table(pci_dev)[CIO2_PCI_BAR]; pci_set_drvdata(pci_dev, cio2); @@ -1784,9 +1745,11 @@ static int cio2_pci_probe(struct pci_dev *pci_dev, return -ENODEV; } - r = cio2_pci_config_setup(pci_dev); - if (r) - return -ENODEV; + r = pci_enable_msi(pci_dev); + if (r) { + dev_err(&pci_dev->dev, "failed to enable MSI (%d)\n", r); + return r; + } r = cio2_fbpt_init_dummy(cio2); if (r) @@ -2012,8 +1975,8 @@ static int __maybe_unused cio2_suspend(struct device *dev) static int __maybe_unused cio2_resume(struct device *dev) { struct cio2_device *cio2 = dev_get_drvdata(dev); - int r = 0; struct cio2_queue *q = cio2->cur_queue; + int r; dev_dbg(dev, "cio2 resume\n"); if (!cio2->streaming) @@ -2040,7 +2003,7 @@ static const struct dev_pm_ops cio2_pm_ops = { static const struct pci_device_id cio2_pci_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, CIO2_PCI_ID) }, - { 0 } + { } }; MODULE_DEVICE_TABLE(pci, cio2_pci_id_table); diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h index 7caab9b8c2b9..549b08f88f0c 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h @@ -4,6 +4,8 @@ #ifndef __IPU3_CIO2_H #define __IPU3_CIO2_H +#include <linux/types.h> + #define CIO2_NAME "ipu3-cio2" #define CIO2_DEVICE_NAME "Intel IPU3 CIO2" #define CIO2_ENTITY_NAME "ipu3-csi2" @@ -17,6 +19,7 @@ /* 32MB = 8xFBPT_entry */ #define CIO2_MAX_LOPS 8 #define CIO2_MAX_BUFFERS (PAGE_SIZE / 16 / CIO2_MAX_LOPS) +#define CIO2_LOP_ENTRIES (PAGE_SIZE / sizeof(u32)) #define CIO2_PAD_SINK 0 #define CIO2_PAD_SOURCE 1 @@ -389,7 +392,6 @@ struct cio2_device { sizeof(struct cio2_fbpt_entry)) #define CIO2_FBPT_SUBENTRY_UNIT 4 -#define CIO2_PAGE_SIZE 4096 /* cio2 fbpt first_entry ctrl status */ #define CIO2_FBPT_CTRL_VALID BIT(0) diff --git a/drivers/media/pci/mantis/mantis_dma.c b/drivers/media/pci/mantis/mantis_dma.c index affc5977387f..4df571ff272b 100644 --- a/drivers/media/pci/mantis/mantis_dma.c +++ b/drivers/media/pci/mantis/mantis_dma.c @@ -200,9 +200,9 @@ void mantis_dma_stop(struct mantis_pci *mantis) } -void mantis_dma_xfer(unsigned long data) +void mantis_dma_xfer(struct tasklet_struct *t) { - struct mantis_pci *mantis = (struct mantis_pci *) data; + struct mantis_pci *mantis = from_tasklet(mantis, t, tasklet); struct mantis_hwconfig *config = mantis->hwconfig; while (mantis->last_block != mantis->busy_block) { diff --git a/drivers/media/pci/mantis/mantis_dma.h b/drivers/media/pci/mantis/mantis_dma.h index 421663443d62..37da982c9c29 100644 --- a/drivers/media/pci/mantis/mantis_dma.h +++ b/drivers/media/pci/mantis/mantis_dma.h @@ -13,6 +13,6 @@ extern int mantis_dma_init(struct mantis_pci *mantis); extern int mantis_dma_exit(struct mantis_pci *mantis); extern void mantis_dma_start(struct mantis_pci *mantis); extern void mantis_dma_stop(struct mantis_pci *mantis); -extern void mantis_dma_xfer(unsigned long data); +extern void mantis_dma_xfer(struct tasklet_struct *t); #endif /* __MANTIS_DMA_H */ diff --git a/drivers/media/pci/mantis/mantis_dvb.c b/drivers/media/pci/mantis/mantis_dvb.c index 2da94be5b373..c7ba4a76e608 100644 --- a/drivers/media/pci/mantis/mantis_dvb.c +++ b/drivers/media/pci/mantis/mantis_dvb.c @@ -205,7 +205,7 @@ int mantis_dvb_init(struct mantis_pci *mantis) } dvb_net_init(&mantis->dvb_adapter, &mantis->dvbnet, &mantis->demux.dmx); - tasklet_init(&mantis->tasklet, mantis_dma_xfer, (unsigned long) mantis); + tasklet_setup(&mantis->tasklet, mantis_dma_xfer); tasklet_disable(&mantis->tasklet); if (mantis->hwconfig) { result = config->frontend_init(mantis, mantis->fe); diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c index 80a7c41baa90..6f3125c2d097 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c @@ -1016,8 +1016,6 @@ static struct pci_driver netup_unidvb_pci_driver = { .id_table = netup_unidvb_pci_tbl, .probe = netup_unidvb_initdev, .remove = netup_unidvb_finidev, - .suspend = NULL, - .resume = NULL, }; module_pci_driver(netup_unidvb_pci_driver); diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c index af15ca1c501b..f9f94f47d76b 100644 --- a/drivers/media/pci/ngene/ngene-core.c +++ b/drivers/media/pci/ngene/ngene-core.c @@ -50,9 +50,9 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); /* nGene interrupt handler **************************************************/ /****************************************************************************/ -static void event_tasklet(unsigned long data) +static void event_tasklet(struct tasklet_struct *t) { - struct ngene *dev = (struct ngene *)data; + struct ngene *dev = from_tasklet(dev, t, event_tasklet); while (dev->EventQueueReadIndex != dev->EventQueueWriteIndex) { struct EVENT_BUFFER Event = @@ -68,9 +68,9 @@ static void event_tasklet(unsigned long data) } } -static void demux_tasklet(unsigned long data) +static void demux_tasklet(struct tasklet_struct *t) { - struct ngene_channel *chan = (struct ngene_channel *)data; + struct ngene_channel *chan = from_tasklet(chan, t, demux_tasklet); struct device *pdev = &chan->dev->pci_dev->dev; struct SBufferHeader *Cur = chan->nextBuffer; @@ -1181,7 +1181,7 @@ static void ngene_init(struct ngene *dev) struct device *pdev = &dev->pci_dev->dev; int i; - tasklet_init(&dev->event_tasklet, event_tasklet, (unsigned long)dev); + tasklet_setup(&dev->event_tasklet, event_tasklet); memset_io(dev->iomem + 0xc000, 0x00, 0x220); memset_io(dev->iomem + 0xc400, 0x00, 0x100); @@ -1445,7 +1445,7 @@ static int init_channel(struct ngene_channel *chan) struct ngene_info *ni = dev->card_info; int io = ni->io_type[nr]; - tasklet_init(&chan->demux_tasklet, demux_tasklet, (unsigned long)chan); + tasklet_setup(&chan->demux_tasklet, demux_tasklet); chan->users = 0; chan->type = io; chan->mode = chan->type; /* for now only one mode */ diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c index 544ca57eee75..7a1fb067b0e0 100644 --- a/drivers/media/pci/saa7134/saa7134-alsa.c +++ b/drivers/media/pci/saa7134/saa7134-alsa.c @@ -252,7 +252,8 @@ static int snd_card_saa7134_capture_trigger(struct snd_pcm_substream * substream return err; } -static int saa7134_alsa_dma_init(struct saa7134_dev *dev, int nr_pages) +static int saa7134_alsa_dma_init(struct saa7134_dev *dev, + unsigned long nr_pages) { struct saa7134_dmasound *dma = &dev->dmasound; struct page *pg; @@ -260,11 +261,11 @@ static int saa7134_alsa_dma_init(struct saa7134_dev *dev, int nr_pages) dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); if (NULL == dma->vaddr) { - pr_debug("vmalloc_32(%d pages) failed\n", nr_pages); + pr_debug("vmalloc_32(%lu pages) failed\n", nr_pages); return -ENOMEM; } - pr_debug("vmalloc is at addr %p, size=%d\n", + pr_debug("vmalloc is at addr %p, size=%lu\n", dma->vaddr, nr_pages << PAGE_SHIFT); memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT); @@ -297,7 +298,7 @@ static int saa7134_alsa_dma_map(struct saa7134_dev *dev) struct saa7134_dmasound *dma = &dev->dmasound; dma->sglen = dma_map_sg(&dev->pci->dev, dma->sglist, - dma->nr_pages, PCI_DMA_FROMDEVICE); + dma->nr_pages, DMA_FROM_DEVICE); if (0 == dma->sglen) { pr_warn("%s: saa7134_alsa_map_sg failed\n", __func__); @@ -313,7 +314,7 @@ static int saa7134_alsa_dma_unmap(struct saa7134_dev *dev) if (!dma->sglen) return 0; - dma_unmap_sg(&dev->pci->dev, dma->sglist, dma->sglen, PCI_DMA_FROMDEVICE); + dma_unmap_sg(&dev->pci->dev, dma->sglist, dma->nr_pages, DMA_FROM_DEVICE); dma->sglen = 0; return 0; } diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c index c1937c33c33d..ce449c941171 100644 --- a/drivers/media/pci/saa7134/saa7134-cards.c +++ b/drivers/media/pci/saa7134/saa7134-cards.c @@ -7812,7 +7812,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) dev->name, saa7134_boards[dev->board].name); break; } - /* fall-through */ + fallthrough; case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_ASUS_EUROPA2_HYBRID: case SAA7134_BOARD_ASUS_EUROPA_HYBRID: @@ -7870,7 +7870,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: hauppauge_eeprom(dev, dev->eedata+0x80); - /* fall-through */ + fallthrough; case SAA7134_BOARD_PINNACLE_PCTV_310i: case SAA7134_BOARD_KWORLD_DVBT_210: case SAA7134_BOARD_TEVION_DVBT_220RF: diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index e4623ed2f831..391572a6ec76 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -359,14 +359,12 @@ void saa7134_stop_streaming(struct saa7134_dev *dev, struct saa7134_dmaqueue *q) struct saa7134_buf *tmp; spin_lock_irqsave(&dev->slock, flags); - if (!list_empty(&q->queue)) { - list_for_each_safe(pos, n, &q->queue) { - tmp = list_entry(pos, struct saa7134_buf, entry); - vb2_buffer_done(&tmp->vb2.vb2_buf, - VB2_BUF_STATE_ERROR); - list_del(pos); - tmp = NULL; - } + list_for_each_safe(pos, n, &q->queue) { + tmp = list_entry(pos, struct saa7134_buf, entry); + vb2_buffer_done(&tmp->vb2.vb2_buf, + VB2_BUF_STATE_ERROR); + list_del(pos); + tmp = NULL; } spin_unlock_irqrestore(&dev->slock, flags); saa7134_buffer_timeout(&q->timeout); /* also calls del_timer(&q->timeout) */ @@ -965,21 +963,21 @@ static void saa7134_unregister_video(struct saa7134_dev *dev) if (dev->video_dev) { if (video_is_registered(dev->video_dev)) - video_unregister_device(dev->video_dev); + vb2_video_unregister_device(dev->video_dev); else video_device_release(dev->video_dev); dev->video_dev = NULL; } if (dev->vbi_dev) { if (video_is_registered(dev->vbi_dev)) - video_unregister_device(dev->vbi_dev); + vb2_video_unregister_device(dev->vbi_dev); else video_device_release(dev->vbi_dev); dev->vbi_dev = NULL; } if (dev->radio_dev) { if (video_is_registered(dev->radio_dev)) - video_unregister_device(dev->radio_dev); + vb2_video_unregister_device(dev->radio_dev); else video_device_release(dev->radio_dev); dev->radio_dev = NULL; @@ -1370,11 +1368,9 @@ static void saa7134_finidev(struct pci_dev *pci_dev) kfree(dev); } -#ifdef CONFIG_PM - /* resends a current buffer in queue after resume */ -static int saa7134_buffer_requeue(struct saa7134_dev *dev, - struct saa7134_dmaqueue *q) +static int __maybe_unused saa7134_buffer_requeue(struct saa7134_dev *dev, + struct saa7134_dmaqueue *q) { struct saa7134_buf *buf, *next; @@ -1397,8 +1393,9 @@ static int saa7134_buffer_requeue(struct saa7134_dev *dev, return 0; } -static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state) +static int __maybe_unused saa7134_suspend(struct device *dev_d) { + struct pci_dev *pci_dev = to_pci_dev(dev_d); struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev); @@ -1428,21 +1425,15 @@ static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state) if (dev->remote && dev->remote->dev->users) saa7134_ir_close(dev->remote->dev); - pci_save_state(pci_dev); - pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); - return 0; } -static int saa7134_resume(struct pci_dev *pci_dev) +static int __maybe_unused saa7134_resume(struct device *dev_d) { - struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d); struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev); unsigned long flags; - pci_set_power_state(pci_dev, PCI_D0); - pci_restore_state(pci_dev); - /* Do things that are done in saa7134_initdev , except of initializing memory structures.*/ @@ -1490,7 +1481,6 @@ static int saa7134_resume(struct pci_dev *pci_dev) return 0; } -#endif /* ----------------------------------------------------------- */ @@ -1522,15 +1512,14 @@ EXPORT_SYMBOL(saa7134_ts_unregister); /* ----------------------------------------------------------- */ +static SIMPLE_DEV_PM_OPS(saa7134_pm_ops, saa7134_suspend, saa7134_resume); + static struct pci_driver saa7134_pci_driver = { .name = "saa7134", .id_table = saa7134_pci_tbl, .probe = saa7134_initdev, .remove = saa7134_finidev, -#ifdef CONFIG_PM - .suspend = saa7134_suspend, - .resume = saa7134_resume -#endif + .driver.pm = &saa7134_pm_ops, }; static int __init saa7134_init(void) diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index 8ad7879bd840..39e3c7f8c5b4 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -314,8 +314,7 @@ static int empress_fini(struct saa7134_dev *dev) if (NULL == dev->empress_dev) return 0; flush_work(&dev->empress_workqueue); - video_unregister_device(dev->empress_dev); - vb2_queue_release(&dev->empress_vbq); + vb2_video_unregister_device(dev->empress_dev); v4l2_ctrl_handler_free(&dev->empress_ctrl_handler); dev->empress_dev = NULL; return 0; diff --git a/drivers/media/pci/saa7134/saa7134-go7007.c b/drivers/media/pci/saa7134/saa7134-go7007.c index e1b034663958..f319edb39c0e 100644 --- a/drivers/media/pci/saa7134/saa7134-go7007.c +++ b/drivers/media/pci/saa7134/saa7134-go7007.c @@ -493,7 +493,7 @@ static int saa7134_go7007_fini(struct saa7134_dev *dev) free_page((unsigned long)saa->bottom); v4l2_device_unregister_subdev(&saa->sd); kfree(saa); - video_unregister_device(&go->vdev); + vb2_video_unregister_device(&go->vdev); v4l2_device_put(&go->v4l2_dev); dev->empress_dev = NULL; diff --git a/drivers/media/pci/saa7134/saa7134-tvaudio.c b/drivers/media/pci/saa7134/saa7134-tvaudio.c index 79e1afb71075..5cc4ef21f9d3 100644 --- a/drivers/media/pci/saa7134/saa7134-tvaudio.c +++ b/drivers/media/pci/saa7134/saa7134-tvaudio.c @@ -683,7 +683,8 @@ int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value) { int err; - audio_dbg(2, "dsp write reg 0x%x = 0x%06x\n", reg << 2, value); + audio_dbg(2, "dsp write reg 0x%x = 0x%06x\n", + (reg << 2) & 0xffffffff, value); err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_WRR); if (err < 0) return err; diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index a8ac94fadc14..9a6a6b68f8e3 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -2154,9 +2154,7 @@ int saa7134_video_init1(struct saa7134_dev *dev) void saa7134_video_fini(struct saa7134_dev *dev) { /* free stuff */ - vb2_queue_release(&dev->video_vbq); saa7134_pgtable_free(dev->pci, &dev->video_q.pt); - vb2_queue_release(&dev->vbi_vbq); saa7134_pgtable_free(dev->pci, &dev->vbi_q.pt); v4l2_ctrl_handler_free(&dev->ctrl_handler); if (card_has_radio(dev)) diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index 77c325e64a97..d29499cd7370 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -509,7 +509,7 @@ struct saa7134_dmasound { void *vaddr; struct scatterlist *sglist; int sglen; - int nr_pages; + unsigned long nr_pages; unsigned int dma_blk; unsigned int read_offset; unsigned int read_count; diff --git a/drivers/media/pci/saa7164/saa7164-buffer.c b/drivers/media/pci/saa7164/saa7164-buffer.c index 289cb901985b..245d9db280aa 100644 --- a/drivers/media/pci/saa7164/saa7164-buffer.c +++ b/drivers/media/pci/saa7164/saa7164-buffer.c @@ -250,15 +250,14 @@ int saa7164_buffer_cfg_port(struct saa7164_port *port) list_for_each_safe(c, n, &port->dmaqueue.list) { buf = list_entry(c, struct saa7164_buffer, list); - if (buf->flags != SAA7164_BUFFER_FREE) - BUG(); + BUG_ON(buf->flags != SAA7164_BUFFER_FREE); /* Place the buffer in the h/w queue */ saa7164_buffer_activate(buf, i); /* Don't exceed the device maximum # bufs */ - if (i++ > port->hwcfg.buffercount) - BUG(); + BUG_ON(i > port->hwcfg.buffercount); + i++; } mutex_unlock(&port->dmaqueue_lock); @@ -302,4 +301,3 @@ void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf) kfree(buf); } - diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index 4b637891b79a..6c08b77bfd47 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -575,8 +575,8 @@ static irqreturn_t saa7164_irq_ts(struct saa7164_port *port) /* Find the current write point from the hardware */ wp = saa7164_readl(port->bufcounter); - if (wp > (port->hwcfg.buffercount - 1)) - BUG(); + + BUG_ON(wp > (port->hwcfg.buffercount - 1)); /* Find the previous buffer to the current write point */ if (wp == 0) @@ -588,8 +588,8 @@ static irqreturn_t saa7164_irq_ts(struct saa7164_port *port) /* TODO: turn this into a worker thread */ list_for_each_safe(c, n, &port->dmaqueue.list) { buf = list_entry(c, struct saa7164_buffer, list); - if (i++ > port->hwcfg.buffercount) - BUG(); + BUG_ON(i > port->hwcfg.buffercount); + i++; if (buf->idx == rp) { /* Found the buffer, deal with it */ @@ -894,8 +894,7 @@ static int saa7164_port_init(struct saa7164_dev *dev, int portnr) { struct saa7164_port *port = NULL; - if ((portnr < 0) || (portnr >= SAA7164_MAX_PORTS)) - BUG(); + BUG_ON((portnr < 0) || (portnr >= SAA7164_MAX_PORTS)); port = &dev->ports[portnr]; @@ -1563,4 +1562,3 @@ static void __exit saa7164_fini(void) module_init(saa7164_init); module_exit(saa7164_fini); - diff --git a/drivers/media/pci/saa7164/saa7164-dvb.c b/drivers/media/pci/saa7164/saa7164-dvb.c index bf8c2bb8852e..24421c116b0b 100644 --- a/drivers/media/pci/saa7164/saa7164-dvb.c +++ b/drivers/media/pci/saa7164/saa7164-dvb.c @@ -337,8 +337,7 @@ static int dvb_register(struct saa7164_port *port) dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); - if (port->type != SAA7164_MPEG_DVB) - BUG(); + BUG_ON(port->type != SAA7164_MPEG_DVB); /* Sanity check that the PCI configuration space is active */ if (port->hwcfg.BARLocation == 0) { @@ -479,8 +478,7 @@ int saa7164_dvb_unregister(struct saa7164_port *port) dprintk(DBGLVL_DVB, "%s()\n", __func__); - if (port->type != SAA7164_MPEG_DVB) - BUG(); + BUG_ON(port->type != SAA7164_MPEG_DVB); /* Remove any allocated buffers */ mutex_lock(&port->dmaqueue_lock); @@ -740,4 +738,3 @@ frontend_detach: printk(KERN_ERR "%s() Frontend/I2C initialization failed\n", __func__); return -1; } - diff --git a/drivers/media/pci/saa7164/saa7164-vbi.c b/drivers/media/pci/saa7164/saa7164-vbi.c index 49d61a64c8cb..cb2e09f0841d 100644 --- a/drivers/media/pci/saa7164/saa7164-vbi.c +++ b/drivers/media/pci/saa7164/saa7164-vbi.c @@ -703,8 +703,7 @@ int saa7164_vbi_register(struct saa7164_port *port) dprintk(DBGLVL_VBI, "%s()\n", __func__); - if (port->type != SAA7164_MPEG_VBI) - BUG(); + BUG_ON(port->type != SAA7164_MPEG_VBI); /* Sanity check that the PCI configuration space is active */ if (port->hwcfg.BARLocation == 0) { @@ -756,8 +755,7 @@ void saa7164_vbi_unregister(struct saa7164_port *port) dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr); - if (port->type != SAA7164_MPEG_VBI) - BUG(); + BUG_ON(port->type != SAA7164_MPEG_VBI); if (port->v4l_device) { if (port->v4l_device->minor != -1) diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c index 9445d792bfc9..e6b74e161a05 100644 --- a/drivers/media/pci/smipcie/smipcie-ir.c +++ b/drivers/media/pci/smipcie/smipcie-ir.c @@ -87,8 +87,7 @@ static void smi_ir_decode(struct smi_rc *ir) struct ir_raw_event rawir = {}; rawir.pulse = 0; - rawir.duration = US_TO_NS(SMI_SAMPLE_PERIOD * - SMI_SAMPLE_IDLEMIN); + rawir.duration = SMI_SAMPLE_PERIOD * SMI_SAMPLE_IDLEMIN; ir_raw_event_store_with_filter(rc_dev, &rawir); smi_set(IR_Init_Reg, rbIRhighidle); } @@ -151,8 +150,8 @@ int smi_ir_init(struct smi_dev *dev) rc_dev->dev.parent = &dev->pci_dev->dev; rc_dev->map_name = dev->info->rc_map; - rc_dev->timeout = MS_TO_NS(100); - rc_dev->rx_resolution = US_TO_NS(SMI_SAMPLE_PERIOD); + rc_dev->timeout = MS_TO_US(100); + rc_dev->rx_resolution = SMI_SAMPLE_PERIOD; ir->rc_dev = rc_dev; ir->dev = dev; diff --git a/drivers/media/pci/smipcie/smipcie-main.c b/drivers/media/pci/smipcie/smipcie-main.c index 9ca0fc3e6f80..e7604b7ecc8d 100644 --- a/drivers/media/pci/smipcie/smipcie-main.c +++ b/drivers/media/pci/smipcie/smipcie-main.c @@ -280,9 +280,9 @@ static void smi_port_clearInterrupt(struct smi_port *port) } /* tasklet handler: DMA data to dmx.*/ -static void smi_dma_xfer(unsigned long data) +static void smi_dma_xfer(struct tasklet_struct *t) { - struct smi_port *port = (struct smi_port *) data; + struct smi_port *port = from_tasklet(port, t, tasklet); struct smi_dev *dev = port->dev; u32 intr_status, finishedData, dmaManagement; u8 dmaChan0State, dmaChan1State; @@ -422,7 +422,7 @@ static int smi_port_init(struct smi_port *port, int dmaChanUsed) } smi_port_disableInterrupt(port); - tasklet_init(&port->tasklet, smi_dma_xfer, (unsigned long)port); + tasklet_setup(&port->tasklet, smi_dma_xfer); tasklet_disable(&port->tasklet); port->enable = 1; return 0; diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index c6e0090f27e8..d497afc7e7b7 100644 --- a/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -503,7 +503,7 @@ static int solo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) default: dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, assuming 4 ch\n", chip_id); - /* fall through */ + fallthrough; case 5: solo_dev->nr_chans = 4; solo_dev->nr_ext = 1; diff --git a/drivers/media/pci/solo6x10/solo6x10-i2c.c b/drivers/media/pci/solo6x10/solo6x10-i2c.c index f86f12fa6350..7db785e9c997 100644 --- a/drivers/media/pci/solo6x10/solo6x10-i2c.c +++ b/drivers/media/pci/solo6x10/solo6x10-i2c.c @@ -183,7 +183,7 @@ int solo_i2c_isr(struct solo_dev *solo_dev) } solo_dev->i2c_state = IIC_STATE_WRITE; - /* fall through */ + fallthrough; case IIC_STATE_WRITE: ret = solo_i2c_handle_write(solo_dev); break; diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 0fdb0fd6e764..336df65c8af1 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -1101,12 +1101,11 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, vunreg: video_set_drvdata(&vip->video_dev, NULL); vrelease: - video_unregister_device(&vip->video_dev); + vb2_video_unregister_device(&vip->video_dev); free_irq(pdev->irq, vip); release_buf: pci_disable_msi(pdev); unmap: - vb2_queue_release(&vip->vb_vidq); pci_iounmap(pdev, vip->iomem); release: pci_release_regions(pdev); @@ -1146,10 +1145,9 @@ static void sta2x11_vip_remove_one(struct pci_dev *pdev) sta2x11_vip_clear_register(vip); video_set_drvdata(&vip->video_dev, NULL); - video_unregister_device(&vip->video_dev); + vb2_video_unregister_device(&vip->video_dev); free_irq(pdev->irq, vip); pci_disable_msi(pdev); - vb2_queue_release(&vip->vb_vidq); pci_iounmap(pdev, vip->iomem); pci_release_regions(pdev); diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index 45228f4f6fc6..2f7069e19b78 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -357,9 +357,9 @@ static inline void start_debi_dma(struct av7110 *av7110, int dir, irdebi(av7110, DEBISWAB, addr, 0, len); } -static void debiirq(unsigned long cookie) +static void debiirq(struct tasklet_struct *t) { - struct av7110 *av7110 = (struct av7110 *)cookie; + struct av7110 *av7110 = from_tasklet(av7110, t, debi_tasklet); int type = av7110->debitype; int handle = (type >> 8) & 0x1f; unsigned int xfer = 0; @@ -458,9 +458,9 @@ debi_done: } /* irq from av7110 firmware writing the mailbox register in the DPRAM */ -static void gpioirq(unsigned long cookie) +static void gpioirq(struct tasklet_struct *t) { - struct av7110 *av7110 = (struct av7110 *)cookie; + struct av7110 *av7110 = from_tasklet(av7110, t, gpio_tasklet); u32 rxbuf, txbuf; int len; @@ -1230,9 +1230,9 @@ static int budget_stop_feed(struct dvb_demux_feed *feed) return status; } -static void vpeirq(unsigned long cookie) +static void vpeirq(struct tasklet_struct *t) { - struct av7110 *budget = (struct av7110 *)cookie; + struct av7110 *budget = from_tasklet(budget, t, vpe_tasklet); u8 *mem = (u8 *) (budget->grabbing); u32 olddma = budget->ttbp; u32 newdma = saa7146_read(budget->dev, PCI_VDP3); @@ -2518,7 +2518,7 @@ static int av7110_attach(struct saa7146_dev* dev, saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH); saa7146_write(dev, MC2, MASK_04 | MASK_20); - tasklet_init(&av7110->vpe_tasklet, vpeirq, (unsigned long) av7110); + tasklet_setup(&av7110->vpe_tasklet, vpeirq); } else if (budgetpatch) { spin_lock_init(&av7110->feedlock1); @@ -2599,7 +2599,7 @@ static int av7110_attach(struct saa7146_dev* dev, saa7146_write(dev, MC1, (MASK_13 | MASK_29)); /* end of budgetpatch register initialization */ - tasklet_init (&av7110->vpe_tasklet, vpeirq, (unsigned long) av7110); + tasklet_setup(&av7110->vpe_tasklet, vpeirq); } else { saa7146_write(dev, PCI_BT_V1, 0x1c00101f); saa7146_write(dev, BCS_CTRL, 0x80400040); @@ -2614,8 +2614,8 @@ static int av7110_attach(struct saa7146_dev* dev, saa7146_write(dev, GPIO_CTRL, 0x000000); } - tasklet_init (&av7110->debi_tasklet, debiirq, (unsigned long) av7110); - tasklet_init (&av7110->gpio_tasklet, gpioirq, (unsigned long) av7110); + tasklet_setup(&av7110->debi_tasklet, debiirq); + tasklet_setup(&av7110->gpio_tasklet, gpioirq); mutex_init(&av7110->pid_mutex); diff --git a/drivers/media/pci/ttpci/av7110_v4l.c b/drivers/media/pci/ttpci/av7110_v4l.c index cabe006658dd..c89f536f699c 100644 --- a/drivers/media/pci/ttpci/av7110_v4l.c +++ b/drivers/media/pci/ttpci/av7110_v4l.c @@ -160,9 +160,9 @@ static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq) buf[1] = div & 0xff; buf[2] = 0x8e; - if (freq < (u32) (16 * 168.25)) + if (freq < 16U * 16825 / 100) config = 0xa0; - else if (freq < (u32) (16 * 447.25)) + else if (freq < 16U * 44725 / 100) config = 0x90; else config = 0x30; diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c index 77b102b8a013..d59d18647371 100644 --- a/drivers/media/pci/ttpci/budget-ci.c +++ b/drivers/media/pci/ttpci/budget-ci.c @@ -99,9 +99,10 @@ struct budget_ci { u8 tuner_pll_address; /* used for philips_tdm1316l configs */ }; -static void msp430_ir_interrupt(unsigned long data) +static void msp430_ir_interrupt(struct tasklet_struct *t) { - struct budget_ci *budget_ci = (struct budget_ci *) data; + struct budget_ci_ir *ir = from_tasklet(ir, t, msp430_irq_tasklet); + struct budget_ci *budget_ci = container_of(ir, typeof(*budget_ci), ir); struct rc_dev *dev = budget_ci->ir.dev; u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; @@ -229,8 +230,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) budget_ci->ir.dev = dev; - tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt, - (unsigned long) budget_ci); + tasklet_setup(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt); SAA7146_IER_ENABLE(saa, MASK_06); saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI); @@ -348,9 +348,10 @@ static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) return 0; } -static void ciintf_interrupt(unsigned long data) +static void ciintf_interrupt(struct tasklet_struct *t) { - struct budget_ci *budget_ci = (struct budget_ci *) data; + struct budget_ci *budget_ci = from_tasklet(budget_ci, t, + ciintf_irq_tasklet); struct saa7146_dev *saa = budget_ci->budget.dev; unsigned int flags; @@ -491,7 +492,7 @@ static int ciintf_init(struct budget_ci *budget_ci) // Setup CI slot IRQ if (budget_ci->ci_irq) { - tasklet_init(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci); + tasklet_setup(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt); if (budget_ci->slot_status != SLOTSTATUS_NONE) { saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); } else { diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c index 293867b9e796..d405eea5c37f 100644 --- a/drivers/media/pci/ttpci/budget-core.c +++ b/drivers/media/pci/ttpci/budget-core.c @@ -171,9 +171,9 @@ static int budget_read_fe_status(struct dvb_frontend *fe, return ret; } -static void vpeirq(unsigned long data) +static void vpeirq(struct tasklet_struct *t) { - struct budget *budget = (struct budget *) data; + struct budget *budget = from_tasklet(budget, t, vpe_tasklet); u8 *mem = (u8 *) (budget->grabbing); u32 olddma = budget->ttbp; u32 newdma = saa7146_read(budget->dev, PCI_VDP3); @@ -519,7 +519,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, /* upload all */ saa7146_write(dev, GPIO_CTRL, 0x000000); - tasklet_init(&budget->vpe_tasklet, vpeirq, (unsigned long) budget); + tasklet_setup(&budget->vpe_tasklet, vpeirq); /* frontend power on */ if (bi->type != BUDGET_FS_ACTIVY) diff --git a/drivers/media/pci/tw5864/tw5864-video.c b/drivers/media/pci/tw5864/tw5864-video.c index ec1e06da7e4f..9131265c2b87 100644 --- a/drivers/media/pci/tw5864/tw5864-video.c +++ b/drivers/media/pci/tw5864/tw5864-video.c @@ -175,7 +175,7 @@ static const unsigned int intra4x4_lambda3[] = { static v4l2_std_id tw5864_get_v4l2_std(enum tw5864_vid_std std); static enum tw5864_vid_std tw5864_from_v4l2_std(v4l2_std_id v4l2_std); -static void tw5864_handle_frame_task(unsigned long data); +static void tw5864_handle_frame_task(struct tasklet_struct *t); static void tw5864_handle_frame(struct tw5864_h264_frame *frame); static void tw5864_frame_interval_set(struct tw5864_input *input); @@ -767,6 +767,9 @@ static int tw5864_enum_frameintervals(struct file *file, void *priv, fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE; ret = tw5864_frameinterval_get(input, &frameinterval); + if (ret) + return ret; + fintv->stepwise.step = frameinterval; fintv->stepwise.min = frameinterval; fintv->stepwise.max = frameinterval; @@ -785,6 +788,9 @@ static int tw5864_g_parm(struct file *file, void *priv, cp->capability = V4L2_CAP_TIMEPERFRAME; ret = tw5864_frameinterval_get(input, &cp->timeperframe); + if (ret) + return ret; + cp->timeperframe.numerator *= input->frame_interval; cp->capturemode = 0; cp->readbuffers = 2; @@ -1057,8 +1063,7 @@ int tw5864_video_init(struct tw5864_dev *dev, int *video_nr) dev->irqmask |= TW5864_INTR_VLC_DONE | TW5864_INTR_TIMER; tw5864_irqmask_apply(dev); - tasklet_init(&dev->tasklet, tw5864_handle_frame_task, - (unsigned long)dev); + tasklet_setup(&dev->tasklet, tw5864_handle_frame_task); for (i = 0; i < TW5864_INPUTS; i++) { dev->inputs[i].root = dev; @@ -1178,7 +1183,6 @@ static int tw5864_video_input_init(struct tw5864_input *input, int video_nr) free_v4l2_hdl: v4l2_ctrl_handler_free(hdl); - vb2_queue_release(&input->vidq); free_mutex: mutex_destroy(&input->lock); @@ -1187,9 +1191,8 @@ free_mutex: static void tw5864_video_input_fini(struct tw5864_input *dev) { - video_unregister_device(&dev->vdev); + vb2_video_unregister_device(&dev->vdev); v4l2_ctrl_handler_free(&dev->hdl); - vb2_queue_release(&dev->vidq); } void tw5864_video_fini(struct tw5864_dev *dev) @@ -1313,9 +1316,9 @@ static int tw5864_is_motion_triggered(struct tw5864_h264_frame *frame) return detected; } -static void tw5864_handle_frame_task(unsigned long data) +static void tw5864_handle_frame_task(struct tasklet_struct *t) { - struct tw5864_dev *dev = (struct tw5864_dev *)data; + struct tw5864_dev *dev = from_tasklet(dev, t, tasklet); unsigned long flags; int batch_size = H264_BUF_CNT; diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index c57ee78fa99d..a3cb104956d5 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -256,13 +256,14 @@ config VIDEO_MEDIATEK_VCODEC select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV select VIDEO_MEDIATEK_VPU + select MTK_SCP help Mediatek video codec driver provides HW capability to encode and decode in a range of video formats This driver rely on VPU driver to communicate with VPU. - To compile this driver as a module, choose M here: the - module will be called mtk-vcodec + To compile this driver as modules, choose M here: the + modules will be called mtk-vcodec-dec and mtk-vcodec-enc. config VIDEO_MEM2MEM_DEINTERLACE tristate "Deinterlace support" @@ -426,8 +427,8 @@ config VIDEO_RENESAS_FCP help This is a driver for the Renesas Frame Compression Processor (FCP). The FCP is a companion module of video processing modules in the - Renesas R-Car Gen3 SoCs. It handles memory access for the codec, - VSP and FDP modules. + Renesas R-Car Gen3 and RZ/G2 SoCs. It handles memory access for + the codec, VSP and FDP modules. To compile this driver as a module, choose M here: the module will be called rcar-fcp. diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c index 7d98db1d9b52..c46a79eace98 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c @@ -1597,7 +1597,6 @@ static int aspeed_video_setup_video(struct aspeed_video *video) video_set_drvdata(vdev, video); rc = video_register_device(vdev, VFL_TYPE_VIDEO, 0); if (rc) { - vb2_queue_release(vbq); v4l2_ctrl_handler_free(&video->ctrl_handler); v4l2_device_unregister(v4l2_dev); @@ -1737,9 +1736,7 @@ static int aspeed_video_remove(struct platform_device *pdev) clk_unprepare(video->vclk); clk_unprepare(video->eclk); - video_unregister_device(&video->vdev); - - vb2_queue_release(&video->queue); + vb2_video_unregister_device(&video->vdev); v4l2_ctrl_handler_free(&video->ctrl_handler); diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index b021604eceaa..bf75927bac4e 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -1101,7 +1101,7 @@ static int coda_start_encoding(struct coda_ctx *ctx) break; case CODA_960: coda_write(dev, 0, CODA9_GDI_WPROT_RGN_EN); - /* fallthrough */ + fallthrough; case CODA_HX4: case CODA_7541: coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN | @@ -1141,7 +1141,7 @@ static int coda_start_encoding(struct coda_ctx *ctx) CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; break; } - /* fallthrough */ + fallthrough; case CODA_960: value = (q_data_src->rect.width & CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET; diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 3ab3d976d8ca..87a2c706f747 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -808,7 +808,7 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f, ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP; break; } - /* else fall through */ + fallthrough; case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_YUV422P: @@ -1015,7 +1015,7 @@ static int coda_g_selection(struct file *file, void *fh, case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: rsel = &r; - /* fallthrough */ + fallthrough; case V4L2_SEL_TGT_CROP: if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || ctx->inst_type == CODA_INST_DECODER) @@ -1024,7 +1024,7 @@ static int coda_g_selection(struct file *file, void *fh, case V4L2_SEL_TGT_COMPOSE_BOUNDS: case V4L2_SEL_TGT_COMPOSE_PADDED: rsel = &r; - /* fallthrough */ + fallthrough; case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_DEFAULT: if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || @@ -1074,7 +1074,7 @@ static int coda_s_selection(struct file *file, void *fh, return 0; } - /* else fall through */ + fallthrough; case V4L2_SEL_TGT_NATIVE_SIZE: case V4L2_SEL_TGT_COMPOSE: return coda_g_selection(file, fh, s); @@ -1937,9 +1937,6 @@ int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf, buf->blob.size = size; buf->dentry = debugfs_create_blob(name, 0644, parent, &buf->blob); - if (!buf->dentry) - dev_warn(dev->dev, - "failed to create debugfs entry %s\n", name); } return 0; @@ -2628,7 +2625,7 @@ static int coda_open(struct file *file) */ if (enable_bwb || ctx->inst_type == CODA_INST_ENCODER) ctx->frame_mem_ctrl = CODA9_FRAME_ENABLE_BWB; - /* fallthrough */ + fallthrough; case CODA_HX4: case CODA_7541: ctx->reg_idx = 0; @@ -3211,8 +3208,6 @@ static int coda_probe(struct platform_device *pdev) ida_init(&dev->ida); dev->debugfs_root = debugfs_create_dir("coda", NULL); - if (!dev->debugfs_root) - dev_warn(&pdev->dev, "failed to create debugfs root\n"); /* allocate auxiliary per-device buffers for the BIT processor */ if (dev->devtype->product == CODA_DX6) { @@ -3269,6 +3264,8 @@ static int coda_probe(struct platform_device *pdev) return 0; err_alloc_workqueue: + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); destroy_workqueue(dev->workqueue); err_v4l2_register: v4l2_device_unregister(&dev->v4l2_dev); diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index e7a4b06e6dfe..6000a4e789ad 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -495,17 +495,6 @@ static int fimc_capture_open(struct file *file) ret = fimc_pipeline_call(ve, open, &ve->vdev.entity, true); - if (ret == 0 && vc->user_subdev_api && vc->inh_sensor_ctrls) { - /* - * Recreate controls of the the video node to drop - * any controls inherited from the sensor subdev. - */ - fimc_ctrls_delete(vc->ctx); - - ret = fimc_ctrls_create(vc->ctx); - if (ret == 0) - vc->inh_sensor_ctrls = false; - } if (ret == 0) ve->vdev.entity.use_count++; @@ -1246,8 +1235,11 @@ static int fimc_cap_streamoff(struct file *file, void *priv, if (ret < 0) return ret; - media_pipeline_stop(&vc->ve.vdev.entity); - vc->streaming = false; + if (vc->streaming) { + media_pipeline_stop(&vc->ve.vdev.entity); + vc->streaming = false; + } + return 0; } @@ -1279,7 +1271,7 @@ static int fimc_cap_g_selection(struct file *file, void *fh, case V4L2_SEL_TGT_COMPOSE_DEFAULT: case V4L2_SEL_TGT_COMPOSE_BOUNDS: f = &ctx->d_frame; - /* fall through */ + fallthrough; case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: s->r.left = 0; @@ -1290,7 +1282,7 @@ static int fimc_cap_g_selection(struct file *file, void *fh, case V4L2_SEL_TGT_COMPOSE: f = &ctx->d_frame; - /* fall through */ + fallthrough; case V4L2_SEL_TGT_CROP: s->r.left = f->offs_h; s->r.top = f->offs_v; @@ -1398,7 +1390,7 @@ static int fimc_link_setup(struct media_entity *entity, vc->input = sd->grp_id; - if (vc->user_subdev_api || vc->inh_sensor_ctrls) + if (vc->user_subdev_api) return 0; /* Inherit V4L2 controls from the image sensor subdev. */ @@ -1601,7 +1593,7 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_COMPOSE_BOUNDS: f = &ctx->d_frame; - /* fall through */ + fallthrough; case V4L2_SEL_TGT_CROP_BOUNDS: r->width = f->o_width; r->height = f->o_height; @@ -1888,6 +1880,7 @@ int fimc_initialize_capture_subdev(struct fimc_dev *fimc) return ret; sd->entity.ops = &fimc_sd_media_ops; + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; sd->internal_ops = &fimc_capture_sd_internal_ops; v4l2_set_subdevdata(sd, fimc); return 0; diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index cde60fbb23a8..08d1f39a914c 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -954,9 +954,11 @@ static int fimc_probe(struct platform_device *pdev) spin_lock_init(&fimc->slock); mutex_init(&fimc->lock); - fimc->sysreg = fimc_get_sysreg_regmap(dev->of_node); - if (IS_ERR(fimc->sysreg)) - return PTR_ERR(fimc->sysreg); + if (fimc->variant->has_isp_wb) { + fimc->sysreg = fimc_get_sysreg_regmap(dev->of_node); + if (IS_ERR(fimc->sysreg)) + return PTR_ERR(fimc->sysreg); + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); fimc->regs = devm_ioremap_resource(dev, res); @@ -1110,67 +1112,8 @@ static int fimc_remove(struct platform_device *pdev) return 0; } -/* Image pixel limits, similar across several FIMC HW revisions. */ -static const struct fimc_pix_limit s5p_pix_limit[4] = { - [0] = { - .scaler_en_w = 3264, - .scaler_dis_w = 8192, - .out_rot_en_w = 1920, - .out_rot_dis_w = 4224, - }, - [1] = { - .scaler_en_w = 4224, - .scaler_dis_w = 8192, - .out_rot_en_w = 1920, - .out_rot_dis_w = 4224, - }, - [2] = { - .scaler_en_w = 1920, - .scaler_dis_w = 8192, - .out_rot_en_w = 1280, - .out_rot_dis_w = 1920, - }, -}; - -static const struct fimc_variant fimc0_variant_s5pv210 = { - .has_inp_rot = 1, - .has_out_rot = 1, - .has_cam_if = 1, - .min_inp_pixsize = 16, - .min_out_pixsize = 16, - .hor_offs_align = 8, - .min_vsize_align = 16, - .pix_limit = &s5p_pix_limit[1], -}; - -static const struct fimc_variant fimc1_variant_s5pv210 = { - .has_inp_rot = 1, - .has_out_rot = 1, - .has_cam_if = 1, - .has_mainscaler_ext = 1, - .min_inp_pixsize = 16, - .min_out_pixsize = 16, - .hor_offs_align = 1, - .min_vsize_align = 1, - .pix_limit = &s5p_pix_limit[2], -}; - -static const struct fimc_variant fimc2_variant_s5pv210 = { - .has_cam_if = 1, - .min_inp_pixsize = 16, - .min_out_pixsize = 16, - .hor_offs_align = 8, - .min_vsize_align = 16, - .pix_limit = &s5p_pix_limit[2], -}; - /* S5PV210, S5PC110 */ static const struct fimc_drvdata fimc_drvdata_s5pv210 = { - .variant = { - [0] = &fimc0_variant_s5pv210, - [1] = &fimc1_variant_s5pv210, - [2] = &fimc2_variant_s5pv210, - }, .num_entities = 3, .lclk_frequency = 166000000UL, .out_buf_count = 4, diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h index d130f664a60b..e4a56232907a 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.h +++ b/drivers/media/platform/exynos4-is/fimc-core.h @@ -296,11 +296,8 @@ struct fimc_m2m_device { * @buf_index: index for managing the output DMA buffers * @frame_count: the frame counter for statistics * @reqbufs_count: the number of buffers requested in REQBUFS ioctl - * @input_index: input (camera sensor) index * @input: capture input type, grp_id of the attached subdev * @user_subdev_api: true if subdevs are not configured by the host driver - * @inh_sensor_ctrls: a flag indicating v4l2 controls are inherited from - * an image sensor subdev */ struct fimc_vid_cap { struct fimc_ctx *ctx; @@ -319,10 +316,8 @@ struct fimc_vid_cap { unsigned int frame_count; unsigned int reqbufs_count; bool streaming; - int input_index; u32 input; bool user_subdev_api; - bool inh_sensor_ctrls; }; /** diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index a474014f0a0f..019bb47df915 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -756,18 +756,12 @@ static void fimc_is_debugfs_remove(struct fimc_is *is) is->debugfs_entry = NULL; } -static int fimc_is_debugfs_create(struct fimc_is *is) +static void fimc_is_debugfs_create(struct fimc_is *is) { - struct dentry *dentry; - is->debugfs_entry = debugfs_create_dir("fimc_is", NULL); - dentry = debugfs_create_file("fw_log", S_IRUGO, is->debugfs_entry, - is, &fimc_is_fops); - if (!dentry) - fimc_is_debugfs_remove(is); - - return is->debugfs_entry == NULL ? -EIO : 0; + debugfs_create_file("fw_log", S_IRUGO, is->debugfs_entry, is, + &fimc_is_fops); } static int fimc_is_runtime_resume(struct device *dev); @@ -853,9 +847,7 @@ static int fimc_is_probe(struct platform_device *pdev) if (ret < 0) goto err_pm; - ret = fimc_is_debugfs_create(is); - if (ret < 0) - goto err_sd; + fimc_is_debugfs_create(is); ret = fimc_is_request_firmware(is, FIMC_IS_FW_FILENAME); if (ret < 0) @@ -868,7 +860,6 @@ static int fimc_is_probe(struct platform_device *pdev) err_dfs: fimc_is_debugfs_remove(is); -err_sd: fimc_is_unregister_subdevs(is); err_pm: pm_runtime_put_noidle(dev); diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c index cde0d254ec1c..a77c49b18511 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.c +++ b/drivers/media/platform/exynos4-is/fimc-isp.c @@ -305,8 +305,10 @@ static int fimc_isp_subdev_s_power(struct v4l2_subdev *sd, int on) if (on) { ret = pm_runtime_get_sync(&is->pdev->dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put(&is->pdev->dev); return ret; + } set_bit(IS_ST_PWR_ON, &is->state); ret = fimc_is_start_firmware(is); diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 9c666f663ab4..fdd0d369b192 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -471,7 +471,7 @@ static int fimc_lite_open(struct file *file) set_bit(ST_FLITE_IN_USE, &fimc->state); ret = pm_runtime_get_sync(&fimc->pdev->dev); if (ret < 0) - goto unlock; + goto err_pm; ret = v4l2_fh_open(file); if (ret < 0) diff --git a/drivers/media/platform/exynos4-is/fimc-reg.c b/drivers/media/platform/exynos4-is/fimc-reg.c index 5ce2bdebd424..8764999a5fd7 100644 --- a/drivers/media/platform/exynos4-is/fimc-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-reg.c @@ -606,6 +606,11 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, switch (source->fimc_bus_type) { case FIMC_BUS_TYPE_ITU_601: case FIMC_BUS_TYPE_ITU_656: + if (fimc_fmt_is_user_defined(f->fmt->color)) { + cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT; + break; + } + for (i = 0; i < ARRAY_SIZE(pix_desc); i++) { if (vc->ci_fmt.code == pix_desc[i].pixelcode) { cfg = pix_desc[i].cisrcfmt; @@ -707,10 +712,12 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, case FIMC_BUS_TYPE_ITU_601...FIMC_BUS_TYPE_ITU_656: if (source->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */ cfg |= FIMC_REG_CIGCTRL_SELCAM_ITU_A; + if (vid_cap->ci_fmt.code == MEDIA_BUS_FMT_JPEG_1X8) + cfg |= FIMC_REG_CIGCTRL_CAM_JPEG; break; case FIMC_BUS_TYPE_LCD_WRITEBACK_A: cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB; - /* fall through */ + fallthrough; case FIMC_BUS_TYPE_ISP_WRITEBACK: if (fimc->variant->has_isp_wb) cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB; diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 16dd660137a8..e636c33e847b 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -19,6 +19,7 @@ #include <linux/of_platform.h> #include <linux/of_device.h> #include <linux/of_graph.h> +#include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/types.h> @@ -92,7 +93,7 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p, switch (sd->grp_id) { case GRP_ID_SENSOR: sensor = sd; - /* fall through */ + fallthrough; case GRP_ID_FIMC_IS_SENSOR: p->subdevs[IDX_SENSOR] = sd; break; @@ -289,11 +290,26 @@ static int __fimc_pipeline_s_stream(struct exynos_media_pipeline *ep, bool on) { IDX_CSIS, IDX_FLITE, IDX_FIMC, IDX_SENSOR, IDX_IS_ISP }, }; struct fimc_pipeline *p = to_fimc_pipeline(ep); - struct fimc_md *fmd = entity_to_fimc_mdev(&p->subdevs[IDX_CSIS]->entity); enum fimc_subdev_index sd_id; int i, ret = 0; if (p->subdevs[IDX_SENSOR] == NULL) { + struct fimc_md *fmd; + struct v4l2_subdev *sd = p->subdevs[IDX_CSIS]; + + if (!sd) + sd = p->subdevs[IDX_FIMC]; + + if (!sd) { + /* + * If neither CSIS nor FIMC was set up, + * it's impossible to have any sensors + */ + return -ENODEV; + } + + fmd = entity_to_fimc_mdev(&sd->entity); + if (!fmd->user_subdev_api) { /* * Sensor must be already discovered if we @@ -379,21 +395,15 @@ static void fimc_md_pipelines_free(struct fimc_md *fmd) } } -/* Parse port node and register as a sub-device any sensor specified there. */ -static int fimc_md_parse_port_node(struct fimc_md *fmd, - struct device_node *port, - unsigned int index) +static int fimc_md_parse_one_endpoint(struct fimc_md *fmd, + struct device_node *ep) { + int index = fmd->num_sensors; struct fimc_source_info *pd = &fmd->sensor[index].pdata; - struct device_node *rem, *ep, *np; + struct device_node *rem, *np; struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 }; int ret; - /* Assume here a port node can have only one endpoint node. */ - ep = of_get_next_child(port, NULL); - if (!ep) - return 0; - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &endpoint); if (ret) { of_node_put(ep); @@ -467,13 +477,28 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, return 0; } +/* Parse port node and register as a sub-device any sensor specified there. */ +static int fimc_md_parse_port_node(struct fimc_md *fmd, + struct device_node *port) +{ + struct device_node *ep; + int ret; + + for_each_child_of_node(port, ep) { + ret = fimc_md_parse_one_endpoint(fmd, ep); + if (ret < 0) + return ret; + } + + return 0; +} + /* Register all SoC external sub-devices */ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) { struct device_node *parent = fmd->pdev->dev.of_node; struct device_node *ports = NULL; struct device_node *node; - int index = 0; int ret; /* @@ -484,8 +509,10 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) return -ENXIO; ret = pm_runtime_get_sync(fmd->pmf); - if (ret < 0) + if (ret < 0) { + pm_runtime_put(fmd->pmf); return ret; + } fmd->num_sensors = 0; @@ -500,13 +527,12 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) if (!port) continue; - ret = fimc_md_parse_port_node(fmd, port, index); + ret = fimc_md_parse_port_node(fmd, port); of_node_put(port); if (ret < 0) { of_node_put(node); goto cleanup; } - index++; } /* Attach sensors listed in the parallel-ports node */ @@ -515,12 +541,11 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) goto rpm_put; for_each_child_of_node(ports, node) { - ret = fimc_md_parse_port_node(fmd, node, index); + ret = fimc_md_parse_port_node(fmd, node); if (ret < 0) { of_node_put(node); goto cleanup; } - index++; } of_node_put(ports); @@ -1254,28 +1279,6 @@ static ssize_t fimc_md_sysfs_store(struct device *dev, static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO, fimc_md_sysfs_show, fimc_md_sysfs_store); -static int fimc_md_get_pinctrl(struct fimc_md *fmd) -{ - struct device *dev = &fmd->pdev->dev; - struct fimc_pinctrl *pctl = &fmd->pinctl; - - pctl->pinctrl = devm_pinctrl_get(dev); - if (IS_ERR(pctl->pinctrl)) - return PTR_ERR(pctl->pinctrl); - - pctl->state_default = pinctrl_lookup_state(pctl->pinctrl, - PINCTRL_STATE_DEFAULT); - if (IS_ERR(pctl->state_default)) - return PTR_ERR(pctl->state_default); - - pctl->state_idle = pinctrl_lookup_state(pctl->pinctrl, - PINCTRL_STATE_IDLE); - if (IS_ERR(pctl->state_idle)) - return PTR_ERR(pctl->state_idle); - - return 0; -} - static int cam_clk_prepare(struct clk_hw *hw) { struct cam_clk *camclk = to_cam_clk(hw); @@ -1431,6 +1434,7 @@ static int fimc_md_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct v4l2_device *v4l2_dev; + struct pinctrl *pinctrl; struct fimc_md *fmd; int ret; @@ -1467,8 +1471,9 @@ static int fimc_md_probe(struct platform_device *pdev) if (ret) goto err_v4l2dev; - ret = fimc_md_get_pinctrl(fmd); - if (ret < 0) { + pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(pinctrl)) { + ret = PTR_ERR(pinctrl); if (ret != EPROBE_DEFER) dev_err(dev, "Failed to get pinctrl: %d\n", ret); goto err_clk; diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h index 4b8f9ac52ebc..9447fafe23c6 100644 --- a/drivers/media/platform/exynos4-is/media-dev.h +++ b/drivers/media/platform/exynos4-is/media-dev.h @@ -27,8 +27,6 @@ #define FIMC_IS_OF_NODE_NAME "fimc-is" #define CSIS_OF_NODE_NAME "csis" -#define PINCTRL_STATE_IDLE "idle" - #define FIMC_MAX_SENSORS 4 #define FIMC_MAX_CAMCLKS 2 #define DEFAULT_SENSOR_CLK_FREQ 24000000U @@ -109,9 +107,6 @@ struct cam_clk { * @media_dev: top level media device * @v4l2_dev: top level v4l2_device holding up the subdevs * @pdev: platform device this media device is hooked up into - * @pinctrl: camera port pinctrl handle - * @state_default: pinctrl default state handle - * @state_idle: pinctrl idle state handle * @cam_clk_provider: CAMCLK clock provider structure * @user_subdev_api: true if subdevs are not configured by the host driver * @slock: spinlock protecting @sensor array @@ -131,12 +126,6 @@ struct fimc_md { struct v4l2_device v4l2_dev; struct platform_device *pdev; - struct fimc_pinctrl { - struct pinctrl *pinctrl; - struct pinctrl_state *state_default; - struct pinctrl_state *state_idle; - } pinctl; - struct cam_clk_provider { struct clk *clks[FIMC_MAX_CAMCLKS]; struct clk_onecell_data clk_data; diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index 540151bbf58f..1aac167abb17 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -510,8 +510,10 @@ static int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable) if (enable) { s5pcsis_clear_counters(state); ret = pm_runtime_get_sync(&state->pdev->dev); - if (ret && ret != 1) + if (ret && ret != 1) { + pm_runtime_put_noidle(&state->pdev->dev); return ret; + } } mutex_lock(&state->lock); diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 84633a3b8475..4f2a0f992905 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -32,7 +32,7 @@ #define VIU_VERSION "0.5.1" /* Allow building this driver with COMPILE_TEST */ -#if !defined(CONFIG_PPC) && !defined(CONFIG_MICROBLAZE) +#if !defined(CONFIG_PPC) && !defined(CONFIG_MICROBLAZE) && !defined(CONFIG_M68K) #define out_be32(v, a) iowrite32be(a, (void __iomem *)v) #define in_be32(a) ioread32be((void __iomem *)a) #endif diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c index 58b9915ac7a4..00f623d62c96 100644 --- a/drivers/media/platform/marvell-ccic/cafe-driver.c +++ b/drivers/media/platform/marvell-ccic/cafe-driver.c @@ -497,6 +497,7 @@ static int cafe_pci_probe(struct pci_dev *pdev, cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL); if (cam == NULL) goto out; + pci_set_drvdata(pdev, cam); cam->pdev = pdev; mcam = &cam->mcam; mcam->chip_id = MCAM_CAFE; @@ -592,8 +593,7 @@ static void cafe_shutdown(struct cafe_camera *cam) static void cafe_pci_remove(struct pci_dev *pdev) { - struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); - struct cafe_camera *cam = to_cam(v4l2_dev); + struct cafe_camera *cam = pci_get_drvdata(pdev); if (cam == NULL) { printk(KERN_WARNING "pci_remove on unknown pdev %p\n", pdev); @@ -609,8 +609,7 @@ static void cafe_pci_remove(struct pci_dev *pdev) */ static int __maybe_unused cafe_pci_suspend(struct device *dev) { - struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); - struct cafe_camera *cam = to_cam(v4l2_dev); + struct cafe_camera *cam = dev_get_drvdata(dev); mccic_suspend(&cam->mcam); return 0; @@ -619,8 +618,7 @@ static int __maybe_unused cafe_pci_suspend(struct device *dev) static int __maybe_unused cafe_pci_resume(struct device *dev) { - struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); - struct cafe_camera *cam = to_cam(v4l2_dev); + struct cafe_camera *cam = dev_get_drvdata(dev); cafe_ctlr_init(&cam->mcam); return mccic_resume(&cam->mcam); diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 3d4242b8182b..c012fd2e1d29 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -24,6 +24,7 @@ #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/videodev2.h> +#include <linux/pm_runtime.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-ctrls.h> @@ -388,7 +389,7 @@ static int mcam_alloc_dma_bufs(struct mcam_camera *cam, int loadtime) dma_free_coherent(cam->dev, cam->dma_buf_size, cam->dma_bufs[0], cam->dma_handles[0]); cam->nbufs = 0; - /* fall-through */ + fallthrough; case 0: cam_err(cam, "Insufficient DMA buffers, cannot operate\n"); return -ENOMEM; @@ -438,9 +439,9 @@ static void mcam_ctlr_dma_vmalloc(struct mcam_camera *cam) /* * Copy data out to user space in the vmalloc case */ -static void mcam_frame_tasklet(unsigned long data) +static void mcam_frame_tasklet(struct tasklet_struct *t) { - struct mcam_camera *cam = (struct mcam_camera *) data; + struct mcam_camera *cam = from_tasklet(cam, t, s_tasklet); int i; unsigned long flags; struct mcam_vb_buffer *buf; @@ -895,30 +896,6 @@ static void mcam_ctlr_power_down(struct mcam_camera *cam) /* ---------------------------------------------------------------------- */ /* - * Controller clocks. - */ -static void mcam_clk_enable(struct mcam_camera *mcam) -{ - unsigned int i; - - for (i = 0; i < NR_MCAM_CLK; i++) { - if (!IS_ERR(mcam->clk[i])) - clk_prepare_enable(mcam->clk[i]); - } -} - -static void mcam_clk_disable(struct mcam_camera *mcam) -{ - int i; - - for (i = NR_MCAM_CLK - 1; i >= 0; i--) { - if (!IS_ERR(mcam->clk[i])) - clk_disable_unprepare(mcam->clk[i]); - } -} - -/* ---------------------------------------------------------------------- */ -/* * Master sensor clock. */ static int mclk_prepare(struct clk_hw *hw) @@ -1323,8 +1300,7 @@ static int mcam_setup_vb2(struct mcam_camera *cam) break; case B_vmalloc: #ifdef MCAM_MODE_VMALLOC - tasklet_init(&cam->s_tasklet, mcam_frame_tasklet, - (unsigned long) cam); + tasklet_setup(&cam->s_tasklet, mcam_frame_tasklet); vq->ops = &mcam_vb2_ops; vq->mem_ops = &vb2_vmalloc_memops; cam->dma_setup = mcam_ctlr_dma_vmalloc; @@ -1633,7 +1609,7 @@ static int mcam_v4l_open(struct file *filp) ret = sensor_call(cam, core, s_power, 1); if (ret) goto out; - mcam_clk_enable(cam); + pm_runtime_get_sync(cam->dev); __mcam_cam_reset(cam); mcam_set_config_needed(cam, 1); } @@ -1656,7 +1632,7 @@ static int mcam_v4l_release(struct file *filp) if (last_open) { mcam_disable_mipi(cam); sensor_call(cam, core, s_power, 0); - mcam_clk_disable(cam); + pm_runtime_put(cam->dev); if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read) mcam_free_dma_bufs(cam); } @@ -1977,7 +1953,6 @@ void mccic_suspend(struct mcam_camera *cam) mcam_ctlr_stop_dma(cam); sensor_call(cam, core, s_power, 0); - mcam_clk_disable(cam); cam->state = cstate; } mutex_unlock(&cam->s_mutex); @@ -1990,7 +1965,6 @@ int mccic_resume(struct mcam_camera *cam) mutex_lock(&cam->s_mutex); if (!list_empty(&cam->vdev.fh_list)) { - mcam_clk_enable(cam); ret = sensor_call(cam, core, s_power, 1); if (ret) { mutex_unlock(&cam->s_mutex); diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index 92b92255dac6..cd902b180669 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -20,6 +20,7 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/io.h> #include <linux/list.h> #include <linux/pm.h> @@ -47,49 +48,6 @@ static inline struct mmp_camera *mcam_to_cam(struct mcam_camera *mcam) } /* - * A silly little infrastructure so we can keep track of our devices. - * Chances are that we will never have more than one of them, but - * the Armada 610 *does* have two controllers... - */ - -static LIST_HEAD(mmpcam_devices); -static struct mutex mmpcam_devices_lock; - -static void mmpcam_add_device(struct mmp_camera *cam) -{ - mutex_lock(&mmpcam_devices_lock); - list_add(&cam->devlist, &mmpcam_devices); - mutex_unlock(&mmpcam_devices_lock); -} - -static void mmpcam_remove_device(struct mmp_camera *cam) -{ - mutex_lock(&mmpcam_devices_lock); - list_del(&cam->devlist); - mutex_unlock(&mmpcam_devices_lock); -} - -/* - * Platform dev remove passes us a platform_device, and there's - * no handy unused drvdata to stash a backpointer in. So just - * dig it out of our list. - */ -static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev) -{ - struct mmp_camera *cam; - - mutex_lock(&mmpcam_devices_lock); - list_for_each_entry(cam, &mmpcam_devices, devlist) { - if (cam->pdev == pdev) { - mutex_unlock(&mmpcam_devices_lock); - return cam; - } - } - mutex_unlock(&mmpcam_devices_lock); - return NULL; -} - -/* * calc the dphy register values * There are three dphy registers being used. * dphy[0] - CSI2_DPHY3 @@ -227,6 +185,7 @@ static int mmpcam_probe(struct platform_device *pdev) cam = devm_kzalloc(&pdev->dev, sizeof(*cam), GFP_KERNEL); if (cam == NULL) return -ENOMEM; + platform_set_drvdata(pdev, cam); cam->pdev = pdev; INIT_LIST_HEAD(&cam->devlist); @@ -313,11 +272,11 @@ static int mmpcam_probe(struct platform_device *pdev) cam->irq = res->start; ret = devm_request_irq(&pdev->dev, cam->irq, mmpcam_irq, IRQF_SHARED, "mmp-camera", mcam); - if (ret == 0) { - mmpcam_add_device(cam); - return 0; - } + if (ret) + goto out; + pm_runtime_enable(&pdev->dev); + return 0; out: fwnode_handle_put(mcam->asd.match.fwnode); mccic_shutdown(mcam); @@ -330,14 +289,14 @@ static int mmpcam_remove(struct mmp_camera *cam) { struct mcam_camera *mcam = &cam->mcam; - mmpcam_remove_device(cam); mccic_shutdown(mcam); + pm_runtime_force_suspend(mcam->dev); return 0; } static int mmpcam_platform_remove(struct platform_device *pdev) { - struct mmp_camera *cam = mmpcam_find_device(pdev); + struct mmp_camera *cam = platform_get_drvdata(pdev); if (cam == NULL) return -ENODEV; @@ -347,26 +306,57 @@ static int mmpcam_platform_remove(struct platform_device *pdev) /* * Suspend/resume support. */ -#ifdef CONFIG_PM -static int mmpcam_suspend(struct platform_device *pdev, pm_message_t state) +static int mmpcam_runtime_resume(struct device *dev) +{ + struct mmp_camera *cam = dev_get_drvdata(dev); + struct mcam_camera *mcam = &cam->mcam; + unsigned int i; + + for (i = 0; i < NR_MCAM_CLK; i++) { + if (!IS_ERR(mcam->clk[i])) + clk_prepare_enable(mcam->clk[i]); + } + + return 0; +} + +static int mmpcam_runtime_suspend(struct device *dev) +{ + struct mmp_camera *cam = dev_get_drvdata(dev); + struct mcam_camera *mcam = &cam->mcam; + int i; + + for (i = NR_MCAM_CLK - 1; i >= 0; i--) { + if (!IS_ERR(mcam->clk[i])) + clk_disable_unprepare(mcam->clk[i]); + } + + return 0; +} + +static int __maybe_unused mmpcam_suspend(struct device *dev) { - struct mmp_camera *cam = mmpcam_find_device(pdev); + struct mmp_camera *cam = dev_get_drvdata(dev); - if (state.event != PM_EVENT_SUSPEND) - return 0; - mccic_suspend(&cam->mcam); + if (!pm_runtime_suspended(dev)) + mccic_suspend(&cam->mcam); return 0; } -static int mmpcam_resume(struct platform_device *pdev) +static int __maybe_unused mmpcam_resume(struct device *dev) { - struct mmp_camera *cam = mmpcam_find_device(pdev); + struct mmp_camera *cam = dev_get_drvdata(dev); - return mccic_resume(&cam->mcam); + if (!pm_runtime_suspended(dev)) + return mccic_resume(&cam->mcam); + return 0; } -#endif +static const struct dev_pm_ops mmpcam_pm_ops = { + SET_RUNTIME_PM_OPS(mmpcam_runtime_suspend, mmpcam_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(mmpcam_suspend, mmpcam_resume) +}; static const struct of_device_id mmpcam_of_match[] = { { .compatible = "marvell,mmp2-ccic", }, @@ -377,32 +367,11 @@ MODULE_DEVICE_TABLE(of, mmpcam_of_match); static struct platform_driver mmpcam_driver = { .probe = mmpcam_probe, .remove = mmpcam_platform_remove, -#ifdef CONFIG_PM - .suspend = mmpcam_suspend, - .resume = mmpcam_resume, -#endif .driver = { .name = "mmp-camera", .of_match_table = of_match_ptr(mmpcam_of_match), + .pm = &mmpcam_pm_ops, } }; - -static int __init mmpcam_init_module(void) -{ - mutex_init(&mmpcam_devices_lock); - return platform_driver_register(&mmpcam_driver); -} - -static void __exit mmpcam_exit_module(void) -{ - platform_driver_unregister(&mmpcam_driver); - /* - * platform_driver_unregister() should have emptied the list - */ - if (!list_empty(&mmpcam_devices)) - printk(KERN_ERR "mmp_camera leaving devices behind\n"); -} - -module_init(mmpcam_init_module); -module_exit(mmpcam_exit_module); +module_platform_driver(mmpcam_driver); diff --git a/drivers/media/platform/mtk-jpeg/Makefile b/drivers/media/platform/mtk-jpeg/Makefile index 92a4fc046bfe..76c33aad0f3f 100644 --- a/drivers/media/platform/mtk-jpeg/Makefile +++ b/drivers/media/platform/mtk-jpeg/Makefile @@ -1,3 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -mtk_jpeg-objs := mtk_jpeg_core.o mtk_jpeg_hw.o mtk_jpeg_parse.o +mtk_jpeg-objs := mtk_jpeg_core.o \ + mtk_jpeg_dec_hw.o \ + mtk_jpeg_dec_parse.o \ + mtk_jpeg_enc_hw.o obj-$(CONFIG_VIDEO_MEDIATEK_JPEG) += mtk_jpeg.o diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c index 61fed1e35a00..227245ccaedc 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c @@ -3,6 +3,7 @@ * Copyright (c) 2016 MediaTek Inc. * Author: Ming Hsiu Tsai <minghsiu.tsai@mediatek.com> * Rick Chang <rick.chang@mediatek.com> + * Xia Jiang <xia.jiang@mediatek.com> */ #include <linux/clk.h> @@ -23,15 +24,64 @@ #include <media/videobuf2-dma-contig.h> #include <soc/mediatek/smi.h> -#include "mtk_jpeg_hw.h" +#include "mtk_jpeg_enc_hw.h" +#include "mtk_jpeg_dec_hw.h" #include "mtk_jpeg_core.h" -#include "mtk_jpeg_parse.h" +#include "mtk_jpeg_dec_parse.h" -static struct mtk_jpeg_fmt mtk_jpeg_formats[] = { +static struct mtk_jpeg_fmt mtk_jpeg_enc_formats[] = { { .fourcc = V4L2_PIX_FMT_JPEG, .colplanes = 1, - .flags = MTK_JPEG_FMT_FLAG_DEC_OUTPUT, + .flags = MTK_JPEG_FMT_FLAG_CAPTURE, + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .hw_format = JPEG_ENC_YUV_FORMAT_NV12, + .h_sample = {4, 4}, + .v_sample = {4, 2}, + .colplanes = 2, + .h_align = 4, + .v_align = 4, + .flags = MTK_JPEG_FMT_FLAG_OUTPUT, + }, + { + .fourcc = V4L2_PIX_FMT_NV21M, + .hw_format = JEPG_ENC_YUV_FORMAT_NV21, + .h_sample = {4, 4}, + .v_sample = {4, 2}, + .colplanes = 2, + .h_align = 4, + .v_align = 4, + .flags = MTK_JPEG_FMT_FLAG_OUTPUT, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .hw_format = JPEG_ENC_YUV_FORMAT_YUYV, + .h_sample = {8}, + .v_sample = {4}, + .colplanes = 1, + .h_align = 5, + .v_align = 3, + .flags = MTK_JPEG_FMT_FLAG_OUTPUT, + }, + { + .fourcc = V4L2_PIX_FMT_YVYU, + .hw_format = JPEG_ENC_YUV_FORMAT_YVYU, + .h_sample = {8}, + .v_sample = {4}, + .colplanes = 1, + .h_align = 5, + .v_align = 3, + .flags = MTK_JPEG_FMT_FLAG_OUTPUT, + }, +}; + +static struct mtk_jpeg_fmt mtk_jpeg_dec_formats[] = { + { + .fourcc = V4L2_PIX_FMT_JPEG, + .colplanes = 1, + .flags = MTK_JPEG_FMT_FLAG_OUTPUT, }, { .fourcc = V4L2_PIX_FMT_YUV420M, @@ -40,7 +90,7 @@ static struct mtk_jpeg_fmt mtk_jpeg_formats[] = { .colplanes = 3, .h_align = 5, .v_align = 4, - .flags = MTK_JPEG_FMT_FLAG_DEC_CAPTURE, + .flags = MTK_JPEG_FMT_FLAG_CAPTURE, }, { .fourcc = V4L2_PIX_FMT_YUV422M, @@ -49,27 +99,27 @@ static struct mtk_jpeg_fmt mtk_jpeg_formats[] = { .colplanes = 3, .h_align = 5, .v_align = 3, - .flags = MTK_JPEG_FMT_FLAG_DEC_CAPTURE, + .flags = MTK_JPEG_FMT_FLAG_CAPTURE, }, }; -#define MTK_JPEG_NUM_FORMATS ARRAY_SIZE(mtk_jpeg_formats) - -enum { - MTK_JPEG_BUF_FLAGS_INIT = 0, - MTK_JPEG_BUF_FLAGS_LAST_FRAME = 1, -}; +#define MTK_JPEG_ENC_NUM_FORMATS ARRAY_SIZE(mtk_jpeg_enc_formats) +#define MTK_JPEG_DEC_NUM_FORMATS ARRAY_SIZE(mtk_jpeg_dec_formats) struct mtk_jpeg_src_buf { struct vb2_v4l2_buffer b; struct list_head list; - int flags; struct mtk_jpeg_dec_param dec_param; }; static int debug; module_param(debug, int, 0644); +static inline struct mtk_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct mtk_jpeg_ctx, ctrl_hdl); +} + static inline struct mtk_jpeg_ctx *mtk_jpeg_fh_to_ctx(struct v4l2_fh *fh) { return container_of(fh, struct mtk_jpeg_ctx, fh); @@ -86,14 +136,61 @@ static int mtk_jpeg_querycap(struct file *file, void *priv, { struct mtk_jpeg_dev *jpeg = video_drvdata(file); - strscpy(cap->driver, MTK_JPEG_NAME " decoder", sizeof(cap->driver)); - strscpy(cap->card, MTK_JPEG_NAME " decoder", sizeof(cap->card)); + strscpy(cap->driver, jpeg->variant->dev_name, sizeof(cap->driver)); + strscpy(cap->card, jpeg->variant->dev_name, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(jpeg->dev)); return 0; } +static int vidioc_jpeg_enc_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mtk_jpeg_ctx *ctx = ctrl_to_ctx(ctrl); + + switch (ctrl->id) { + case V4L2_CID_JPEG_RESTART_INTERVAL: + ctx->restart_interval = ctrl->val; + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ctx->enc_quality = ctrl->val; + break; + case V4L2_CID_JPEG_ACTIVE_MARKER: + ctx->enable_exif = ctrl->val & V4L2_JPEG_ACTIVE_MARKER_APP1; + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops mtk_jpeg_enc_ctrl_ops = { + .s_ctrl = vidioc_jpeg_enc_s_ctrl, +}; + +static int mtk_jpeg_enc_ctrls_setup(struct mtk_jpeg_ctx *ctx) +{ + const struct v4l2_ctrl_ops *ops = &mtk_jpeg_enc_ctrl_ops; + struct v4l2_ctrl_handler *handler = &ctx->ctrl_hdl; + + v4l2_ctrl_handler_init(handler, 3); + + v4l2_ctrl_new_std(handler, ops, V4L2_CID_JPEG_RESTART_INTERVAL, 0, 100, + 1, 0); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_JPEG_COMPRESSION_QUALITY, 48, + 100, 1, 90); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_JPEG_ACTIVE_MARKER, 0, + V4L2_JPEG_ACTIVE_MARKER_APP1, 0, 0); + + if (handler->error) { + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); + return handler->error; + } + + v4l2_ctrl_handler_setup(&ctx->ctrl_hdl); + + return 0; +} + static int mtk_jpeg_enum_fmt(struct mtk_jpeg_fmt *mtk_jpeg_formats, int n, struct v4l2_fmtdesc *f, u32 type) { @@ -118,15 +215,23 @@ static int mtk_jpeg_enum_fmt(struct mtk_jpeg_fmt *mtk_jpeg_formats, int n, static int mtk_jpeg_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - return mtk_jpeg_enum_fmt(mtk_jpeg_formats, MTK_JPEG_NUM_FORMATS, f, - MTK_JPEG_FMT_FLAG_DEC_CAPTURE); + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv); + struct mtk_jpeg_dev *jpeg = ctx->jpeg; + + return mtk_jpeg_enum_fmt(jpeg->variant->formats, + jpeg->variant->num_formats, f, + MTK_JPEG_FMT_FLAG_CAPTURE); } static int mtk_jpeg_enum_fmt_vid_out(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - return mtk_jpeg_enum_fmt(mtk_jpeg_formats, MTK_JPEG_NUM_FORMATS, f, - MTK_JPEG_FMT_FLAG_DEC_OUTPUT); + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv); + struct mtk_jpeg_dev *jpeg = ctx->jpeg; + + return mtk_jpeg_enum_fmt(jpeg->variant->formats, + jpeg->variant->num_formats, f, + MTK_JPEG_FMT_FLAG_OUTPUT); } static struct mtk_jpeg_q_data *mtk_jpeg_get_q_data(struct mtk_jpeg_ctx *ctx, @@ -137,126 +242,63 @@ static struct mtk_jpeg_q_data *mtk_jpeg_get_q_data(struct mtk_jpeg_ctx *ctx, return &ctx->cap_q; } -static struct mtk_jpeg_fmt *mtk_jpeg_find_format(struct mtk_jpeg_ctx *ctx, - u32 pixelformat, - unsigned int fmt_type) +static struct mtk_jpeg_fmt * +mtk_jpeg_find_format(struct mtk_jpeg_fmt *mtk_jpeg_formats, int num_formats, + u32 pixelformat, unsigned int fmt_type) { - unsigned int k, fmt_flag; - - fmt_flag = (fmt_type == MTK_JPEG_FMT_TYPE_OUTPUT) ? - MTK_JPEG_FMT_FLAG_DEC_OUTPUT : - MTK_JPEG_FMT_FLAG_DEC_CAPTURE; + unsigned int k; + struct mtk_jpeg_fmt *fmt; - for (k = 0; k < MTK_JPEG_NUM_FORMATS; k++) { - struct mtk_jpeg_fmt *fmt = &mtk_jpeg_formats[k]; + for (k = 0; k < num_formats; k++) { + fmt = &mtk_jpeg_formats[k]; - if (fmt->fourcc == pixelformat && fmt->flags & fmt_flag) + if (fmt->fourcc == pixelformat && fmt->flags & fmt_type) return fmt; } return NULL; } -static void mtk_jpeg_bound_align_image(u32 *w, unsigned int wmin, - unsigned int wmax, unsigned int walign, - u32 *h, unsigned int hmin, - unsigned int hmax, unsigned int halign) +static int mtk_jpeg_try_fmt_mplane(struct v4l2_pix_format_mplane *pix_mp, + struct mtk_jpeg_fmt *fmt) { - int width, height, w_step, h_step; - - width = *w; - height = *h; - w_step = 1 << walign; - h_step = 1 << halign; - - v4l_bound_align_image(w, wmin, wmax, walign, h, hmin, hmax, halign, 0); - if (*w < width && (*w + w_step) <= wmax) - *w += w_step; - if (*h < height && (*h + h_step) <= hmax) - *h += h_step; -} - -static void mtk_jpeg_adjust_fmt_mplane(struct mtk_jpeg_ctx *ctx, - struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; - struct mtk_jpeg_q_data *q_data; - int i; - - q_data = mtk_jpeg_get_q_data(ctx, f->type); - - pix_mp->width = q_data->w; - pix_mp->height = q_data->h; - pix_mp->pixelformat = q_data->fmt->fourcc; - pix_mp->num_planes = q_data->fmt->colplanes; - - for (i = 0; i < pix_mp->num_planes; i++) { - pix_mp->plane_fmt[i].bytesperline = q_data->bytesperline[i]; - pix_mp->plane_fmt[i].sizeimage = q_data->sizeimage[i]; - } -} - -static int mtk_jpeg_try_fmt_mplane(struct v4l2_format *f, - struct mtk_jpeg_fmt *fmt, - struct mtk_jpeg_ctx *ctx, int q_type) -{ - struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; - struct mtk_jpeg_dev *jpeg = ctx->jpeg; int i; - memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved)); pix_mp->field = V4L2_FIELD_NONE; - if (ctx->state != MTK_JPEG_INIT) { - mtk_jpeg_adjust_fmt_mplane(ctx, f); - goto end; - } - pix_mp->num_planes = fmt->colplanes; pix_mp->pixelformat = fmt->fourcc; - if (q_type == MTK_JPEG_FMT_TYPE_OUTPUT) { + if (fmt->fourcc == V4L2_PIX_FMT_JPEG) { struct v4l2_plane_pix_format *pfmt = &pix_mp->plane_fmt[0]; - mtk_jpeg_bound_align_image(&pix_mp->width, MTK_JPEG_MIN_WIDTH, - MTK_JPEG_MAX_WIDTH, 0, - &pix_mp->height, MTK_JPEG_MIN_HEIGHT, - MTK_JPEG_MAX_HEIGHT, 0); + pix_mp->height = clamp(pix_mp->height, MTK_JPEG_MIN_HEIGHT, + MTK_JPEG_MAX_HEIGHT); + pix_mp->width = clamp(pix_mp->width, MTK_JPEG_MIN_WIDTH, + MTK_JPEG_MAX_WIDTH); - memset(pfmt->reserved, 0, sizeof(pfmt->reserved)); pfmt->bytesperline = 0; /* Source size must be aligned to 128 */ - pfmt->sizeimage = mtk_jpeg_align(pfmt->sizeimage, 128); + pfmt->sizeimage = round_up(pfmt->sizeimage, 128); if (pfmt->sizeimage == 0) pfmt->sizeimage = MTK_JPEG_DEFAULT_SIZEIMAGE; - goto end; + return 0; } - /* type is MTK_JPEG_FMT_TYPE_CAPTURE */ - mtk_jpeg_bound_align_image(&pix_mp->width, MTK_JPEG_MIN_WIDTH, - MTK_JPEG_MAX_WIDTH, fmt->h_align, - &pix_mp->height, MTK_JPEG_MIN_HEIGHT, - MTK_JPEG_MAX_HEIGHT, fmt->v_align); + /* other fourcc */ + pix_mp->height = clamp(round_up(pix_mp->height, fmt->v_align), + MTK_JPEG_MIN_HEIGHT, MTK_JPEG_MAX_HEIGHT); + pix_mp->width = clamp(round_up(pix_mp->width, fmt->h_align), + MTK_JPEG_MIN_WIDTH, MTK_JPEG_MAX_WIDTH); for (i = 0; i < fmt->colplanes; i++) { struct v4l2_plane_pix_format *pfmt = &pix_mp->plane_fmt[i]; u32 stride = pix_mp->width * fmt->h_sample[i] / 4; u32 h = pix_mp->height * fmt->v_sample[i] / 4; - memset(pfmt->reserved, 0, sizeof(pfmt->reserved)); pfmt->bytesperline = stride; pfmt->sizeimage = stride * h; } -end: - v4l2_dbg(2, debug, &jpeg->v4l2_dev, "wxh:%ux%u\n", - pix_mp->width, pix_mp->height); - for (i = 0; i < pix_mp->num_planes; i++) { - v4l2_dbg(2, debug, &jpeg->v4l2_dev, - "plane[%d] bpl=%u, size=%u\n", - i, - pix_mp->plane_fmt[i].bytesperline, - pix_mp->plane_fmt[i].sizeimage); - } return 0; } @@ -276,16 +318,15 @@ static int mtk_jpeg_g_fmt_vid_mplane(struct file *file, void *priv, q_data = mtk_jpeg_get_q_data(ctx, f->type); - memset(pix_mp->reserved, 0, sizeof(pix_mp->reserved)); - pix_mp->width = q_data->w; - pix_mp->height = q_data->h; + pix_mp->width = q_data->pix_mp.width; + pix_mp->height = q_data->pix_mp.height; pix_mp->field = V4L2_FIELD_NONE; pix_mp->pixelformat = q_data->fmt->fourcc; pix_mp->num_planes = q_data->fmt->colplanes; - pix_mp->colorspace = ctx->colorspace; - pix_mp->ycbcr_enc = ctx->ycbcr_enc; - pix_mp->xfer_func = ctx->xfer_func; - pix_mp->quantization = ctx->quantization; + pix_mp->colorspace = q_data->pix_mp.colorspace; + pix_mp->ycbcr_enc = q_data->pix_mp.ycbcr_enc; + pix_mp->xfer_func = q_data->pix_mp.xfer_func; + pix_mp->quantization = q_data->pix_mp.quantization; v4l2_dbg(1, debug, &jpeg->v4l2_dev, "(%d) g_fmt:%c%c%c%c wxh:%ux%u\n", f->type, @@ -298,9 +339,8 @@ static int mtk_jpeg_g_fmt_vid_mplane(struct file *file, void *priv, for (i = 0; i < pix_mp->num_planes; i++) { struct v4l2_plane_pix_format *pfmt = &pix_mp->plane_fmt[i]; - pfmt->bytesperline = q_data->bytesperline[i]; - pfmt->sizeimage = q_data->sizeimage[i]; - memset(pfmt->reserved, 0, sizeof(pfmt->reserved)); + pfmt->bytesperline = q_data->pix_mp.plane_fmt[i].bytesperline; + pfmt->sizeimage = q_data->pix_mp.plane_fmt[i].sizeimage; v4l2_dbg(1, debug, &jpeg->v4l2_dev, "plane[%d] bpl=%u, size=%u\n", @@ -315,10 +355,13 @@ static int mtk_jpeg_try_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) { struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv); + struct mtk_jpeg_dev *jpeg = ctx->jpeg; struct mtk_jpeg_fmt *fmt; - fmt = mtk_jpeg_find_format(ctx, f->fmt.pix_mp.pixelformat, - MTK_JPEG_FMT_TYPE_CAPTURE); + fmt = mtk_jpeg_find_format(jpeg->variant->formats, + jpeg->variant->num_formats, + f->fmt.pix_mp.pixelformat, + MTK_JPEG_FMT_FLAG_CAPTURE); if (!fmt) fmt = ctx->cap_q.fmt; @@ -329,17 +372,25 @@ static int mtk_jpeg_try_fmt_vid_cap_mplane(struct file *file, void *priv, (fmt->fourcc >> 16 & 0xff), (fmt->fourcc >> 24 & 0xff)); - return mtk_jpeg_try_fmt_mplane(f, fmt, ctx, MTK_JPEG_FMT_TYPE_CAPTURE); + if (ctx->state != MTK_JPEG_INIT) { + mtk_jpeg_g_fmt_vid_mplane(file, priv, f); + return 0; + } + + return mtk_jpeg_try_fmt_mplane(&f->fmt.pix_mp, fmt); } static int mtk_jpeg_try_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f) { struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv); + struct mtk_jpeg_dev *jpeg = ctx->jpeg; struct mtk_jpeg_fmt *fmt; - fmt = mtk_jpeg_find_format(ctx, f->fmt.pix_mp.pixelformat, - MTK_JPEG_FMT_TYPE_OUTPUT); + fmt = mtk_jpeg_find_format(jpeg->variant->formats, + jpeg->variant->num_formats, + f->fmt.pix_mp.pixelformat, + MTK_JPEG_FMT_FLAG_OUTPUT); if (!fmt) fmt = ctx->out_q.fmt; @@ -350,17 +401,21 @@ static int mtk_jpeg_try_fmt_vid_out_mplane(struct file *file, void *priv, (fmt->fourcc >> 16 & 0xff), (fmt->fourcc >> 24 & 0xff)); - return mtk_jpeg_try_fmt_mplane(f, fmt, ctx, MTK_JPEG_FMT_TYPE_OUTPUT); + if (ctx->state != MTK_JPEG_INIT) { + mtk_jpeg_g_fmt_vid_mplane(file, priv, f); + return 0; + } + + return mtk_jpeg_try_fmt_mplane(&f->fmt.pix_mp, fmt); } static int mtk_jpeg_s_fmt_mplane(struct mtk_jpeg_ctx *ctx, - struct v4l2_format *f) + struct v4l2_format *f, unsigned int fmt_type) { struct vb2_queue *vq; struct mtk_jpeg_q_data *q_data = NULL; struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; struct mtk_jpeg_dev *jpeg = ctx->jpeg; - unsigned int f_type; int i; vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); @@ -374,16 +429,17 @@ static int mtk_jpeg_s_fmt_mplane(struct mtk_jpeg_ctx *ctx, return -EBUSY; } - f_type = V4L2_TYPE_IS_OUTPUT(f->type) ? - MTK_JPEG_FMT_TYPE_OUTPUT : MTK_JPEG_FMT_TYPE_CAPTURE; - - q_data->fmt = mtk_jpeg_find_format(ctx, pix_mp->pixelformat, f_type); - q_data->w = pix_mp->width; - q_data->h = pix_mp->height; - ctx->colorspace = pix_mp->colorspace; - ctx->ycbcr_enc = pix_mp->ycbcr_enc; - ctx->xfer_func = pix_mp->xfer_func; - ctx->quantization = pix_mp->quantization; + q_data->fmt = mtk_jpeg_find_format(jpeg->variant->formats, + jpeg->variant->num_formats, + pix_mp->pixelformat, fmt_type); + q_data->pix_mp.width = pix_mp->width; + q_data->pix_mp.height = pix_mp->height; + q_data->enc_crop_rect.width = pix_mp->width; + q_data->enc_crop_rect.height = pix_mp->height; + q_data->pix_mp.colorspace = V4L2_COLORSPACE_SRGB; + q_data->pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_601; + q_data->pix_mp.xfer_func = V4L2_XFER_FUNC_SRGB; + q_data->pix_mp.quantization = V4L2_QUANTIZATION_FULL_RANGE; v4l2_dbg(1, debug, &jpeg->v4l2_dev, "(%d) s_fmt:%c%c%c%c wxh:%ux%u\n", f->type, @@ -391,15 +447,18 @@ static int mtk_jpeg_s_fmt_mplane(struct mtk_jpeg_ctx *ctx, (q_data->fmt->fourcc >> 8 & 0xff), (q_data->fmt->fourcc >> 16 & 0xff), (q_data->fmt->fourcc >> 24 & 0xff), - q_data->w, q_data->h); + q_data->pix_mp.width, q_data->pix_mp.height); for (i = 0; i < q_data->fmt->colplanes; i++) { - q_data->bytesperline[i] = pix_mp->plane_fmt[i].bytesperline; - q_data->sizeimage[i] = pix_mp->plane_fmt[i].sizeimage; + q_data->pix_mp.plane_fmt[i].bytesperline = + pix_mp->plane_fmt[i].bytesperline; + q_data->pix_mp.plane_fmt[i].sizeimage = + pix_mp->plane_fmt[i].sizeimage; v4l2_dbg(1, debug, &jpeg->v4l2_dev, "plane[%d] bpl=%u, size=%u\n", - i, q_data->bytesperline[i], q_data->sizeimage[i]); + i, q_data->pix_mp.plane_fmt[i].bytesperline, + q_data->pix_mp.plane_fmt[i].sizeimage); } return 0; @@ -414,7 +473,8 @@ static int mtk_jpeg_s_fmt_vid_out_mplane(struct file *file, void *priv, if (ret) return ret; - return mtk_jpeg_s_fmt_mplane(mtk_jpeg_fh_to_ctx(priv), f); + return mtk_jpeg_s_fmt_mplane(mtk_jpeg_fh_to_ctx(priv), f, + MTK_JPEG_FMT_FLAG_OUTPUT); } static int mtk_jpeg_s_fmt_vid_cap_mplane(struct file *file, void *priv, @@ -426,7 +486,8 @@ static int mtk_jpeg_s_fmt_vid_cap_mplane(struct file *file, void *priv, if (ret) return ret; - return mtk_jpeg_s_fmt_mplane(mtk_jpeg_fh_to_ctx(priv), f); + return mtk_jpeg_s_fmt_mplane(mtk_jpeg_fh_to_ctx(priv), f, + MTK_JPEG_FMT_FLAG_CAPTURE); } static void mtk_jpeg_queue_src_chg_event(struct mtk_jpeg_ctx *ctx) @@ -446,13 +507,38 @@ static int mtk_jpeg_subscribe_event(struct v4l2_fh *fh, switch (sub->type) { case V4L2_EVENT_SOURCE_CHANGE: return v4l2_src_change_event_subscribe(fh, sub); + } + + return v4l2_ctrl_subscribe_event(fh, sub); +} + +static int mtk_jpeg_enc_g_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv); + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + s->r = ctx->out_q.enc_crop_rect; + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + s->r.width = ctx->out_q.pix_mp.width; + s->r.height = ctx->out_q.pix_mp.height; + s->r.left = 0; + s->r.top = 0; + break; default: return -EINVAL; } + return 0; } -static int mtk_jpeg_g_selection(struct file *file, void *priv, - struct v4l2_selection *s) +static int mtk_jpeg_dec_g_selection(struct file *file, void *priv, + struct v4l2_selection *s) { struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv); @@ -462,15 +548,15 @@ static int mtk_jpeg_g_selection(struct file *file, void *priv, switch (s->target) { case V4L2_SEL_TGT_COMPOSE: case V4L2_SEL_TGT_COMPOSE_DEFAULT: - s->r.width = ctx->out_q.w; - s->r.height = ctx->out_q.h; + s->r.width = ctx->out_q.pix_mp.width; + s->r.height = ctx->out_q.pix_mp.height; s->r.left = 0; s->r.top = 0; break; case V4L2_SEL_TGT_COMPOSE_BOUNDS: case V4L2_SEL_TGT_COMPOSE_PADDED: - s->r.width = ctx->cap_q.w; - s->r.height = ctx->cap_q.h; + s->r.width = ctx->cap_q.pix_mp.width; + s->r.height = ctx->cap_q.pix_mp.height; s->r.left = 0; s->r.top = 0; break; @@ -480,53 +566,57 @@ static int mtk_jpeg_g_selection(struct file *file, void *priv, return 0; } -static int mtk_jpeg_s_selection(struct file *file, void *priv, - struct v4l2_selection *s) +static int mtk_jpeg_enc_s_selection(struct file *file, void *priv, + struct v4l2_selection *s) { struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv); - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) return -EINVAL; switch (s->target) { - case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_CROP: s->r.left = 0; s->r.top = 0; - s->r.width = ctx->out_q.w; - s->r.height = ctx->out_q.h; + s->r.width = min(s->r.width, ctx->out_q.pix_mp.width); + s->r.height = min(s->r.height, ctx->out_q.pix_mp.height); + ctx->out_q.enc_crop_rect = s->r; break; default: return -EINVAL; } + return 0; } -static int mtk_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct v4l2_fh *fh = file->private_data; - struct mtk_jpeg_ctx *ctx = mtk_jpeg_fh_to_ctx(priv); - struct vb2_queue *vq; - struct vb2_buffer *vb; - struct mtk_jpeg_src_buf *jpeg_src_buf; - - if (buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - goto end; +static const struct v4l2_ioctl_ops mtk_jpeg_enc_ioctl_ops = { + .vidioc_querycap = mtk_jpeg_querycap, + .vidioc_enum_fmt_vid_cap = mtk_jpeg_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = mtk_jpeg_enum_fmt_vid_out, + .vidioc_try_fmt_vid_cap_mplane = mtk_jpeg_try_fmt_vid_cap_mplane, + .vidioc_try_fmt_vid_out_mplane = mtk_jpeg_try_fmt_vid_out_mplane, + .vidioc_g_fmt_vid_cap_mplane = mtk_jpeg_g_fmt_vid_mplane, + .vidioc_g_fmt_vid_out_mplane = mtk_jpeg_g_fmt_vid_mplane, + .vidioc_s_fmt_vid_cap_mplane = mtk_jpeg_s_fmt_vid_cap_mplane, + .vidioc_s_fmt_vid_out_mplane = mtk_jpeg_s_fmt_vid_out_mplane, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_subscribe_event = mtk_jpeg_subscribe_event, + .vidioc_g_selection = mtk_jpeg_enc_g_selection, + .vidioc_s_selection = mtk_jpeg_enc_s_selection, - vq = v4l2_m2m_get_vq(fh->m2m_ctx, buf->type); - if (buf->index >= vq->num_buffers) { - dev_err(ctx->jpeg->dev, "buffer index out of range\n"); - return -EINVAL; - } + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, - vb = vb2_get_buffer(vq, buf->index); - jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(vb); - jpeg_src_buf->flags = (buf->m.planes[0].bytesused == 0) ? - MTK_JPEG_BUF_FLAGS_LAST_FRAME : MTK_JPEG_BUF_FLAGS_INIT; -end: - return v4l2_m2m_qbuf(file, fh->m2m_ctx, buf); -} + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; -static const struct v4l2_ioctl_ops mtk_jpeg_ioctl_ops = { +static const struct v4l2_ioctl_ops mtk_jpeg_dec_ioctl_ops = { .vidioc_querycap = mtk_jpeg_querycap, .vidioc_enum_fmt_vid_cap = mtk_jpeg_enum_fmt_vid_cap, .vidioc_enum_fmt_vid_out = mtk_jpeg_enum_fmt_vid_out, @@ -536,10 +626,9 @@ static const struct v4l2_ioctl_ops mtk_jpeg_ioctl_ops = { .vidioc_g_fmt_vid_out_mplane = mtk_jpeg_g_fmt_vid_mplane, .vidioc_s_fmt_vid_cap_mplane = mtk_jpeg_s_fmt_vid_cap_mplane, .vidioc_s_fmt_vid_out_mplane = mtk_jpeg_s_fmt_vid_out_mplane, - .vidioc_qbuf = mtk_jpeg_qbuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, .vidioc_subscribe_event = mtk_jpeg_subscribe_event, - .vidioc_g_selection = mtk_jpeg_g_selection, - .vidioc_s_selection = mtk_jpeg_s_selection, + .vidioc_g_selection = mtk_jpeg_dec_g_selection, .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, @@ -571,9 +660,16 @@ static int mtk_jpeg_queue_setup(struct vb2_queue *q, if (!q_data) return -EINVAL; + if (*num_planes) { + for (i = 0; i < *num_planes; i++) + if (sizes[i] < q_data->pix_mp.plane_fmt[i].sizeimage) + return -EINVAL; + return 0; + } + *num_planes = q_data->fmt->colplanes; for (i = 0; i < q_data->fmt->colplanes; i++) { - sizes[i] = q_data->sizeimage[i]; + sizes[i] = q_data->pix_mp.plane_fmt[i].sizeimage; v4l2_dbg(1, debug, &jpeg->v4l2_dev, "sizeimage[%d]=%u\n", i, sizes[i]); } @@ -585,14 +681,22 @@ static int mtk_jpeg_buf_prepare(struct vb2_buffer *vb) { struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct mtk_jpeg_q_data *q_data = NULL; + struct v4l2_plane_pix_format plane_fmt = {}; int i; q_data = mtk_jpeg_get_q_data(ctx, vb->vb2_queue->type); if (!q_data) return -EINVAL; - for (i = 0; i < q_data->fmt->colplanes; i++) - vb2_set_plane_payload(vb, i, q_data->sizeimage[i]); + for (i = 0; i < q_data->fmt->colplanes; i++) { + plane_fmt = q_data->pix_mp.plane_fmt[i]; + if (ctx->enable_exif && + q_data->fmt->fourcc == V4L2_PIX_FMT_JPEG) + vb2_set_plane_payload(vb, i, plane_fmt.sizeimage + + MTK_JPEG_MAX_EXIF_SIZE); + else + vb2_set_plane_payload(vb, i, plane_fmt.sizeimage); + } return 0; } @@ -604,14 +708,17 @@ static bool mtk_jpeg_check_resolution_change(struct mtk_jpeg_ctx *ctx, struct mtk_jpeg_q_data *q_data; q_data = &ctx->out_q; - if (q_data->w != param->pic_w || q_data->h != param->pic_h) { + if (q_data->pix_mp.width != param->pic_w || + q_data->pix_mp.height != param->pic_h) { v4l2_dbg(1, debug, &jpeg->v4l2_dev, "Picture size change\n"); return true; } q_data = &ctx->cap_q; - if (q_data->fmt != mtk_jpeg_find_format(ctx, param->dst_fourcc, - MTK_JPEG_FMT_TYPE_CAPTURE)) { + if (q_data->fmt != + mtk_jpeg_find_format(jpeg->variant->formats, + jpeg->variant->num_formats, param->dst_fourcc, + MTK_JPEG_FMT_FLAG_CAPTURE)) { v4l2_dbg(1, debug, &jpeg->v4l2_dev, "format change\n"); return true; } @@ -626,19 +733,20 @@ static void mtk_jpeg_set_queue_data(struct mtk_jpeg_ctx *ctx, int i; q_data = &ctx->out_q; - q_data->w = param->pic_w; - q_data->h = param->pic_h; + q_data->pix_mp.width = param->pic_w; + q_data->pix_mp.height = param->pic_h; q_data = &ctx->cap_q; - q_data->w = param->dec_w; - q_data->h = param->dec_h; - q_data->fmt = mtk_jpeg_find_format(ctx, + q_data->pix_mp.width = param->dec_w; + q_data->pix_mp.height = param->dec_h; + q_data->fmt = mtk_jpeg_find_format(jpeg->variant->formats, + jpeg->variant->num_formats, param->dst_fourcc, - MTK_JPEG_FMT_TYPE_CAPTURE); + MTK_JPEG_FMT_FLAG_CAPTURE); for (i = 0; i < q_data->fmt->colplanes; i++) { - q_data->bytesperline[i] = param->mem_stride[i]; - q_data->sizeimage[i] = param->comp_size[i]; + q_data->pix_mp.plane_fmt[i].bytesperline = param->mem_stride[i]; + q_data->pix_mp.plane_fmt[i].sizeimage = param->comp_size[i]; } v4l2_dbg(1, debug, &jpeg->v4l2_dev, @@ -651,7 +759,18 @@ static void mtk_jpeg_set_queue_data(struct mtk_jpeg_ctx *ctx, param->dec_w, param->dec_h); } -static void mtk_jpeg_buf_queue(struct vb2_buffer *vb) +static void mtk_jpeg_enc_buf_queue(struct vb2_buffer *vb) +{ + struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct mtk_jpeg_dev *jpeg = ctx->jpeg; + + v4l2_dbg(2, debug, &jpeg->v4l2_dev, "(%d) buf_q id=%d, vb=%p\n", + vb->vb2_queue->type, vb->index, vb); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, to_vb2_v4l2_buffer(vb)); +} + +static void mtk_jpeg_dec_buf_queue(struct vb2_buffer *vb) { struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct mtk_jpeg_dec_param *param; @@ -669,10 +788,6 @@ static void mtk_jpeg_buf_queue(struct vb2_buffer *vb) param = &jpeg_src_buf->dec_param; memset(param, 0, sizeof(*param)); - if (jpeg_src_buf->flags & MTK_JPEG_BUF_FLAGS_LAST_FRAME) { - v4l2_dbg(1, debug, &jpeg->v4l2_dev, "Got eos\n"); - goto end; - } header_valid = mtk_jpeg_parse(param, (u8 *)vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); if (!header_valid) { @@ -703,24 +818,16 @@ static struct vb2_v4l2_buffer *mtk_jpeg_buf_remove(struct mtk_jpeg_ctx *ctx, return v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); } -static int mtk_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) +static void mtk_jpeg_enc_stop_streaming(struct vb2_queue *q) { struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(q); struct vb2_v4l2_buffer *vb; - int ret = 0; - ret = pm_runtime_get_sync(ctx->jpeg->dev); - if (ret < 0) - goto err; - - return 0; -err: while ((vb = mtk_jpeg_buf_remove(ctx, q->type))) - v4l2_m2m_buf_done(vb, VB2_BUF_STATE_QUEUED); - return ret; + v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR); } -static void mtk_jpeg_stop_streaming(struct vb2_queue *q) +static void mtk_jpeg_dec_stop_streaming(struct vb2_queue *q) { struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(q); struct vb2_v4l2_buffer *vb; @@ -744,18 +851,24 @@ static void mtk_jpeg_stop_streaming(struct vb2_queue *q) while ((vb = mtk_jpeg_buf_remove(ctx, q->type))) v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR); - - pm_runtime_put_sync(ctx->jpeg->dev); } -static const struct vb2_ops mtk_jpeg_qops = { +static const struct vb2_ops mtk_jpeg_dec_qops = { + .queue_setup = mtk_jpeg_queue_setup, + .buf_prepare = mtk_jpeg_buf_prepare, + .buf_queue = mtk_jpeg_dec_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .stop_streaming = mtk_jpeg_dec_stop_streaming, +}; + +static const struct vb2_ops mtk_jpeg_enc_qops = { .queue_setup = mtk_jpeg_queue_setup, .buf_prepare = mtk_jpeg_buf_prepare, - .buf_queue = mtk_jpeg_buf_queue, + .buf_queue = mtk_jpeg_enc_buf_queue, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, - .start_streaming = mtk_jpeg_start_streaming, - .stop_streaming = mtk_jpeg_stop_streaming, + .stop_streaming = mtk_jpeg_enc_stop_streaming, }; static void mtk_jpeg_set_dec_src(struct mtk_jpeg_ctx *ctx, @@ -764,8 +877,8 @@ static void mtk_jpeg_set_dec_src(struct mtk_jpeg_ctx *ctx, { bs->str_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); bs->end_addr = bs->str_addr + - mtk_jpeg_align(vb2_get_plane_payload(src_buf, 0), 16); - bs->size = mtk_jpeg_align(vb2_plane_size(src_buf, 0), 128); + round_up(vb2_get_plane_payload(src_buf, 0), 16); + bs->size = round_up(vb2_plane_size(src_buf, 0), 128); } static int mtk_jpeg_set_dec_dst(struct mtk_jpeg_ctx *ctx, @@ -795,7 +908,49 @@ static int mtk_jpeg_set_dec_dst(struct mtk_jpeg_ctx *ctx, return 0; } -static void mtk_jpeg_device_run(void *priv) +static void mtk_jpeg_enc_device_run(void *priv) +{ + struct mtk_jpeg_ctx *ctx = priv; + struct mtk_jpeg_dev *jpeg = ctx->jpeg; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR; + unsigned long flags; + int ret; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + ret = pm_runtime_get_sync(jpeg->dev); + if (ret < 0) + goto enc_end; + + schedule_delayed_work(&jpeg->job_timeout_work, + msecs_to_jiffies(MTK_JPEG_HW_TIMEOUT_MSEC)); + + spin_lock_irqsave(&jpeg->hw_lock, flags); + + /* + * Resetting the hardware every frame is to ensure that all the + * registers are cleared. This is a hardware requirement. + */ + mtk_jpeg_enc_reset(jpeg->reg_base); + + mtk_jpeg_set_enc_src(ctx, jpeg->reg_base, &src_buf->vb2_buf); + mtk_jpeg_set_enc_dst(ctx, jpeg->reg_base, &dst_buf->vb2_buf); + mtk_jpeg_set_enc_params(ctx, jpeg->reg_base); + mtk_jpeg_enc_start(jpeg->reg_base); + spin_unlock_irqrestore(&jpeg->hw_lock, flags); + return; + +enc_end: + v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + v4l2_m2m_buf_done(src_buf, buf_state); + v4l2_m2m_buf_done(dst_buf, buf_state); + v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx); +} + +static void mtk_jpeg_dec_device_run(void *priv) { struct mtk_jpeg_ctx *ctx = priv; struct mtk_jpeg_dev *jpeg = ctx->jpeg; @@ -805,19 +960,12 @@ static void mtk_jpeg_device_run(void *priv) struct mtk_jpeg_src_buf *jpeg_src_buf; struct mtk_jpeg_bs bs; struct mtk_jpeg_fb fb; - int i; + int ret; src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(&src_buf->vb2_buf); - if (jpeg_src_buf->flags & MTK_JPEG_BUF_FLAGS_LAST_FRAME) { - for (i = 0; i < dst_buf->vb2_buf.num_planes; i++) - vb2_set_plane_payload(&dst_buf->vb2_buf, i, 0); - buf_state = VB2_BUF_STATE_DONE; - goto dec_end; - } - if (mtk_jpeg_check_resolution_change(ctx, &jpeg_src_buf->dec_param)) { mtk_jpeg_queue_src_chg_event(ctx); ctx->state = MTK_JPEG_SOURCE_CHANGE; @@ -825,16 +973,23 @@ static void mtk_jpeg_device_run(void *priv) return; } + ret = pm_runtime_get_sync(jpeg->dev); + if (ret < 0) + goto dec_end; + + schedule_delayed_work(&jpeg->job_timeout_work, + msecs_to_jiffies(MTK_JPEG_HW_TIMEOUT_MSEC)); + mtk_jpeg_set_dec_src(ctx, &src_buf->vb2_buf, &bs); if (mtk_jpeg_set_dec_dst(ctx, &jpeg_src_buf->dec_param, &dst_buf->vb2_buf, &fb)) goto dec_end; spin_lock_irqsave(&jpeg->hw_lock, flags); - mtk_jpeg_dec_reset(jpeg->dec_reg_base); - mtk_jpeg_dec_set_config(jpeg->dec_reg_base, + mtk_jpeg_dec_reset(jpeg->reg_base); + mtk_jpeg_dec_set_config(jpeg->reg_base, &jpeg_src_buf->dec_param, &bs, &fb); - mtk_jpeg_dec_start(jpeg->dec_reg_base); + mtk_jpeg_dec_start(jpeg->reg_base); spin_unlock_irqrestore(&jpeg->hw_lock, flags); return; @@ -846,29 +1001,34 @@ dec_end: v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx); } -static int mtk_jpeg_job_ready(void *priv) +static int mtk_jpeg_dec_job_ready(void *priv) { struct mtk_jpeg_ctx *ctx = priv; return (ctx->state == MTK_JPEG_RUNNING) ? 1 : 0; } -static const struct v4l2_m2m_ops mtk_jpeg_m2m_ops = { - .device_run = mtk_jpeg_device_run, - .job_ready = mtk_jpeg_job_ready, +static const struct v4l2_m2m_ops mtk_jpeg_enc_m2m_ops = { + .device_run = mtk_jpeg_enc_device_run, +}; + +static const struct v4l2_m2m_ops mtk_jpeg_dec_m2m_ops = { + .device_run = mtk_jpeg_dec_device_run, + .job_ready = mtk_jpeg_dec_job_ready, }; static int mtk_jpeg_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) { struct mtk_jpeg_ctx *ctx = priv; + struct mtk_jpeg_dev *jpeg = ctx->jpeg; int ret; src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; src_vq->io_modes = VB2_DMABUF | VB2_MMAP; src_vq->drv_priv = ctx; src_vq->buf_struct_size = sizeof(struct mtk_jpeg_src_buf); - src_vq->ops = &mtk_jpeg_qops; + src_vq->ops = jpeg->variant->qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; src_vq->lock = &ctx->jpeg->lock; @@ -881,7 +1041,7 @@ static int mtk_jpeg_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - dst_vq->ops = &mtk_jpeg_qops; + dst_vq->ops = jpeg->variant->qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->lock = &ctx->jpeg->lock; @@ -898,17 +1058,68 @@ static void mtk_jpeg_clk_on(struct mtk_jpeg_dev *jpeg) ret = mtk_smi_larb_get(jpeg->larb); if (ret) dev_err(jpeg->dev, "mtk_smi_larb_get larbvdec fail %d\n", ret); - clk_prepare_enable(jpeg->clk_jdec_smi); - clk_prepare_enable(jpeg->clk_jdec); + + ret = clk_bulk_prepare_enable(jpeg->variant->num_clks, + jpeg->variant->clks); + if (ret) + dev_err(jpeg->dev, "Failed to open jpeg clk: %d\n", ret); } static void mtk_jpeg_clk_off(struct mtk_jpeg_dev *jpeg) { - clk_disable_unprepare(jpeg->clk_jdec); - clk_disable_unprepare(jpeg->clk_jdec_smi); + clk_bulk_disable_unprepare(jpeg->variant->num_clks, + jpeg->variant->clks); mtk_smi_larb_put(jpeg->larb); } +static irqreturn_t mtk_jpeg_enc_done(struct mtk_jpeg_dev *jpeg) +{ + struct mtk_jpeg_ctx *ctx; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + enum vb2_buffer_state buf_state = VB2_BUF_STATE_ERROR; + u32 result_size; + + ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); + if (!ctx) { + v4l2_err(&jpeg->v4l2_dev, "Context is NULL\n"); + return IRQ_HANDLED; + } + + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + result_size = mtk_jpeg_enc_get_file_size(jpeg->reg_base); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, result_size); + + buf_state = VB2_BUF_STATE_DONE; + + v4l2_m2m_buf_done(src_buf, buf_state); + v4l2_m2m_buf_done(dst_buf, buf_state); + v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx); + pm_runtime_put(ctx->jpeg->dev); + return IRQ_HANDLED; +} + +static irqreturn_t mtk_jpeg_enc_irq(int irq, void *priv) +{ + struct mtk_jpeg_dev *jpeg = priv; + u32 irq_status; + irqreturn_t ret = IRQ_NONE; + + cancel_delayed_work(&jpeg->job_timeout_work); + + irq_status = readl(jpeg->reg_base + JPEG_ENC_INT_STS) & + JPEG_ENC_INT_STATUS_MASK_ALLIRQ; + if (irq_status) + writel(0, jpeg->reg_base + JPEG_ENC_INT_STS); + + if (!(irq_status & JPEG_ENC_INT_STATUS_DONE)) + return ret; + + ret = mtk_jpeg_enc_done(jpeg); + return ret; +} + static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv) { struct mtk_jpeg_dev *jpeg = priv; @@ -920,7 +1131,9 @@ static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv) u32 dec_ret; int i; - dec_ret = mtk_jpeg_dec_get_int_status(jpeg->dec_reg_base); + cancel_delayed_work(&jpeg->job_timeout_work); + + dec_ret = mtk_jpeg_dec_get_int_status(jpeg->reg_base); dec_irq_ret = mtk_jpeg_dec_enum_result(dec_ret); ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); if (!ctx) { @@ -933,7 +1146,7 @@ static irqreturn_t mtk_jpeg_dec_irq(int irq, void *priv) jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(&src_buf->vb2_buf); if (dec_irq_ret >= MTK_JPEG_DEC_RESULT_UNDERFLOW) - mtk_jpeg_dec_reset(jpeg->dec_reg_base); + mtk_jpeg_dec_reset(jpeg->reg_base); if (dec_irq_ret != MTK_JPEG_DEC_RESULT_EOF_DONE) { dev_err(jpeg->dev, "decode failed\n"); @@ -950,39 +1163,42 @@ dec_end: v4l2_m2m_buf_done(src_buf, buf_state); v4l2_m2m_buf_done(dst_buf, buf_state); v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx); + pm_runtime_put(ctx->jpeg->dev); return IRQ_HANDLED; } static void mtk_jpeg_set_default_params(struct mtk_jpeg_ctx *ctx) { struct mtk_jpeg_q_data *q = &ctx->out_q; - int i; + struct mtk_jpeg_dev *jpeg = ctx->jpeg; - ctx->colorspace = V4L2_COLORSPACE_JPEG, - ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - ctx->quantization = V4L2_QUANTIZATION_DEFAULT; - ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT; + ctx->fh.ctrl_handler = &ctx->ctrl_hdl; + q->pix_mp.colorspace = V4L2_COLORSPACE_SRGB; + q->pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_601; + q->pix_mp.quantization = V4L2_QUANTIZATION_FULL_RANGE; + q->pix_mp.xfer_func = V4L2_XFER_FUNC_SRGB; - q->fmt = mtk_jpeg_find_format(ctx, V4L2_PIX_FMT_JPEG, - MTK_JPEG_FMT_TYPE_OUTPUT); - q->w = MTK_JPEG_MIN_WIDTH; - q->h = MTK_JPEG_MIN_HEIGHT; - q->bytesperline[0] = 0; - q->sizeimage[0] = MTK_JPEG_DEFAULT_SIZEIMAGE; + q->fmt = mtk_jpeg_find_format(jpeg->variant->formats, + jpeg->variant->num_formats, + jpeg->variant->out_q_default_fourcc, + MTK_JPEG_FMT_FLAG_OUTPUT); + q->pix_mp.width = MTK_JPEG_MIN_WIDTH; + q->pix_mp.height = MTK_JPEG_MIN_HEIGHT; + mtk_jpeg_try_fmt_mplane(&q->pix_mp, q->fmt); q = &ctx->cap_q; - q->fmt = mtk_jpeg_find_format(ctx, V4L2_PIX_FMT_YUV420M, - MTK_JPEG_FMT_TYPE_CAPTURE); - q->w = MTK_JPEG_MIN_WIDTH; - q->h = MTK_JPEG_MIN_HEIGHT; - - for (i = 0; i < q->fmt->colplanes; i++) { - u32 stride = q->w * q->fmt->h_sample[i] / 4; - u32 h = q->h * q->fmt->v_sample[i] / 4; - - q->bytesperline[i] = stride; - q->sizeimage[i] = stride * h; - } + q->fmt = mtk_jpeg_find_format(jpeg->variant->formats, + jpeg->variant->num_formats, + jpeg->variant->cap_q_default_fourcc, + MTK_JPEG_FMT_FLAG_CAPTURE); + q->pix_mp.colorspace = V4L2_COLORSPACE_SRGB; + q->pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_601; + q->pix_mp.quantization = V4L2_QUANTIZATION_FULL_RANGE; + q->pix_mp.xfer_func = V4L2_XFER_FUNC_SRGB; + q->pix_mp.width = MTK_JPEG_MIN_WIDTH; + q->pix_mp.height = MTK_JPEG_MIN_HEIGHT; + + mtk_jpeg_try_fmt_mplane(&q->pix_mp, q->fmt); } static int mtk_jpeg_open(struct file *file) @@ -1013,6 +1229,15 @@ static int mtk_jpeg_open(struct file *file) goto error; } + if (jpeg->variant->cap_q_default_fourcc == V4L2_PIX_FMT_JPEG) { + ret = mtk_jpeg_enc_ctrls_setup(ctx); + if (ret) { + v4l2_err(&jpeg->v4l2_dev, "Failed to setup jpeg enc controls\n"); + goto error; + } + } else { + v4l2_ctrl_handler_init(&ctx->ctrl_hdl, 0); + } mtk_jpeg_set_default_params(ctx); mutex_unlock(&jpeg->lock); return 0; @@ -1033,6 +1258,7 @@ static int mtk_jpeg_release(struct file *file) mutex_lock(&jpeg->lock); v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); kfree(ctx); @@ -1049,10 +1275,20 @@ static const struct v4l2_file_operations mtk_jpeg_fops = { .mmap = v4l2_m2m_fop_mmap, }; +static struct clk_bulk_data mt8173_jpeg_dec_clocks[] = { + { .id = "jpgdec-smi" }, + { .id = "jpgdec" }, +}; + +static struct clk_bulk_data mtk_jpeg_clocks[] = { + { .id = "jpgenc" }, +}; + static int mtk_jpeg_clk_init(struct mtk_jpeg_dev *jpeg) { struct device_node *node; struct platform_device *pdev; + int ret; node = of_parse_phandle(jpeg->dev->of_node, "mediatek,larb", 0); if (!node) @@ -1066,19 +1302,40 @@ static int mtk_jpeg_clk_init(struct mtk_jpeg_dev *jpeg) jpeg->larb = &pdev->dev; - jpeg->clk_jdec = devm_clk_get(jpeg->dev, "jpgdec"); - if (IS_ERR(jpeg->clk_jdec)) - return PTR_ERR(jpeg->clk_jdec); + ret = devm_clk_bulk_get(jpeg->dev, jpeg->variant->num_clks, + jpeg->variant->clks); + if (ret) { + dev_err(&pdev->dev, "failed to get jpeg clock:%d\n", ret); + return ret; + } - jpeg->clk_jdec_smi = devm_clk_get(jpeg->dev, "jpgdec-smi"); - return PTR_ERR_OR_ZERO(jpeg->clk_jdec_smi); + return 0; } +static void mtk_jpeg_job_timeout_work(struct work_struct *work) +{ + struct mtk_jpeg_dev *jpeg = container_of(work, struct mtk_jpeg_dev, + job_timeout_work.work); + struct mtk_jpeg_ctx *ctx; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + + ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + + jpeg->variant->hw_reset(jpeg->reg_base); + + pm_runtime_put(jpeg->dev); + + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx); +} static int mtk_jpeg_probe(struct platform_device *pdev) { struct mtk_jpeg_dev *jpeg; struct resource *res; - int dec_irq; + int jpeg_irq; int ret; jpeg = devm_kzalloc(&pdev->dev, sizeof(*jpeg), GFP_KERNEL); @@ -1088,28 +1345,27 @@ static int mtk_jpeg_probe(struct platform_device *pdev) mutex_init(&jpeg->lock); spin_lock_init(&jpeg->hw_lock); jpeg->dev = &pdev->dev; + jpeg->variant = of_device_get_match_data(jpeg->dev); + INIT_DELAYED_WORK(&jpeg->job_timeout_work, mtk_jpeg_job_timeout_work); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - jpeg->dec_reg_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(jpeg->dec_reg_base)) { - ret = PTR_ERR(jpeg->dec_reg_base); + jpeg->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(jpeg->reg_base)) { + ret = PTR_ERR(jpeg->reg_base); return ret; } - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - dec_irq = platform_get_irq(pdev, 0); - if (!res || dec_irq < 0) { - dev_err(&pdev->dev, "Failed to get dec_irq %d.\n", dec_irq); - ret = -EINVAL; - return ret; + jpeg_irq = platform_get_irq(pdev, 0); + if (jpeg_irq < 0) { + dev_err(&pdev->dev, "Failed to get jpeg_irq %d.\n", jpeg_irq); + return jpeg_irq; } - ret = devm_request_irq(&pdev->dev, dec_irq, mtk_jpeg_dec_irq, 0, - pdev->name, jpeg); + ret = devm_request_irq(&pdev->dev, jpeg_irq, + jpeg->variant->irq_handler, 0, pdev->name, jpeg); if (ret) { - dev_err(&pdev->dev, "Failed to request dec_irq %d (%d)\n", - dec_irq, ret); - ret = -EINVAL; + dev_err(&pdev->dev, "Failed to request jpeg_irq %d (%d)\n", + jpeg_irq, ret); goto err_req_irq; } @@ -1126,40 +1382,42 @@ static int mtk_jpeg_probe(struct platform_device *pdev) goto err_dev_register; } - jpeg->m2m_dev = v4l2_m2m_init(&mtk_jpeg_m2m_ops); + jpeg->m2m_dev = v4l2_m2m_init(jpeg->variant->m2m_ops); + if (IS_ERR(jpeg->m2m_dev)) { v4l2_err(&jpeg->v4l2_dev, "Failed to init mem2mem device\n"); ret = PTR_ERR(jpeg->m2m_dev); goto err_m2m_init; } - jpeg->dec_vdev = video_device_alloc(); - if (!jpeg->dec_vdev) { + jpeg->vdev = video_device_alloc(); + if (!jpeg->vdev) { ret = -ENOMEM; - goto err_dec_vdev_alloc; + goto err_vfd_jpeg_alloc; } - snprintf(jpeg->dec_vdev->name, sizeof(jpeg->dec_vdev->name), - "%s-dec", MTK_JPEG_NAME); - jpeg->dec_vdev->fops = &mtk_jpeg_fops; - jpeg->dec_vdev->ioctl_ops = &mtk_jpeg_ioctl_ops; - jpeg->dec_vdev->minor = -1; - jpeg->dec_vdev->release = video_device_release; - jpeg->dec_vdev->lock = &jpeg->lock; - jpeg->dec_vdev->v4l2_dev = &jpeg->v4l2_dev; - jpeg->dec_vdev->vfl_dir = VFL_DIR_M2M; - jpeg->dec_vdev->device_caps = V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_M2M_MPLANE; - - ret = video_register_device(jpeg->dec_vdev, VFL_TYPE_VIDEO, 3); + snprintf(jpeg->vdev->name, sizeof(jpeg->vdev->name), + "%s", jpeg->variant->dev_name); + jpeg->vdev->fops = &mtk_jpeg_fops; + jpeg->vdev->ioctl_ops = jpeg->variant->ioctl_ops; + jpeg->vdev->minor = -1; + jpeg->vdev->release = video_device_release; + jpeg->vdev->lock = &jpeg->lock; + jpeg->vdev->v4l2_dev = &jpeg->v4l2_dev; + jpeg->vdev->vfl_dir = VFL_DIR_M2M; + jpeg->vdev->device_caps = V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_M2M_MPLANE; + + ret = video_register_device(jpeg->vdev, VFL_TYPE_VIDEO, -1); if (ret) { v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n"); - goto err_dec_vdev_register; + goto err_vfd_jpeg_register; } - video_set_drvdata(jpeg->dec_vdev, jpeg); + video_set_drvdata(jpeg->vdev, jpeg); v4l2_info(&jpeg->v4l2_dev, - "decoder device registered as /dev/video%d (%d,%d)\n", - jpeg->dec_vdev->num, VIDEO_MAJOR, jpeg->dec_vdev->minor); + "%s device registered as /dev/video%d (%d,%d)\n", + jpeg->variant->dev_name, jpeg->vdev->num, + VIDEO_MAJOR, jpeg->vdev->minor); platform_set_drvdata(pdev, jpeg); @@ -1167,10 +1425,10 @@ static int mtk_jpeg_probe(struct platform_device *pdev) return 0; -err_dec_vdev_register: - video_device_release(jpeg->dec_vdev); +err_vfd_jpeg_register: + video_device_release(jpeg->vdev); -err_dec_vdev_alloc: +err_vfd_jpeg_alloc: v4l2_m2m_release(jpeg->m2m_dev); err_m2m_init: @@ -1190,8 +1448,8 @@ static int mtk_jpeg_remove(struct platform_device *pdev) struct mtk_jpeg_dev *jpeg = platform_get_drvdata(pdev); pm_runtime_disable(&pdev->dev); - video_unregister_device(jpeg->dec_vdev); - video_device_release(jpeg->dec_vdev); + video_unregister_device(jpeg->vdev); + video_device_release(jpeg->vdev); v4l2_m2m_release(jpeg->m2m_dev); v4l2_device_unregister(&jpeg->v4l2_dev); @@ -1202,7 +1460,6 @@ static __maybe_unused int mtk_jpeg_pm_suspend(struct device *dev) { struct mtk_jpeg_dev *jpeg = dev_get_drvdata(dev); - mtk_jpeg_dec_reset(jpeg->dec_reg_base); mtk_jpeg_clk_off(jpeg); return 0; @@ -1213,31 +1470,28 @@ static __maybe_unused int mtk_jpeg_pm_resume(struct device *dev) struct mtk_jpeg_dev *jpeg = dev_get_drvdata(dev); mtk_jpeg_clk_on(jpeg); - mtk_jpeg_dec_reset(jpeg->dec_reg_base); return 0; } static __maybe_unused int mtk_jpeg_suspend(struct device *dev) { - int ret; - - if (pm_runtime_suspended(dev)) - return 0; + struct mtk_jpeg_dev *jpeg = dev_get_drvdata(dev); - ret = mtk_jpeg_pm_suspend(dev); - return ret; + v4l2_m2m_suspend(jpeg->m2m_dev); + return pm_runtime_force_suspend(dev); } static __maybe_unused int mtk_jpeg_resume(struct device *dev) { + struct mtk_jpeg_dev *jpeg = dev_get_drvdata(dev); int ret; - if (pm_runtime_suspended(dev)) - return 0; - - ret = mtk_jpeg_pm_resume(dev); + ret = pm_runtime_force_resume(dev); + if (ret < 0) + return ret; + v4l2_m2m_resume(jpeg->m2m_dev); return ret; } @@ -1246,14 +1500,48 @@ static const struct dev_pm_ops mtk_jpeg_pm_ops = { SET_RUNTIME_PM_OPS(mtk_jpeg_pm_suspend, mtk_jpeg_pm_resume, NULL) }; +static const struct mtk_jpeg_variant mt8173_jpeg_drvdata = { + .clks = mt8173_jpeg_dec_clocks, + .num_clks = ARRAY_SIZE(mt8173_jpeg_dec_clocks), + .formats = mtk_jpeg_dec_formats, + .num_formats = MTK_JPEG_DEC_NUM_FORMATS, + .qops = &mtk_jpeg_dec_qops, + .irq_handler = mtk_jpeg_dec_irq, + .hw_reset = mtk_jpeg_dec_reset, + .m2m_ops = &mtk_jpeg_dec_m2m_ops, + .dev_name = "mtk-jpeg-dec", + .ioctl_ops = &mtk_jpeg_dec_ioctl_ops, + .out_q_default_fourcc = V4L2_PIX_FMT_JPEG, + .cap_q_default_fourcc = V4L2_PIX_FMT_YUV420M, +}; + +static const struct mtk_jpeg_variant mtk_jpeg_drvdata = { + .clks = mtk_jpeg_clocks, + .num_clks = ARRAY_SIZE(mtk_jpeg_clocks), + .formats = mtk_jpeg_enc_formats, + .num_formats = MTK_JPEG_ENC_NUM_FORMATS, + .qops = &mtk_jpeg_enc_qops, + .irq_handler = mtk_jpeg_enc_irq, + .hw_reset = mtk_jpeg_enc_reset, + .m2m_ops = &mtk_jpeg_enc_m2m_ops, + .dev_name = "mtk-jpeg-enc", + .ioctl_ops = &mtk_jpeg_enc_ioctl_ops, + .out_q_default_fourcc = V4L2_PIX_FMT_YUYV, + .cap_q_default_fourcc = V4L2_PIX_FMT_JPEG, +}; + static const struct of_device_id mtk_jpeg_match[] = { { .compatible = "mediatek,mt8173-jpgdec", - .data = NULL, + .data = &mt8173_jpeg_drvdata, }, { .compatible = "mediatek,mt2701-jpgdec", - .data = NULL, + .data = &mt8173_jpeg_drvdata, + }, + { + .compatible = "mediatek,mtk-jpgenc", + .data = &mtk_jpeg_drvdata, }, {}, }; diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h index 999bd1427809..68e634f02e00 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h @@ -3,6 +3,7 @@ * Copyright (c) 2016 MediaTek Inc. * Author: Ming Hsiu Tsai <minghsiu.tsai@mediatek.com> * Rick Chang <rick.chang@mediatek.com> + * Xia Jiang <xia.jiang@mediatek.com> */ #ifndef _MTK_JPEG_CORE_H @@ -15,19 +16,28 @@ #define MTK_JPEG_NAME "mtk-jpeg" -#define MTK_JPEG_FMT_FLAG_DEC_OUTPUT BIT(0) -#define MTK_JPEG_FMT_FLAG_DEC_CAPTURE BIT(1) +#define MTK_JPEG_COMP_MAX 3 -#define MTK_JPEG_FMT_TYPE_OUTPUT 1 -#define MTK_JPEG_FMT_TYPE_CAPTURE 2 +#define MTK_JPEG_FMT_FLAG_OUTPUT BIT(0) +#define MTK_JPEG_FMT_FLAG_CAPTURE BIT(1) -#define MTK_JPEG_MIN_WIDTH 32 -#define MTK_JPEG_MIN_HEIGHT 32 -#define MTK_JPEG_MAX_WIDTH 8192 -#define MTK_JPEG_MAX_HEIGHT 8192 +#define MTK_JPEG_MIN_WIDTH 32U +#define MTK_JPEG_MIN_HEIGHT 32U +#define MTK_JPEG_MAX_WIDTH 65535U +#define MTK_JPEG_MAX_HEIGHT 65535U #define MTK_JPEG_DEFAULT_SIZEIMAGE (1 * 1024 * 1024) +#define MTK_JPEG_HW_TIMEOUT_MSEC 1000 + +#define MTK_JPEG_MAX_EXIF_SIZE (64 * 1024) + +/** + * enum mtk_jpeg_ctx_state - states of the context state machine + * @MTK_JPEG_INIT: current state is initialized + * @MTK_JPEG_RUNNING: current state is running + * @MTK_JPEG_SOURCE_CHANGE: current state is source resolution change + */ enum mtk_jpeg_ctx_state { MTK_JPEG_INIT = 0, MTK_JPEG_RUNNING, @@ -35,6 +45,36 @@ enum mtk_jpeg_ctx_state { }; /** + * mtk_jpeg_variant - mtk jpeg driver variant + * @clks: clock names + * @num_clks: numbers of clock + * @format: jpeg driver's internal color format + * @num_format: number of format + * @qops: the callback of jpeg vb2_ops + * @irq_handler: jpeg irq handler callback + * @hw_reset: jpeg hardware reset callback + * @m2m_ops: the callback of jpeg v4l2_m2m_ops + * @dev_name: jpeg device name + * @ioctl_ops: the callback of jpeg v4l2_ioctl_ops + * @out_q_default_fourcc: output queue default fourcc + * @cap_q_default_fourcc: capture queue default fourcc + */ +struct mtk_jpeg_variant { + struct clk_bulk_data *clks; + int num_clks; + struct mtk_jpeg_fmt *formats; + int num_formats; + const struct vb2_ops *qops; + irqreturn_t (*irq_handler)(int irq, void *priv); + void (*hw_reset)(void __iomem *base); + const struct v4l2_m2m_ops *m2m_ops; + const char *dev_name; + const struct v4l2_ioctl_ops *ioctl_ops; + u32 out_q_default_fourcc; + u32 cap_q_default_fourcc; +}; + +/** * struct mt_jpeg - JPEG IP abstraction * @lock: the mutex protecting this structure * @hw_lock: spinlock protecting the hw device resource @@ -43,11 +83,11 @@ enum mtk_jpeg_ctx_state { * @v4l2_dev: v4l2 device for mem2mem mode * @m2m_dev: v4l2 mem2mem device data * @alloc_ctx: videobuf2 memory allocator's context - * @dec_vdev: video device node for decoder mem2mem mode - * @dec_reg_base: JPEG registers mapping - * @clk_jdec: JPEG hw working clock - * @clk_jdec_smi: JPEG SMI bus clock + * @vdev: video device node for jpeg mem2mem mode + * @reg_base: JPEG registers mapping * @larb: SMI device + * @job_timeout_work: IRQ timeout structure + * @variant: driver variant to be used */ struct mtk_jpeg_dev { struct mutex lock; @@ -57,16 +97,17 @@ struct mtk_jpeg_dev { struct v4l2_device v4l2_dev; struct v4l2_m2m_dev *m2m_dev; void *alloc_ctx; - struct video_device *dec_vdev; - void __iomem *dec_reg_base; - struct clk *clk_jdec; - struct clk *clk_jdec_smi; + struct video_device *vdev; + void __iomem *reg_base; struct device *larb; + struct delayed_work job_timeout_work; + const struct mtk_jpeg_variant *variant; }; /** * struct jpeg_fmt - driver's internal color format data * @fourcc: the fourcc code, 0 if not applicable + * @hw_format: hardware format value * @h_sample: horizontal sample count of plane in 4 * 4 pixel image * @v_sample: vertical sample count of plane in 4 * 4 pixel image * @colplanes: number of color planes (1 for packed formats) @@ -76,6 +117,7 @@ struct mtk_jpeg_dev { */ struct mtk_jpeg_fmt { u32 fourcc; + u32 hw_format; int h_sample[VIDEO_MAX_PLANES]; int v_sample[VIDEO_MAX_PLANES]; int colplanes; @@ -87,18 +129,13 @@ struct mtk_jpeg_fmt { /** * mtk_jpeg_q_data - parameters of one queue * @fmt: driver-specific format of this queue - * @w: image width - * @h: image height - * @bytesperline: distance in bytes between the leftmost pixels in two adjacent - * lines - * @sizeimage: image buffer size in bytes + * @pix_mp: multiplanar format + * @enc_crop_rect: jpeg encoder crop information */ struct mtk_jpeg_q_data { struct mtk_jpeg_fmt *fmt; - u32 w; - u32 h; - u32 bytesperline[VIDEO_MAX_PLANES]; - u32 sizeimage[VIDEO_MAX_PLANES]; + struct v4l2_pix_format_mplane pix_mp; + struct v4l2_rect enc_crop_rect; }; /** @@ -107,13 +144,11 @@ struct mtk_jpeg_q_data { * @out_q: source (output) queue information * @cap_q: destination (capture) queue queue information * @fh: V4L2 file handle - * @dec_param parameters for HW decoding * @state: state of the context - * @header_valid: set if header has been parsed and valid - * @colorspace: enum v4l2_colorspace; supplemental to pixelformat - * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding - * @quantization: enum v4l2_quantization, colorspace quantization - * @xfer_func: enum v4l2_xfer_func, colorspace transfer function + * @enable_exif: enable exif mode of jpeg encoder + * @enc_quality: jpeg encoder quality + * @restart_interval: jpeg encoder restart interval + * @ctrl_hdl: controls handler */ struct mtk_jpeg_ctx { struct mtk_jpeg_dev *jpeg; @@ -121,11 +156,10 @@ struct mtk_jpeg_ctx { struct mtk_jpeg_q_data cap_q; struct v4l2_fh fh; enum mtk_jpeg_ctx_state state; - - enum v4l2_colorspace colorspace; - enum v4l2_ycbcr_encoding ycbcr_enc; - enum v4l2_quantization quantization; - enum v4l2_xfer_func xfer_func; + bool enable_exif; + u8 enc_quality; + u8 restart_interval; + struct v4l2_ctrl_handler ctrl_hdl; }; #endif /* _MTK_JPEG_CORE_H */ diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.c index ddf0dfa78e20..afbbfd5d02bc 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.c @@ -9,7 +9,7 @@ #include <linux/kernel.h> #include <media/videobuf2-core.h> -#include "mtk_jpeg_hw.h" +#include "mtk_jpeg_dec_hw.h" #define MTK_JPEG_DUNUM_MASK(val) (((val) - 1) & 0x3) @@ -153,10 +153,10 @@ static int mtk_jpeg_calc_dst_size(struct mtk_jpeg_dec_param *param) param->sampling_w[i]; /* output format is 420/422 */ param->comp_w[i] = padding_w >> brz_w[i]; - param->comp_w[i] = mtk_jpeg_align(param->comp_w[i], - MTK_JPEG_DCTSIZE); - param->img_stride[i] = i ? mtk_jpeg_align(param->comp_w[i], 16) - : mtk_jpeg_align(param->comp_w[i], 32); + param->comp_w[i] = round_up(param->comp_w[i], + MTK_JPEG_DCTSIZE); + param->img_stride[i] = i ? round_up(param->comp_w[i], 16) + : round_up(param->comp_w[i], 32); ds_row_h[i] = (MTK_JPEG_DCTSIZE * param->sampling_h[i]); } param->dec_w = param->img_stride[0]; diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.h b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.h index 9c6584eaad99..fa0d45fd7c34 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_hw.h +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.h @@ -3,15 +3,16 @@ * Copyright (c) 2016 MediaTek Inc. * Author: Ming Hsiu Tsai <minghsiu.tsai@mediatek.com> * Rick Chang <rick.chang@mediatek.com> + * Xia Jiang <xia.jiang@mediatek.com> */ -#ifndef _MTK_JPEG_HW_H -#define _MTK_JPEG_HW_H +#ifndef _MTK_JPEG_DEC_HW_H +#define _MTK_JPEG_DEC_HW_H #include <media/videobuf2-core.h> #include "mtk_jpeg_core.h" -#include "mtk_jpeg_reg.h" +#include "mtk_jpeg_dec_reg.h" enum { MTK_JPEG_DEC_RESULT_EOF_DONE = 0, @@ -54,11 +55,6 @@ struct mtk_jpeg_dec_param { u8 uv_brz_w; }; -static inline u32 mtk_jpeg_align(u32 val, u32 align) -{ - return (val + align - 1) & ~(align - 1); -} - struct mtk_jpeg_bs { dma_addr_t str_addr; dma_addr_t end_addr; diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.c index f862d38f3af7..b95c45791c29 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.c @@ -8,7 +8,7 @@ #include <linux/kernel.h> #include <linux/videodev2.h> -#include "mtk_jpeg_parse.h" +#include "mtk_jpeg_dec_parse.h" #define TEM 0x01 #define SOF0 0xc0 diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.h b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.h index 0a48eeabaff2..2918f15811f8 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_parse.h +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.h @@ -8,7 +8,7 @@ #ifndef _MTK_JPEG_PARSE_H #define _MTK_JPEG_PARSE_H -#include "mtk_jpeg_hw.h" +#include "mtk_jpeg_dec_hw.h" bool mtk_jpeg_parse(struct mtk_jpeg_dec_param *param, u8 *src_addr_va, u32 src_size); diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_reg.h b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_reg.h index 94db04e9cdb6..21ec8f96797f 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_reg.h +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_reg.h @@ -8,7 +8,6 @@ #ifndef _MTK_JPEG_REG_H #define _MTK_JPEG_REG_H -#define MTK_JPEG_COMP_MAX 3 #define MTK_JPEG_BLOCK_MAX 10 #define MTK_JPEG_DCTSIZE 8 @@ -20,29 +19,29 @@ #define BIT_INQST_MASK_ALLIRQ 0x37 #define JPGDEC_REG_RESET 0x0090 -#define JPGDEC_REG_BRZ_FACTOR 0x00F8 -#define JPGDEC_REG_DU_NUM 0x00FC +#define JPGDEC_REG_BRZ_FACTOR 0x00f8 +#define JPGDEC_REG_DU_NUM 0x00fc #define JPGDEC_REG_DEST_ADDR0_Y 0x0140 #define JPGDEC_REG_DEST_ADDR0_U 0x0144 #define JPGDEC_REG_DEST_ADDR0_V 0x0148 -#define JPGDEC_REG_DEST_ADDR1_Y 0x014C +#define JPGDEC_REG_DEST_ADDR1_Y 0x014c #define JPGDEC_REG_DEST_ADDR1_U 0x0150 #define JPGDEC_REG_DEST_ADDR1_V 0x0154 #define JPGDEC_REG_STRIDE_Y 0x0158 -#define JPGDEC_REG_STRIDE_UV 0x015C +#define JPGDEC_REG_STRIDE_UV 0x015c #define JPGDEC_REG_IMG_STRIDE_Y 0x0160 #define JPGDEC_REG_IMG_STRIDE_UV 0x0164 -#define JPGDEC_REG_WDMA_CTRL 0x016C +#define JPGDEC_REG_WDMA_CTRL 0x016c #define JPGDEC_REG_PAUSE_MCU_NUM 0x0170 -#define JPGDEC_REG_OPERATION_MODE 0x017C +#define JPGDEC_REG_OPERATION_MODE 0x017c #define JPGDEC_REG_FILE_ADDR 0x0200 -#define JPGDEC_REG_COMP_ID 0x020C +#define JPGDEC_REG_COMP_ID 0x020c #define JPGDEC_REG_TOTAL_MCU_NUM 0x0210 #define JPGDEC_REG_COMP0_DATA_UNIT_NUM 0x0224 -#define JPGDEC_REG_DU_CTRL 0x023C +#define JPGDEC_REG_DU_CTRL 0x023c #define JPGDEC_REG_TRIG 0x0240 #define JPGDEC_REG_FILE_BRP 0x0248 -#define JPGDEC_REG_FILE_TOTAL_SIZE 0x024C +#define JPGDEC_REG_FILE_TOTAL_SIZE 0x024c #define JPGDEC_REG_QT_ID 0x0270 #define JPGDEC_REG_INTERRUPT_STATUS 0x0274 #define JPGDEC_REG_STATUS 0x0278 diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.c new file mode 100644 index 000000000000..1cf037bf72dd --- /dev/null +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019 MediaTek Inc. + * Author: Xia Jiang <xia.jiang@mediatek.com> + * + */ + +#include <linux/io.h> +#include <linux/kernel.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-dma-contig.h> + +#include "mtk_jpeg_enc_hw.h" + +static const struct mtk_jpeg_enc_qlt mtk_jpeg_enc_quality[] = { + {.quality_param = 34, .hardware_value = JPEG_ENC_QUALITY_Q34}, + {.quality_param = 39, .hardware_value = JPEG_ENC_QUALITY_Q39}, + {.quality_param = 48, .hardware_value = JPEG_ENC_QUALITY_Q48}, + {.quality_param = 60, .hardware_value = JPEG_ENC_QUALITY_Q60}, + {.quality_param = 64, .hardware_value = JPEG_ENC_QUALITY_Q64}, + {.quality_param = 68, .hardware_value = JPEG_ENC_QUALITY_Q68}, + {.quality_param = 74, .hardware_value = JPEG_ENC_QUALITY_Q74}, + {.quality_param = 80, .hardware_value = JPEG_ENC_QUALITY_Q80}, + {.quality_param = 82, .hardware_value = JPEG_ENC_QUALITY_Q82}, + {.quality_param = 84, .hardware_value = JPEG_ENC_QUALITY_Q84}, + {.quality_param = 87, .hardware_value = JPEG_ENC_QUALITY_Q87}, + {.quality_param = 90, .hardware_value = JPEG_ENC_QUALITY_Q90}, + {.quality_param = 92, .hardware_value = JPEG_ENC_QUALITY_Q92}, + {.quality_param = 95, .hardware_value = JPEG_ENC_QUALITY_Q95}, + {.quality_param = 97, .hardware_value = JPEG_ENC_QUALITY_Q97}, +}; + +void mtk_jpeg_enc_reset(void __iomem *base) +{ + writel(0, base + JPEG_ENC_RSTB); + writel(JPEG_ENC_RESET_BIT, base + JPEG_ENC_RSTB); + writel(0, base + JPEG_ENC_CODEC_SEL); +} + +u32 mtk_jpeg_enc_get_file_size(void __iomem *base) +{ + return readl(base + JPEG_ENC_DMA_ADDR0) - + readl(base + JPEG_ENC_DST_ADDR0); +} + +void mtk_jpeg_enc_start(void __iomem *base) +{ + u32 value; + + value = readl(base + JPEG_ENC_CTRL); + value |= JPEG_ENC_CTRL_INT_EN_BIT | JPEG_ENC_CTRL_ENABLE_BIT; + writel(value, base + JPEG_ENC_CTRL); +} + +void mtk_jpeg_set_enc_src(struct mtk_jpeg_ctx *ctx, void __iomem *base, + struct vb2_buffer *src_buf) +{ + int i; + dma_addr_t dma_addr; + + for (i = 0; i < src_buf->num_planes; i++) { + dma_addr = vb2_dma_contig_plane_dma_addr(src_buf, i) + + src_buf->planes[i].data_offset; + if (!i) + writel(dma_addr, base + JPEG_ENC_SRC_LUMA_ADDR); + else + writel(dma_addr, base + JPEG_ENC_SRC_CHROMA_ADDR); + } +} + +void mtk_jpeg_set_enc_dst(struct mtk_jpeg_ctx *ctx, void __iomem *base, + struct vb2_buffer *dst_buf) +{ + dma_addr_t dma_addr; + size_t size; + u32 dma_addr_offset; + u32 dma_addr_offsetmask; + + dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + dma_addr_offset = ctx->enable_exif ? MTK_JPEG_MAX_EXIF_SIZE : 0; + dma_addr_offsetmask = dma_addr & JPEG_ENC_DST_ADDR_OFFSET_MASK; + size = vb2_plane_size(dst_buf, 0); + + writel(dma_addr_offset & ~0xf, base + JPEG_ENC_OFFSET_ADDR); + writel(dma_addr_offsetmask & 0xf, base + JPEG_ENC_BYTE_OFFSET_MASK); + writel(dma_addr & ~0xf, base + JPEG_ENC_DST_ADDR0); + writel((dma_addr + size) & ~0xf, base + JPEG_ENC_STALL_ADDR0); +} + +void mtk_jpeg_set_enc_params(struct mtk_jpeg_ctx *ctx, void __iomem *base) +{ + u32 value; + u32 width = ctx->out_q.enc_crop_rect.width; + u32 height = ctx->out_q.enc_crop_rect.height; + u32 enc_format = ctx->out_q.fmt->fourcc; + u32 bytesperline = ctx->out_q.pix_mp.plane_fmt[0].bytesperline; + u32 blk_num; + u32 img_stride; + u32 mem_stride; + u32 i, enc_quality; + + value = width << 16 | height; + writel(value, base + JPEG_ENC_IMG_SIZE); + + if (enc_format == V4L2_PIX_FMT_NV12M || + enc_format == V4L2_PIX_FMT_NV21M) + /* + * Total 8 x 8 block number of luma and chroma. + * The number of blocks is counted from 0. + */ + blk_num = DIV_ROUND_UP(width, 16) * + DIV_ROUND_UP(height, 16) * 6 - 1; + else + blk_num = DIV_ROUND_UP(width, 16) * + DIV_ROUND_UP(height, 8) * 4 - 1; + writel(blk_num, base + JPEG_ENC_BLK_NUM); + + if (enc_format == V4L2_PIX_FMT_NV12M || + enc_format == V4L2_PIX_FMT_NV21M) { + /* 4:2:0 */ + img_stride = round_up(width, 16); + mem_stride = bytesperline; + } else { + /* 4:2:2 */ + img_stride = round_up(width * 2, 32); + mem_stride = img_stride; + } + writel(img_stride, base + JPEG_ENC_IMG_STRIDE); + writel(mem_stride, base + JPEG_ENC_STRIDE); + + enc_quality = mtk_jpeg_enc_quality[0].hardware_value; + for (i = 0; i < ARRAY_SIZE(mtk_jpeg_enc_quality); i++) { + if (ctx->enc_quality <= mtk_jpeg_enc_quality[i].quality_param) { + enc_quality = mtk_jpeg_enc_quality[i].hardware_value; + break; + } + } + writel(enc_quality, base + JPEG_ENC_QUALITY); + + value = readl(base + JPEG_ENC_CTRL); + value &= ~JPEG_ENC_CTRL_YUV_FORMAT_MASK; + value |= (ctx->out_q.fmt->hw_format & 3) << 3; + if (ctx->enable_exif) + value |= JPEG_ENC_CTRL_FILE_FORMAT_BIT; + else + value &= ~JPEG_ENC_CTRL_FILE_FORMAT_BIT; + if (ctx->restart_interval) + value |= JPEG_ENC_CTRL_RESTART_EN_BIT; + else + value &= ~JPEG_ENC_CTRL_RESTART_EN_BIT; + writel(value, base + JPEG_ENC_CTRL); + + writel(ctx->restart_interval, base + JPEG_ENC_RST_MCU_NUM); +} diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.h b/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.h new file mode 100644 index 000000000000..61c60e4e58ea --- /dev/null +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2019 MediaTek Inc. + * Author: Xia Jiang <xia.jiang@mediatek.com> + * + */ + +#ifndef _MTK_JPEG_ENC_HW_H +#define _MTK_JPEG_ENC_HW_H + +#include <media/videobuf2-core.h> + +#include "mtk_jpeg_core.h" + +#define JPEG_ENC_INT_STATUS_DONE BIT(0) +#define JPEG_ENC_INT_STATUS_MASK_ALLIRQ 0x13 + +#define JPEG_ENC_DST_ADDR_OFFSET_MASK GENMASK(3, 0) + +#define JPEG_ENC_CTRL_YUV_FORMAT_MASK 0x18 +#define JPEG_ENC_CTRL_RESTART_EN_BIT BIT(10) +#define JPEG_ENC_CTRL_FILE_FORMAT_BIT BIT(5) +#define JPEG_ENC_CTRL_INT_EN_BIT BIT(2) +#define JPEG_ENC_CTRL_ENABLE_BIT BIT(0) +#define JPEG_ENC_RESET_BIT BIT(0) + +#define JPEG_ENC_YUV_FORMAT_YUYV 0 +#define JPEG_ENC_YUV_FORMAT_YVYU 1 +#define JPEG_ENC_YUV_FORMAT_NV12 2 +#define JEPG_ENC_YUV_FORMAT_NV21 3 + +#define JPEG_ENC_QUALITY_Q60 0x0 +#define JPEG_ENC_QUALITY_Q80 0x1 +#define JPEG_ENC_QUALITY_Q90 0x2 +#define JPEG_ENC_QUALITY_Q95 0x3 +#define JPEG_ENC_QUALITY_Q39 0x4 +#define JPEG_ENC_QUALITY_Q68 0x5 +#define JPEG_ENC_QUALITY_Q84 0x6 +#define JPEG_ENC_QUALITY_Q92 0x7 +#define JPEG_ENC_QUALITY_Q48 0x8 +#define JPEG_ENC_QUALITY_Q74 0xa +#define JPEG_ENC_QUALITY_Q87 0xb +#define JPEG_ENC_QUALITY_Q34 0xc +#define JPEG_ENC_QUALITY_Q64 0xe +#define JPEG_ENC_QUALITY_Q82 0xf +#define JPEG_ENC_QUALITY_Q97 0x10 + +#define JPEG_ENC_RSTB 0x100 +#define JPEG_ENC_CTRL 0x104 +#define JPEG_ENC_QUALITY 0x108 +#define JPEG_ENC_BLK_NUM 0x10C +#define JPEG_ENC_BLK_CNT 0x110 +#define JPEG_ENC_INT_STS 0x11c +#define JPEG_ENC_DST_ADDR0 0x120 +#define JPEG_ENC_DMA_ADDR0 0x124 +#define JPEG_ENC_STALL_ADDR0 0x128 +#define JPEG_ENC_OFFSET_ADDR 0x138 +#define JPEG_ENC_RST_MCU_NUM 0x150 +#define JPEG_ENC_IMG_SIZE 0x154 +#define JPEG_ENC_DEBUG_INFO0 0x160 +#define JPEG_ENC_DEBUG_INFO1 0x164 +#define JPEG_ENC_TOTAL_CYCLE 0x168 +#define JPEG_ENC_BYTE_OFFSET_MASK 0x16c +#define JPEG_ENC_SRC_LUMA_ADDR 0x170 +#define JPEG_ENC_SRC_CHROMA_ADDR 0x174 +#define JPEG_ENC_STRIDE 0x178 +#define JPEG_ENC_IMG_STRIDE 0x17c +#define JPEG_ENC_DCM_CTRL 0x300 +#define JPEG_ENC_CODEC_SEL 0x314 +#define JPEG_ENC_ULTRA_THRES 0x318 + +/** + * struct mtk_jpeg_enc_qlt - JPEG encoder quality data + * @quality_param: quality value + * @hardware_value: hardware value of quality + */ +struct mtk_jpeg_enc_qlt { + u8 quality_param; + u8 hardware_value; +}; + +void mtk_jpeg_enc_reset(void __iomem *base); +u32 mtk_jpeg_enc_get_file_size(void __iomem *base); +void mtk_jpeg_enc_start(void __iomem *enc_reg_base); +void mtk_jpeg_set_enc_src(struct mtk_jpeg_ctx *ctx, void __iomem *base, + struct vb2_buffer *src_buf); +void mtk_jpeg_set_enc_dst(struct mtk_jpeg_ctx *ctx, void __iomem *base, + struct vb2_buffer *dst_buf); +void mtk_jpeg_set_enc_params(struct mtk_jpeg_ctx *ctx, void __iomem *base); + +#endif /* _MTK_JPEG_ENC_HW_H */ diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c index f96c8b3bf861..976aa1f4829b 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_core.c @@ -94,7 +94,7 @@ static void mtk_mdp_reset_handler(void *priv) void mtk_mdp_register_component(struct mtk_mdp_dev *mdp, struct mtk_mdp_comp *comp) { - list_add(&mdp->comp_list, &comp->node); + list_add(&comp->node, &mdp->comp_list); } void mtk_mdp_unregister_component(struct mtk_mdp_dev *mdp, diff --git a/drivers/media/platform/mtk-vcodec/Makefile b/drivers/media/platform/mtk-vcodec/Makefile index 37b94b555fa1..f679c6e1a3e9 100644 --- a/drivers/media/platform/mtk-vcodec/Makefile +++ b/drivers/media/platform/mtk-vcodec/Makefile @@ -13,7 +13,6 @@ mtk-vcodec-dec-y := vdec/vdec_h264_if.o \ mtk_vcodec_dec.o \ mtk_vcodec_dec_pm.o \ - mtk-vcodec-enc-y := venc/venc_vp8_if.o \ venc/venc_h264_if.o \ mtk_vcodec_enc.o \ @@ -24,6 +23,5 @@ mtk-vcodec-enc-y := venc/venc_vp8_if.o \ mtk-vcodec-common-y := mtk_vcodec_intr.o \ - mtk_vcodec_util.o\ - -ccflags-y += -I$(srctree)/drivers/media/platform/mtk-vpu + mtk_vcodec_util.o \ + mtk_vcodec_fw.o diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c index 0f3e710aed4e..c768a587a944 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c @@ -194,8 +194,7 @@ static struct vb2_buffer *get_free_buffer(struct mtk_vcodec_ctx *ctx) vb->vb2_buf.index, dstbuf->queued_in_vb2); v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); - } else if ((dstbuf->queued_in_vb2 == false) && - (dstbuf->queued_in_v4l2 == true)) { + } else if (!dstbuf->queued_in_vb2 && dstbuf->queued_in_v4l2) { /* * If buffer in v4l2 driver but not in vb2 queue yet, * and we get this buffer from free_list, it means @@ -448,7 +447,7 @@ static void mtk_vdec_worker(struct work_struct *work) mutex_unlock(&ctx->lock); } v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); - } else if (res_chg == false) { + } else if (!res_chg) { /* * we only return src buffer with VB2_BUF_STATE_DONE * when decode success without resolution change @@ -1156,7 +1155,7 @@ static void vb2ops_vdec_buf_queue(struct vb2_buffer *vb) buf = container_of(vb2_v4l2, struct mtk_video_dec_buf, m2m_buf.vb); mutex_lock(&ctx->lock); - if (buf->used == false) { + if (!buf->used) { v4l2_m2m_buf_queue(ctx->m2m_ctx, vb2_v4l2); buf->queued_in_vb2 = true; buf->queued_in_v4l2 = true; @@ -1525,10 +1524,8 @@ int mtk_vcodec_dec_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->dev = &ctx->dev->plat_dev->dev; ret = vb2_queue_init(dst_vq); - if (ret) { - vb2_queue_release(src_vq); + if (ret) mtk_v4l2_err("Failed to initialize videobuf2 queue(capture)"); - } return ret; } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c index 97a1b6664c20..d14bc208ea5e 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c @@ -20,7 +20,7 @@ #include "mtk_vcodec_dec_pm.h" #include "mtk_vcodec_intr.h" #include "mtk_vcodec_util.h" -#include "mtk_vpu.h" +#include "mtk_vcodec_fw.h" #define VDEC_HW_ACTIVE 0x10 #define VDEC_IRQ_CFG 0x11 @@ -77,22 +77,6 @@ static irqreturn_t mtk_vcodec_dec_irq_handler(int irq, void *priv) return IRQ_HANDLED; } -static void mtk_vcodec_dec_reset_handler(void *priv) -{ - struct mtk_vcodec_dev *dev = priv; - struct mtk_vcodec_ctx *ctx; - - mtk_v4l2_err("Watchdog timeout!!"); - - mutex_lock(&dev->dev_mutex); - list_for_each_entry(ctx, &dev->ctx_list, list) { - ctx->state = MTK_STATE_ABORT; - mtk_v4l2_debug(0, "[%d] Change to state MTK_STATE_ERROR", - ctx->id); - } - mutex_unlock(&dev->dev_mutex); -} - static int fops_vcodec_open(struct file *file) { struct mtk_vcodec_dev *dev = video_drvdata(file); @@ -144,21 +128,20 @@ static int fops_vcodec_open(struct file *file) if (v4l2_fh_is_singular(&ctx->fh)) { mtk_vcodec_dec_pw_on(&dev->pm); /* - * vpu_load_firmware checks if it was loaded already and - * does nothing in that case + * Does nothing if firmware was already loaded. */ - ret = vpu_load_firmware(dev->vpu_plat_dev); + ret = mtk_vcodec_fw_load_firmware(dev->fw_handler); if (ret < 0) { /* * Return 0 if downloading firmware successfully, * otherwise it is failed */ - mtk_v4l2_err("vpu_load_firmware failed!"); + mtk_v4l2_err("failed to load firmware!"); goto err_load_fw; } dev->dec_capability = - vpu_get_vdec_hw_capa(dev->vpu_plat_dev); + mtk_vcodec_fw_get_vdec_capa(dev->fw_handler); mtk_v4l2_debug(0, "decoder capability %x", dev->dec_capability); } @@ -228,6 +211,8 @@ static int mtk_vcodec_probe(struct platform_device *pdev) struct mtk_vcodec_dev *dev; struct video_device *vfd_dec; struct resource *res; + phandle rproc_phandle; + enum mtk_vcodec_fw_type fw_type; int i, ret; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); @@ -237,19 +222,33 @@ static int mtk_vcodec_probe(struct platform_device *pdev) INIT_LIST_HEAD(&dev->ctx_list); dev->plat_dev = pdev; - dev->vpu_plat_dev = vpu_get_plat_device(dev->plat_dev); - if (dev->vpu_plat_dev == NULL) { - mtk_v4l2_err("[VPU] vpu device in not ready"); - return -EPROBE_DEFER; + if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vpu", + &rproc_phandle)) { + fw_type = VPU; + } else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,scp", + &rproc_phandle)) { + fw_type = SCP; + } else { + mtk_v4l2_err("Could not get vdec IPI device"); + return -ENODEV; + } + if (!pdev->dev.dma_parms) { + pdev->dev.dma_parms = devm_kzalloc(&pdev->dev, + sizeof(*pdev->dev.dma_parms), + GFP_KERNEL); + if (!pdev->dev.dma_parms) + return -ENOMEM; } + dma_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); - vpu_wdt_reg_handler(dev->vpu_plat_dev, mtk_vcodec_dec_reset_handler, - dev, VPU_RST_DEC); + dev->fw_handler = mtk_vcodec_fw_select(dev, fw_type, VPU_RST_DEC); + if (IS_ERR(dev->fw_handler)) + return PTR_ERR(dev->fw_handler); ret = mtk_vcodec_init_dec_pm(dev); if (ret < 0) { dev_err(&pdev->dev, "Failed to get mt vcodec clock source"); - return ret; + goto err_dec_pm; } for (i = 0; i < NUM_MAX_VDEC_REG_BASE; i++) { @@ -269,6 +268,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) } dev->dec_irq = platform_get_irq(pdev, 0); + irq_set_status_flags(dev->dec_irq, IRQ_NOAUTOEN); ret = devm_request_irq(&pdev->dev, dev->dec_irq, mtk_vcodec_dec_irq_handler, 0, pdev->name, dev); if (ret) { @@ -278,7 +278,6 @@ static int mtk_vcodec_probe(struct platform_device *pdev) goto err_res; } - disable_irq(dev->dec_irq); mutex_init(&dev->dec_mutex); mutex_init(&dev->dev_mutex); spin_lock_init(&dev->irqlock); @@ -352,6 +351,8 @@ err_dec_alloc: v4l2_device_unregister(&dev->v4l2_dev); err_res: mtk_vcodec_release_dec_pm(dev); +err_dec_pm: + mtk_vcodec_fw_release(dev->fw_handler); return ret; } @@ -376,6 +377,7 @@ static int mtk_vcodec_dec_remove(struct platform_device *pdev) v4l2_device_unregister(&dev->v4l2_dev); mtk_vcodec_release_dec_pm(dev); + mtk_vcodec_fw_release(dev->fw_handler); return 0; } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c index 5a6ec8fb52da..36dfe3fc056a 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c @@ -12,7 +12,6 @@ #include "mtk_vcodec_dec_pm.h" #include "mtk_vcodec_util.h" -#include "mtk_vpu.h" int mtk_vcodec_init_dec_pm(struct mtk_vcodec_dev *mtkdev) { diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h index 9fd56dee7fd1..3dd010cba23e 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h @@ -300,6 +300,40 @@ struct mtk_vcodec_ctx { }; +enum mtk_chip { + MTK_MT8173, + MTK_MT8183, +}; + +/** + * struct mtk_vcodec_enc_pdata - compatible data for each IC + * + * @chip: chip this encoder is compatible with + * + * @uses_ext: whether the encoder uses the extended firmware messaging format + * @has_lt_irq: whether the encoder uses the LT irq + * @min_birate: minimum supported encoding bitrate + * @max_bitrate: maximum supported encoding bitrate + * @capture_formats: array of supported capture formats + * @num_capture_formats: number of entries in capture_formats + * @output_formats: array of supported output formats + * @num_output_formats: number of entries in output_formats + */ +struct mtk_vcodec_enc_pdata { + enum mtk_chip chip; + + bool uses_ext; + bool has_lt_irq; + unsigned long min_bitrate; + unsigned long max_bitrate; + const struct mtk_video_fmt *capture_formats; + size_t num_capture_formats; + const struct mtk_video_fmt *output_formats; + size_t num_output_formats; +}; + +#define MTK_ENC_CTX_IS_EXT(ctx) ((ctx)->dev->venc_pdata->uses_ext) + /** * struct mtk_vcodec_dev - driver data * @v4l2_dev: V4L2 device to register video devices for. @@ -309,13 +343,13 @@ struct mtk_vcodec_ctx { * @m2m_dev_dec: m2m device for decoder * @m2m_dev_enc: m2m device for encoder. * @plat_dev: platform device - * @vpu_plat_dev: mtk vpu platform device * @ctx_list: list of struct mtk_vcodec_ctx * @irqlock: protect data access by irq handler and work thread * @curr_ctx: The context that is waiting for codec hardware * * @reg_base: Mapped address of MTK Vcodec registers. * + * @fw_handler: used to communicate with the firmware. * @id_counter: used to identify current opened instance * * @encode_workqueue: encode work queue @@ -344,11 +378,13 @@ struct mtk_vcodec_dev { struct v4l2_m2m_dev *m2m_dev_dec; struct v4l2_m2m_dev *m2m_dev_enc; struct platform_device *plat_dev; - struct platform_device *vpu_plat_dev; struct list_head ctx_list; spinlock_t irqlock; struct mtk_vcodec_ctx *curr_ctx; void __iomem *reg_base[NUM_MAX_VCODEC_REG_BASE]; + const struct mtk_vcodec_enc_pdata *venc_pdata; + + struct mtk_vcodec_fw *fw_handler; unsigned long id_counter; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c index d469ff6464b2..21de1431cfcb 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c @@ -23,58 +23,15 @@ #define DFT_CFG_WIDTH MTK_VENC_MIN_W #define DFT_CFG_HEIGHT MTK_VENC_MIN_H #define MTK_MAX_CTRLS_HINT 20 -#define OUT_FMT_IDX 0 -#define CAP_FMT_IDX 4 +#define MTK_DEFAULT_FRAMERATE_NUM 1001 +#define MTK_DEFAULT_FRAMERATE_DENOM 30000 static void mtk_venc_worker(struct work_struct *work); -static const struct mtk_video_fmt mtk_video_formats[] = { - { - .fourcc = V4L2_PIX_FMT_NV12M, - .type = MTK_FMT_FRAME, - .num_planes = 2, - }, - { - .fourcc = V4L2_PIX_FMT_NV21M, - .type = MTK_FMT_FRAME, - .num_planes = 2, - }, - { - .fourcc = V4L2_PIX_FMT_YUV420M, - .type = MTK_FMT_FRAME, - .num_planes = 3, - }, - { - .fourcc = V4L2_PIX_FMT_YVU420M, - .type = MTK_FMT_FRAME, - .num_planes = 3, - }, - { - .fourcc = V4L2_PIX_FMT_H264, - .type = MTK_FMT_ENC, - .num_planes = 1, - }, - { - .fourcc = V4L2_PIX_FMT_VP8, - .type = MTK_FMT_ENC, - .num_planes = 1, - }, -}; - -#define NUM_FORMATS ARRAY_SIZE(mtk_video_formats) - -static const struct mtk_codec_framesizes mtk_venc_framesizes[] = { - { - .fourcc = V4L2_PIX_FMT_H264, - .stepwise = { MTK_VENC_MIN_W, MTK_VENC_MAX_W, 16, - MTK_VENC_MIN_H, MTK_VENC_MAX_H, 16 }, - }, - { - .fourcc = V4L2_PIX_FMT_VP8, - .stepwise = { MTK_VENC_MIN_W, MTK_VENC_MAX_W, 16, - MTK_VENC_MIN_H, MTK_VENC_MAX_H, 16 }, - }, +static const struct v4l2_frmsize_stepwise mtk_venc_framesizes = { + MTK_VENC_MIN_W, MTK_VENC_MAX_W, 16, + MTK_VENC_MIN_H, MTK_VENC_MAX_H, 16, }; #define NUM_SUPPORTED_FRAMESIZE ARRAY_SIZE(mtk_venc_framesizes) @@ -156,59 +113,77 @@ static const struct v4l2_ctrl_ops mtk_vcodec_enc_ctrl_ops = { .s_ctrl = vidioc_venc_s_ctrl, }; -static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool output_queue) +static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, + const struct mtk_video_fmt *formats, + size_t num_formats) +{ + if (f->index >= num_formats) + return -EINVAL; + + f->pixelformat = formats[f->index].fourcc; + memset(f->reserved, 0, sizeof(f->reserved)); + + return 0; +} + +static const struct mtk_video_fmt * +mtk_venc_find_format(u32 fourcc, const struct mtk_vcodec_enc_pdata *pdata) { const struct mtk_video_fmt *fmt; - int i, j = 0; - - for (i = 0; i < NUM_FORMATS; ++i) { - if (output_queue && mtk_video_formats[i].type != MTK_FMT_FRAME) - continue; - if (!output_queue && mtk_video_formats[i].type != MTK_FMT_ENC) - continue; - - if (j == f->index) { - fmt = &mtk_video_formats[i]; - f->pixelformat = fmt->fourcc; - memset(f->reserved, 0, sizeof(f->reserved)); - return 0; - } - ++j; + unsigned int k; + + for (k = 0; k < pdata->num_capture_formats; k++) { + fmt = &pdata->capture_formats[k]; + if (fmt->fourcc == fourcc) + return fmt; + } + + for (k = 0; k < pdata->num_output_formats; k++) { + fmt = &pdata->output_formats[k]; + if (fmt->fourcc == fourcc) + return fmt; } - return -EINVAL; + return NULL; } static int vidioc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { - int i = 0; + const struct mtk_video_fmt *fmt; if (fsize->index != 0) return -EINVAL; - for (i = 0; i < NUM_SUPPORTED_FRAMESIZE; ++i) { - if (fsize->pixel_format != mtk_venc_framesizes[i].fourcc) - continue; + fmt = mtk_venc_find_format(fsize->pixel_format, + fh_to_ctx(fh)->dev->venc_pdata); + if (!fmt) + return -EINVAL; - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise = mtk_venc_framesizes[i].stepwise; - return 0; - } + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise = mtk_venc_framesizes; - return -EINVAL; + return 0; } static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - return vidioc_enum_fmt(f, false); + const struct mtk_vcodec_enc_pdata *pdata = + fh_to_ctx(priv)->dev->venc_pdata; + + return vidioc_enum_fmt(f, pdata->capture_formats, + pdata->num_capture_formats); } static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - return vidioc_enum_fmt(f, true); + const struct mtk_vcodec_enc_pdata *pdata = + fh_to_ctx(priv)->dev->venc_pdata; + + return vidioc_enum_fmt(f, pdata->output_formats, + pdata->num_output_formats); } static int vidioc_venc_querycap(struct file *file, void *priv, @@ -225,14 +200,18 @@ static int vidioc_venc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *a) { struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct v4l2_fract *timeperframe = &a->parm.output.timeperframe; if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) return -EINVAL; - ctx->enc_params.framerate_num = - a->parm.output.timeperframe.denominator; - ctx->enc_params.framerate_denom = - a->parm.output.timeperframe.numerator; + if (timeperframe->numerator == 0 || timeperframe->denominator == 0) { + timeperframe->numerator = MTK_DEFAULT_FRAMERATE_NUM; + timeperframe->denominator = MTK_DEFAULT_FRAMERATE_DENOM; + } + + ctx->enc_params.framerate_num = timeperframe->denominator; + ctx->enc_params.framerate_denom = timeperframe->numerator; ctx->param_change |= MTK_ENCODE_PARAM_FRAMERATE; a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; @@ -266,20 +245,6 @@ static struct mtk_q_data *mtk_venc_get_q_data(struct mtk_vcodec_ctx *ctx, return &ctx->q_data[MTK_Q_DATA_DST]; } -static const struct mtk_video_fmt *mtk_venc_find_format(struct v4l2_format *f) -{ - const struct mtk_video_fmt *fmt; - unsigned int k; - - for (k = 0; k < NUM_FORMATS; k++) { - fmt = &mtk_video_formats[k]; - if (fmt->fourcc == f->fmt.pix.pixelformat) - return fmt; - } - - return NULL; -} - /* V4L2 specification suggests the driver corrects the format struct if any of * the dimensions is unsupported */ @@ -332,12 +297,14 @@ static int vidioc_try_fmt(struct v4l2_format *f, pix_fmt_mp->num_planes = fmt->num_planes; pix_fmt_mp->plane_fmt[0].sizeimage = - pix_fmt_mp->width * pix_fmt_mp->height; + pix_fmt_mp->width * pix_fmt_mp->height + + ((ALIGN(pix_fmt_mp->width, 16) * 2) * 16); pix_fmt_mp->plane_fmt[0].bytesperline = pix_fmt_mp->width; if (pix_fmt_mp->num_planes == 2) { pix_fmt_mp->plane_fmt[1].sizeimage = - (pix_fmt_mp->width * pix_fmt_mp->height) / 2; + (pix_fmt_mp->width * pix_fmt_mp->height) / 2 + + (ALIGN(pix_fmt_mp->width, 16) * 16); pix_fmt_mp->plane_fmt[2].sizeimage = 0; pix_fmt_mp->plane_fmt[1].bytesperline = pix_fmt_mp->width; @@ -345,7 +312,8 @@ static int vidioc_try_fmt(struct v4l2_format *f, } else if (pix_fmt_mp->num_planes == 3) { pix_fmt_mp->plane_fmt[1].sizeimage = pix_fmt_mp->plane_fmt[2].sizeimage = - (pix_fmt_mp->width * pix_fmt_mp->height) / 4; + (pix_fmt_mp->width * pix_fmt_mp->height) / 4 + + ((ALIGN(pix_fmt_mp->width, 16) / 2) * 16); pix_fmt_mp->plane_fmt[1].bytesperline = pix_fmt_mp->plane_fmt[2].bytesperline = pix_fmt_mp->width / 2; @@ -414,6 +382,7 @@ static int vidioc_venc_s_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) { struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata; struct vb2_queue *vq; struct mtk_q_data *q_data; int i, ret; @@ -436,10 +405,10 @@ static int vidioc_venc_s_fmt_cap(struct file *file, void *priv, return -EINVAL; } - fmt = mtk_venc_find_format(f); + fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata); if (!fmt) { - f->fmt.pix.pixelformat = mtk_video_formats[CAP_FMT_IDX].fourcc; - fmt = mtk_venc_find_format(f); + fmt = &ctx->dev->venc_pdata->capture_formats[0]; + f->fmt.pix.pixelformat = fmt->fourcc; } q_data->fmt = fmt; @@ -476,6 +445,7 @@ static int vidioc_venc_s_fmt_out(struct file *file, void *priv, struct v4l2_format *f) { struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata; struct vb2_queue *vq; struct mtk_q_data *q_data; int ret, i; @@ -499,10 +469,10 @@ static int vidioc_venc_s_fmt_out(struct file *file, void *priv, return -EINVAL; } - fmt = mtk_venc_find_format(f); + fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata); if (!fmt) { - f->fmt.pix.pixelformat = mtk_video_formats[OUT_FMT_IDX].fourcc; - fmt = mtk_venc_find_format(f); + fmt = &ctx->dev->venc_pdata->output_formats[0]; + f->fmt.pix.pixelformat = fmt->fourcc; } pix_fmt_mp->height = clamp(pix_fmt_mp->height, @@ -580,11 +550,12 @@ static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, { const struct mtk_video_fmt *fmt; struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata; - fmt = mtk_venc_find_format(f); + fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata); if (!fmt) { - f->fmt.pix.pixelformat = mtk_video_formats[CAP_FMT_IDX].fourcc; - fmt = mtk_venc_find_format(f); + fmt = &ctx->dev->venc_pdata->capture_formats[0]; + f->fmt.pix.pixelformat = fmt->fourcc; } f->fmt.pix_mp.colorspace = ctx->colorspace; f->fmt.pix_mp.ycbcr_enc = ctx->ycbcr_enc; @@ -598,11 +569,13 @@ static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f) { const struct mtk_video_fmt *fmt; + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata; - fmt = mtk_venc_find_format(f); + fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata); if (!fmt) { - f->fmt.pix.pixelformat = mtk_video_formats[OUT_FMT_IDX].fourcc; - fmt = mtk_venc_find_format(f); + fmt = &ctx->dev->venc_pdata->output_formats[0]; + f->fmt.pix.pixelformat = fmt->fourcc; } if (!f->fmt.pix_mp.colorspace) { f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; @@ -918,8 +891,17 @@ static void vb2ops_venc_stop_streaming(struct vb2_queue *q) ctx->state = MTK_STATE_FREE; } +static int vb2ops_venc_buf_out_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + vbuf->field = V4L2_FIELD_NONE; + return 0; +} + static const struct vb2_ops mtk_venc_vb2_ops = { .queue_setup = vb2ops_venc_queue_setup, + .buf_out_validate = vb2ops_venc_buf_out_validate, .buf_prepare = vb2ops_venc_buf_prepare, .buf_queue = vb2ops_venc_buf_queue, .wait_prepare = vb2_ops_wait_prepare, @@ -1187,7 +1169,7 @@ void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx) q_data->coded_height = DFT_CFG_HEIGHT; q_data->field = V4L2_FIELD_NONE; - q_data->fmt = &mtk_video_formats[OUT_FMT_IDX]; + q_data->fmt = &ctx->dev->venc_pdata->output_formats[0]; v4l_bound_align_image(&q_data->coded_width, MTK_VENC_MIN_W, @@ -1216,12 +1198,14 @@ void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx) memset(q_data, 0, sizeof(struct mtk_q_data)); q_data->coded_width = DFT_CFG_WIDTH; q_data->coded_height = DFT_CFG_HEIGHT; - q_data->fmt = &mtk_video_formats[CAP_FMT_IDX]; + q_data->fmt = &ctx->dev->venc_pdata->capture_formats[0]; q_data->field = V4L2_FIELD_NONE; ctx->q_data[MTK_Q_DATA_DST].sizeimage[0] = DFT_CFG_WIDTH * DFT_CFG_HEIGHT; ctx->q_data[MTK_Q_DATA_DST].bytesperline[0] = 0; + ctx->enc_params.framerate_num = MTK_DEFAULT_FRAMERATE_NUM; + ctx->enc_params.framerate_denom = MTK_DEFAULT_FRAMERATE_DENOM; } int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx) @@ -1231,8 +1215,11 @@ int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx) v4l2_ctrl_handler_init(handler, MTK_MAX_CTRLS_HINT); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, + 1, 1, 1, 1); v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_BITRATE, - 1, 4000000, 1, 4000000); + ctx->dev->venc_pdata->min_bitrate, + ctx->dev->venc_pdata->max_bitrate, 1, 4000000); v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 2, 1, 0); v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c index 4d31f1ed113f..dcfa2c2d4def 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c @@ -21,11 +21,55 @@ #include "mtk_vcodec_enc_pm.h" #include "mtk_vcodec_intr.h" #include "mtk_vcodec_util.h" -#include "mtk_vpu.h" +#include "mtk_vcodec_fw.h" module_param(mtk_v4l2_dbg_level, int, S_IRUGO | S_IWUSR); module_param(mtk_vcodec_dbg, bool, S_IRUGO | S_IWUSR); +static const struct mtk_video_fmt mtk_video_formats_output_mt8173[] = { + { + .fourcc = V4L2_PIX_FMT_NV12M, + .type = MTK_FMT_FRAME, + .num_planes = 2, + }, + { + .fourcc = V4L2_PIX_FMT_NV21M, + .type = MTK_FMT_FRAME, + .num_planes = 2, + }, + { + .fourcc = V4L2_PIX_FMT_YUV420M, + .type = MTK_FMT_FRAME, + .num_planes = 3, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420M, + .type = MTK_FMT_FRAME, + .num_planes = 3, + }, +}; + +static const struct mtk_video_fmt mtk_video_formats_capture_mt8173[] = { + { + .fourcc = V4L2_PIX_FMT_H264, + .type = MTK_FMT_ENC, + .num_planes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_VP8, + .type = MTK_FMT_ENC, + .num_planes = 1, + }, +}; + +static const struct mtk_video_fmt mtk_video_formats_capture_mt8183[] = { + { + .fourcc = V4L2_PIX_FMT_H264, + .type = MTK_FMT_ENC, + .num_planes = 1, + }, +}; + /* Wake up context wait_queue */ static void wake_up_ctx(struct mtk_vcodec_ctx *ctx, unsigned int reason) { @@ -101,22 +145,6 @@ static irqreturn_t mtk_vcodec_enc_lt_irq_handler(int irq, void *priv) return IRQ_HANDLED; } -static void mtk_vcodec_enc_reset_handler(void *priv) -{ - struct mtk_vcodec_dev *dev = priv; - struct mtk_vcodec_ctx *ctx; - - mtk_v4l2_debug(0, "Watchdog timeout!!"); - - mutex_lock(&dev->dev_mutex); - list_for_each_entry(ctx, &dev->ctx_list, list) { - ctx->state = MTK_STATE_ABORT; - mtk_v4l2_debug(0, "[%d] Change to state MTK_STATE_ABORT", - ctx->id); - } - mutex_unlock(&dev->dev_mutex); -} - static int fops_vcodec_open(struct file *file) { struct mtk_vcodec_dev *dev = video_drvdata(file); @@ -159,10 +187,10 @@ static int fops_vcodec_open(struct file *file) if (v4l2_fh_is_singular(&ctx->fh)) { /* - * vpu_load_firmware checks if it was loaded already and + * load fireware to checks if it was loaded already and * does nothing in that case */ - ret = vpu_load_firmware(dev->vpu_plat_dev); + ret = mtk_vcodec_fw_load_firmware(dev->fw_handler); if (ret < 0) { /* * Return 0 if downloading firmware successfully, @@ -173,7 +201,7 @@ static int fops_vcodec_open(struct file *file) } dev->enc_capability = - vpu_get_venc_hw_capa(dev->vpu_plat_dev); + mtk_vcodec_fw_get_venc_capa(dev->fw_handler); mtk_v4l2_debug(0, "encoder capability %x", dev->enc_capability); } @@ -235,7 +263,9 @@ static int mtk_vcodec_probe(struct platform_device *pdev) struct mtk_vcodec_dev *dev; struct video_device *vfd_enc; struct resource *res; - int i, j, ret; + phandle rproc_phandle; + enum mtk_vcodec_fw_type fw_type; + int ret; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) @@ -244,30 +274,43 @@ static int mtk_vcodec_probe(struct platform_device *pdev) INIT_LIST_HEAD(&dev->ctx_list); dev->plat_dev = pdev; - dev->vpu_plat_dev = vpu_get_plat_device(dev->plat_dev); - if (dev->vpu_plat_dev == NULL) { - mtk_v4l2_err("[VPU] vpu device in not ready"); - return -EPROBE_DEFER; + if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vpu", + &rproc_phandle)) { + fw_type = VPU; + } else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,scp", + &rproc_phandle)) { + fw_type = SCP; + } else { + mtk_v4l2_err("Could not get venc IPI device"); + return -ENODEV; } + if (!pdev->dev.dma_parms) { + pdev->dev.dma_parms = devm_kzalloc(&pdev->dev, + sizeof(*pdev->dev.dma_parms), + GFP_KERNEL); + if (!pdev->dev.dma_parms) + return -ENOMEM; + } + dma_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); - vpu_wdt_reg_handler(dev->vpu_plat_dev, mtk_vcodec_enc_reset_handler, - dev, VPU_RST_ENC); + dev->fw_handler = mtk_vcodec_fw_select(dev, fw_type, VPU_RST_ENC); + if (IS_ERR(dev->fw_handler)) + return PTR_ERR(dev->fw_handler); + dev->venc_pdata = of_device_get_match_data(&pdev->dev); ret = mtk_vcodec_init_enc_pm(dev); if (ret < 0) { dev_err(&pdev->dev, "Failed to get mt vcodec clock source!"); - return ret; + goto err_enc_pm; } - for (i = VENC_SYS, j = 0; i < NUM_MAX_VCODEC_REG_BASE; i++, j++) { - res = platform_get_resource(pdev, IORESOURCE_MEM, j); - dev->reg_base[i] = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR((__force void *)dev->reg_base[i])) { - ret = PTR_ERR((__force void *)dev->reg_base[i]); - goto err_res; - } - mtk_v4l2_debug(2, "reg[%d] base=0x%p", i, dev->reg_base[i]); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dev->reg_base[VENC_SYS] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR((__force void *)dev->reg_base[VENC_SYS])) { + ret = PTR_ERR((__force void *)dev->reg_base[VENC_SYS]); + goto err_res; } + mtk_v4l2_debug(2, "reg[%d] base=0x%p", i, dev->reg_base[VENC_SYS]); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { @@ -277,6 +320,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) } dev->enc_irq = platform_get_irq(pdev, 0); + irq_set_status_flags(dev->enc_irq, IRQ_NOAUTOEN); ret = devm_request_irq(&pdev->dev, dev->enc_irq, mtk_vcodec_enc_irq_handler, 0, pdev->name, dev); @@ -288,20 +332,30 @@ static int mtk_vcodec_probe(struct platform_device *pdev) goto err_res; } - dev->enc_lt_irq = platform_get_irq(pdev, 1); - ret = devm_request_irq(&pdev->dev, - dev->enc_lt_irq, mtk_vcodec_enc_lt_irq_handler, - 0, pdev->name, dev); - if (ret) { - dev_err(&pdev->dev, - "Failed to install dev->enc_lt_irq %d (%d)", - dev->enc_lt_irq, ret); - ret = -EINVAL; - goto err_res; + if (dev->venc_pdata->has_lt_irq) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + dev->reg_base[VENC_LT_SYS] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR((__force void *)dev->reg_base[VENC_LT_SYS])) { + ret = PTR_ERR((__force void *)dev->reg_base[VENC_LT_SYS]); + goto err_res; + } + mtk_v4l2_debug(2, "reg[%d] base=0x%p", i, dev->reg_base[VENC_LT_SYS]); + + dev->enc_lt_irq = platform_get_irq(pdev, 1); + irq_set_status_flags(dev->enc_lt_irq, IRQ_NOAUTOEN); + ret = devm_request_irq(&pdev->dev, + dev->enc_lt_irq, + mtk_vcodec_enc_lt_irq_handler, + 0, pdev->name, dev); + if (ret) { + dev_err(&pdev->dev, + "Failed to install dev->enc_lt_irq %d (%d)", + dev->enc_lt_irq, ret); + ret = -EINVAL; + goto err_res; + } } - disable_irq(dev->enc_irq); - disable_irq(dev->enc_lt_irq); /* VENC_LT */ mutex_init(&dev->enc_mutex); mutex_init(&dev->dev_mutex); spin_lock_init(&dev->irqlock); @@ -377,11 +431,38 @@ err_enc_alloc: v4l2_device_unregister(&dev->v4l2_dev); err_res: mtk_vcodec_release_enc_pm(dev); +err_enc_pm: + mtk_vcodec_fw_release(dev->fw_handler); return ret; } +static const struct mtk_vcodec_enc_pdata mt8173_pdata = { + .chip = MTK_MT8173, + .has_lt_irq = true, + .capture_formats = mtk_video_formats_capture_mt8173, + .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_mt8173), + .output_formats = mtk_video_formats_output_mt8173, + .num_output_formats = ARRAY_SIZE(mtk_video_formats_output_mt8173), + .min_bitrate = 1, + .max_bitrate = 4000000, +}; + +static const struct mtk_vcodec_enc_pdata mt8183_pdata = { + .chip = MTK_MT8183, + .has_lt_irq = false, + .uses_ext = true, + .capture_formats = mtk_video_formats_capture_mt8183, + .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_mt8183), + /* MT8183 supports the same output formats as MT8173 */ + .output_formats = mtk_video_formats_output_mt8173, + .num_output_formats = ARRAY_SIZE(mtk_video_formats_output_mt8173), + .min_bitrate = 64, + .max_bitrate = 40000000, +}; + static const struct of_device_id mtk_vcodec_enc_match[] = { - {.compatible = "mediatek,mt8173-vcodec-enc",}, + {.compatible = "mediatek,mt8173-vcodec-enc", .data = &mt8173_pdata}, + {.compatible = "mediatek,mt8183-vcodec-enc", .data = &mt8183_pdata}, {}, }; MODULE_DEVICE_TABLE(of, mtk_vcodec_enc_match); @@ -401,6 +482,7 @@ static int mtk_vcodec_enc_remove(struct platform_device *pdev) v4l2_device_unregister(&dev->v4l2_dev); mtk_vcodec_release_enc_pm(dev); + mtk_vcodec_fw_release(dev->fw_handler); return 0; } diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c index 3e2bfded79a6..ee22902aaa71 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c @@ -12,8 +12,6 @@ #include "mtk_vcodec_enc_pm.h" #include "mtk_vcodec_util.h" -#include "mtk_vpu.h" - int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev) { diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.c new file mode 100644 index 000000000000..6c2a2568d844 --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "mtk_vcodec_fw.h" +#include "mtk_vcodec_util.h" +#include "mtk_vcodec_drv.h" + +struct mtk_vcodec_fw_ops { + int (*load_firmware)(struct mtk_vcodec_fw *fw); + unsigned int (*get_vdec_capa)(struct mtk_vcodec_fw *fw); + unsigned int (*get_venc_capa)(struct mtk_vcodec_fw *fw); + void * (*map_dm_addr)(struct mtk_vcodec_fw *fw, u32 dtcm_dmem_addr); + int (*ipi_register)(struct mtk_vcodec_fw *fw, int id, + mtk_vcodec_ipi_handler handler, const char *name, void *priv); + int (*ipi_send)(struct mtk_vcodec_fw *fw, int id, void *buf, + unsigned int len, unsigned int wait); +}; + +struct mtk_vcodec_fw { + enum mtk_vcodec_fw_type type; + const struct mtk_vcodec_fw_ops *ops; + struct platform_device *pdev; + struct mtk_scp *scp; +}; + +static int mtk_vcodec_vpu_load_firmware(struct mtk_vcodec_fw *fw) +{ + return vpu_load_firmware(fw->pdev); +} + +static unsigned int mtk_vcodec_vpu_get_vdec_capa(struct mtk_vcodec_fw *fw) +{ + return vpu_get_vdec_hw_capa(fw->pdev); +} + +static unsigned int mtk_vcodec_vpu_get_venc_capa(struct mtk_vcodec_fw *fw) +{ + return vpu_get_venc_hw_capa(fw->pdev); +} + +static void *mtk_vcodec_vpu_map_dm_addr(struct mtk_vcodec_fw *fw, + u32 dtcm_dmem_addr) +{ + return vpu_mapping_dm_addr(fw->pdev, dtcm_dmem_addr); +} + +static int mtk_vcodec_vpu_set_ipi_register(struct mtk_vcodec_fw *fw, int id, + mtk_vcodec_ipi_handler handler, + const char *name, void *priv) +{ + /* + * The handler we receive takes a void * as its first argument. We + * cannot change this because it needs to be passed down to the rproc + * subsystem when SCP is used. VPU takes a const argument, which is + * more constrained, so the conversion below is safe. + */ + ipi_handler_t handler_const = (ipi_handler_t)handler; + + return vpu_ipi_register(fw->pdev, id, handler_const, name, priv); +} + +static int mtk_vcodec_vpu_ipi_send(struct mtk_vcodec_fw *fw, int id, void *buf, + unsigned int len, unsigned int wait) +{ + return vpu_ipi_send(fw->pdev, id, buf, len); +} + +static const struct mtk_vcodec_fw_ops mtk_vcodec_vpu_msg = { + .load_firmware = mtk_vcodec_vpu_load_firmware, + .get_vdec_capa = mtk_vcodec_vpu_get_vdec_capa, + .get_venc_capa = mtk_vcodec_vpu_get_venc_capa, + .map_dm_addr = mtk_vcodec_vpu_map_dm_addr, + .ipi_register = mtk_vcodec_vpu_set_ipi_register, + .ipi_send = mtk_vcodec_vpu_ipi_send, +}; + +static int mtk_vcodec_scp_load_firmware(struct mtk_vcodec_fw *fw) +{ + return rproc_boot(scp_get_rproc(fw->scp)); +} + +static unsigned int mtk_vcodec_scp_get_vdec_capa(struct mtk_vcodec_fw *fw) +{ + return scp_get_vdec_hw_capa(fw->scp); +} + +static unsigned int mtk_vcodec_scp_get_venc_capa(struct mtk_vcodec_fw *fw) +{ + return scp_get_venc_hw_capa(fw->scp); +} + +static void *mtk_vcodec_vpu_scp_dm_addr(struct mtk_vcodec_fw *fw, + u32 dtcm_dmem_addr) +{ + return scp_mapping_dm_addr(fw->scp, dtcm_dmem_addr); +} + +static int mtk_vcodec_scp_set_ipi_register(struct mtk_vcodec_fw *fw, int id, + mtk_vcodec_ipi_handler handler, + const char *name, void *priv) +{ + return scp_ipi_register(fw->scp, id, handler, priv); +} + +static int mtk_vcodec_scp_ipi_send(struct mtk_vcodec_fw *fw, int id, void *buf, + unsigned int len, unsigned int wait) +{ + return scp_ipi_send(fw->scp, id, buf, len, wait); +} + +static const struct mtk_vcodec_fw_ops mtk_vcodec_rproc_msg = { + .load_firmware = mtk_vcodec_scp_load_firmware, + .get_vdec_capa = mtk_vcodec_scp_get_vdec_capa, + .get_venc_capa = mtk_vcodec_scp_get_venc_capa, + .map_dm_addr = mtk_vcodec_vpu_scp_dm_addr, + .ipi_register = mtk_vcodec_scp_set_ipi_register, + .ipi_send = mtk_vcodec_scp_ipi_send, +}; + +static void mtk_vcodec_reset_handler(void *priv) +{ + struct mtk_vcodec_dev *dev = priv; + struct mtk_vcodec_ctx *ctx; + + mtk_v4l2_err("Watchdog timeout!!"); + + mutex_lock(&dev->dev_mutex); + list_for_each_entry(ctx, &dev->ctx_list, list) { + ctx->state = MTK_STATE_ABORT; + mtk_v4l2_debug(0, "[%d] Change to state MTK_STATE_ABORT", + ctx->id); + } + mutex_unlock(&dev->dev_mutex); +} + +struct mtk_vcodec_fw *mtk_vcodec_fw_select(struct mtk_vcodec_dev *dev, + enum mtk_vcodec_fw_type type, + enum rst_id rst_id) +{ + const struct mtk_vcodec_fw_ops *ops; + struct mtk_vcodec_fw *fw; + struct platform_device *fw_pdev = NULL; + struct mtk_scp *scp = NULL; + + switch (type) { + case VPU: + ops = &mtk_vcodec_vpu_msg; + fw_pdev = vpu_get_plat_device(dev->plat_dev); + if (!fw_pdev) { + mtk_v4l2_err("firmware device is not ready"); + return ERR_PTR(-EINVAL); + } + vpu_wdt_reg_handler(fw_pdev, mtk_vcodec_reset_handler, + dev, rst_id); + break; + case SCP: + ops = &mtk_vcodec_rproc_msg; + scp = scp_get(dev->plat_dev); + if (!scp) { + mtk_v4l2_err("could not get vdec scp handle"); + return ERR_PTR(-EPROBE_DEFER); + } + break; + default: + mtk_v4l2_err("invalid vcodec fw type"); + return ERR_PTR(-EINVAL); + } + + fw = devm_kzalloc(&dev->plat_dev->dev, sizeof(*fw), GFP_KERNEL); + if (!fw) + return ERR_PTR(-EINVAL); + + fw->type = type; + fw->ops = ops; + fw->pdev = fw_pdev; + fw->scp = scp; + + return fw; +} +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_select); + +void mtk_vcodec_fw_release(struct mtk_vcodec_fw *fw) +{ + switch (fw->type) { + case VPU: + put_device(&fw->pdev->dev); + break; + case SCP: + scp_put(fw->scp); + break; + } +} +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_release); + +int mtk_vcodec_fw_load_firmware(struct mtk_vcodec_fw *fw) +{ + return fw->ops->load_firmware(fw); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_load_firmware); + +unsigned int mtk_vcodec_fw_get_vdec_capa(struct mtk_vcodec_fw *fw) +{ + return fw->ops->get_vdec_capa(fw); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_get_vdec_capa); + +unsigned int mtk_vcodec_fw_get_venc_capa(struct mtk_vcodec_fw *fw) +{ + return fw->ops->get_venc_capa(fw); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_get_venc_capa); + +void *mtk_vcodec_fw_map_dm_addr(struct mtk_vcodec_fw *fw, u32 mem_addr) +{ + return fw->ops->map_dm_addr(fw, mem_addr); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_map_dm_addr); + +int mtk_vcodec_fw_ipi_register(struct mtk_vcodec_fw *fw, int id, + mtk_vcodec_ipi_handler handler, + const char *name, void *priv) +{ + return fw->ops->ipi_register(fw, id, handler, name, priv); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_ipi_register); + +int mtk_vcodec_fw_ipi_send(struct mtk_vcodec_fw *fw, int id, void *buf, + unsigned int len, unsigned int wait) +{ + return fw->ops->ipi_send(fw, id, buf, len, wait); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_ipi_send); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.h new file mode 100644 index 000000000000..fadbbe6ba6cd --- /dev/null +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _MTK_VCODEC_FW_H_ +#define _MTK_VCODEC_FW_H_ + +#include <linux/remoteproc.h> +#include <linux/remoteproc/mtk_scp.h> + +#include "../mtk-vpu/mtk_vpu.h" + +struct mtk_vcodec_dev; + +enum mtk_vcodec_fw_type { + VPU, + SCP, +}; + +struct mtk_vcodec_fw; + +typedef void (*mtk_vcodec_ipi_handler) (void *data, + unsigned int len, void *priv); + +struct mtk_vcodec_fw *mtk_vcodec_fw_select(struct mtk_vcodec_dev *dev, + enum mtk_vcodec_fw_type type, + enum rst_id rst_id); +void mtk_vcodec_fw_release(struct mtk_vcodec_fw *fw); + +int mtk_vcodec_fw_load_firmware(struct mtk_vcodec_fw *fw); +unsigned int mtk_vcodec_fw_get_vdec_capa(struct mtk_vcodec_fw *fw); +unsigned int mtk_vcodec_fw_get_venc_capa(struct mtk_vcodec_fw *fw); +void *mtk_vcodec_fw_map_dm_addr(struct mtk_vcodec_fw *fw, u32 mem_addr); +int mtk_vcodec_fw_ipi_register(struct mtk_vcodec_fw *fw, int id, + mtk_vcodec_ipi_handler handler, + const char *name, void *priv); +int mtk_vcodec_fw_ipi_send(struct mtk_vcodec_fw *fw, int id, + void *buf, unsigned int len, unsigned int wait); + +#endif /* _MTK_VCODEC_FW_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c index d48f542db1a9..ac5973b6735f 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c @@ -9,7 +9,6 @@ #include "mtk_vcodec_drv.h" #include "mtk_vcodec_util.h" -#include "mtk_vpu.h" /* For encoder, this will enable logs in venc/*/ bool mtk_vcodec_dbg; diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c index 50048c170b99..40d6e6c5ac7a 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c @@ -281,7 +281,6 @@ static int vdec_h264_init(struct mtk_vcodec_ctx *ctx) inst->ctx = ctx; inst->vpu.id = IPI_VDEC_H264; - inst->vpu.dev = ctx->dev->vpu_plat_dev; inst->vpu.ctx = ctx; err = vpu_dec_init(&inst->vpu); diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c index 6011fdd60a22..e5393f841080 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c @@ -400,7 +400,6 @@ static int vdec_vp8_init(struct mtk_vcodec_ctx *ctx) inst->ctx = ctx; inst->vpu.id = IPI_VDEC_VP8; - inst->vpu.dev = ctx->dev->vpu_plat_dev; inst->vpu.ctx = ctx; err = vpu_dec_init(&inst->vpu); diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c index 257a5b5ad212..5ea153a68522 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c @@ -795,7 +795,6 @@ static int vdec_vp9_init(struct mtk_vcodec_ctx *ctx) inst->ctx = ctx; inst->vpu.id = IPI_VDEC_VP9; - inst->vpu.dev = ctx->dev->vpu_plat_dev; inst->vpu.ctx = ctx; if (vpu_dec_init(&inst->vpu)) { @@ -960,7 +959,7 @@ static int vdec_vp9_decode(void *h_vdec, struct mtk_vcodec_mem *bs, goto DECODE_ERROR; } - if (vp9_decode_end_proc(inst) != true) { + if (!vp9_decode_end_proc(inst)) { mtk_vcodec_err(inst, "vp9_decode_end_proc"); ret = -EINVAL; goto DECODE_ERROR; diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_base.h b/drivers/media/platform/mtk-vcodec/vdec_drv_base.h index ceb4db4cb3be..e913f963b7db 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_drv_base.h +++ b/drivers/media/platform/mtk-vcodec/vdec_drv_base.h @@ -7,8 +7,6 @@ #ifndef _VDEC_DRV_BASE_ #define _VDEC_DRV_BASE_ -#include "mtk_vcodec_drv.h" - #include "vdec_drv_if.h" struct vdec_common_if { diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_if.c b/drivers/media/platform/mtk-vcodec/vdec_drv_if.c index 2e43dd4486e0..b18743b906ea 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_drv_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec_drv_if.c @@ -13,7 +13,6 @@ #include "mtk_vcodec_dec.h" #include "vdec_drv_base.h" #include "mtk_vcodec_dec_pm.h" -#include "mtk_vpu.h" int vdec_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) { diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c index 948a12fd9d46..58b0e6fa8fd2 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c @@ -8,6 +8,7 @@ #include "mtk_vcodec_util.h" #include "vdec_ipi_msg.h" #include "vdec_vpu_if.h" +#include "mtk_vcodec_fw.h" static void handle_init_ack_msg(const struct vdec_vpu_ipi_init_ack *msg) { @@ -18,7 +19,8 @@ static void handle_init_ack_msg(const struct vdec_vpu_ipi_init_ack *msg) /* mapping VPU address to kernel virtual address */ /* the content in vsi is initialized to 0 in VPU */ - vpu->vsi = vpu_mapping_dm_addr(vpu->dev, msg->vpu_inst_addr); + vpu->vsi = mtk_vcodec_fw_map_dm_addr(vpu->ctx->dev->fw_handler, + msg->vpu_inst_addr); vpu->inst_addr = msg->vpu_inst_addr; mtk_vcodec_debug(vpu, "- vpu_inst_addr = 0x%x", vpu->inst_addr); @@ -34,7 +36,7 @@ static void handle_init_ack_msg(const struct vdec_vpu_ipi_init_ack *msg) * This function runs in interrupt context and it means there's an IPI MSG * from VPU. */ -static void vpu_dec_ipi_handler(const void *data, unsigned int len, void *priv) +static void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv) { const struct vdec_vpu_ipi_ack *msg = data; struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *) @@ -74,7 +76,8 @@ static int vcodec_vpu_send_msg(struct vdec_vpu_inst *vpu, void *msg, int len) vpu->failure = 0; vpu->signaled = 0; - err = vpu_ipi_send(vpu->dev, vpu->id, msg, len); + err = mtk_vcodec_fw_ipi_send(vpu->ctx->dev->fw_handler, vpu->id, msg, + len, 2000); if (err) { mtk_vcodec_err(vpu, "send fail vpu_id=%d msg_id=%X status=%d", vpu->id, *(uint32_t *)msg, err); @@ -110,7 +113,8 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu) init_waitqueue_head(&vpu->wq); vpu->handler = vpu_dec_ipi_handler; - err = vpu_ipi_register(vpu->dev, vpu->id, vpu->handler, "vdec", NULL); + err = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, vpu->id, + vpu->handler, "vdec", NULL); if (err != 0) { mtk_vcodec_err(vpu, "vpu_ipi_register fail status=%d", err); return err; diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h index f779b0676fbd..85224eb7e34b 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h +++ b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h @@ -7,11 +7,13 @@ #ifndef _VDEC_VPU_IF_H_ #define _VDEC_VPU_IF_H_ -#include "mtk_vpu.h" +#include "mtk_vcodec_fw.h" + +struct mtk_vcodec_ctx; /** * struct vdec_vpu_inst - VPU instance for video codec - * @ipi_id : ipi id for each decoder + * @id : ipi msg id for each decoder * @vsi : driver structure allocated by VPU side and shared to AP side * for control and info share * @failure : VPU execution result status, 0: success, others: fail @@ -23,15 +25,14 @@ * @handler : ipi handler for each decoder */ struct vdec_vpu_inst { - enum ipi_id id; + int id; void *vsi; int32_t failure; uint32_t inst_addr; unsigned int signaled; struct mtk_vcodec_ctx *ctx; - struct platform_device *dev; wait_queue_head_t wq; - ipi_handler_t handler; + mtk_vcodec_ipi_handler handler; }; /** diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c index b9624f8df0e9..d0123dfc5f93 100644 --- a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c +++ b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c @@ -18,7 +18,6 @@ #include "../venc_drv_base.h" #include "../venc_ipi_msg.h" #include "../venc_vpu_if.h" -#include "mtk_vpu.h" static const char h264_filler_marker[] = {0x0, 0x0, 0x0, 0x1, 0xc}; @@ -26,6 +25,16 @@ static const char h264_filler_marker[] = {0x0, 0x0, 0x0, 0x1, 0xc}; #define VENC_PIC_BITSTREAM_BYTE_CNT 0x0098 /* + * enum venc_h264_frame_type - h264 encoder output bitstream frame type + */ +enum venc_h264_frame_type { + VENC_H264_IDR_FRM, + VENC_H264_I_FRM, + VENC_H264_P_FRM, + VENC_H264_B_FRM, +}; + +/* * enum venc_h264_vpu_work_buf - h264 encoder buffer index */ enum venc_h264_vpu_work_buf { @@ -139,6 +148,7 @@ struct venc_h264_inst { struct mtk_vcodec_mem pps_buf; bool work_buf_allocated; unsigned int frm_cnt; + unsigned int skip_frm_cnt; unsigned int prepend_hdr; struct venc_vpu_inst vpu_inst; struct venc_h264_vsi *vsi; @@ -257,8 +267,11 @@ static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst) */ inst->work_bufs[i].size = wb[i].size; if (i == VENC_H264_VPU_WORK_BUF_SKIP_FRAME) { - inst->work_bufs[i].va = vpu_mapping_dm_addr( - inst->vpu_inst.dev, wb[i].vpua); + struct mtk_vcodec_fw *handler; + + handler = inst->vpu_inst.ctx->dev->fw_handler; + inst->work_bufs[i].va = + mtk_vcodec_fw_map_dm_addr(handler, wb[i].vpua); inst->work_bufs[i].dma_addr = 0; } else { ret = mtk_vcodec_mem_alloc(inst->ctx, @@ -275,10 +288,12 @@ static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst) * setting in VPU side. */ if (i == VENC_H264_VPU_WORK_BUF_RC_CODE) { + struct mtk_vcodec_fw *handler; void *tmp_va; - tmp_va = vpu_mapping_dm_addr(inst->vpu_inst.dev, - wb[i].vpua); + handler = inst->vpu_inst.ctx->dev->fw_handler; + tmp_va = mtk_vcodec_fw_map_dm_addr(handler, + wb[i].vpua); memcpy(inst->work_bufs[i].va, tmp_va, wb[i].size); } @@ -323,6 +338,22 @@ static unsigned int h264_enc_wait_venc_done(struct venc_h264_inst *inst) return irq_status; } +static int h264_frame_type(struct venc_h264_inst *inst) +{ + if ((inst->vsi->config.gop_size != 0 && + (inst->frm_cnt % inst->vsi->config.gop_size) == 0) || + (inst->frm_cnt == 0 && inst->vsi->config.gop_size == 0)) { + /* IDR frame */ + return VENC_H264_IDR_FRM; + } else if ((inst->vsi->config.intra_period != 0 && + (inst->frm_cnt % inst->vsi->config.intra_period) == 0) || + (inst->frm_cnt == 0 && inst->vsi->config.intra_period == 0)) { + /* I frame */ + return VENC_H264_I_FRM; + } else { + return VENC_H264_P_FRM; /* Note: B frames are not supported */ + } +} static int h264_encode_sps(struct venc_h264_inst *inst, struct mtk_vcodec_mem *bs_buf, unsigned int *bs_size) @@ -333,7 +364,7 @@ static int h264_encode_sps(struct venc_h264_inst *inst, mtk_vcodec_debug_enter(inst); ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_SPS, NULL, - bs_buf, bs_size); + bs_buf, bs_size, NULL); if (ret) return ret; @@ -360,7 +391,7 @@ static int h264_encode_pps(struct venc_h264_inst *inst, mtk_vcodec_debug_enter(inst); ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_PPS, NULL, - bs_buf, bs_size); + bs_buf, bs_size, NULL); if (ret) return ret; @@ -406,11 +437,18 @@ static int h264_encode_frame(struct venc_h264_inst *inst, { int ret = 0; unsigned int irq_status; + struct venc_frame_info frame_info; mtk_vcodec_debug_enter(inst); - + mtk_vcodec_debug(inst, "frm_cnt = %d\n ", inst->frm_cnt); + frame_info.frm_count = inst->frm_cnt; + frame_info.skip_frm_count = inst->skip_frm_cnt; + frame_info.frm_type = h264_frame_type(inst); + mtk_vcodec_debug(inst, "frm_count = %d,skip_frm_count =%d,frm_type=%d.\n", + frame_info.frm_count, frame_info.skip_frm_count, + frame_info.frm_type); ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_FRAME, frm_buf, - bs_buf, bs_size); + bs_buf, bs_size, &frame_info); if (ret) return ret; @@ -424,6 +462,7 @@ static int h264_encode_frame(struct venc_h264_inst *inst, inst->work_bufs[VENC_H264_VPU_WORK_BUF_SKIP_FRAME].va, *bs_size); ++inst->frm_cnt; + ++inst->skip_frm_cnt; return ret; } @@ -460,6 +499,7 @@ static void h264_encode_filler(struct venc_h264_inst *inst, void *buf, static int h264_enc_init(struct mtk_vcodec_ctx *ctx) { + const bool is_ext = MTK_ENC_CTX_IS_EXT(ctx); int ret = 0; struct venc_h264_inst *inst; @@ -469,8 +509,7 @@ static int h264_enc_init(struct mtk_vcodec_ctx *ctx) inst->ctx = ctx; inst->vpu_inst.ctx = ctx; - inst->vpu_inst.dev = ctx->dev->vpu_plat_dev; - inst->vpu_inst.id = IPI_VENC_H264; + inst->vpu_inst.id = is_ext ? SCP_IPI_VENC_H264 : IPI_VENC_H264; inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_SYS); mtk_vcodec_debug_enter(inst); @@ -626,7 +665,12 @@ static int h264_enc_set_param(void *handle, inst->prepend_hdr = 1; mtk_vcodec_debug(inst, "set prepend header mode"); break; - + case VENC_SET_PARAM_FORCE_INTRA: + case VENC_SET_PARAM_GOP_SIZE: + case VENC_SET_PARAM_INTRA_PERIOD: + inst->frm_cnt = 0; + inst->skip_frm_cnt = 0; + fallthrough; default: ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); break; diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c index 8d36f0362efe..11abb191ada5 100644 --- a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c +++ b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c @@ -17,7 +17,6 @@ #include "../venc_drv_base.h" #include "../venc_ipi_msg.h" #include "../venc_vpu_if.h" -#include "mtk_vpu.h" #define VENC_BITSTREAM_FRAME_SIZE 0x0098 #define VENC_BITSTREAM_HEADER_LEN 0x00e8 @@ -190,10 +189,12 @@ static int vp8_enc_alloc_work_buf(struct venc_vp8_inst *inst) if (i == VENC_VP8_VPU_WORK_BUF_RC_CODE || i == VENC_VP8_VPU_WORK_BUF_RC_CODE2 || i == VENC_VP8_VPU_WORK_BUF_RC_CODE3) { + struct mtk_vcodec_fw *handler; void *tmp_va; - tmp_va = vpu_mapping_dm_addr(inst->vpu_inst.dev, - wb[i].vpua); + handler = inst->vpu_inst.ctx->dev->fw_handler; + tmp_va = mtk_vcodec_fw_map_dm_addr(handler, + wb[i].vpua); memcpy(inst->work_bufs[i].va, tmp_va, wb[i].size); } wb[i].iova = inst->work_bufs[i].dma_addr; @@ -301,7 +302,8 @@ static int vp8_enc_encode_frame(struct venc_vp8_inst *inst, mtk_vcodec_debug(inst, "->frm_cnt=%d", inst->frm_cnt); - ret = vpu_enc_encode(&inst->vpu_inst, 0, frm_buf, bs_buf, bs_size); + ret = vpu_enc_encode(&inst->vpu_inst, 0, frm_buf, bs_buf, bs_size, + NULL); if (ret) return ret; @@ -334,7 +336,6 @@ static int vp8_enc_init(struct mtk_vcodec_ctx *ctx) inst->ctx = ctx; inst->vpu_inst.ctx = ctx; - inst->vpu_inst.dev = ctx->dev->vpu_plat_dev; inst->vpu_inst.id = IPI_VENC_VP8; inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_LT_SYS); diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.c b/drivers/media/platform/mtk-vcodec/venc_drv_if.c index c6bb82ac2dcd..ce0bce811615 100644 --- a/drivers/media/platform/mtk-vcodec/venc_drv_if.c +++ b/drivers/media/platform/mtk-vcodec/venc_drv_if.c @@ -15,7 +15,6 @@ #include "mtk_vcodec_enc.h" #include "mtk_vcodec_enc_pm.h" -#include "mtk_vpu.h" int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) { diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.h b/drivers/media/platform/mtk-vcodec/venc_drv_if.h index 52fc9cc812fc..0b04a1020873 100644 --- a/drivers/media/platform/mtk-vcodec/venc_drv_if.h +++ b/drivers/media/platform/mtk-vcodec/venc_drv_if.h @@ -92,6 +92,19 @@ struct venc_enc_param { unsigned int gop_size; }; +/** + * struct venc_frame_info - per-frame information to pass to the firmware. + * + * @frm_count: sequential number for this frame + * @skip_frm_count: number of frames skipped so far while decoding + * @frm_type: type of the frame, from enum venc_h264_frame_type + */ +struct venc_frame_info { + unsigned int frm_count; /* per frame update */ + unsigned int skip_frm_count; /* per frame update */ + unsigned int frm_type; /* per frame update */ +}; + /* * struct venc_frm_buf - frame buffer information used in venc_if_encode() * @fb_addr: plane frame buffer addresses diff --git a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h b/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h index 28ee04ca6241..2feb0365179f 100644 --- a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h +++ b/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h @@ -62,6 +62,11 @@ struct venc_ap_ipi_msg_set_param { uint32_t data[8]; }; +struct venc_ap_ipi_msg_set_param_ext { + struct venc_ap_ipi_msg_set_param base; + uint32_t data_ext[24]; +}; + /** * struct venc_ap_ipi_msg_enc - AP to VPU enc cmd structure * @msg_id: message id (AP_IPIMSG_XXX_ENC_ENCODE) @@ -83,6 +88,19 @@ struct venc_ap_ipi_msg_enc { }; /** + * struct venc_ap_ipi_msg_enc_ext - AP to SCP extended enc cmd structure + * + * @base: base msg structure + * @data_item: number of items in the data array + * @data[8]: data array to store the set parameters + */ +struct venc_ap_ipi_msg_enc_ext { + struct venc_ap_ipi_msg_enc base; + uint32_t data_item; + uint32_t data[32]; +}; + +/** * struct venc_ap_ipi_msg_deinit - AP to VPU deinit cmd structure * @msg_id: message id (AP_IPIMSG_XXX_ENC_DEINIT) * @vpu_inst_addr: VPU encoder instance addr @@ -120,16 +138,17 @@ struct venc_vpu_ipi_msg_common { * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) * @vpu_inst_addr: VPU encoder instance addr * (struct venc_vp8_vsi/venc_h264_vsi *) - * @reserved: reserved for future use. vpu is running in 32bit. Without - * this reserved field, if kernel run in 64bit. this struct size - * will be different between kernel and vpu + * @venc_abi_version: ABI version of the firmware. Kernel can use it to + * ensure that it is compatible with the firmware. + * For MT8173 the value of this field is undefined and + * should not be used. */ struct venc_vpu_ipi_msg_init { uint32_t msg_id; uint32_t status; uint64_t venc_inst; uint32_t vpu_inst_addr; - uint32_t reserved; + uint32_t venc_abi_version; }; /** diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c b/drivers/media/platform/mtk-vcodec/venc_vpu_if.c index 9540709c1905..be6d8790a41e 100644 --- a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c +++ b/drivers/media/platform/mtk-vcodec/venc_vpu_if.c @@ -4,7 +4,8 @@ * Author: PoChun Lin <pochun.lin@mediatek.com> */ -#include "mtk_vpu.h" +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_fw.h" #include "venc_ipi_msg.h" #include "venc_vpu_if.h" @@ -13,7 +14,25 @@ static void handle_enc_init_msg(struct venc_vpu_inst *vpu, const void *data) const struct venc_vpu_ipi_msg_init *msg = data; vpu->inst_addr = msg->vpu_inst_addr; - vpu->vsi = vpu_mapping_dm_addr(vpu->dev, msg->vpu_inst_addr); + vpu->vsi = mtk_vcodec_fw_map_dm_addr(vpu->ctx->dev->fw_handler, + msg->vpu_inst_addr); + + /* Firmware version field value is unspecified on MT8173. */ + if (vpu->ctx->dev->venc_pdata->chip == MTK_MT8173) + return; + + /* Check firmware version. */ + mtk_vcodec_debug(vpu, "firmware version: 0x%x\n", + msg->venc_abi_version); + switch (msg->venc_abi_version) { + case 1: + break; + default: + mtk_vcodec_err(vpu, "unhandled firmware version 0x%x\n", + msg->venc_abi_version); + vpu->failure = 1; + break; + } } static void handle_enc_encode_msg(struct venc_vpu_inst *vpu, const void *data) @@ -25,7 +44,7 @@ static void handle_enc_encode_msg(struct venc_vpu_inst *vpu, const void *data) vpu->is_key_frm = msg->is_key_frm; } -static void vpu_enc_ipi_handler(const void *data, unsigned int len, void *priv) +static void vpu_enc_ipi_handler(void *data, unsigned int len, void *priv) { const struct venc_vpu_ipi_msg_common *msg = data; struct venc_vpu_inst *vpu = @@ -34,6 +53,11 @@ static void vpu_enc_ipi_handler(const void *data, unsigned int len, void *priv) mtk_vcodec_debug(vpu, "msg_id %x inst %p status %d", msg->msg_id, vpu, msg->status); + vpu->signaled = 1; + vpu->failure = (msg->status != VENC_IPI_MSG_STATUS_OK); + if (vpu->failure) + goto failure; + switch (msg->msg_id) { case VPU_IPIMSG_ENC_INIT_DONE: handle_enc_init_msg(vpu, data); @@ -50,9 +74,7 @@ static void vpu_enc_ipi_handler(const void *data, unsigned int len, void *priv) break; } - vpu->signaled = 1; - vpu->failure = (msg->status != VENC_IPI_MSG_STATUS_OK); - +failure: mtk_vcodec_debug_leave(vpu); } @@ -63,12 +85,13 @@ static int vpu_enc_send_msg(struct venc_vpu_inst *vpu, void *msg, mtk_vcodec_debug_enter(vpu); - if (!vpu->dev) { + if (!vpu->ctx->dev->fw_handler) { mtk_vcodec_err(vpu, "inst dev is NULL"); return -EINVAL; } - status = vpu_ipi_send(vpu->dev, vpu->id, msg, len); + status = mtk_vcodec_fw_ipi_send(vpu->ctx->dev->fw_handler, vpu->id, msg, + len, 2000); if (status) { mtk_vcodec_err(vpu, "vpu_ipi_send msg_id %x len %d fail %d", *(uint32_t *)msg, len, status); @@ -93,8 +116,9 @@ int vpu_enc_init(struct venc_vpu_inst *vpu) vpu->signaled = 0; vpu->failure = 0; - status = vpu_ipi_register(vpu->dev, vpu->id, vpu_enc_ipi_handler, - NULL, NULL); + status = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, vpu->id, + vpu_enc_ipi_handler, "venc", NULL); + if (status) { mtk_vcodec_err(vpu, "vpu_ipi_register fail %d", status); return -EINVAL; @@ -113,49 +137,81 @@ int vpu_enc_init(struct venc_vpu_inst *vpu) return 0; } +static unsigned int venc_enc_param_crop_right(struct venc_vpu_inst *vpu, + struct venc_enc_param *enc_prm) +{ + unsigned int img_crop_right = enc_prm->buf_width - enc_prm->width; + + return img_crop_right % 16; +} + +static unsigned int venc_enc_param_crop_bottom(struct venc_enc_param *enc_prm) +{ + return round_up(enc_prm->height, 16) - enc_prm->height; +} + +static unsigned int venc_enc_param_num_mb(struct venc_enc_param *enc_prm) +{ + return DIV_ROUND_UP(enc_prm->width, 16) * + DIV_ROUND_UP(enc_prm->height, 16); +} + int vpu_enc_set_param(struct venc_vpu_inst *vpu, enum venc_set_param_type id, struct venc_enc_param *enc_param) { - struct venc_ap_ipi_msg_set_param out; + const bool is_ext = MTK_ENC_CTX_IS_EXT(vpu->ctx); + size_t msg_size = is_ext ? + sizeof(struct venc_ap_ipi_msg_set_param_ext) : + sizeof(struct venc_ap_ipi_msg_set_param); + struct venc_ap_ipi_msg_set_param_ext out; mtk_vcodec_debug(vpu, "id %d ->", id); memset(&out, 0, sizeof(out)); - out.msg_id = AP_IPIMSG_ENC_SET_PARAM; - out.vpu_inst_addr = vpu->inst_addr; - out.param_id = id; + out.base.msg_id = AP_IPIMSG_ENC_SET_PARAM; + out.base.vpu_inst_addr = vpu->inst_addr; + out.base.param_id = id; switch (id) { case VENC_SET_PARAM_ENC: - out.data_item = 0; + if (is_ext) { + out.base.data_item = 3; + out.base.data[0] = + venc_enc_param_crop_right(vpu, enc_param); + out.base.data[1] = + venc_enc_param_crop_bottom(enc_param); + out.base.data[2] = venc_enc_param_num_mb(enc_param); + } else { + out.base.data_item = 0; + } break; case VENC_SET_PARAM_FORCE_INTRA: - out.data_item = 0; + out.base.data_item = 0; break; case VENC_SET_PARAM_ADJUST_BITRATE: - out.data_item = 1; - out.data[0] = enc_param->bitrate; + out.base.data_item = 1; + out.base.data[0] = enc_param->bitrate; break; case VENC_SET_PARAM_ADJUST_FRAMERATE: - out.data_item = 1; - out.data[0] = enc_param->frm_rate; + out.base.data_item = 1; + out.base.data[0] = enc_param->frm_rate; break; case VENC_SET_PARAM_GOP_SIZE: - out.data_item = 1; - out.data[0] = enc_param->gop_size; + out.base.data_item = 1; + out.base.data[0] = enc_param->gop_size; break; case VENC_SET_PARAM_INTRA_PERIOD: - out.data_item = 1; - out.data[0] = enc_param->intra_period; + out.base.data_item = 1; + out.base.data[0] = enc_param->intra_period; break; case VENC_SET_PARAM_SKIP_FRAME: - out.data_item = 0; + out.base.data_item = 0; break; default: mtk_vcodec_err(vpu, "id %d not supported", id); return -EINVAL; } - if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { + if (vpu_enc_send_msg(vpu, &out, msg_size)) { mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_SET_PARAM %d fail", id); return -EINVAL; @@ -169,33 +225,44 @@ int vpu_enc_set_param(struct venc_vpu_inst *vpu, int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode, struct venc_frm_buf *frm_buf, struct mtk_vcodec_mem *bs_buf, - unsigned int *bs_size) + unsigned int *bs_size, + struct venc_frame_info *frame_info) { - struct venc_ap_ipi_msg_enc out; + const bool is_ext = MTK_ENC_CTX_IS_EXT(vpu->ctx); + size_t msg_size = is_ext ? + sizeof(struct venc_ap_ipi_msg_enc_ext) : + sizeof(struct venc_ap_ipi_msg_enc); + struct venc_ap_ipi_msg_enc_ext out; mtk_vcodec_debug(vpu, "bs_mode %d ->", bs_mode); memset(&out, 0, sizeof(out)); - out.msg_id = AP_IPIMSG_ENC_ENCODE; - out.vpu_inst_addr = vpu->inst_addr; - out.bs_mode = bs_mode; + out.base.msg_id = AP_IPIMSG_ENC_ENCODE; + out.base.vpu_inst_addr = vpu->inst_addr; + out.base.bs_mode = bs_mode; if (frm_buf) { if ((frm_buf->fb_addr[0].dma_addr % 16 == 0) && (frm_buf->fb_addr[1].dma_addr % 16 == 0) && (frm_buf->fb_addr[2].dma_addr % 16 == 0)) { - out.input_addr[0] = frm_buf->fb_addr[0].dma_addr; - out.input_addr[1] = frm_buf->fb_addr[1].dma_addr; - out.input_addr[2] = frm_buf->fb_addr[2].dma_addr; + out.base.input_addr[0] = frm_buf->fb_addr[0].dma_addr; + out.base.input_addr[1] = frm_buf->fb_addr[1].dma_addr; + out.base.input_addr[2] = frm_buf->fb_addr[2].dma_addr; } else { mtk_vcodec_err(vpu, "dma_addr not align to 16"); return -EINVAL; } } if (bs_buf) { - out.bs_addr = bs_buf->dma_addr; - out.bs_size = bs_buf->size; + out.base.bs_addr = bs_buf->dma_addr; + out.base.bs_size = bs_buf->size; } - if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { + if (is_ext && frame_info) { + out.data_item = 3; + out.data[0] = frame_info->frm_count; + out.data[1] = frame_info->skip_frm_count; + out.data[2] = frame_info->frm_type; + } + if (vpu_enc_send_msg(vpu, &out, msg_size)) { mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_ENCODE %d fail", bs_mode); return -EINVAL; diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h b/drivers/media/platform/mtk-vcodec/venc_vpu_if.h index ba301a138a5a..f9be9cab7ff7 100644 --- a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h +++ b/drivers/media/platform/mtk-vcodec/venc_vpu_if.h @@ -7,7 +7,7 @@ #ifndef _VENC_VPU_IF_H_ #define _VENC_VPU_IF_H_ -#include "mtk_vpu.h" +#include "mtk_vcodec_fw.h" #include "venc_drv_if.h" /* @@ -34,9 +34,8 @@ struct venc_vpu_inst { int is_key_frm; unsigned int inst_addr; void *vsi; - enum ipi_id id; + int id; struct mtk_vcodec_ctx *ctx; - struct platform_device *dev; }; int vpu_enc_init(struct venc_vpu_inst *vpu); @@ -46,7 +45,8 @@ int vpu_enc_set_param(struct venc_vpu_inst *vpu, int vpu_enc_encode(struct venc_vpu_inst *vpu, unsigned int bs_mode, struct venc_frm_buf *frm_buf, struct mtk_vcodec_mem *bs_buf, - unsigned int *bs_size); + unsigned int *bs_size, + struct venc_frame_info *frame_info); int vpu_enc_deinit(struct venc_vpu_inst *vpu); #endif diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mtk-vpu/mtk_vpu.c index d30c08983f56..36cb9b6131f7 100644 --- a/drivers/media/platform/mtk-vpu/mtk_vpu.c +++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c @@ -849,10 +849,6 @@ static int mtk_vpu_probe(struct platform_device *pdev) #ifdef CONFIG_DEBUG_FS vpu_debugfs = debugfs_create_file("mtk_vpu", S_IRUGO, NULL, (void *)dev, &vpu_debug_fops); - if (!vpu_debugfs) { - ret = -ENOMEM; - goto cleanup_ipi; - } #endif /* Set PTCM to 96K and DTCM to 32K */ @@ -910,7 +906,6 @@ remove_debugfs: of_reserved_mem_device_release(dev); #ifdef CONFIG_DEBUG_FS debugfs_remove(vpu_debugfs); -cleanup_ipi: #endif memset(vpu->ipi_desc, 0, sizeof(struct vpu_ipi_desc) * IPI_MAX); vpu_mutex_destroy: diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index df78df59da45..08a5473b5610 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -852,8 +852,11 @@ static int emmaprp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pcdev); irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + if (irq < 0) { + ret = irq; + goto rel_vdev; + } + ret = devm_request_irq(&pdev->dev, irq, emmaprp_irq, 0, dev_name(&pdev->dev), pcdev); if (ret) diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index b91e472ee764..b1fc4518e275 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -142,7 +142,7 @@ static struct isp_reg isp_reg_list[] = { * readback the same register, in this case the revision register. * * See this link for reference: - * http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html + * https://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html */ void omap3isp_flush(struct isp_device *isp) { @@ -2328,8 +2328,10 @@ static int isp_probe(struct platform_device *pdev) mem = platform_get_resource(pdev, IORESOURCE_MEM, i); isp->mmio_base[map_idx] = devm_ioremap_resource(isp->dev, mem); - if (IS_ERR(isp->mmio_base[map_idx])) - return PTR_ERR(isp->mmio_base[map_idx]); + if (IS_ERR(isp->mmio_base[map_idx])) { + ret = PTR_ERR(isp->mmio_base[map_idx]); + goto error; + } } ret = isp_get_clocks(isp); diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index 1ac9aef70dff..8811d6dd4ee7 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -703,7 +703,7 @@ isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format) * requested. */ format->fmt.pix.field = V4L2_FIELD_INTERLACED_TB; - /* Fall-through */ + fallthrough; case V4L2_FIELD_INTERLACED_TB: case V4L2_FIELD_INTERLACED_BT: /* Interlaced orders are only supported at the CCDC output. */ diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 6dce33f35041..e47520fcb93c 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -605,42 +605,6 @@ static const struct pxa_mbus_pixelfmt *pxa_mbus_get_fmtdesc( return pxa_mbus_find_fmtdesc(code, mbus_fmt, ARRAY_SIZE(mbus_fmt)); } -static unsigned int pxa_mbus_config_compatible(const struct v4l2_mbus_config *cfg, - unsigned int flags) -{ - unsigned long common_flags; - bool hsync = true, vsync = true, pclk, data, mode; - bool mipi_lanes, mipi_clock; - - common_flags = cfg->flags & flags; - - switch (cfg->type) { - case V4L2_MBUS_PARALLEL: - hsync = common_flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_HSYNC_ACTIVE_LOW); - vsync = common_flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_VSYNC_ACTIVE_LOW); - /* fall through */ - case V4L2_MBUS_BT656: - pclk = common_flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_PCLK_SAMPLE_FALLING); - data = common_flags & (V4L2_MBUS_DATA_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_LOW); - mode = common_flags & (V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE); - return (!hsync || !vsync || !pclk || !data || !mode) ? - 0 : common_flags; - case V4L2_MBUS_CSI2_DPHY: - mipi_lanes = common_flags & V4L2_MBUS_CSI2_LANES; - mipi_clock = common_flags & (V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK | - V4L2_MBUS_CSI2_CONTINUOUS_CLOCK); - return (!mipi_lanes || !mipi_clock) ? 0 : common_flags; - default: - WARN_ON(1); - return -EINVAL; - } - return 0; -} - /** * struct pxa_camera_format_xlate - match between host and sensor formats * @code: code of a sensor provided format @@ -1186,9 +1150,9 @@ static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev) clk_disable_unprepare(pcdev->clk); } -static void pxa_camera_eof(unsigned long arg) +static void pxa_camera_eof(struct tasklet_struct *t) { - struct pxa_camera_dev *pcdev = (struct pxa_camera_dev *)arg; + struct pxa_camera_dev *pcdev = from_tasklet(pcdev, t, task_eof); unsigned long cifr; struct pxa_buffer *buf; @@ -1231,31 +1195,6 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) return IRQ_HANDLED; } -static int test_platform_param(struct pxa_camera_dev *pcdev, - unsigned char buswidth, unsigned long *flags) -{ - /* - * Platform specified synchronization and pixel clock polarities are - * only a recommendation and are only used during probing. The PXA270 - * quick capture interface supports both. - */ - *flags = (pcdev->platform_flags & PXA_CAMERA_MASTER ? - V4L2_MBUS_MASTER : V4L2_MBUS_SLAVE) | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_HSYNC_ACTIVE_LOW | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_VSYNC_ACTIVE_LOW | - V4L2_MBUS_DATA_ACTIVE_HIGH | - V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_PCLK_SAMPLE_FALLING; - - /* If requested data width is supported by the platform, use it */ - if ((1 << (buswidth - 1)) & pcdev->width_flags) - return 0; - - return -EINVAL; -} - static void pxa_camera_setup_cicr(struct pxa_camera_dev *pcdev, unsigned long flags, __u32 pixfmt) { @@ -1598,99 +1537,78 @@ static int pxa_camera_init_videobuf2(struct pxa_camera_dev *pcdev) */ static int pxa_camera_set_bus_param(struct pxa_camera_dev *pcdev) { + unsigned int bus_width = pcdev->current_fmt->host_fmt->bits_per_sample; struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; u32 pixfmt = pcdev->current_fmt->host_fmt->fourcc; - unsigned long bus_flags, common_flags; + int mbus_config; int ret; - ret = test_platform_param(pcdev, - pcdev->current_fmt->host_fmt->bits_per_sample, - &bus_flags); - if (ret < 0) - return ret; - - ret = sensor_call(pcdev, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = pxa_mbus_config_compatible(&cfg, - bus_flags); - if (!common_flags) { - dev_warn(pcdev_to_dev(pcdev), - "Flags incompatible: camera 0x%x, host 0x%lx\n", - cfg.flags, bus_flags); - return -EINVAL; - } - } else if (ret != -ENOIOCTLCMD) { - return ret; - } else { - common_flags = bus_flags; + if (!((1 << (bus_width - 1)) & pcdev->width_flags)) { + dev_err(pcdev_to_dev(pcdev), "Unsupported bus width %u", + bus_width); + return -EINVAL; } pcdev->channels = 1; /* Make choices, based on platform preferences */ - if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { - if (pcdev->platform_flags & PXA_CAMERA_HSP) - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; - } + mbus_config = 0; + if (pcdev->platform_flags & PXA_CAMERA_MASTER) + mbus_config |= V4L2_MBUS_MASTER; + else + mbus_config |= V4L2_MBUS_SLAVE; - if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && - (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { - if (pcdev->platform_flags & PXA_CAMERA_VSP) - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; - else - common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; - } + if (pcdev->platform_flags & PXA_CAMERA_HSP) + mbus_config |= V4L2_MBUS_HSYNC_ACTIVE_HIGH; + else + mbus_config |= V4L2_MBUS_HSYNC_ACTIVE_LOW; - if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) && - (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) { - if (pcdev->platform_flags & PXA_CAMERA_PCP) - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING; - else - common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING; - } + if (pcdev->platform_flags & PXA_CAMERA_VSP) + mbus_config |= V4L2_MBUS_VSYNC_ACTIVE_HIGH; + else + mbus_config |= V4L2_MBUS_VSYNC_ACTIVE_LOW; - cfg.flags = common_flags; - ret = sensor_call(pcdev, video, s_mbus_config, &cfg); + if (pcdev->platform_flags & PXA_CAMERA_PCP) + mbus_config |= V4L2_MBUS_PCLK_SAMPLE_RISING; + else + mbus_config |= V4L2_MBUS_PCLK_SAMPLE_FALLING; + mbus_config |= V4L2_MBUS_DATA_ACTIVE_HIGH; + + cfg.flags = mbus_config; + ret = sensor_call(pcdev, pad, set_mbus_config, 0, &cfg); if (ret < 0 && ret != -ENOIOCTLCMD) { - dev_dbg(pcdev_to_dev(pcdev), - "camera s_mbus_config(0x%lx) returned %d\n", - common_flags, ret); + dev_err(pcdev_to_dev(pcdev), + "Failed to call set_mbus_config: %d\n", ret); return ret; } - pxa_camera_setup_cicr(pcdev, common_flags, pixfmt); - - return 0; -} - -static int pxa_camera_try_bus_param(struct pxa_camera_dev *pcdev, - unsigned char buswidth) -{ - struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; - unsigned long bus_flags, common_flags; - int ret = test_platform_param(pcdev, buswidth, &bus_flags); - - if (ret < 0) - return ret; + /* + * If the requested media bus configuration has not been fully applied + * make sure it is supported by the platform. + * + * PXA does not support V4L2_MBUS_DATA_ACTIVE_LOW and the bus mastering + * roles should match. + */ + if (cfg.flags != mbus_config) { + unsigned int pxa_mbus_role = mbus_config & (V4L2_MBUS_MASTER | + V4L2_MBUS_SLAVE); + if (pxa_mbus_role != (cfg.flags & (V4L2_MBUS_MASTER | + V4L2_MBUS_SLAVE))) { + dev_err(pcdev_to_dev(pcdev), + "Unsupported mbus configuration: bus mastering\n"); + return -EINVAL; + } - ret = sensor_call(pcdev, video, g_mbus_config, &cfg); - if (!ret) { - common_flags = pxa_mbus_config_compatible(&cfg, - bus_flags); - if (!common_flags) { - dev_warn(pcdev_to_dev(pcdev), - "Flags incompatible: camera 0x%x, host 0x%lx\n", - cfg.flags, bus_flags); + if (cfg.flags & V4L2_MBUS_DATA_ACTIVE_LOW) { + dev_err(pcdev_to_dev(pcdev), + "Unsupported mbus configuration: DATA_ACTIVE_LOW\n"); return -EINVAL; } - } else if (ret == -ENOIOCTLCMD) { - ret = 0; } - return ret; + pxa_camera_setup_cicr(pcdev, cfg.flags, pixfmt); + + return 0; } static const struct pxa_mbus_pixelfmt pxa_camera_formats[] = { @@ -1738,11 +1656,6 @@ static int pxa_camera_get_formats(struct v4l2_device *v4l2_dev, return 0; } - /* This also checks support for the requested bits-per-sample */ - ret = pxa_camera_try_bus_param(pcdev, fmt->bits_per_sample); - if (ret < 0) - return 0; - switch (code.code) { case MEDIA_BUS_FMT_UYVY8_2X8: formats++; @@ -2478,7 +2391,7 @@ static int pxa_camera_probe(struct platform_device *pdev) goto exit_free_dma; } - tasklet_init(&pcdev->task_eof, pxa_camera_eof, (unsigned long)pcdev); + tasklet_setup(&pcdev->task_eof, pxa_camera_eof); pxa_camera_activate(pcdev); diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 03ef9c5f4774..85b24054f35e 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -176,8 +176,10 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) int ret; ret = pm_runtime_get_sync(dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_sync(dev); return ret; + } ret = csiphy_set_clock_rates(csiphy); if (ret < 0) { diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index fc31c2c169cd..b7d2293a5004 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -2205,14 +2205,6 @@ static const struct camss_video_ops camss_vfe_video_ops = { .flush_buffers = vfe_flush_buffers, }; -void msm_vfe_stop_streaming(struct vfe_device *vfe) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(vfe->line); i++) - msm_video_stop_streaming(&vfe->line[i].video_out); -} - /* * msm_vfe_register_entities - Register subdev node for VFE module * @vfe: VFE device diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index 0d10071ae881..a90b0d2cc6de 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -178,8 +178,6 @@ void msm_vfe_unregister_entities(struct vfe_device *vfe); void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id); void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id); -void msm_vfe_stop_streaming(struct vfe_device *vfe); - extern const struct vfe_hw_ops vfe_ops_4_1; extern const struct vfe_hw_ops vfe_ops_4_7; diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index cdbd6dba1122..114c3ae4a4ab 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -18,6 +18,12 @@ #include "camss-video.h" #include "camss.h" +#define CAMSS_FRAME_MIN_WIDTH 1 +#define CAMSS_FRAME_MAX_WIDTH 8191 +#define CAMSS_FRAME_MIN_HEIGHT 1 +#define CAMSS_FRAME_MAX_HEIGHT_RDI 8191 +#define CAMSS_FRAME_MAX_HEIGHT_PIX 4096 + struct fract { u8 numerator; u8 denominator; @@ -529,17 +535,16 @@ static int video_querycap(struct file *file, void *fh, return 0; } -static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +/* + * Returns the index in the video->formats[] array of the element which + * has the "ndx"th unique value of pixelformat field. + * If not found (no more unique pixelformat's) returns -EINVAL. + */ +static int video_get_unique_pixelformat_by_index(struct camss_video *video, + int ndx) { - struct camss_video *video = video_drvdata(file); int i, j, k; - if (f->type != video->type) - return -EINVAL; - - if (f->index >= video->nformats) - return -EINVAL; - /* find index "i" of "k"th unique pixelformat in formats array */ k = -1; for (i = 0; i < video->nformats; i++) { @@ -552,11 +557,53 @@ static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) if (j == i) k++; - if (k == f->index) - break; + if (k == ndx) + return i; + } + + return -EINVAL; +} + +/* + * Returns the index in the video->formats[] array of the element which + * has code equal to mcode. + * If not found returns -EINVAL. + */ +static int video_get_pixelformat_by_mbus_code(struct camss_video *video, + u32 mcode) +{ + int i; + + for (i = 0; i < video->nformats; i++) { + if (video->formats[i].code == mcode) + return i; + } + + return -EINVAL; +} + +static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct camss_video *video = video_drvdata(file); + int i; + + if (f->type != video->type) + return -EINVAL; + + if (f->index >= video->nformats) + return -EINVAL; + + if (f->mbus_code) { + /* Each entry in formats[] table has unique mbus_code */ + if (f->index > 0) + return -EINVAL; + + i = video_get_pixelformat_by_mbus_code(video, f->mbus_code); + } else { + i = video_get_unique_pixelformat_by_index(video, f->index); } - if (k < f->index) + if (i < 0) return -EINVAL; f->pixelformat = video->formats[i].pixelformat; @@ -564,6 +611,36 @@ static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) return 0; } +static int video_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct camss_video *video = video_drvdata(file); + int i; + + if (fsize->index) + return -EINVAL; + + /* Only accept pixel format present in the formats[] table */ + for (i = 0; i < video->nformats; i++) { + if (video->formats[i].pixelformat == fsize->pixel_format) + break; + } + + if (i == video->nformats) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = CAMSS_FRAME_MIN_WIDTH; + fsize->stepwise.max_width = CAMSS_FRAME_MAX_WIDTH; + fsize->stepwise.min_height = CAMSS_FRAME_MIN_HEIGHT; + fsize->stepwise.max_height = (video->line_based) ? + CAMSS_FRAME_MAX_HEIGHT_PIX : CAMSS_FRAME_MAX_HEIGHT_RDI; + fsize->stepwise.step_width = 1; + fsize->stepwise.step_height = 1; + + return 0; +} + static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f) { struct camss_video *video = video_drvdata(file); @@ -593,7 +670,7 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) 1, 65528); sizeimage[i] = clamp_t(u32, p->sizeimage, bytesperline[i], - bytesperline[i] * 4096); + bytesperline[i] * CAMSS_FRAME_MAX_HEIGHT_PIX); } for (j = 0; j < video->nformats; j++) @@ -610,8 +687,8 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) memset(pix_mp, 0, sizeof(*pix_mp)); pix_mp->pixelformat = fi->pixelformat; - pix_mp->width = clamp_t(u32, width, 1, 8191); - pix_mp->height = clamp_t(u32, height, 1, 8191); + pix_mp->width = clamp_t(u32, width, 1, CAMSS_FRAME_MAX_WIDTH); + pix_mp->height = clamp_t(u32, height, 1, CAMSS_FRAME_MAX_HEIGHT_RDI); pix_mp->num_planes = fi->planes; for (i = 0; i < pix_mp->num_planes; i++) { bpl = pix_mp->width / fi->hsub[i].numerator * @@ -637,7 +714,7 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) 1, 65528); p->sizeimage = clamp_t(u32, p->sizeimage, p->bytesperline, - p->bytesperline * 4096); + p->bytesperline * CAMSS_FRAME_MAX_HEIGHT_PIX); lines = p->sizeimage / p->bytesperline; if (p->bytesperline < bytesperline[i]) @@ -704,6 +781,7 @@ static int video_s_input(struct file *file, void *fh, unsigned int input) static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = { .vidioc_querycap = video_querycap, .vidioc_enum_fmt_vid_cap = video_enum_fmt, + .vidioc_enum_framesizes = video_enum_framesizes, .vidioc_g_fmt_vid_cap_mplane = video_g_fmt, .vidioc_s_fmt_vid_cap_mplane = video_s_fmt, .vidioc_try_fmt_vid_cap_mplane = video_try_fmt, @@ -879,7 +957,7 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, if (ret < 0) { dev_err(v4l2_dev->dev, "Failed to init video entity: %d\n", ret); - goto error_media_init; + goto error_vb2_init; } mutex_init(&video->lock); @@ -911,8 +989,8 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, } vdev->fops = &msm_vid_fops; - vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING + | V4L2_CAP_READWRITE | V4L2_CAP_IO_MC; vdev->ioctl_ops = &msm_vid_ioctl_ops; vdev->release = msm_video_release; vdev->v4l2_dev = v4l2_dev; @@ -936,23 +1014,15 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, error_video_register: media_entity_cleanup(&vdev->entity); mutex_destroy(&video->lock); -error_media_init: - vb2_queue_release(&video->vb2_q); error_vb2_init: mutex_destroy(&video->q_lock); return ret; } -void msm_video_stop_streaming(struct camss_video *video) -{ - if (vb2_is_streaming(&video->vb2_q)) - vb2_queue_release(&video->vb2_q); -} - void msm_video_unregister(struct camss_video *video) { atomic_inc(&video->camss->ref_count); - video_unregister_device(&video->vdev); + vb2_video_unregister_device(&video->vdev); atomic_dec(&video->camss->ref_count); } diff --git a/drivers/media/platform/qcom/camss/camss-video.h b/drivers/media/platform/qcom/camss/camss-video.h index aa35e8cc6fd5..bdbae8424140 100644 --- a/drivers/media/platform/qcom/camss/camss-video.h +++ b/drivers/media/platform/qcom/camss/camss-video.h @@ -52,8 +52,6 @@ struct camss_video { unsigned int nformats; }; -void msm_video_stop_streaming(struct camss_video *video); - int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, const char *name, int is_pix); diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 2483641799df..9186881afc98 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -974,13 +974,8 @@ void camss_delete(struct camss *camss) */ static int camss_remove(struct platform_device *pdev) { - unsigned int i; - struct camss *camss = platform_get_drvdata(pdev); - for (i = 0; i < camss->vfe_num; i++) - msm_vfe_stop_streaming(&camss->vfe[i]); - v4l2_async_notifier_unregister(&camss->notifier); v4l2_async_notifier_cleanup(&camss->notifier); camss_unregister_entities(camss); diff --git a/drivers/media/platform/qcom/venus/Makefile b/drivers/media/platform/qcom/venus/Makefile index 64af0bc1edae..dfc636865709 100644 --- a/drivers/media/platform/qcom/venus/Makefile +++ b/drivers/media/platform/qcom/venus/Makefile @@ -3,7 +3,7 @@ venus-core-objs += core.o helpers.o firmware.o \ hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o \ - hfi_parser.o pm_helpers.o + hfi_parser.o pm_helpers.o dbgfs.o venus-dec-objs += vdec.o vdec_ctrls.o venus-enc-objs += venc.o venc_ctrls.o diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 203c6538044f..6103aaf43987 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -6,6 +6,7 @@ #include <linux/init.h> #include <linux/interconnect.h> #include <linux/ioctl.h> +#include <linux/delay.h> #include <linux/list.h> #include <linux/module.h> #include <linux/of_device.h> @@ -40,13 +41,7 @@ static void venus_event_notify(struct venus_core *core, u32 event) mutex_unlock(&core->lock); disable_irq_nosync(core->irq); - - /* - * Delay recovery to ensure venus has completed any pending cache - * operations. Without this sleep, we see device reset when firmware is - * unloaded after a system error. - */ - schedule_delayed_work(&core->work, msecs_to_jiffies(100)); + schedule_delayed_work(&core->work, msecs_to_jiffies(10)); } static const struct hfi_core_ops venus_core_ops = { @@ -59,23 +54,29 @@ static void venus_sys_error_handler(struct work_struct *work) container_of(work, struct venus_core, work.work); int ret = 0; - dev_warn(core->dev, "system error has occurred, starting recovery!\n"); - pm_runtime_get_sync(core->dev); hfi_core_deinit(core, true); - hfi_destroy(core); + + dev_warn(core->dev, "system error has occurred, starting recovery!\n"); + mutex_lock(&core->lock); + + while (pm_runtime_active(core->dev_dec) || pm_runtime_active(core->dev_enc)) + msleep(10); + venus_shutdown(core); pm_runtime_put_sync(core->dev); - ret |= hfi_create(core, &venus_core_ops); + while (core->pmdomains[0] && pm_runtime_active(core->pmdomains[0])) + usleep_range(1000, 1500); + + hfi_reinit(core); pm_runtime_get_sync(core->dev); ret |= venus_boot(core); - ret |= hfi_core_resume(core, true); enable_irq(core->irq); @@ -224,15 +225,9 @@ static int venus_probe(struct platform_device *pdev) ret = dma_set_mask_and_coherent(dev, core->res->dma_mask); if (ret) - return ret; + goto err_core_put; - if (!dev->dma_parms) { - dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms), - GFP_KERNEL); - if (!dev->dma_parms) - return -ENOMEM; - } - dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); + dma_set_max_seg_size(dev, UINT_MAX); INIT_LIST_HEAD(&core->instances); mutex_init(&core->lock); @@ -242,11 +237,11 @@ static int venus_probe(struct platform_device *pdev) IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "venus", core); if (ret) - return ret; + goto err_core_put; ret = hfi_create(core, &venus_core_ops); if (ret) - return ret; + goto err_core_put; pm_runtime_enable(dev); @@ -287,8 +282,12 @@ static int venus_probe(struct platform_device *pdev) goto err_core_deinit; ret = pm_runtime_put_sync(dev); - if (ret) + if (ret) { + pm_runtime_get_noresume(dev); goto err_dev_unregister; + } + + venus_dbgfs_init(core); return 0; @@ -299,9 +298,13 @@ err_core_deinit: err_venus_shutdown: venus_shutdown(core); err_runtime_disable: + pm_runtime_put_noidle(dev); pm_runtime_set_suspended(dev); pm_runtime_disable(dev); hfi_destroy(core); +err_core_put: + if (core->pm_ops->core_put) + core->pm_ops->core_put(dev); return ret; } @@ -337,6 +340,7 @@ static int venus_remove(struct platform_device *pdev) v4l2_device_unregister(&core->v4l2_dev); mutex_destroy(&core->pm_lock); mutex_destroy(&core->lock); + venus_dbgfs_deinit(core); return ret; } @@ -520,6 +524,7 @@ static const struct venus_resources sdm845_res_v2 = { .vcodec_clks_num = 2, .vcodec_pmdomains = { "venus", "vcodec0", "vcodec1" }, .vcodec_pmdomains_num = 3, + .opp_pmdomain = (const char *[]) { "cx", NULL }, .vcodec_num = 2, .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_4XX, @@ -527,6 +532,10 @@ static const struct venus_resources sdm845_res_v2 = { .vmem_size = 0, .vmem_addr = 0, .dma_mask = 0xe0000000 - 1, + .cp_start = 0, + .cp_size = 0x70800000, + .cp_nonpixel_start = 0x1000000, + .cp_nonpixel_size = 0x24800000, .fwname = "qcom/venus-5.2/venus.mdt", }; @@ -565,6 +574,7 @@ static const struct venus_resources sc7180_res = { .vcodec_clks_num = 2, .vcodec_pmdomains = { "venus", "vcodec0" }, .vcodec_pmdomains_num = 2, + .opp_pmdomain = (const char *[]) { "cx", NULL }, .vcodec_num = 1, .hfi_version = HFI_VERSION_4XX, .vmem_id = VIDC_RESOURCE_NONE, diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 7118612673c9..7b79a33dc9d6 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -12,12 +12,20 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> +#include "dbgfs.h" #include "hfi.h" +#define VDBGL "VenusLow : " +#define VDBGM "VenusMed : " +#define VDBGH "VenusHigh: " +#define VDBGFW "VenusFW : " + #define VIDC_CLKS_NUM_MAX 4 #define VIDC_VCODEC_CLKS_NUM_MAX 2 #define VIDC_PMDOMAINS_NUM_MAX 3 +extern int venus_fw_debug; + struct freq_tbl { unsigned int load; unsigned long freq; @@ -62,12 +70,17 @@ struct venus_resources { unsigned int vcodec_clks_num; const char * const vcodec_pmdomains[VIDC_PMDOMAINS_NUM_MAX]; unsigned int vcodec_pmdomains_num; + const char **opp_pmdomain; unsigned int vcodec_num; enum hfi_version hfi_version; u32 max_load; unsigned int vmem_id; u32 vmem_size; u32 vmem_addr; + u32 cp_start; + u32 cp_size; + u32 cp_nonpixel_start; + u32 cp_nonpixel_size; const char *fwname; }; @@ -136,6 +149,7 @@ struct venus_caps { * @priv: a private filed for HFI operations * @ops: the core HFI operations * @work: a delayed work for handling system fatal error + * @root: debugfs root directory */ struct venus_core { void __iomem *base; @@ -145,8 +159,12 @@ struct venus_core { struct clk *vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX]; struct icc_path *video_path; struct icc_path *cpucfg_path; + struct opp_table *opp_table; + bool has_opp_table; struct device_link *pd_dl_venus; struct device *pmdomains[VIDC_PMDOMAINS_NUM_MAX]; + struct device_link *opp_dl_venus; + struct device *opp_pmdomain; struct video_device *vdev_dec; struct video_device *vdev_enc; struct v4l2_device v4l2_dev; @@ -185,6 +203,7 @@ struct venus_core { unsigned int codecs_count; unsigned int core0_usage_count; unsigned int core1_usage_count; + struct dentry *root; }; struct vdec_controls { @@ -201,6 +220,8 @@ struct venc_controls { u32 bitrate; u32 bitrate_peak; u32 rc_enable; + u32 const_quality; + u32 frame_skip_mode; u32 h264_i_period; u32 h264_entropy_mode; @@ -222,17 +243,8 @@ struct venc_controls { u32 header_mode; - struct { - u32 mpeg4; - u32 h264; - u32 vpx; - u32 hevc; - } profile; - struct { - u32 mpeg4; - u32 h264; - u32 hevc; - } level; + u32 profile; + u32 level; }; struct venus_buffer { diff --git a/drivers/media/platform/qcom/venus/dbgfs.c b/drivers/media/platform/qcom/venus/dbgfs.c new file mode 100644 index 000000000000..52de47f2ca88 --- /dev/null +++ b/drivers/media/platform/qcom/venus/dbgfs.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Linaro Ltd. + */ + +#include <linux/debugfs.h> + +#include "core.h" + +void venus_dbgfs_init(struct venus_core *core) +{ + core->root = debugfs_create_dir("venus", NULL); + debugfs_create_x32("fw_level", 0644, core->root, &venus_fw_debug); +} + +void venus_dbgfs_deinit(struct venus_core *core) +{ + debugfs_remove_recursive(core->root); +} diff --git a/drivers/media/platform/qcom/venus/dbgfs.h b/drivers/media/platform/qcom/venus/dbgfs.h new file mode 100644 index 000000000000..b7b621a8472f --- /dev/null +++ b/drivers/media/platform/qcom/venus/dbgfs.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2020 Linaro Ltd. */ + +#ifndef __VENUS_DBGFS_H__ +#define __VENUS_DBGFS_H__ + +struct venus_core; + +void venus_dbgfs_init(struct venus_core *core); +void venus_dbgfs_deinit(struct venus_core *core); + +#endif diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index 8801a6a7543d..1db64a854b88 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -181,6 +181,7 @@ static int venus_shutdown_no_tz(struct venus_core *core) int venus_boot(struct venus_core *core) { struct device *dev = core->dev; + const struct venus_resources *res = core->res; phys_addr_t mem_phys; size_t mem_size; int ret; @@ -200,7 +201,23 @@ int venus_boot(struct venus_core *core) else ret = venus_boot_no_tz(core, mem_phys, mem_size); - return ret; + if (ret) + return ret; + + if (core->use_tz && res->cp_size) { + ret = qcom_scm_mem_protect_video_var(res->cp_start, + res->cp_size, + res->cp_nonpixel_start, + res->cp_nonpixel_size); + if (ret) { + qcom_scm_pas_shutdown(VENUS_PAS_ID); + dev_err(dev, "set virtual address ranges fail (%d)\n", + ret); + return ret; + } + } + + return 0; } int venus_shutdown(struct venus_core *core) diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 0143af7822b2..50439eb1ffea 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -6,6 +6,7 @@ #include <linux/list.h> #include <linux/mutex.h> #include <linux/slab.h> +#include <linux/kernel.h> #include <media/videobuf2-dma-sg.h> #include <media/v4l2-mem2mem.h> #include <asm/div64.h> @@ -396,7 +397,7 @@ put_ts_metadata(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf) } if (slot == -1) { - dev_dbg(inst->core->dev, "%s: no free slot\n", __func__); + dev_dbg(inst->core->dev, VDBGL "no free slot\n"); return; } @@ -582,6 +583,244 @@ int venus_helper_get_bufreq(struct venus_inst *inst, u32 type, } EXPORT_SYMBOL_GPL(venus_helper_get_bufreq); +struct id_mapping { + u32 hfi_id; + u32 v4l2_id; +}; + +static const struct id_mapping mpeg4_profiles[] = { + { HFI_MPEG4_PROFILE_SIMPLE, V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE }, + { HFI_MPEG4_PROFILE_ADVANCEDSIMPLE, V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE }, +}; + +static const struct id_mapping mpeg4_levels[] = { + { HFI_MPEG4_LEVEL_0, V4L2_MPEG_VIDEO_MPEG4_LEVEL_0 }, + { HFI_MPEG4_LEVEL_0b, V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B }, + { HFI_MPEG4_LEVEL_1, V4L2_MPEG_VIDEO_MPEG4_LEVEL_1 }, + { HFI_MPEG4_LEVEL_2, V4L2_MPEG_VIDEO_MPEG4_LEVEL_2 }, + { HFI_MPEG4_LEVEL_3, V4L2_MPEG_VIDEO_MPEG4_LEVEL_3 }, + { HFI_MPEG4_LEVEL_4, V4L2_MPEG_VIDEO_MPEG4_LEVEL_4 }, + { HFI_MPEG4_LEVEL_5, V4L2_MPEG_VIDEO_MPEG4_LEVEL_5 }, +}; + +static const struct id_mapping mpeg2_profiles[] = { + { HFI_MPEG2_PROFILE_SIMPLE, V4L2_MPEG_VIDEO_MPEG2_PROFILE_SIMPLE }, + { HFI_MPEG2_PROFILE_MAIN, V4L2_MPEG_VIDEO_MPEG2_PROFILE_MAIN }, + { HFI_MPEG2_PROFILE_SNR, V4L2_MPEG_VIDEO_MPEG2_PROFILE_SNR_SCALABLE }, + { HFI_MPEG2_PROFILE_SPATIAL, V4L2_MPEG_VIDEO_MPEG2_PROFILE_SPATIALLY_SCALABLE }, + { HFI_MPEG2_PROFILE_HIGH, V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH }, +}; + +static const struct id_mapping mpeg2_levels[] = { + { HFI_MPEG2_LEVEL_LL, V4L2_MPEG_VIDEO_MPEG2_LEVEL_LOW }, + { HFI_MPEG2_LEVEL_ML, V4L2_MPEG_VIDEO_MPEG2_LEVEL_MAIN }, + { HFI_MPEG2_LEVEL_H14, V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH_1440 }, + { HFI_MPEG2_LEVEL_HL, V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH }, +}; + +static const struct id_mapping h264_profiles[] = { + { HFI_H264_PROFILE_BASELINE, V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE }, + { HFI_H264_PROFILE_MAIN, V4L2_MPEG_VIDEO_H264_PROFILE_MAIN }, + { HFI_H264_PROFILE_HIGH, V4L2_MPEG_VIDEO_H264_PROFILE_HIGH }, + { HFI_H264_PROFILE_STEREO_HIGH, V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH }, + { HFI_H264_PROFILE_MULTIVIEW_HIGH, V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH }, + { HFI_H264_PROFILE_CONSTRAINED_BASE, V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE }, + { HFI_H264_PROFILE_CONSTRAINED_HIGH, V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH }, +}; + +static const struct id_mapping h264_levels[] = { + { HFI_H264_LEVEL_1, V4L2_MPEG_VIDEO_H264_LEVEL_1_0 }, + { HFI_H264_LEVEL_1b, V4L2_MPEG_VIDEO_H264_LEVEL_1B }, + { HFI_H264_LEVEL_11, V4L2_MPEG_VIDEO_H264_LEVEL_1_1 }, + { HFI_H264_LEVEL_12, V4L2_MPEG_VIDEO_H264_LEVEL_1_2 }, + { HFI_H264_LEVEL_13, V4L2_MPEG_VIDEO_H264_LEVEL_1_3 }, + { HFI_H264_LEVEL_2, V4L2_MPEG_VIDEO_H264_LEVEL_2_0 }, + { HFI_H264_LEVEL_21, V4L2_MPEG_VIDEO_H264_LEVEL_2_1 }, + { HFI_H264_LEVEL_22, V4L2_MPEG_VIDEO_H264_LEVEL_2_2 }, + { HFI_H264_LEVEL_3, V4L2_MPEG_VIDEO_H264_LEVEL_3_0 }, + { HFI_H264_LEVEL_31, V4L2_MPEG_VIDEO_H264_LEVEL_3_1 }, + { HFI_H264_LEVEL_32, V4L2_MPEG_VIDEO_H264_LEVEL_3_2 }, + { HFI_H264_LEVEL_4, V4L2_MPEG_VIDEO_H264_LEVEL_4_0 }, + { HFI_H264_LEVEL_41, V4L2_MPEG_VIDEO_H264_LEVEL_4_1 }, + { HFI_H264_LEVEL_42, V4L2_MPEG_VIDEO_H264_LEVEL_4_2 }, + { HFI_H264_LEVEL_5, V4L2_MPEG_VIDEO_H264_LEVEL_5_0 }, + { HFI_H264_LEVEL_51, V4L2_MPEG_VIDEO_H264_LEVEL_5_1 }, + { HFI_H264_LEVEL_52, V4L2_MPEG_VIDEO_H264_LEVEL_5_1 }, +}; + +static const struct id_mapping hevc_profiles[] = { + { HFI_HEVC_PROFILE_MAIN, V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN }, + { HFI_HEVC_PROFILE_MAIN_STILL_PIC, V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE }, + { HFI_HEVC_PROFILE_MAIN10, V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10 }, +}; + +static const struct id_mapping hevc_levels[] = { + { HFI_HEVC_LEVEL_1, V4L2_MPEG_VIDEO_HEVC_LEVEL_1 }, + { HFI_HEVC_LEVEL_2, V4L2_MPEG_VIDEO_HEVC_LEVEL_2 }, + { HFI_HEVC_LEVEL_21, V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1 }, + { HFI_HEVC_LEVEL_3, V4L2_MPEG_VIDEO_HEVC_LEVEL_3 }, + { HFI_HEVC_LEVEL_31, V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1 }, + { HFI_HEVC_LEVEL_4, V4L2_MPEG_VIDEO_HEVC_LEVEL_4 }, + { HFI_HEVC_LEVEL_41, V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1 }, + { HFI_HEVC_LEVEL_5, V4L2_MPEG_VIDEO_HEVC_LEVEL_5 }, + { HFI_HEVC_LEVEL_51, V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1 }, + { HFI_HEVC_LEVEL_52, V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2 }, + { HFI_HEVC_LEVEL_6, V4L2_MPEG_VIDEO_HEVC_LEVEL_6 }, + { HFI_HEVC_LEVEL_61, V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1 }, + { HFI_HEVC_LEVEL_62, V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2 }, +}; + +static const struct id_mapping vp8_profiles[] = { + { HFI_VPX_PROFILE_VERSION_0, V4L2_MPEG_VIDEO_VP8_PROFILE_0 }, + { HFI_VPX_PROFILE_VERSION_1, V4L2_MPEG_VIDEO_VP8_PROFILE_1 }, + { HFI_VPX_PROFILE_VERSION_2, V4L2_MPEG_VIDEO_VP8_PROFILE_2 }, + { HFI_VPX_PROFILE_VERSION_3, V4L2_MPEG_VIDEO_VP8_PROFILE_3 }, +}; + +static const struct id_mapping vp9_profiles[] = { + { HFI_VP9_PROFILE_P0, V4L2_MPEG_VIDEO_VP9_PROFILE_0 }, + { HFI_VP9_PROFILE_P2_10B, V4L2_MPEG_VIDEO_VP9_PROFILE_2 }, +}; + +static const struct id_mapping vp9_levels[] = { + { HFI_VP9_LEVEL_1, V4L2_MPEG_VIDEO_VP9_LEVEL_1_0 }, + { HFI_VP9_LEVEL_11, V4L2_MPEG_VIDEO_VP9_LEVEL_1_1 }, + { HFI_VP9_LEVEL_2, V4L2_MPEG_VIDEO_VP9_LEVEL_2_0}, + { HFI_VP9_LEVEL_21, V4L2_MPEG_VIDEO_VP9_LEVEL_2_1 }, + { HFI_VP9_LEVEL_3, V4L2_MPEG_VIDEO_VP9_LEVEL_3_0}, + { HFI_VP9_LEVEL_31, V4L2_MPEG_VIDEO_VP9_LEVEL_3_1 }, + { HFI_VP9_LEVEL_4, V4L2_MPEG_VIDEO_VP9_LEVEL_4_0 }, + { HFI_VP9_LEVEL_41, V4L2_MPEG_VIDEO_VP9_LEVEL_4_1 }, + { HFI_VP9_LEVEL_5, V4L2_MPEG_VIDEO_VP9_LEVEL_5_0 }, + { HFI_VP9_LEVEL_51, V4L2_MPEG_VIDEO_VP9_LEVEL_5_1 }, + { HFI_VP9_LEVEL_6, V4L2_MPEG_VIDEO_VP9_LEVEL_6_0 }, + { HFI_VP9_LEVEL_61, V4L2_MPEG_VIDEO_VP9_LEVEL_6_1 }, +}; + +static u32 find_v4l2_id(u32 hfi_id, const struct id_mapping *array, unsigned int array_sz) +{ + unsigned int i; + + if (!array || !array_sz) + return 0; + + for (i = 0; i < array_sz; i++) + if (hfi_id == array[i].hfi_id) + return array[i].v4l2_id; + + return 0; +} + +static u32 find_hfi_id(u32 v4l2_id, const struct id_mapping *array, unsigned int array_sz) +{ + unsigned int i; + + if (!array || !array_sz) + return 0; + + for (i = 0; i < array_sz; i++) + if (v4l2_id == array[i].v4l2_id) + return array[i].hfi_id; + + return 0; +} + +static void +v4l2_id_profile_level(u32 hfi_codec, struct hfi_profile_level *pl, u32 *profile, u32 *level) +{ + u32 hfi_pf = pl->profile; + u32 hfi_lvl = pl->level; + + switch (hfi_codec) { + case HFI_VIDEO_CODEC_H264: + *profile = find_v4l2_id(hfi_pf, h264_profiles, ARRAY_SIZE(h264_profiles)); + *level = find_v4l2_id(hfi_lvl, h264_levels, ARRAY_SIZE(h264_levels)); + break; + case HFI_VIDEO_CODEC_MPEG2: + *profile = find_v4l2_id(hfi_pf, mpeg2_profiles, ARRAY_SIZE(mpeg2_profiles)); + *level = find_v4l2_id(hfi_lvl, mpeg2_levels, ARRAY_SIZE(mpeg2_levels)); + break; + case HFI_VIDEO_CODEC_MPEG4: + *profile = find_v4l2_id(hfi_pf, mpeg4_profiles, ARRAY_SIZE(mpeg4_profiles)); + *level = find_v4l2_id(hfi_lvl, mpeg4_levels, ARRAY_SIZE(mpeg4_levels)); + break; + case HFI_VIDEO_CODEC_VP8: + *profile = find_v4l2_id(hfi_pf, vp8_profiles, ARRAY_SIZE(vp8_profiles)); + *level = 0; + break; + case HFI_VIDEO_CODEC_VP9: + *profile = find_v4l2_id(hfi_pf, vp9_profiles, ARRAY_SIZE(vp9_profiles)); + *level = find_v4l2_id(hfi_lvl, vp9_levels, ARRAY_SIZE(vp9_levels)); + break; + case HFI_VIDEO_CODEC_HEVC: + *profile = find_v4l2_id(hfi_pf, hevc_profiles, ARRAY_SIZE(hevc_profiles)); + *level = find_v4l2_id(hfi_lvl, hevc_levels, ARRAY_SIZE(hevc_levels)); + break; + default: + break; + } +} + +static void +hfi_id_profile_level(u32 hfi_codec, u32 v4l2_pf, u32 v4l2_lvl, struct hfi_profile_level *pl) +{ + switch (hfi_codec) { + case HFI_VIDEO_CODEC_H264: + pl->profile = find_hfi_id(v4l2_pf, h264_profiles, ARRAY_SIZE(h264_profiles)); + pl->level = find_hfi_id(v4l2_lvl, h264_levels, ARRAY_SIZE(h264_levels)); + break; + case HFI_VIDEO_CODEC_MPEG2: + pl->profile = find_hfi_id(v4l2_pf, mpeg2_profiles, ARRAY_SIZE(mpeg2_profiles)); + pl->level = find_hfi_id(v4l2_lvl, mpeg2_levels, ARRAY_SIZE(mpeg2_levels)); + break; + case HFI_VIDEO_CODEC_MPEG4: + pl->profile = find_hfi_id(v4l2_pf, mpeg4_profiles, ARRAY_SIZE(mpeg4_profiles)); + pl->level = find_hfi_id(v4l2_lvl, mpeg4_levels, ARRAY_SIZE(mpeg4_levels)); + break; + case HFI_VIDEO_CODEC_VP8: + pl->profile = find_hfi_id(v4l2_pf, vp8_profiles, ARRAY_SIZE(vp8_profiles)); + pl->level = 0; + break; + case HFI_VIDEO_CODEC_VP9: + pl->profile = find_hfi_id(v4l2_pf, vp9_profiles, ARRAY_SIZE(vp9_profiles)); + pl->level = find_hfi_id(v4l2_lvl, vp9_levels, ARRAY_SIZE(vp9_levels)); + break; + case HFI_VIDEO_CODEC_HEVC: + pl->profile = find_hfi_id(v4l2_pf, hevc_profiles, ARRAY_SIZE(hevc_profiles)); + pl->level = find_hfi_id(v4l2_lvl, hevc_levels, ARRAY_SIZE(hevc_levels)); + break; + default: + break; + } +} + +int venus_helper_get_profile_level(struct venus_inst *inst, u32 *profile, u32 *level) +{ + const u32 ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT; + union hfi_get_property hprop; + int ret; + + ret = hfi_session_get_property(inst, ptype, &hprop); + if (ret) + return ret; + + v4l2_id_profile_level(inst->hfi_codec, &hprop.profile_level, profile, level); + + return 0; +} +EXPORT_SYMBOL_GPL(venus_helper_get_profile_level); + +int venus_helper_set_profile_level(struct venus_inst *inst, u32 profile, u32 level) +{ + const u32 ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT; + struct hfi_profile_level pl; + + hfi_id_profile_level(inst->hfi_codec, profile, level, &pl); + + return hfi_session_set_property(inst, ptype, &pl); +} +EXPORT_SYMBOL_GPL(venus_helper_set_profile_level); + static u32 get_framesize_raw_nv12(u32 width, u32 height) { u32 y_stride, uv_stride, y_plane; diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 8fbbda12a4fe..a4a0562bc83f 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -61,4 +61,6 @@ int venus_helper_process_initial_cap_bufs(struct venus_inst *inst); int venus_helper_process_initial_out_bufs(struct venus_inst *inst); void venus_helper_get_ts_metadata(struct venus_inst *inst, u64 timestamp_us, struct vb2_v4l2_buffer *vbuf); +int venus_helper_get_profile_level(struct venus_inst *inst, u32 *profile, u32 *level); +int venus_helper_set_profile_level(struct venus_inst *inst, u32 profile, u32 level); #endif diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c index a211eb93e0f9..a59022adb14c 100644 --- a/drivers/media/platform/qcom/venus/hfi.c +++ b/drivers/media/platform/qcom/venus/hfi.c @@ -517,3 +517,8 @@ void hfi_destroy(struct venus_core *core) { venus_hfi_destroy(core); } + +void hfi_reinit(struct venus_core *core) +{ + venus_hfi_queues_reinit(core); +} diff --git a/drivers/media/platform/qcom/venus/hfi.h b/drivers/media/platform/qcom/venus/hfi.h index 62c315291484..f25d412d6553 100644 --- a/drivers/media/platform/qcom/venus/hfi.h +++ b/drivers/media/platform/qcom/venus/hfi.h @@ -145,6 +145,7 @@ struct hfi_ops { int hfi_create(struct venus_core *core, const struct hfi_core_ops *ops); void hfi_destroy(struct venus_core *core); +void hfi_reinit(struct venus_core *core); int hfi_core_init(struct venus_core *core); int hfi_core_deinit(struct venus_core *core, bool blocking); diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index c67e412f8201..7022368c1e63 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -640,6 +640,7 @@ static int pkt_session_set_property_1x(struct hfi_session_set_property_pkt *pkt, case HFI_RATE_CONTROL_CBR_VFR: case HFI_RATE_CONTROL_VBR_CFR: case HFI_RATE_CONTROL_VBR_VFR: + case HFI_RATE_CONTROL_CQ: break; default: ret = -EINVAL; @@ -1218,6 +1219,37 @@ pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt, return 0; } +static int +pkt_session_set_property_6xx(struct hfi_session_set_property_pkt *pkt, + void *cookie, u32 ptype, void *pdata) +{ + void *prop_data; + + if (!pkt || !cookie || !pdata) + return -EINVAL; + + prop_data = &pkt->data[1]; + + pkt->shdr.hdr.size = sizeof(*pkt); + pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_PROPERTY; + pkt->shdr.session_id = hash32_ptr(cookie); + pkt->num_properties = 1; + pkt->data[0] = ptype; + + switch (ptype) { + case HFI_PROPERTY_CONFIG_HEIC_FRAME_QUALITY: { + struct hfi_heic_frame_quality *in = pdata, *cq = prop_data; + + cq->frame_quality = in->frame_quality; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*cq); + break; + } default: + return pkt_session_set_property_4xx(pkt, cookie, ptype, pdata); + } + + return 0; +} + int pkt_session_get_property(struct hfi_session_get_property_pkt *pkt, void *cookie, u32 ptype) { @@ -1236,7 +1268,10 @@ int pkt_session_set_property(struct hfi_session_set_property_pkt *pkt, if (hfi_ver == HFI_VERSION_3XX) return pkt_session_set_property_3xx(pkt, cookie, ptype, pdata); - return pkt_session_set_property_4xx(pkt, cookie, ptype, pdata); + if (hfi_ver == HFI_VERSION_4XX) + return pkt_session_set_property_4xx(pkt, cookie, ptype, pdata); + + return pkt_session_set_property_6xx(pkt, cookie, ptype, pdata); } void pkt_set_version(enum hfi_version version) diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index f6613df1d16b..60ee2479f7a6 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -231,6 +231,7 @@ #define HFI_RATE_CONTROL_VBR_CFR 0x1000003 #define HFI_RATE_CONTROL_CBR_VFR 0x1000004 #define HFI_RATE_CONTROL_CBR_CFR 0x1000005 +#define HFI_RATE_CONTROL_CQ 0x1000008 #define HFI_VIDEO_CODEC_H264 0x00000002 #define HFI_VIDEO_CODEC_H263 0x00000004 @@ -363,6 +364,24 @@ #define HFI_HEVC_TIER_MAIN 0x1 #define HFI_HEVC_TIER_HIGH0 0x2 +/* VP9 Profile 0, 8-bit */ +#define HFI_VP9_PROFILE_P0 0x00000001 +/* VP9 Profile 2, 10-bit */ +#define HFI_VP9_PROFILE_P2_10B 0x00000004 + +#define HFI_VP9_LEVEL_1 0x00000001 +#define HFI_VP9_LEVEL_11 0x00000002 +#define HFI_VP9_LEVEL_2 0x00000004 +#define HFI_VP9_LEVEL_21 0x00000008 +#define HFI_VP9_LEVEL_3 0x00000010 +#define HFI_VP9_LEVEL_31 0x00000020 +#define HFI_VP9_LEVEL_4 0x00000040 +#define HFI_VP9_LEVEL_41 0x00000080 +#define HFI_VP9_LEVEL_5 0x00000100 +#define HFI_VP9_LEVEL_51 0x00000200 +#define HFI_VP9_LEVEL_6 0x00000400 +#define HFI_VP9_LEVEL_61 0x00000800 + #define HFI_BUFFER_INPUT 0x1 #define HFI_BUFFER_OUTPUT 0x2 #define HFI_BUFFER_OUTPUT2 0x3 @@ -504,6 +523,7 @@ #define HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER 0x200600b #define HFI_PROPERTY_CONFIG_VENC_LTRPERIOD 0x200600c #define HFI_PROPERTY_CONFIG_VENC_PERF_MODE 0x200600e +#define HFI_PROPERTY_CONFIG_HEIC_FRAME_QUALITY 0x2006014 /* * HFI_PROPERTY_PARAM_VPE_COMMON_START @@ -520,7 +540,8 @@ enum hfi_version { HFI_VERSION_1XX, HFI_VERSION_3XX, - HFI_VERSION_4XX + HFI_VERSION_4XX, + HFI_VERSION_6XX, }; struct hfi_buffer_info { @@ -725,6 +746,11 @@ struct hfi_quality_vs_speed { u32 quality_vs_speed; }; +struct hfi_heic_frame_quality { + u32 frame_quality; + u32 reserved[3]; +}; + struct hfi_quantization { u32 qp_i; u32 qp_p; diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c index 279a9d6fe737..06a1908ca225 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.c +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -138,7 +138,7 @@ static void event_sys_error(struct venus_core *core, u32 event, struct hfi_msg_event_notify_pkt *pkt) { if (pkt) - dev_dbg(core->dev, + dev_dbg(core->dev, VDBGH "sys error (session id:%x, data1:%x, data2:%x)\n", pkt->shdr.session_id, pkt->event_data1, pkt->event_data2); @@ -152,7 +152,7 @@ event_session_error(struct venus_core *core, struct venus_inst *inst, { struct device *dev = core->dev; - dev_dbg(dev, "session error: event id:%x, session id:%x\n", + dev_dbg(dev, VDBGH "session error: event id:%x, session id:%x\n", pkt->event_data1, pkt->shdr.session_id); if (!inst) @@ -247,7 +247,7 @@ sys_get_prop_image_version(struct device *dev, /* bad packet */ return; - dev_dbg(dev, "F/W version: %s\n", (u8 *)&pkt->data[1]); + dev_dbg(dev, VDBGL "F/W version: %s\n", (u8 *)&pkt->data[1]); } static void hfi_sys_property_info(struct venus_core *core, @@ -257,7 +257,7 @@ static void hfi_sys_property_info(struct venus_core *core, struct device *dev = core->dev; if (!pkt->num_properties) { - dev_dbg(dev, "%s: no properties\n", __func__); + dev_dbg(dev, VDBGL "no properties\n"); return; } @@ -266,7 +266,7 @@ static void hfi_sys_property_info(struct venus_core *core, sys_get_prop_image_version(dev, pkt); break; default: - dev_dbg(dev, "%s: unknown property data\n", __func__); + dev_dbg(dev, VDBGL "unknown property data\n"); break; } } @@ -297,7 +297,7 @@ static void hfi_sys_ping_done(struct venus_core *core, struct venus_inst *inst, static void hfi_sys_idle_done(struct venus_core *core, struct venus_inst *inst, void *packet) { - dev_dbg(core->dev, "sys idle\n"); + dev_dbg(core->dev, VDBGL "sys idle\n"); } static void hfi_sys_pc_prepare_done(struct venus_core *core, @@ -305,7 +305,8 @@ static void hfi_sys_pc_prepare_done(struct venus_core *core, { struct hfi_msg_sys_pc_prep_done_pkt *pkt = packet; - dev_dbg(core->dev, "pc prepare done (error %x)\n", pkt->error_type); + dev_dbg(core->dev, VDBGL "pc prepare done (error %x)\n", + pkt->error_type); } static unsigned int @@ -387,8 +388,7 @@ static void hfi_session_prop_info(struct venus_core *core, case HFI_PROPERTY_CONFIG_VDEC_ENTROPY: break; default: - dev_dbg(dev, "%s: unknown property id:%x\n", __func__, - pkt->data[0]); + dev_dbg(dev, VDBGM "unknown property id:%x\n", pkt->data[0]); return; } diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 7f515a4b9bd1..363ee2a65453 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -239,6 +239,9 @@ u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf, parser_init(inst, &codecs, &domain); + core->codecs_count = 0; + memset(core->caps, 0, sizeof(core->caps)); + while (words_count) { data = word + 1; diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index 0d8855014ab3..4be4a75ddcb6 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -130,7 +130,7 @@ struct venus_hfi_device { }; static bool venus_pkt_debug; -static int venus_fw_debug = HFI_DEBUG_MSG_ERROR | HFI_DEBUG_MSG_FATAL; +int venus_fw_debug = HFI_DEBUG_MSG_ERROR | HFI_DEBUG_MSG_FATAL; static bool venus_sys_idle_indicator; static bool venus_fw_low_power_mode = true; static int venus_hw_rsp_timeout = 1000; @@ -477,7 +477,7 @@ static u32 venus_hwversion(struct venus_hfi_device *hdev) minor = minor >> WRAPPER_HW_VERSION_MINOR_VERSION_SHIFT; step = ver & WRAPPER_HW_VERSION_STEP_VERSION_MASK; - dev_dbg(dev, "venus hw version %x.%x.%x\n", major, minor, step); + dev_dbg(dev, VDBGL "venus hw version %x.%x.%x\n", major, minor, step); return major; } @@ -906,7 +906,7 @@ static void venus_flush_debug_queue(struct venus_hfi_device *hdev) if (pkt->hdr.pkt_type != HFI_MSG_SYS_COV) { struct hfi_msg_sys_debug_pkt *pkt = packet; - dev_dbg(dev, "%s", pkt->msg_data); + dev_dbg(dev, VDBGFW "%s", pkt->msg_data); } } } @@ -986,13 +986,6 @@ static void venus_process_msg_sys_error(struct venus_hfi_device *hdev, venus_set_state(hdev, VENUS_STATE_DEINIT); - /* - * Once SYS_ERROR received from HW, it is safe to halt the AXI. - * With SYS_ERROR, Venus FW may have crashed and HW might be - * active and causing unnecessary transactions. Hence it is - * safe to stop all AXI transactions from venus subsystem. - */ - venus_halt_axi(hdev); venus_sfr_print(hdev); } @@ -1009,10 +1002,6 @@ static irqreturn_t venus_isr_thread(struct venus_core *core) res = hdev->core->res; pkt = hdev->pkt_buf; - if (hdev->irq_status & WRAPPER_INTR_STATUS_A2HWD_MASK) { - venus_sfr_print(hdev); - hfi_process_watchdog_timeout(core); - } while (!venus_iface_msgq_read(hdev, pkt)) { msg_ret = hfi_process_msg_packet(core, pkt); @@ -1133,6 +1122,10 @@ static int venus_session_init(struct venus_inst *inst, u32 session_type, struct hfi_session_init_pkt pkt; int ret; + ret = venus_sys_set_debug(hdev, venus_fw_debug); + if (ret) + goto err; + ret = pkt_session_init(&pkt, inst, session_type, codec); if (ret) goto err; @@ -1614,3 +1607,54 @@ err_kfree: core->ops = NULL; return ret; } + +void venus_hfi_queues_reinit(struct venus_core *core) +{ + struct venus_hfi_device *hdev = to_hfi_priv(core); + struct hfi_queue_table_header *tbl_hdr; + struct iface_queue *queue; + struct hfi_sfr *sfr; + unsigned int i; + + mutex_lock(&hdev->lock); + + for (i = 0; i < IFACEQ_NUM; i++) { + queue = &hdev->queues[i]; + queue->qhdr = + IFACEQ_GET_QHDR_START_ADDR(hdev->ifaceq_table.kva, i); + + venus_set_qhdr_defaults(queue->qhdr); + + queue->qhdr->start_addr = queue->qmem.da; + + if (i == IFACEQ_CMD_IDX) + queue->qhdr->type |= HFI_HOST_TO_CTRL_CMD_Q; + else if (i == IFACEQ_MSG_IDX) + queue->qhdr->type |= HFI_CTRL_TO_HOST_MSG_Q; + else if (i == IFACEQ_DBG_IDX) + queue->qhdr->type |= HFI_CTRL_TO_HOST_DBG_Q; + } + + tbl_hdr = hdev->ifaceq_table.kva; + tbl_hdr->version = 0; + tbl_hdr->size = IFACEQ_TABLE_SIZE; + tbl_hdr->qhdr0_offset = sizeof(struct hfi_queue_table_header); + tbl_hdr->qhdr_size = sizeof(struct hfi_queue_header); + tbl_hdr->num_q = IFACEQ_NUM; + tbl_hdr->num_active_q = IFACEQ_NUM; + + /* + * Set receive request to zero on debug queue as there is no + * need of interrupt from video hardware for debug messages + */ + queue = &hdev->queues[IFACEQ_DBG_IDX]; + queue->qhdr->rx_req = 0; + + sfr = hdev->sfr.kva; + sfr->buf_size = ALIGNED_SFR_SIZE; + + /* ensure table and queue header structs are settled in memory */ + wmb(); + + mutex_unlock(&hdev->lock); +} diff --git a/drivers/media/platform/qcom/venus/hfi_venus.h b/drivers/media/platform/qcom/venus/hfi_venus.h index 57154832090e..1b656ef2bf07 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.h +++ b/drivers/media/platform/qcom/venus/hfi_venus.h @@ -10,5 +10,6 @@ struct venus_core; void venus_hfi_destroy(struct venus_core *core); int venus_hfi_create(struct venus_core *core); +void venus_hfi_queues_reinit(struct venus_core *core); #endif diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index 531e7a41658f..57877eacecf0 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -9,6 +9,7 @@ #include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/pm_domain.h> +#include <linux/pm_opp.h> #include <linux/pm_runtime.h> #include <linux/types.h> #include <media/v4l2-mem2mem.h> @@ -66,10 +67,9 @@ static void core_clks_disable(struct venus_core *core) static int core_clks_set_rate(struct venus_core *core, unsigned long freq) { - struct clk *clk = core->clks[0]; int ret; - ret = clk_set_rate(clk, freq); + ret = dev_pm_opp_set_rate(core->dev, freq); if (ret) return ret; @@ -212,7 +212,7 @@ static int load_scale_bw(struct venus_core *core) } mutex_unlock(&core->lock); - dev_dbg(core->dev, "total: avg_bw: %u, peak_bw: %u\n", + dev_dbg(core->dev, VDBGL "total: avg_bw: %u, peak_bw: %u\n", total_avg, total_peak); return icc_set_bw(core->video_path, total_avg, total_peak); @@ -744,13 +744,16 @@ static int venc_power_v4(struct device *dev, int on) static int vcodec_domains_get(struct device *dev) { + int ret; + struct opp_table *opp_table; + struct device **opp_virt_dev; struct venus_core *core = dev_get_drvdata(dev); const struct venus_resources *res = core->res; struct device *pd; unsigned int i; if (!res->vcodec_pmdomains_num) - return -ENODEV; + goto skip_pmdomains; for (i = 0; i < res->vcodec_pmdomains_num; i++) { pd = dev_pm_domain_attach_by_name(dev, @@ -767,7 +770,41 @@ static int vcodec_domains_get(struct device *dev) if (!core->pd_dl_venus) return -ENODEV; +skip_pmdomains: + if (!core->has_opp_table) + return 0; + + /* Attach the power domain for setting performance state */ + opp_table = dev_pm_opp_attach_genpd(dev, res->opp_pmdomain, &opp_virt_dev); + if (IS_ERR(opp_table)) { + ret = PTR_ERR(opp_table); + goto opp_attach_err; + } + + core->opp_pmdomain = *opp_virt_dev; + core->opp_dl_venus = device_link_add(dev, core->opp_pmdomain, + DL_FLAG_RPM_ACTIVE | + DL_FLAG_PM_RUNTIME | + DL_FLAG_STATELESS); + if (!core->opp_dl_venus) { + ret = -ENODEV; + goto opp_dl_add_err; + } + return 0; + +opp_dl_add_err: + dev_pm_domain_detach(core->opp_pmdomain, true); +opp_attach_err: + if (core->pd_dl_venus) { + device_link_del(core->pd_dl_venus); + for (i = 0; i < res->vcodec_pmdomains_num; i++) { + if (IS_ERR_OR_NULL(core->pmdomains[i])) + continue; + dev_pm_domain_detach(core->pmdomains[i], true); + } + } + return ret; } static void vcodec_domains_put(struct device *dev) @@ -777,7 +814,7 @@ static void vcodec_domains_put(struct device *dev) unsigned int i; if (!res->vcodec_pmdomains_num) - return; + goto skip_pmdomains; if (core->pd_dl_venus) device_link_del(core->pd_dl_venus); @@ -787,6 +824,15 @@ static void vcodec_domains_put(struct device *dev) continue; dev_pm_domain_detach(core->pmdomains[i], true); } + +skip_pmdomains: + if (!core->has_opp_table) + return; + + if (core->opp_dl_venus) + device_link_del(core->opp_dl_venus); + + dev_pm_domain_detach(core->opp_pmdomain, true); } static int core_get_v4(struct device *dev) @@ -815,19 +861,46 @@ static int core_get_v4(struct device *dev) if (legacy_binding) return 0; + core->opp_table = dev_pm_opp_set_clkname(dev, "core"); + if (IS_ERR(core->opp_table)) + return PTR_ERR(core->opp_table); + + if (core->res->opp_pmdomain) { + ret = dev_pm_opp_of_add_table(dev); + if (!ret) { + core->has_opp_table = true; + } else if (ret != -ENODEV) { + dev_err(dev, "invalid OPP table in device tree\n"); + dev_pm_opp_put_clkname(core->opp_table); + return ret; + } + } + ret = vcodec_domains_get(dev); - if (ret) + if (ret) { + if (core->has_opp_table) + dev_pm_opp_of_remove_table(dev); + dev_pm_opp_put_clkname(core->opp_table); return ret; + } return 0; } static void core_put_v4(struct device *dev) { + struct venus_core *core = dev_get_drvdata(dev); + if (legacy_binding) return; vcodec_domains_put(dev); + + if (core->has_opp_table) + dev_pm_opp_of_remove_table(dev); + if (core->opp_table) + dev_pm_opp_put_clkname(core->opp_table); + } static int core_power_v4(struct device *dev, int on) @@ -835,10 +908,15 @@ static int core_power_v4(struct device *dev, int on) struct venus_core *core = dev_get_drvdata(dev); int ret = 0; - if (on == POWER_ON) + if (on == POWER_ON) { ret = core_clks_enable(core); - else + } else { + /* Drop the performance state vote */ + if (core->opp_pmdomain) + dev_pm_opp_set_rate(dev, 0); + core_clks_disable(core); + } return ret; } diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 7c4c483d5438..ea13170a6a2c 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -225,7 +225,7 @@ static int vdec_check_src_change(struct venus_inst *inst) if (!(inst->codec_state == VENUS_DEC_STATE_CAPTURE_SETUP) || !inst->reconfig) - dev_dbg(inst->core->dev, "%s: wrong state\n", __func__); + dev_dbg(inst->core->dev, VDBGH "wrong state\n"); done: return 0; @@ -1072,7 +1072,7 @@ static int vdec_stop_capture(struct venus_inst *inst) switch (inst->codec_state) { case VENUS_DEC_STATE_DECODING: ret = hfi_session_flush(inst, HFI_FLUSH_ALL, true); - /* fallthrough */ + fallthrough; case VENUS_DEC_STATE_DRAIN: vdec_cancel_dst_buffers(inst); inst->codec_state = VENUS_DEC_STATE_STOPPED; @@ -1088,8 +1088,6 @@ static int vdec_stop_capture(struct venus_inst *inst) break; } - INIT_LIST_HEAD(&inst->registeredbufs); - return ret; } @@ -1189,6 +1187,14 @@ static int vdec_buf_init(struct vb2_buffer *vb) static void vdec_buf_cleanup(struct vb2_buffer *vb) { struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct venus_buffer *buf = to_venus_buffer(vbuf); + + mutex_lock(&inst->lock); + if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + if (!list_empty(&inst->registeredbufs)) + list_del_init(&buf->reg_list); + mutex_unlock(&inst->lock); inst->buf_count--; if (!inst->buf_count) @@ -1310,7 +1316,7 @@ static void vdec_event_change(struct venus_inst *inst, if (inst->bit_depth != ev_data->bit_depth) inst->bit_depth = ev_data->bit_depth; - dev_dbg(dev, "event %s sufficient resources (%ux%u)\n", + dev_dbg(dev, VDBGM "event %s sufficient resources (%ux%u)\n", sufficient ? "" : "not", ev_data->width, ev_data->height); if (sufficient) { @@ -1344,7 +1350,7 @@ static void vdec_event_change(struct venus_inst *inst, ret = hfi_session_flush(inst, HFI_FLUSH_OUTPUT, false); if (ret) - dev_dbg(dev, "flush output error %d\n", ret); + dev_dbg(dev, VDBGH "flush output error %d\n", ret); } inst->reconfig = true; @@ -1453,13 +1459,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->allow_zero_bytesused = 1; dst_vq->min_buffers_needed = 0; dst_vq->dev = inst->core->dev; - ret = vb2_queue_init(dst_vq); - if (ret) { - vb2_queue_release(src_vq); - return ret; - } - - return 0; + return vb2_queue_init(dst_vq); } static int vdec_open(struct file *file) diff --git a/drivers/media/platform/qcom/venus/vdec_ctrls.c b/drivers/media/platform/qcom/venus/vdec_ctrls.c index 3a963cbd342a..974110b75b93 100644 --- a/drivers/media/platform/qcom/venus/vdec_ctrls.c +++ b/drivers/media/platform/qcom/venus/vdec_ctrls.c @@ -22,10 +22,12 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_PROFILE: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: ctr->profile = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_H264_LEVEL: case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: + case V4L2_CID_MPEG_VIDEO_VP9_LEVEL: ctr->level = ctrl->val; break; default: @@ -40,25 +42,26 @@ static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) struct venus_inst *inst = ctrl_to_inst(ctrl); struct vdec_controls *ctr = &inst->controls.dec; struct hfi_buffer_requirements bufreq; - union hfi_get_property hprop; enum hfi_version ver = inst->core->res->hfi_version; - u32 ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT; + u32 profile, level; int ret; switch (ctrl->id) { case V4L2_CID_MPEG_VIDEO_H264_PROFILE: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: - ret = hfi_session_get_property(inst, ptype, &hprop); + case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: + ret = venus_helper_get_profile_level(inst, &profile, &level); if (!ret) - ctr->profile = hprop.profile_level.profile; + ctr->profile = profile; ctrl->val = ctr->profile; break; case V4L2_CID_MPEG_VIDEO_H264_LEVEL: case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: - ret = hfi_session_get_property(inst, ptype, &hprop); + case V4L2_CID_MPEG_VIDEO_VP9_LEVEL: + ret = venus_helper_get_profile_level(inst, &profile, &level); if (!ret) - ctr->level = hprop.profile_level.level; + ctr->level = level; ctrl->val = ctr->level; break; case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: @@ -86,7 +89,7 @@ int vdec_ctrl_init(struct venus_inst *inst) struct v4l2_ctrl *ctrl; int ret; - ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 7); + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 9); if (ret) return ret; @@ -133,6 +136,20 @@ int vdec_ctrl_init(struct venus_inst *inst) if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VP9_PROFILE, + V4L2_MPEG_VIDEO_VP9_PROFILE_3, + 0, V4L2_MPEG_VIDEO_VP9_PROFILE_0); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + + ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VP9_LEVEL, + V4L2_MPEG_VIDEO_VP9_LEVEL_6_2, + 0, V4L2_MPEG_VIDEO_VP9_LEVEL_1_0); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops, V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER, 0, 1, 1, 0); diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 513bbc07f7bc..f8b1484e7dcd 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -113,80 +113,6 @@ find_format_by_index(struct venus_inst *inst, unsigned int index, u32 type) static int venc_v4l2_to_hfi(int id, int value) { switch (id) { - case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: - switch (value) { - case V4L2_MPEG_VIDEO_MPEG4_LEVEL_0: - default: - return HFI_MPEG4_LEVEL_0; - case V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B: - return HFI_MPEG4_LEVEL_0b; - case V4L2_MPEG_VIDEO_MPEG4_LEVEL_1: - return HFI_MPEG4_LEVEL_1; - case V4L2_MPEG_VIDEO_MPEG4_LEVEL_2: - return HFI_MPEG4_LEVEL_2; - case V4L2_MPEG_VIDEO_MPEG4_LEVEL_3: - return HFI_MPEG4_LEVEL_3; - case V4L2_MPEG_VIDEO_MPEG4_LEVEL_4: - return HFI_MPEG4_LEVEL_4; - case V4L2_MPEG_VIDEO_MPEG4_LEVEL_5: - return HFI_MPEG4_LEVEL_5; - } - case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: - switch (value) { - case V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE: - default: - return HFI_MPEG4_PROFILE_SIMPLE; - case V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE: - return HFI_MPEG4_PROFILE_ADVANCEDSIMPLE; - } - case V4L2_CID_MPEG_VIDEO_H264_PROFILE: - switch (value) { - case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: - return HFI_H264_PROFILE_BASELINE; - case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: - return HFI_H264_PROFILE_CONSTRAINED_BASE; - case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: - return HFI_H264_PROFILE_MAIN; - case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: - default: - return HFI_H264_PROFILE_HIGH; - } - case V4L2_CID_MPEG_VIDEO_H264_LEVEL: - switch (value) { - case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: - return HFI_H264_LEVEL_1; - case V4L2_MPEG_VIDEO_H264_LEVEL_1B: - return HFI_H264_LEVEL_1b; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: - return HFI_H264_LEVEL_11; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: - return HFI_H264_LEVEL_12; - case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: - return HFI_H264_LEVEL_13; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: - return HFI_H264_LEVEL_2; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: - return HFI_H264_LEVEL_21; - case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: - return HFI_H264_LEVEL_22; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: - return HFI_H264_LEVEL_3; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: - return HFI_H264_LEVEL_31; - case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: - return HFI_H264_LEVEL_32; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: - return HFI_H264_LEVEL_4; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: - return HFI_H264_LEVEL_41; - case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: - return HFI_H264_LEVEL_42; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: - default: - return HFI_H264_LEVEL_5; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: - return HFI_H264_LEVEL_51; - } case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: switch (value) { case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC: @@ -195,18 +121,6 @@ static int venc_v4l2_to_hfi(int id, int value) case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC: return HFI_H264_ENTROPY_CABAC; } - case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: - switch (value) { - case 0: - default: - return HFI_VPX_PROFILE_VERSION_0; - case 1: - return HFI_VPX_PROFILE_VERSION_1; - case 2: - return HFI_VPX_PROFILE_VERSION_2; - case 3: - return HFI_VPX_PROFILE_VERSION_3; - } case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: switch (value) { case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED: @@ -217,46 +131,6 @@ static int venc_v4l2_to_hfi(int id, int value) case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY: return HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY; } - case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: - switch (value) { - case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN: - default: - return HFI_HEVC_PROFILE_MAIN; - case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE: - return HFI_HEVC_PROFILE_MAIN_STILL_PIC; - case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10: - return HFI_HEVC_PROFILE_MAIN10; - } - case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: - switch (value) { - case V4L2_MPEG_VIDEO_HEVC_LEVEL_1: - default: - return HFI_HEVC_LEVEL_1; - case V4L2_MPEG_VIDEO_HEVC_LEVEL_2: - return HFI_HEVC_LEVEL_2; - case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1: - return HFI_HEVC_LEVEL_21; - case V4L2_MPEG_VIDEO_HEVC_LEVEL_3: - return HFI_HEVC_LEVEL_3; - case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1: - return HFI_HEVC_LEVEL_31; - case V4L2_MPEG_VIDEO_HEVC_LEVEL_4: - return HFI_HEVC_LEVEL_4; - case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1: - return HFI_HEVC_LEVEL_41; - case V4L2_MPEG_VIDEO_HEVC_LEVEL_5: - return HFI_HEVC_LEVEL_5; - case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1: - return HFI_HEVC_LEVEL_51; - case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2: - return HFI_HEVC_LEVEL_52; - case V4L2_MPEG_VIDEO_HEVC_LEVEL_6: - return HFI_HEVC_LEVEL_6; - case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1: - return HFI_HEVC_LEVEL_61; - case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2: - return HFI_HEVC_LEVEL_62; - } } return 0; @@ -584,6 +458,7 @@ static int venc_enum_frameintervals(struct file *file, void *fh, { struct venus_inst *inst = to_inst(file); const struct venus_format *fmt; + unsigned int framerate_factor = 1; fival->type = V4L2_FRMIVAL_TYPE_STEPWISE; @@ -608,12 +483,17 @@ static int venc_enum_frameintervals(struct file *file, void *fh, fival->height < frame_height_min(inst)) return -EINVAL; + if (IS_V1(inst->core)) { + /* framerate is reported in 1/65535 fps unit */ + framerate_factor = (1 << 16); + } + fival->stepwise.min.numerator = 1; - fival->stepwise.min.denominator = frate_max(inst); + fival->stepwise.min.denominator = frate_max(inst) / framerate_factor; fival->stepwise.max.numerator = 1; - fival->stepwise.max.denominator = frate_min(inst); + fival->stepwise.max.denominator = frate_min(inst) / framerate_factor; fival->stepwise.step.numerator = 1; - fival->stepwise.step.denominator = frate_max(inst); + fival->stepwise.step.denominator = frate_max(inst) / framerate_factor; return 0; } @@ -651,13 +531,12 @@ static int venc_set_properties(struct venus_inst *inst) { struct venc_controls *ctr = &inst->controls.enc; struct hfi_intra_period intra_period; - struct hfi_profile_level pl; struct hfi_framerate frate; struct hfi_bitrate brate; struct hfi_idr_period idrp; struct hfi_quantization quant; struct hfi_quantization_range quant_range; - u32 ptype, rate_control, bitrate, profile = 0, level = 0; + u32 ptype, rate_control, bitrate; int ret; ret = venus_helper_set_work_mode(inst, VIDC_WORK_MODE_2); @@ -739,15 +618,29 @@ static int venc_set_properties(struct venus_inst *inst) if (!ctr->rc_enable) rate_control = HFI_RATE_CONTROL_OFF; else if (ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) - rate_control = HFI_RATE_CONTROL_VBR_CFR; - else - rate_control = HFI_RATE_CONTROL_CBR_CFR; + rate_control = ctr->frame_skip_mode ? HFI_RATE_CONTROL_VBR_VFR : + HFI_RATE_CONTROL_VBR_CFR; + else if (ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) + rate_control = ctr->frame_skip_mode ? HFI_RATE_CONTROL_CBR_VFR : + HFI_RATE_CONTROL_CBR_CFR; + else if (ctr->bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CQ) + rate_control = HFI_RATE_CONTROL_CQ; ptype = HFI_PROPERTY_PARAM_VENC_RATE_CONTROL; ret = hfi_session_set_property(inst, ptype, &rate_control); if (ret) return ret; + if (rate_control == HFI_RATE_CONTROL_CQ && ctr->const_quality) { + struct hfi_heic_frame_quality quality = {}; + + ptype = HFI_PROPERTY_CONFIG_HEIC_FRAME_QUALITY; + quality.frame_quality = ctr->const_quality; + ret = hfi_session_set_property(inst, ptype, &quality); + if (ret) + return ret; + } + if (!ctr->bitrate) bitrate = 64000; else @@ -791,35 +684,7 @@ static int venc_set_properties(struct venus_inst *inst) if (ret) return ret; - if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264) { - profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_H264_PROFILE, - ctr->profile.h264); - level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_H264_LEVEL, - ctr->level.h264); - } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_VP8) { - profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_VP8_PROFILE, - ctr->profile.vpx); - level = 0; - } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_MPEG4) { - profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, - ctr->profile.mpeg4); - level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, - ctr->level.mpeg4); - } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H263) { - profile = 0; - level = 0; - } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { - profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, - ctr->profile.hevc); - level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, - ctr->level.hevc); - } - - ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT; - pl.profile = profile; - pl.level = level; - - ret = hfi_session_set_property(inst, ptype, &pl); + ret = venus_helper_set_profile_level(inst, ctr->profile, ctr->level); if (ret) return ret; @@ -1129,13 +994,7 @@ static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->allow_zero_bytesused = 1; dst_vq->min_buffers_needed = 1; dst_vq->dev = inst->core->dev; - ret = vb2_queue_init(dst_vq); - if (ret) { - vb2_queue_release(src_vq); - return ret; - } - - return 0; + return vb2_queue_init(dst_vq); } static void venc_inst_init(struct venus_inst *inst) diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c index 8362dde7949e..0708b3b89d0c 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -103,25 +103,15 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) ctr->h264_entropy_mode = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: - ctr->profile.mpeg4 = ctrl->val; - break; case V4L2_CID_MPEG_VIDEO_H264_PROFILE: - ctr->profile.h264 = ctrl->val; - break; case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: - ctr->profile.hevc = ctrl->val; - break; case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: - ctr->profile.vpx = ctrl->val; + ctr->profile = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: - ctr->level.mpeg4 = ctrl->val; - break; case V4L2_CID_MPEG_VIDEO_H264_LEVEL: - ctr->level.h264 = ctrl->val; - break; case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: - ctr->level.hevc = ctrl->val; + ctr->level = ctrl->val; break; case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: ctr->h264_i_qp = ctrl->val; @@ -202,6 +192,12 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: ctr->rc_enable = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_CONSTANT_QUALITY: + ctr->const_quality = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE: + ctr->frame_skip_mode = ctrl->val; + break; default: return -EINVAL; } @@ -217,7 +213,7 @@ int venc_ctrl_init(struct venus_inst *inst) { int ret; - ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 31); + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 33); if (ret) return ret; @@ -225,7 +221,8 @@ int venc_ctrl_init(struct venus_inst *inst) V4L2_CID_MPEG_VIDEO_BITRATE_MODE, V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, ~((1 << V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) | - (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)), + (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) | + (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CQ)), V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, @@ -357,6 +354,16 @@ int venc_ctrl_init(struct venus_inst *inst) v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, 0, 1, 1, 1); + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_CONSTANT_QUALITY, 0, 100, 1, 0); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE, + V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT, + ~((1 << V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED) | + (1 << V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT)), + V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED); + ret = inst->ctrl_handler.error; if (ret) goto err; diff --git a/drivers/media/platform/rcar-fcp.c b/drivers/media/platform/rcar-fcp.c index 5c6b00737fe7..5c03318ae07b 100644 --- a/drivers/media/platform/rcar-fcp.c +++ b/drivers/media/platform/rcar-fcp.c @@ -22,7 +22,6 @@ struct rcar_fcp_device { struct list_head list; struct device *dev; - struct device_dma_parameters dma_parms; }; static LIST_HEAD(fcp_devices); @@ -103,8 +102,10 @@ int rcar_fcp_enable(struct rcar_fcp_device *fcp) return 0; ret = pm_runtime_get_sync(fcp->dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_noidle(fcp->dev); return ret; + } return 0; } @@ -138,8 +139,7 @@ static int rcar_fcp_probe(struct platform_device *pdev) fcp->dev = &pdev->dev; - fcp->dev->dma_parms = &fcp->dma_parms; - dma_set_max_seg_size(fcp->dev, DMA_BIT_MASK(32)); + dma_set_max_seg_size(fcp->dev, UINT_MAX); pm_runtime_enable(&pdev->dev); diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig index ca0d906dce2f..030312d862e7 100644 --- a/drivers/media/platform/rcar-vin/Kconfig +++ b/drivers/media/platform/rcar-vin/Kconfig @@ -9,7 +9,7 @@ config VIDEO_RCAR_CSI2 select V4L2_FWNODE help Support for Renesas R-Car MIPI CSI-2 receiver. - Supports R-Car Gen3 SoCs. + Supports R-Car Gen3 and RZ/G2 SoCs. To compile this driver as a module, choose M here: the module will be called rcar-csi2. @@ -24,7 +24,7 @@ config VIDEO_RCAR_VIN select V4L2_FWNODE help Support for Renesas R-Car Video Input (VIN) driver. - Supports R-Car Gen2 and Gen3 SoCs. + Supports R-Car Gen{2,3} and RZ/G{1,2} SoCs. To compile this driver as a module, choose M here: the module will be called rcar-vin. diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 7440c8965d27..34d003e0e9b9 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -243,7 +243,6 @@ static struct rvin_group *rvin_group_data; static void rvin_group_cleanup(struct rvin_group *group) { - media_device_unregister(&group->mdev); media_device_cleanup(&group->mdev); mutex_destroy(&group->lock); } @@ -253,7 +252,6 @@ static int rvin_group_init(struct rvin_group *group, struct rvin_dev *vin) struct media_device *mdev = &group->mdev; const struct of_device_id *match; struct device_node *np; - int ret; mutex_init(&group->lock); @@ -278,11 +276,7 @@ static int rvin_group_init(struct rvin_group *group, struct rvin_dev *vin) media_device_init(mdev); - ret = media_device_register(&group->mdev); - if (ret) - rvin_group_cleanup(group); - - return ret; + return 0; } static void rvin_group_release(struct kref *kref) @@ -626,12 +620,11 @@ static int rvin_parallel_parse_v4l2(struct device *dev, switch (vin->parallel->mbus_type) { case V4L2_MBUS_PARALLEL: - vin_dbg(vin, "Found PARALLEL media bus\n"); - vin->parallel->mbus_flags = vep->bus.parallel.flags; - break; case V4L2_MBUS_BT656: - vin_dbg(vin, "Found BT656 media bus\n"); - vin->parallel->mbus_flags = 0; + vin_dbg(vin, "Found %s media bus\n", + vin->parallel->mbus_type == V4L2_MBUS_PARALLEL ? + "PARALLEL" : "BT656"); + vin->parallel->bus = vep->bus.parallel; break; default: vin_err(vin, "Unknown media bus type\n"); @@ -682,6 +675,10 @@ static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) unsigned int i; int ret; + ret = media_device_register(&vin->group->mdev); + if (ret) + return ret; + ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); if (ret) { vin_err(vin, "Failed to register subdev nodes\n"); @@ -762,6 +759,8 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, } mutex_unlock(&vin->group->lock); + + media_device_unregister(&vin->group->mdev); } static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, @@ -944,6 +943,42 @@ static const struct rvin_info rcar_info_gen2 = { .max_height = 2048, }; +static const struct rvin_group_route rcar_info_r8a774e1_routes[] = { + { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) }, + { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | BIT(2) }, + { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) }, + { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) }, + { /* Sentinel */ } +}; + +static const struct rvin_info rcar_info_r8a774e1 = { + .model = RCAR_GEN3, + .use_mc = true, + .max_width = 4096, + .max_height = 4096, + .routes = rcar_info_r8a774e1_routes, +}; + static const struct rvin_group_route rcar_info_r8a7795_routes[] = { { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) }, @@ -1221,6 +1256,10 @@ static const struct of_device_id rvin_of_id_table[] = { .data = &rcar_info_r8a77990, }, { + .compatible = "renesas,vin-r8a774e1", + .data = &rcar_info_r8a774e1, + }, + { .compatible = "renesas,vin-r8a7778", .data = &rcar_info_m1, }, @@ -1370,12 +1409,8 @@ static int rcar_vin_remove(struct platform_device *pdev) v4l2_async_notifier_cleanup(&vin->notifier); if (vin->info->use_mc) { - mutex_lock(&vin->group->lock); - if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) { - v4l2_async_notifier_unregister(&vin->group->notifier); - v4l2_async_notifier_cleanup(&vin->group->notifier); - } - mutex_unlock(&vin->group->lock); + v4l2_async_notifier_unregister(&vin->group->notifier); + v4l2_async_notifier_cleanup(&vin->group->notifier); rvin_group_put(vin); } diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index c6cc4f473a07..79f229756805 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -320,6 +320,9 @@ static const struct rcar_csi2_format rcar_csi2_formats[] = { { .code = MEDIA_BUS_FMT_YUYV8_1X16, .datatype = 0x1e, .bpp = 16 }, { .code = MEDIA_BUS_FMT_UYVY8_2X8, .datatype = 0x1e, .bpp = 16 }, { .code = MEDIA_BUS_FMT_YUYV10_2X10, .datatype = 0x1e, .bpp = 20 }, + { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .datatype = 0x2a, .bpp = 8 }, + { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .datatype = 0x2a, .bpp = 8 }, + { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .datatype = 0x2a, .bpp = 8 }, { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .datatype = 0x2a, .bpp = 8 }, }; @@ -362,8 +365,8 @@ struct rcar_csi2 { struct media_pad pads[NR_OF_RCAR_CSI2_PAD]; struct v4l2_async_notifier notifier; - struct v4l2_async_subdev asd; struct v4l2_subdev *remote; + unsigned int remote_pad; struct v4l2_mbus_framefmt mf; @@ -409,13 +412,14 @@ static void rcsi2_exit_standby(struct rcar_csi2 *priv) reset_control_deassert(priv->rstc); } -static int rcsi2_wait_phy_start(struct rcar_csi2 *priv) +static int rcsi2_wait_phy_start(struct rcar_csi2 *priv, + unsigned int lanes) { unsigned int timeout; /* Wait for the clock and data lanes to enter LP-11 state. */ for (timeout = 0; timeout <= 20; timeout++) { - const u32 lane_mask = (1 << priv->lanes) - 1; + const u32 lane_mask = (1 << lanes) - 1; if ((rcsi2_read(priv, PHCLM_REG) & PHCLM_STOPSTATECKL) && (rcsi2_read(priv, PHDLM_REG) & lane_mask) == lane_mask) @@ -447,7 +451,8 @@ static int rcsi2_set_phypll(struct rcar_csi2 *priv, unsigned int mbps) return 0; } -static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp) +static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp, + unsigned int lanes) { struct v4l2_subdev *source; struct v4l2_ctrl *ctrl; @@ -472,15 +477,64 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp) * bps = link_freq * 2 */ mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * bpp; - do_div(mbps, priv->lanes * 1000000); + do_div(mbps, lanes * 1000000); return mbps; } +static int rcsi2_get_active_lanes(struct rcar_csi2 *priv, + unsigned int *lanes) +{ + struct v4l2_mbus_config mbus_config = { 0 }; + unsigned int num_lanes = UINT_MAX; + int ret; + + *lanes = priv->lanes; + + ret = v4l2_subdev_call(priv->remote, pad, get_mbus_config, + priv->remote_pad, &mbus_config); + if (ret == -ENOIOCTLCMD) { + dev_dbg(priv->dev, "No remote mbus configuration available\n"); + return 0; + } + + if (ret) { + dev_err(priv->dev, "Failed to get remote mbus configuration\n"); + return ret; + } + + if (mbus_config.type != V4L2_MBUS_CSI2_DPHY) { + dev_err(priv->dev, "Unsupported media bus type %u\n", + mbus_config.type); + return -EINVAL; + } + + if (mbus_config.flags & V4L2_MBUS_CSI2_1_LANE) + num_lanes = 1; + else if (mbus_config.flags & V4L2_MBUS_CSI2_2_LANE) + num_lanes = 2; + else if (mbus_config.flags & V4L2_MBUS_CSI2_3_LANE) + num_lanes = 3; + else if (mbus_config.flags & V4L2_MBUS_CSI2_4_LANE) + num_lanes = 4; + + if (num_lanes > priv->lanes) { + dev_err(priv->dev, + "Unsupported mbus config: too many data lanes %u\n", + num_lanes); + return -EINVAL; + } + + *lanes = num_lanes; + + return 0; +} + static int rcsi2_start_receiver(struct rcar_csi2 *priv) { const struct rcar_csi2_format *format; u32 phycnt, vcdt = 0, vcdt2 = 0, fld = 0; + unsigned int lanes; unsigned int i; int mbps, ret; @@ -522,10 +576,18 @@ static int rcsi2_start_receiver(struct rcar_csi2 *priv) fld |= FLD_FLD_NUM(1); } + /* + * Get the number of active data lanes inspecting the remote mbus + * configuration. + */ + ret = rcsi2_get_active_lanes(priv, &lanes); + if (ret) + return ret; + phycnt = PHYCNT_ENABLECLK; - phycnt |= (1 << priv->lanes) - 1; + phycnt |= (1 << lanes) - 1; - mbps = rcsi2_calc_mbps(priv, format->bpp); + mbps = rcsi2_calc_mbps(priv, format->bpp, lanes); if (mbps < 0) return mbps; @@ -572,7 +634,7 @@ static int rcsi2_start_receiver(struct rcar_csi2 *priv) rcsi2_write(priv, PHYCNT_REG, phycnt | PHYCNT_SHUTDOWNZ); rcsi2_write(priv, PHYCNT_REG, phycnt | PHYCNT_SHUTDOWNZ | PHYCNT_RSTZ); - ret = rcsi2_wait_phy_start(priv); + ret = rcsi2_wait_phy_start(priv, lanes); if (ret) return ret; @@ -749,6 +811,7 @@ static int rcsi2_notify_bound(struct v4l2_async_notifier *notifier, } priv->remote = subdev; + priv->remote_pad = pad; dev_dbg(priv->dev, "Bound %s pad: %d\n", subdev->name, pad); @@ -811,6 +874,8 @@ static int rcsi2_parse_v4l2(struct rcar_csi2 *priv, static int rcsi2_parse_dt(struct rcar_csi2 *priv) { + struct v4l2_async_subdev *asd; + struct fwnode_handle *fwnode; struct device_node *ep; struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 }; int ret; @@ -834,24 +899,19 @@ static int rcsi2_parse_dt(struct rcar_csi2 *priv) return ret; } - priv->asd.match.fwnode = - fwnode_graph_get_remote_endpoint(of_fwnode_handle(ep)); - priv->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - + fwnode = fwnode_graph_get_remote_endpoint(of_fwnode_handle(ep)); of_node_put(ep); - v4l2_async_notifier_init(&priv->notifier); - - ret = v4l2_async_notifier_add_subdev(&priv->notifier, &priv->asd); - if (ret) { - fwnode_handle_put(priv->asd.match.fwnode); - return ret; - } + dev_dbg(priv->dev, "Found '%pOF'\n", to_of_node(fwnode)); + v4l2_async_notifier_init(&priv->notifier); priv->notifier.ops = &rcar_csi2_notify_ops; - dev_dbg(priv->dev, "Found '%pOF'\n", - to_of_node(priv->asd.match.fwnode)); + asd = v4l2_async_notifier_add_fwnode_subdev(&priv->notifier, fwnode, + sizeof(*asd)); + fwnode_handle_put(fwnode); + if (IS_ERR(asd)) + return PTR_ERR(asd); ret = v4l2_async_subdev_notifier_register(&priv->subdev, &priv->notifier); @@ -1091,6 +1151,10 @@ static const struct of_device_id rcar_csi2_of_table[] = { .data = &rcar_csi2_info_r8a77990, }, { + .compatible = "renesas,r8a774e1-csi2", + .data = &rcar_csi2_info_r8a7795, + }, + { .compatible = "renesas,r8a7795-csi2", .data = &rcar_csi2_info_r8a7795, }, diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index a5dbb90c5210..692dea300b0d 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -125,6 +125,7 @@ #define VNDMR2_VPS (1 << 30) #define VNDMR2_HPS (1 << 29) #define VNDMR2_CES (1 << 28) +#define VNDMR2_YDS (1 << 22) #define VNDMR2_FTEV (1 << 17) #define VNDMR2_VLV(n) ((n & 0xf) << 12) @@ -598,8 +599,16 @@ void rvin_crop_scale_comp(struct rvin_dev *vin) /* For RAW8 format bpp is 1, but the hardware process RAW8 * format in 2 pixel unit hence configure VNIS_REG as stride / 2. */ - if (vin->format.pixelformat == V4L2_PIX_FMT_SRGGB8) + switch (vin->format.pixelformat) { + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: stride /= 2; + break; + default: + break; + } rvin_write(vin, stride, VNIS_REG); } @@ -683,6 +692,9 @@ static int rvin_setup(struct rvin_dev *vin) input_is_yuv = true; break; + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: case MEDIA_BUS_FMT_SRGGB8_1X8: vnmc |= VNMC_INF_RAW8; break; @@ -698,16 +710,26 @@ static int rvin_setup(struct rvin_dev *vin) if (!vin->is_csi) { /* Hsync Signal Polarity Select */ - if (!(vin->parallel->mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) + if (!(vin->parallel->bus.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) dmr2 |= VNDMR2_HPS; /* Vsync Signal Polarity Select */ - if (!(vin->parallel->mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) + if (!(vin->parallel->bus.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) dmr2 |= VNDMR2_VPS; /* Data Enable Polarity Select */ - if (vin->parallel->mbus_flags & V4L2_MBUS_DATA_ENABLE_LOW) + if (vin->parallel->bus.flags & V4L2_MBUS_DATA_ENABLE_LOW) dmr2 |= VNDMR2_CES; + + switch (vin->mbus_code) { + case MEDIA_BUS_FMT_UYVY8_2X8: + if (vin->parallel->bus.bus_width == 8 && + vin->parallel->bus.data_shift == 8) + dmr2 |= VNDMR2_YDS; + break; + default: + break; + } } /* @@ -747,6 +769,9 @@ static int rvin_setup(struct rvin_dev *vin) case V4L2_PIX_FMT_ABGR32: dmr = VNDMR_A8BIT(vin->alpha) | VNDMR_EXRGB | VNDMR_DTMD_ARGB; break; + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: case V4L2_PIX_FMT_SRGGB8: dmr = 0; break; @@ -1124,6 +1149,18 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, case MEDIA_BUS_FMT_UYVY10_2X10: case MEDIA_BUS_FMT_RGB888_1X24: break; + case MEDIA_BUS_FMT_SBGGR8_1X8: + if (vin->format.pixelformat != V4L2_PIX_FMT_SBGGR8) + return -EPIPE; + break; + case MEDIA_BUS_FMT_SGBRG8_1X8: + if (vin->format.pixelformat != V4L2_PIX_FMT_SGBRG8) + return -EPIPE; + break; + case MEDIA_BUS_FMT_SGRBG8_1X8: + if (vin->format.pixelformat != V4L2_PIX_FMT_SGRBG8) + return -EPIPE; + break; case MEDIA_BUS_FMT_SRGGB8_1X8: if (vin->format.pixelformat != V4L2_PIX_FMT_SRGGB8) return -EPIPE; @@ -1409,8 +1446,10 @@ int rvin_set_channel_routing(struct rvin_dev *vin, u8 chsel) int ret; ret = pm_runtime_get_sync(vin->dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_noidle(vin->dev); return ret; + } /* Make register writes take effect immediately. */ vnmc = rvin_read(vin, VNMC_REG); diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index 0e066bba747e..3e7a3ae2a6b9 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -67,6 +67,18 @@ static const struct rvin_video_format rvin_formats[] = { .bpp = 4, }, { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .bpp = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .bpp = 1, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .bpp = 1, + }, + { .fourcc = V4L2_PIX_FMT_SRGGB8, .bpp = 1, }, @@ -366,6 +378,21 @@ static int rvin_enum_fmt_vid_cap(struct file *file, void *priv, case MEDIA_BUS_FMT_UYVY10_2X10: case MEDIA_BUS_FMT_RGB888_1X24: break; + case MEDIA_BUS_FMT_SBGGR8_1X8: + if (f->index) + return -EINVAL; + f->pixelformat = V4L2_PIX_FMT_SBGGR8; + return 0; + case MEDIA_BUS_FMT_SGBRG8_1X8: + if (f->index) + return -EINVAL; + f->pixelformat = V4L2_PIX_FMT_SGBRG8; + return 0; + case MEDIA_BUS_FMT_SGRBG8_1X8: + if (f->index) + return -EINVAL; + f->pixelformat = V4L2_PIX_FMT_SGRBG8; + return 0; case MEDIA_BUS_FMT_SRGGB8_1X8: if (f->index) return -EINVAL; @@ -844,8 +871,10 @@ static int rvin_open(struct file *file) int ret; ret = pm_runtime_get_sync(vin->dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_noidle(vin->dev); return ret; + } ret = mutex_lock_interruptible(&vin->lock); if (ret) diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index c19d077ce1cb..8396e0e45478 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -19,6 +19,7 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-dev.h> #include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> #include <media/videobuf2-v4l2.h> /* Number of HW buffers */ @@ -92,7 +93,7 @@ struct rvin_video_format { * @asd: sub-device descriptor for async framework * @subdev: subdevice matched using async framework * @mbus_type: media bus type - * @mbus_flags: media bus configuration flags + * @bus: media bus parallel configuration * @source_pad: source pad of remote subdevice * @sink_pad: sink pad of remote subdevice * @@ -102,7 +103,7 @@ struct rvin_parallel_entity { struct v4l2_subdev *subdev; enum v4l2_mbus_type mbus_type; - unsigned int mbus_flags; + struct v4l2_fwnode_bus_parallel bus; unsigned int source_pad; unsigned int sink_pad; diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c index 3d2451ac347d..f318cd4b8086 100644 --- a/drivers/media/platform/rcar_drif.c +++ b/drivers/media/platform/rcar_drif.c @@ -185,7 +185,6 @@ struct rcar_drif_frame_buf { /* OF graph endpoint's V4L2 async data */ struct rcar_drif_graph_ep { struct v4l2_subdev *subdev; /* Async matched subdev */ - struct v4l2_async_subdev asd; /* Async sub-device descriptor */ }; /* DMA buffer */ @@ -1109,12 +1108,6 @@ static int rcar_drif_notify_bound(struct v4l2_async_notifier *notifier, struct rcar_drif_sdr *sdr = container_of(notifier, struct rcar_drif_sdr, notifier); - if (sdr->ep.asd.match.fwnode != - of_fwnode_handle(subdev->dev->of_node)) { - rdrif_err(sdr, "subdev %s cannot bind\n", subdev->name); - return -EINVAL; - } - v4l2_set_subdev_hostdata(subdev, sdr); sdr->ep.subdev = subdev; rdrif_dbg(sdr, "bound asd %s\n", subdev->name); @@ -1218,7 +1211,7 @@ static int rcar_drif_parse_subdevs(struct rcar_drif_sdr *sdr) { struct v4l2_async_notifier *notifier = &sdr->notifier; struct fwnode_handle *fwnode, *ep; - int ret; + struct v4l2_async_subdev *asd; v4l2_async_notifier_init(notifier); @@ -1227,26 +1220,21 @@ static int rcar_drif_parse_subdevs(struct rcar_drif_sdr *sdr) if (!ep) return 0; + /* Get the endpoint properties */ + rcar_drif_get_ep_properties(sdr, ep); + fwnode = fwnode_graph_get_remote_port_parent(ep); + fwnode_handle_put(ep); if (!fwnode) { dev_warn(sdr->dev, "bad remote port parent\n"); - fwnode_handle_put(ep); return -EINVAL; } - sdr->ep.asd.match.fwnode = fwnode; - sdr->ep.asd.match_type = V4L2_ASYNC_MATCH_FWNODE; - ret = v4l2_async_notifier_add_subdev(notifier, &sdr->ep.asd); - if (ret) { - fwnode_handle_put(fwnode); - return ret; - } - - /* Get the endpoint properties */ - rcar_drif_get_ep_properties(sdr, ep); - + asd = v4l2_async_notifier_add_fwnode_subdev(notifier, fwnode, + sizeof(*asd)); fwnode_handle_put(fwnode); - fwnode_handle_put(ep); + if (IS_ERR(asd)) + return PTR_ERR(asd); return 0; } diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c index f7d71a6a7970..4a633ad0e8fa 100644 --- a/drivers/media/platform/renesas-ceu.c +++ b/drivers/media/platform/renesas-ceu.c @@ -405,7 +405,7 @@ static int ceu_hw_config(struct ceu_device *ceudev) /* Non-swapped planar image capture mode. */ case V4L2_PIX_FMT_NV16: cdocr |= CEU_CDOCR_NO_DOWSAMPLE; - /* fall-through */ + fallthrough; case V4L2_PIX_FMT_NV12: if (mbus_fmt->swapped) camcr = mbus_fmt->fmt_order_swap; @@ -419,7 +419,7 @@ static int ceu_hw_config(struct ceu_device *ceudev) /* Swapped planar image capture mode. */ case V4L2_PIX_FMT_NV61: cdocr |= CEU_CDOCR_NO_DOWSAMPLE; - /* fall-through */ + fallthrough; case V4L2_PIX_FMT_NV21: if (mbus_fmt->swapped) camcr = mbus_fmt->fmt_order; diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index 36b821ccc1db..bf9a75b75083 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -81,6 +81,7 @@ static int rga_buf_start_streaming(struct vb2_queue *q, unsigned int count) ret = pm_runtime_get_sync(rga->dev); if (ret < 0) { + pm_runtime_put_noidle(rga->dev); rga_buf_return_buffers(q, VB2_BUF_STATE_QUEUED); return ret; } diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c index 92f43c0cbc0c..422fd549e9c8 100644 --- a/drivers/media/platform/s3c-camif/camif-core.c +++ b/drivers/media/platform/s3c-camif/camif-core.c @@ -464,7 +464,7 @@ static int s3c_camif_probe(struct platform_device *pdev) ret = camif_media_dev_init(camif); if (ret < 0) - goto err_alloc; + goto err_pm; ret = camif_register_sensor(camif); if (ret < 0) @@ -498,10 +498,9 @@ err_sens: media_device_unregister(&camif->media_dev); media_device_cleanup(&camif->media_dev); camif_unregister_media_entities(camif); -err_alloc: +err_pm: pm_runtime_put(dev); pm_runtime_disable(dev); -err_pm: camif_clk_put(camif); err_clk: s3c_camif_unregister_subdev(camif); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 912fe0c5ab18..acc2217dd7e9 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -262,6 +262,12 @@ static struct mfc_control controls[] = { .default_value = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED, }, { + .id = V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE, + .type = V4L2_CTRL_TYPE_MENU, + .maximum = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT, + .default_value = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED, + }, + { .id = V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT, .type = V4L2_CTRL_TYPE_BOOLEAN, .name = "Fixed Target Bit Enable", @@ -1849,6 +1855,7 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl) p->seq_hdr_mode = ctrl->val; break; case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE: + case V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE: p->frame_skip_mode = ctrl->val; break; case V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT: diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c index 7d52431c2c83..62d2320a7218 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c @@ -79,8 +79,10 @@ int s5p_mfc_power_on(void) int i, ret = 0; ret = pm_runtime_get_sync(pm->device); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_noidle(pm->device); return ret; + } /* clock control */ for (i = 0; i < pm->num_clocks; i++) { diff --git a/drivers/media/platform/sti/bdisp/bdisp-debug.c b/drivers/media/platform/sti/bdisp/bdisp-debug.c index 77ca7517fa3e..2b270093009c 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-debug.c +++ b/drivers/media/platform/sti/bdisp/bdisp-debug.c @@ -637,35 +637,18 @@ DEFINE_SHOW_ATTRIBUTE(last_nodes_raw); DEFINE_SHOW_ATTRIBUTE(last_request); DEFINE_SHOW_ATTRIBUTE(perf); -int bdisp_debugfs_create(struct bdisp_dev *bdisp) +void bdisp_debugfs_create(struct bdisp_dev *bdisp) { char dirname[16]; snprintf(dirname, sizeof(dirname), "%s%d", BDISP_NAME, bdisp->id); bdisp->dbg.debugfs_entry = debugfs_create_dir(dirname, NULL); - if (!bdisp->dbg.debugfs_entry) - goto err; - if (!bdisp_dbg_create_entry(regs)) - goto err; - - if (!bdisp_dbg_create_entry(last_nodes)) - goto err; - - if (!bdisp_dbg_create_entry(last_nodes_raw)) - goto err; - - if (!bdisp_dbg_create_entry(last_request)) - goto err; - - if (!bdisp_dbg_create_entry(perf)) - goto err; - - return 0; - -err: - bdisp_debugfs_remove(bdisp); - return -ENOMEM; + bdisp_dbg_create_entry(regs); + bdisp_dbg_create_entry(last_nodes); + bdisp_dbg_create_entry(last_nodes_raw); + bdisp_dbg_create_entry(last_request); + bdisp_dbg_create_entry(perf); } void bdisp_debugfs_remove(struct bdisp_dev *bdisp) diff --git a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c index af2d5eb782ce..060ca85f64d5 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c +++ b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c @@ -1360,18 +1360,14 @@ static int bdisp_probe(struct platform_device *pdev) } /* Debug */ - ret = bdisp_debugfs_create(bdisp); - if (ret) { - dev_err(dev, "failed to create debugfs\n"); - goto err_v4l2; - } + bdisp_debugfs_create(bdisp); /* Power management */ pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); if (ret < 0) { dev_err(dev, "failed to set PM\n"); - goto err_dbg; + goto err_pm; } /* Filters */ @@ -1399,9 +1395,7 @@ err_filter: bdisp_hw_free_filters(bdisp->dev); err_pm: pm_runtime_put(dev); -err_dbg: bdisp_debugfs_remove(bdisp); -err_v4l2: v4l2_device_unregister(&bdisp->v4l2_dev); err_clk: if (!IS_ERR(bdisp->clock)) diff --git a/drivers/media/platform/sti/bdisp/bdisp.h b/drivers/media/platform/sti/bdisp/bdisp.h index e309cde379ca..3fb009d24791 100644 --- a/drivers/media/platform/sti/bdisp/bdisp.h +++ b/drivers/media/platform/sti/bdisp/bdisp.h @@ -209,6 +209,6 @@ int bdisp_hw_get_and_clear_irq(struct bdisp_dev *bdisp); int bdisp_hw_update(struct bdisp_ctx *ctx); void bdisp_debugfs_remove(struct bdisp_dev *bdisp); -int bdisp_debugfs_create(struct bdisp_dev *bdisp); +void bdisp_debugfs_create(struct bdisp_dev *bdisp); void bdisp_dbg_perf_begin(struct bdisp_dev *bdisp); void bdisp_dbg_perf_end(struct bdisp_dev *bdisp); diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c index 5baada4f65e5..dbe7788083a4 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c @@ -77,9 +77,9 @@ static void c8sectpfe_timer_interrupt(struct timer_list *t) add_timer(&fei->timer); } -static void channel_swdemux_tsklet(unsigned long data) +static void channel_swdemux_tsklet(struct tasklet_struct *t) { - struct channel_info *channel = (struct channel_info *)data; + struct channel_info *channel = from_tasklet(channel, t, tsklet); struct c8sectpfei *fei; unsigned long wp, rp; int pos, num_packets, n, size; @@ -208,8 +208,7 @@ static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed) dev_dbg(fei->dev, "Starting channel=%p\n", channel); - tasklet_init(&channel->tsklet, channel_swdemux_tsklet, - (unsigned long) channel); + tasklet_setup(&channel->tsklet, channel_swdemux_tsklet); /* Reset the internal inputblock sram pointers */ writel(channel->fifo, @@ -638,8 +637,7 @@ static int configure_memdma_and_inputblock(struct c8sectpfei *fei, writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSRP_TP(0)); /* initialize tasklet */ - tasklet_init(&tsin->tsklet, channel_swdemux_tsklet, - (unsigned long) tsin); + tasklet_setup(&tsin->tsklet, channel_swdemux_tsklet); return 0; diff --git a/drivers/media/platform/sti/delta/delta-v4l2.c b/drivers/media/platform/sti/delta/delta-v4l2.c index 2503224eeee5..c691b3d81549 100644 --- a/drivers/media/platform/sti/delta/delta-v4l2.c +++ b/drivers/media/platform/sti/delta/delta-v4l2.c @@ -954,8 +954,10 @@ static void delta_run_work(struct work_struct *work) /* enable the hardware */ if (!dec->pm) { ret = delta_get_sync(ctx); - if (ret) + if (ret) { + delta_put_autosuspend(ctx); goto err; + } } /* decode this access unit */ diff --git a/drivers/media/platform/sti/hva/hva-debugfs.c b/drivers/media/platform/sti/hva/hva-debugfs.c index 7d12a5b5d914..a86a07b6fbc7 100644 --- a/drivers/media/platform/sti/hva/hva-debugfs.c +++ b/drivers/media/platform/sti/hva/hva-debugfs.c @@ -337,25 +337,11 @@ DEFINE_SHOW_ATTRIBUTE(regs); void hva_debugfs_create(struct hva_dev *hva) { hva->dbg.debugfs_entry = debugfs_create_dir(HVA_NAME, NULL); - if (!hva->dbg.debugfs_entry) - goto err; - if (!hva_dbg_create_entry(device)) - goto err; - - if (!hva_dbg_create_entry(encoders)) - goto err; - - if (!hva_dbg_create_entry(last)) - goto err; - - if (!hva_dbg_create_entry(regs)) - goto err; - - return; - -err: - hva_debugfs_remove(hva); + hva_dbg_create_entry(device); + hva_dbg_create_entry(encoders); + hva_dbg_create_entry(last); + hva_dbg_create_entry(regs); } void hva_debugfs_remove(struct hva_dev *hva) diff --git a/drivers/media/platform/sti/hva/hva-hw.c b/drivers/media/platform/sti/hva/hva-hw.c index 401aaafa1710..43f279e2a6a3 100644 --- a/drivers/media/platform/sti/hva/hva-hw.c +++ b/drivers/media/platform/sti/hva/hva-hw.c @@ -272,6 +272,7 @@ static unsigned long int hva_hw_get_ip_version(struct hva_dev *hva) if (pm_runtime_get_sync(dev) < 0) { dev_err(dev, "%s failed to get pm_runtime\n", HVA_PREFIX); + pm_runtime_put_noidle(dev); mutex_unlock(&hva->protect_mutex); return -EFAULT; } @@ -388,7 +389,7 @@ int hva_hw_probe(struct platform_device *pdev, struct hva_dev *hva) ret = pm_runtime_get_sync(dev); if (ret < 0) { dev_err(dev, "%s failed to set PM\n", HVA_PREFIX); - goto err_clk; + goto err_pm; } /* check IP hardware version */ @@ -553,6 +554,7 @@ void hva_hw_dump_regs(struct hva_dev *hva, struct seq_file *s) if (pm_runtime_get_sync(dev) < 0) { seq_puts(s, "Cannot wake up IP\n"); + pm_runtime_put_noidle(dev); mutex_unlock(&hva->protect_mutex); return; } diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index b8931490b83b..fd1c41cba52f 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -733,7 +733,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) if (ret < 0) { dev_err(dcmi->dev, "%s: Failed to start streaming, cannot get sync (%d)\n", __func__, ret); - goto err_release_buffers; + goto err_pm_put; } ret = media_pipeline_start(&dcmi->vdev->entity, &dcmi->pipeline); @@ -837,8 +837,6 @@ err_media_pipeline_stop: err_pm_put: pm_runtime_put(dcmi->dev); - -err_release_buffers: spin_lock_irq(&dcmi->irqlock); /* * Return all buffers to vb2 in QUEUED state. diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c index 5319eb1ab309..d226ecadff8e 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c @@ -287,6 +287,7 @@ static int sun4i_csi_remove(struct platform_device *pdev) v4l2_async_notifier_unregister(&csi->notifier); v4l2_async_notifier_cleanup(&csi->notifier); + vb2_video_unregister_device(&csi->vdev); media_device_unregister(&csi->mdev); sun4i_csi_dma_unregister(csi); media_device_cleanup(&csi->mdev); diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c index 3278746246aa..2c39cd7f2862 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c @@ -431,7 +431,7 @@ int sun4i_csi_dma_register(struct sun4i_csi *csi, int irq) ret = v4l2_device_register(csi->dev, &csi->v4l); if (ret) { dev_err(csi->dev, "Couldn't register the v4l2 device\n"); - goto err_free_queue; + goto err_free_mutex; } ret = devm_request_irq(csi->dev, irq, sun4i_csi_irq, 0, @@ -446,9 +446,6 @@ int sun4i_csi_dma_register(struct sun4i_csi *csi, int irq) err_unregister_device: v4l2_device_unregister(&csi->v4l); -err_free_queue: - vb2_queue_release(q); - err_free_mutex: mutex_destroy(&csi->lock); return ret; @@ -457,6 +454,5 @@ err_free_mutex: void sun4i_csi_dma_unregister(struct sun4i_csi *csi) { v4l2_device_unregister(&csi->v4l); - vb2_queue_release(&csi->queue); mutex_destroy(&csi->lock); } diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c index d9648b2810b9..b55de9ab64d8 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c @@ -660,13 +660,11 @@ int sun6i_video_init(struct sun6i_video *video, struct sun6i_csi *csi, if (ret < 0) { v4l2_err(&csi->v4l2_dev, "video_register_device failed: %d\n", ret); - goto release_vb2; + goto clean_entity; } return 0; -release_vb2: - vb2_queue_release(&video->vb2_vidq); clean_entity: media_entity_cleanup(&video->vdev.entity); mutex_destroy(&video->lock); @@ -675,8 +673,7 @@ clean_entity: void sun6i_video_cleanup(struct sun6i_video *video) { - video_unregister_device(&video->vdev); + vb2_video_unregister_device(&video->vdev); media_entity_cleanup(&video->vdev.entity); - vb2_queue_release(&video->vb2_vidq); mutex_destroy(&video->lock); } diff --git a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c index 94f505d3cbad..3f81dd17755c 100644 --- a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c +++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c @@ -747,11 +747,8 @@ static int rotate_probe(struct platform_device *pdev) dev->dev = &pdev->dev; irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(dev->dev, "Failed to get IRQ\n"); - + if (irq <= 0) return irq; - } ret = devm_request_irq(dev->dev, irq, rotate_irq, 0, dev_name(dev->dev), dev); diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 346f8212791c..779dd74b82d0 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -2475,6 +2475,8 @@ static int vpe_runtime_get(struct platform_device *pdev) r = pm_runtime_get_sync(&pdev->dev); WARN_ON(r < 0); + if (r) + pm_runtime_put_noidle(&pdev->dev); return r < 0 ? r : 0; } diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index a4a45d68a6ef..86d5e3f4b1ff 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -912,8 +912,8 @@ int vsp1_du_map_sg(struct device *dev, struct sg_table *sgt) * skip cache sync. This will need to be revisited when support for * non-coherent buffers will be added to the DU driver. */ - return dma_map_sg_attrs(vsp1->bus_master, sgt->sgl, sgt->nents, - DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); + return dma_map_sgtable(vsp1->bus_master, sgt, DMA_TO_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); } EXPORT_SYMBOL_GPL(vsp1_du_map_sg); @@ -921,8 +921,8 @@ void vsp1_du_unmap_sg(struct device *dev, struct sg_table *sgt) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); - dma_unmap_sg_attrs(vsp1->bus_master, sgt->sgl, sgt->nents, - DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); + dma_unmap_sgtable(vsp1->bus_master, sgt, DMA_TO_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); } EXPORT_SYMBOL_GPL(vsp1_du_unmap_sg); diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index c650e45bb0ad..dc62533cf32c 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -562,7 +562,12 @@ int vsp1_device_get(struct vsp1_device *vsp1) int ret; ret = pm_runtime_get_sync(vsp1->dev); - return ret < 0 ? ret : 0; + if (ret < 0) { + pm_runtime_put_noidle(vsp1->dev); + return ret; + } + + return 0; } /* @@ -845,12 +850,12 @@ static int vsp1_probe(struct platform_device *pdev) /* Configure device parameters based on the version register. */ pm_runtime_enable(&pdev->dev); - ret = pm_runtime_get_sync(&pdev->dev); + ret = vsp1_device_get(vsp1); if (ret < 0) goto done; vsp1->version = vsp1_read(vsp1, VI6_IP_VERSION); - pm_runtime_put_sync(&pdev->dev); + vsp1_device_put(vsp1); for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) { if ((vsp1->version & VI6_IP_VERSION_MODEL_MASK) == diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c index 7e2460263882..23997425bdb5 100644 --- a/drivers/media/radio/radio-si476x.c +++ b/drivers/media/radio/radio-si476x.c @@ -1345,60 +1345,24 @@ static const struct file_operations radio_rsq_primary_fops = { }; -static int si476x_radio_init_debugfs(struct si476x_radio *radio) +static void si476x_radio_init_debugfs(struct si476x_radio *radio) { - struct dentry *dentry; - int ret; + radio->debugfs = debugfs_create_dir(dev_name(radio->v4l2dev.dev), NULL); - dentry = debugfs_create_dir(dev_name(radio->v4l2dev.dev), NULL); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - goto exit; - } - radio->debugfs = dentry; - - dentry = debugfs_create_file("acf", S_IRUGO, - radio->debugfs, radio, &radio_acf_fops); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - goto cleanup; - } + debugfs_create_file("acf", S_IRUGO, radio->debugfs, radio, + &radio_acf_fops); - dentry = debugfs_create_file("rds_blckcnt", S_IRUGO, - radio->debugfs, radio, - &radio_rds_blckcnt_fops); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - goto cleanup; - } + debugfs_create_file("rds_blckcnt", S_IRUGO, radio->debugfs, radio, + &radio_rds_blckcnt_fops); - dentry = debugfs_create_file("agc", S_IRUGO, - radio->debugfs, radio, &radio_agc_fops); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - goto cleanup; - } + debugfs_create_file("agc", S_IRUGO, radio->debugfs, radio, + &radio_agc_fops); - dentry = debugfs_create_file("rsq", S_IRUGO, - radio->debugfs, radio, &radio_rsq_fops); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - goto cleanup; - } + debugfs_create_file("rsq", S_IRUGO, radio->debugfs, radio, + &radio_rsq_fops); - dentry = debugfs_create_file("rsq_primary", S_IRUGO, - radio->debugfs, radio, - &radio_rsq_primary_fops); - if (IS_ERR(dentry)) { - ret = PTR_ERR(dentry); - goto cleanup; - } - - return 0; -cleanup: - debugfs_remove_recursive(radio->debugfs); -exit: - return ret; + debugfs_create_file("rsq_primary", S_IRUGO, radio->debugfs, radio, + &radio_rsq_primary_fops); } @@ -1535,11 +1499,7 @@ static int si476x_radio_probe(struct platform_device *pdev) goto exit; } - rval = si476x_radio_init_debugfs(radio); - if (rval < 0) { - dev_err(&pdev->dev, "Could not create debugfs interface\n"); - goto exit; - } + si476x_radio_init_debugfs(radio); return 0; exit: diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c index 7f3aee495ed3..6afa7c3464ab 100644 --- a/drivers/media/radio/si4713/si4713.c +++ b/drivers/media/radio/si4713/si4713.c @@ -1157,7 +1157,7 @@ static int si4713_s_ctrl(struct v4l2_ctrl *ctrl) * V4L2_CID_TUNE_POWER_LEVEL. */ if (force) break; - /* fall through */ + fallthrough; case V4L2_CID_TUNE_POWER_LEVEL: ret = si4713_tx_tune_power(sdev, sdev->tune_pwr_level->val, sdev->tune_ant_cap->val); diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index cce97c9d5409..6142484d5cb4 100644 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -19,9 +19,11 @@ * Author: Manjunatha Halli <manjunatha_halli@ti.com> */ -#include <linux/module.h> -#include <linux/firmware.h> #include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/module.h> +#include <linux/nospec.h> + #include "fmdrv.h" #include "fmdrv_v4l2.h" #include "fmdrv_common.h" @@ -244,7 +246,7 @@ void fmc_update_region_info(struct fmdev *fmdev, u8 region_to_set) * FM common sub-module will schedule this tasklet whenever it receives * FM packet from ST driver. */ -static void recv_tasklet(unsigned long arg) +static void recv_tasklet(struct tasklet_struct *t) { struct fmdev *fmdev; struct fm_irq *irq_info; @@ -253,7 +255,7 @@ static void recv_tasklet(unsigned long arg) u8 num_fm_hci_cmds; unsigned long flags; - fmdev = (struct fmdev *)arg; + fmdev = from_tasklet(fmdev, t, tx_task); irq_info = &fmdev->irq_info; /* Process all packets in the RX queue */ while ((skb = skb_dequeue(&fmdev->rx_q))) { @@ -328,13 +330,13 @@ static void recv_tasklet(unsigned long arg) } /* FM send tasklet: is scheduled when FM packet has to be sent to chip */ -static void send_tasklet(unsigned long arg) +static void send_tasklet(struct tasklet_struct *t) { struct fmdev *fmdev; struct sk_buff *skb; int len; - fmdev = (struct fmdev *)arg; + fmdev = from_tasklet(fmdev, t, tx_task); if (!atomic_read(&fmdev->tx_cnt)) return; @@ -700,7 +702,7 @@ static void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *fmdev) struct fm_rds *rds = &fmdev->rx.rds; unsigned long group_idx, flags; u8 *rds_data, meta_data, tmpbuf[FM_RDS_BLK_SIZE]; - u8 type, blk_idx; + u8 type, blk_idx, idx; u16 cur_picode; u32 rds_len; @@ -733,9 +735,11 @@ static void fm_irq_handle_rdsdata_getcmd_resp(struct fmdev *fmdev) } /* Skip checkword (control) byte and copy only data byte */ - memcpy(&rds_fmt.data.groupdatabuff. - buff[blk_idx * (FM_RDS_BLK_SIZE - 1)], - rds_data, (FM_RDS_BLK_SIZE - 1)); + idx = array_index_nospec(blk_idx * (FM_RDS_BLK_SIZE - 1), + FM_RX_RDS_INFO_FIELD_MAX - (FM_RDS_BLK_SIZE - 1)); + + memcpy(&rds_fmt.data.groupdatabuff.buff[idx], rds_data, + FM_RDS_BLK_SIZE - 1); rds->last_blk_idx = blk_idx; @@ -1535,11 +1539,11 @@ int fmc_prepare(struct fmdev *fmdev) /* Initialize TX queue and TX tasklet */ skb_queue_head_init(&fmdev->tx_q); - tasklet_init(&fmdev->tx_task, send_tasklet, (unsigned long)fmdev); + tasklet_setup(&fmdev->tx_task, send_tasklet); /* Initialize RX Queue and RX tasklet */ skb_queue_head_init(&fmdev->rx_q); - tasklet_init(&fmdev->rx_task, recv_tasklet, (unsigned long)fmdev); + tasklet_setup(&fmdev->rx_task, recv_tasklet); fmdev->irq_info.stage = 0; atomic_set(&fmdev->tx_cnt, 1); diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 9cdef17b4793..c12dda73cdd5 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -835,6 +835,10 @@ static int ati_remote_probe(struct usb_interface *interface, err("%s: endpoint_in message size==0? \n", __func__); return -ENODEV; } + if (!usb_endpoint_is_int_out(endpoint_out)) { + err("%s: Unexpected endpoint_out\n", __func__); + return -ENODEV; + } ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL); rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE); diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index 82867a2a60b0..6049e5c95394 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -432,27 +432,27 @@ static void ene_rx_setup(struct ene_device *dev) select_timeout: if (dev->rx_fan_input_inuse) { - dev->rdev->rx_resolution = US_TO_NS(ENE_FW_SAMPLE_PERIOD_FAN); + dev->rdev->rx_resolution = ENE_FW_SAMPLE_PERIOD_FAN; /* Fan input doesn't support timeouts, it just ends the input with a maximum sample */ dev->rdev->min_timeout = dev->rdev->max_timeout = - US_TO_NS(ENE_FW_SMPL_BUF_FAN_MSK * - ENE_FW_SAMPLE_PERIOD_FAN); + ENE_FW_SMPL_BUF_FAN_MSK * + ENE_FW_SAMPLE_PERIOD_FAN; } else { - dev->rdev->rx_resolution = US_TO_NS(sample_period); + dev->rdev->rx_resolution = sample_period; /* Theoreticly timeout is unlimited, but we cap it * because it was seen that on one device, it * would stop sending spaces after around 250 msec. * Besides, this is close to 2^32 anyway and timeout is u32. */ - dev->rdev->min_timeout = US_TO_NS(127 * sample_period); - dev->rdev->max_timeout = US_TO_NS(200000); + dev->rdev->min_timeout = 127 * sample_period; + dev->rdev->max_timeout = 200000; } if (dev->hw_learning_and_tx_capable) - dev->rdev->tx_resolution = US_TO_NS(sample_period); + dev->rdev->tx_resolution = sample_period; if (dev->rdev->timeout > dev->rdev->max_timeout) dev->rdev->timeout = dev->rdev->max_timeout; @@ -798,7 +798,7 @@ static irqreturn_t ene_isr(int irq, void *data) dbg("RX: %d (%s)", hw_sample, pulse ? "pulse" : "space"); - ev.duration = US_TO_NS(hw_sample); + ev.duration = hw_sample; ev.pulse = pulse; ir_raw_event_store_with_filter(dev->rdev, &ev); } @@ -818,7 +818,7 @@ static void ene_setup_default_settings(struct ene_device *dev) dev->learning_mode_enabled = learning_mode_force; /* Set reasonable default timeout */ - dev->rdev->timeout = US_TO_NS(150000); + dev->rdev->timeout = MS_TO_US(150); } /* Upload all hardware settings at once. Used at load and resume time */ diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index 8e3177c5b586..b0d580566e4e 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -299,8 +299,8 @@ static void fintek_process_rx_ir_data(struct fintek_dev *fintek) case PARSE_IRDATA: fintek->rem--; rawir.pulse = ((sample & BUF_PULSE_BIT) != 0); - rawir.duration = US_TO_NS((sample & BUF_SAMPLE_MASK) - * CIR_SAMPLE_PERIOD); + rawir.duration = (sample & BUF_SAMPLE_MASK) + * CIR_SAMPLE_PERIOD; fit_dbg("Storing %s with duration %d", rawir.pulse ? "pulse" : "space", @@ -524,9 +524,9 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id rdev->dev.parent = &pdev->dev; rdev->driver_name = FINTEK_DRIVER_NAME; rdev->map_name = RC_MAP_RC6_MCE; - rdev->timeout = US_TO_NS(1000); + rdev->timeout = 1000; /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */ - rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD); + rdev->rx_resolution = CIR_SAMPLE_PERIOD; fintek->rdev = rdev; diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index a20413008c3c..22e524b69806 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -11,6 +11,8 @@ #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/pm_qos.h> #include <linux/irq.h> #include <media/rc-core.h> @@ -20,17 +22,38 @@ struct gpio_rc_dev { struct rc_dev *rcdev; struct gpio_desc *gpiod; int irq; + struct device *pmdev; + struct pm_qos_request qos; }; static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id) { int val; struct gpio_rc_dev *gpio_dev = dev_id; + struct device *pmdev = gpio_dev->pmdev; + + /* + * For some cpuidle systems, not all: + * Respond to interrupt taking more latency when cpu in idle. + * Invoke asynchronous pm runtime get from interrupt context, + * this may introduce a millisecond delay to call resume callback, + * where to disable cpuilde. + * + * Two issues lead to fail to decode first frame, one is latency to + * respond to interrupt, another is delay introduced by async api. + */ + if (pmdev) + pm_runtime_get(pmdev); val = gpiod_get_value(gpio_dev->gpiod); if (val >= 0) ir_raw_event_store_edge(gpio_dev->rcdev, val == 1); + if (pmdev) { + pm_runtime_mark_last_busy(pmdev); + pm_runtime_put_autosuspend(pmdev); + } + return IRQ_HANDLED; } @@ -40,6 +63,7 @@ static int gpio_ir_recv_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct gpio_rc_dev *gpio_dev; struct rc_dev *rcdev; + u32 period = 0; int rc; if (!np) @@ -90,6 +114,15 @@ static int gpio_ir_recv_probe(struct platform_device *pdev) return rc; } + of_property_read_u32(np, "linux,autosuspend-period", &period); + if (period) { + gpio_dev->pmdev = dev; + pm_runtime_set_autosuspend_delay(dev, period); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + } + platform_set_drvdata(pdev, gpio_dev); return devm_request_irq(dev, gpio_dev->irq, gpio_ir_recv_irq, @@ -122,9 +155,29 @@ static int gpio_ir_recv_resume(struct device *dev) return 0; } +static int gpio_ir_recv_runtime_suspend(struct device *dev) +{ + struct gpio_rc_dev *gpio_dev = dev_get_drvdata(dev); + + cpu_latency_qos_remove_request(&gpio_dev->qos); + + return 0; +} + +static int gpio_ir_recv_runtime_resume(struct device *dev) +{ + struct gpio_rc_dev *gpio_dev = dev_get_drvdata(dev); + + cpu_latency_qos_add_request(&gpio_dev->qos, 0); + + return 0; +} + static const struct dev_pm_ops gpio_ir_recv_pm_ops = { .suspend = gpio_ir_recv_suspend, .resume = gpio_ir_recv_resume, + .runtime_suspend = gpio_ir_recv_runtime_suspend, + .runtime_resume = gpio_ir_recv_runtime_resume, }; #endif diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c index b981f7290c1b..effaa5751d6c 100644 --- a/drivers/media/rc/igorplugusb.c +++ b/drivers/media/rc/igorplugusb.c @@ -69,7 +69,7 @@ static void igorplugusb_irdata(struct igorplugusb *ir, unsigned len) overflow); do { - rawir.duration = ir->buf_in[i] * 85333; + rawir.duration = ir->buf_in[i] * 85; rawir.pulse = i & 1; ir_raw_event_store_with_filter(ir->rc, &rawir); @@ -202,8 +202,8 @@ static int igorplugusb_probe(struct usb_interface *intf, rc->priv = ir; rc->driver_name = DRIVER_NAME; rc->map_name = RC_MAP_HAUPPAUGE; - rc->timeout = MS_TO_NS(100); - rc->rx_resolution = 85333; + rc->timeout = MS_TO_US(100); + rc->rx_resolution = 85; ir->rc = rc; ret = rc_register_device(rc); diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index 566c2816d5be..84949baf9f6b 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -59,7 +59,7 @@ struct iguanair { #define MAX_IN_PACKET 8u #define MAX_OUT_PACKET (sizeof(struct send_packet) + BUF_SIZE) #define TIMEOUT 1000 -#define RX_RESOLUTION 21333 +#define RX_RESOLUTION 21 struct packet { uint16_t start; @@ -101,7 +101,7 @@ static void process_ir_data(struct iguanair *ir, unsigned len) break; case CMD_TX_OVERFLOW: ir->tx_overflow = true; - /* fall through */ + fallthrough; case CMD_RECEIVER_OFF: case CMD_RECEIVER_ON: case CMD_SEND: @@ -124,7 +124,7 @@ static void process_ir_data(struct iguanair *ir, unsigned len) for (i = 0; i < 7; i++) { if (ir->buf_in[i] == 0x80) { rawir.pulse = false; - rawir.duration = US_TO_NS(21845); + rawir.duration = 21845; } else { rawir.pulse = (ir->buf_in[i] & 0x80) == 0; rawir.duration = ((ir->buf_in[i] & 0x7f) + 1) * diff --git a/drivers/media/rc/imon_raw.c b/drivers/media/rc/imon_raw.c index aae0a3cc9479..d41580f6e4c7 100644 --- a/drivers/media/rc/imon_raw.c +++ b/drivers/media/rc/imon_raw.c @@ -8,7 +8,7 @@ #include <media/rc-core.h> /* Each bit is 250us */ -#define BIT_DURATION 250000 +#define BIT_DURATION 250 struct imon { struct device *dev; diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c index d80cfa455c73..0ffc27514fab 100644 --- a/drivers/media/rc/ir-hix5hd2.c +++ b/drivers/media/rc/ir-hix5hd2.c @@ -214,12 +214,12 @@ static irqreturn_t hix5hd2_ir_rx_interrupt(int irq, void *data) data_h = ((symb_val >> 16) & 0xffff) * 10; symb_time = (data_l + data_h) / 10; - ev.duration = US_TO_NS(data_l); + ev.duration = data_l; ev.pulse = true; ir_raw_event_store(priv->rdev, &ev); if (symb_time < IR_CFG_SYMBOL_MAXWIDTH) { - ev.duration = US_TO_NS(data_h); + ev.duration = data_h; ev.pulse = false; ir_raw_event_store(priv->rdev, &ev); } else { @@ -311,8 +311,8 @@ static int hix5hd2_ir_probe(struct platform_device *pdev) rdev->input_id.vendor = 0x0001; rdev->input_id.product = 0x0001; rdev->input_id.version = 0x0100; - rdev->rx_resolution = US_TO_NS(10); - rdev->timeout = US_TO_NS(IR_CFG_SYMBOL_MAXWIDTH * 10); + rdev->rx_resolution = 10; + rdev->timeout = IR_CFG_SYMBOL_MAXWIDTH * 10; ret = rc_register_device(rdev); if (ret < 0) diff --git a/drivers/media/rc/ir-imon-decoder.c b/drivers/media/rc/ir-imon-decoder.c index a0efe2605393..41dbbef27fa6 100644 --- a/drivers/media/rc/ir-imon-decoder.c +++ b/drivers/media/rc/ir-imon-decoder.c @@ -8,7 +8,7 @@ #include <linux/module.h> #include "rc-core-priv.h" -#define IMON_UNIT 415662 /* ns */ +#define IMON_UNIT 416 /* us */ #define IMON_BITS 30 #define IMON_CHKBITS (BIT(30) | BIT(25) | BIT(24) | BIT(22) | \ BIT(21) | BIT(20) | BIT(19) | BIT(18) | \ @@ -102,8 +102,7 @@ static int ir_imon_decode(struct rc_dev *dev, struct ir_raw_event ev) dev_dbg(&dev->dev, "iMON decode started at state %d bitno %d (%uus %s)\n", - data->state, data->count, TO_US(ev.duration), - TO_STR(ev.pulse)); + data->state, data->count, ev.duration, TO_STR(ev.pulse)); /* * Since iMON protocol is a series of bits, if at any point @@ -116,7 +115,7 @@ static int ir_imon_decode(struct rc_dev *dev, struct ir_raw_event ev) * we're at a new scancode. */ if (data->state == STATE_ERROR) { - if (!ev.pulse && ev.duration > MS_TO_NS(10)) + if (!ev.pulse && ev.duration > MS_TO_US(10)) data->state = STATE_INACTIVE; return 0; } @@ -169,8 +168,7 @@ static int ir_imon_decode(struct rc_dev *dev, struct ir_raw_event ev) err_out: dev_dbg(&dev->dev, "iMON decode failed at state %d bitno %d (%uus %s)\n", - data->state, data->count, TO_US(ev.duration), - TO_STR(ev.pulse)); + data->state, data->count, ev.duration, TO_STR(ev.pulse)); data->state = STATE_ERROR; diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c index 864d9e316c33..470f2e1fd507 100644 --- a/drivers/media/rc/ir-jvc-decoder.c +++ b/drivers/media/rc/ir-jvc-decoder.c @@ -9,7 +9,7 @@ #include "rc-core-priv.h" #define JVC_NBITS 16 /* dev(8) + func(8) */ -#define JVC_UNIT 525000 /* ns */ +#define JVC_UNIT 525 /* us */ #define JVC_HEADER_PULSE (16 * JVC_UNIT) /* lack of header -> repeat */ #define JVC_HEADER_SPACE (8 * JVC_UNIT) #define JVC_BIT_PULSE (1 * JVC_UNIT) @@ -49,7 +49,7 @@ static int ir_jvc_decode(struct rc_dev *dev, struct ir_raw_event ev) goto out; dev_dbg(&dev->dev, "JVC decode started at state %d (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state, ev.duration, TO_STR(ev.pulse)); again: switch (data->state) { @@ -157,7 +157,7 @@ again: out: dev_dbg(&dev->dev, "JVC decode failed at state %d (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state, ev.duration, TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c index cfe837f773c1..be8f2756a444 100644 --- a/drivers/media/rc/ir-mce_kbd-decoder.c +++ b/drivers/media/rc/ir-mce_kbd-decoder.c @@ -21,7 +21,7 @@ * input device for the remote, rather than the keyboard/mouse one. */ -#define MCIR2_UNIT 333333 /* ns */ +#define MCIR2_UNIT 333 /* us */ #define MCIR2_HEADER_NBITS 5 #define MCIR2_MOUSE_NBITS 29 #define MCIR2_KEYBOARD_NBITS 32 @@ -231,7 +231,7 @@ static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev) again: dev_dbg(&dev->dev, "started at state %i (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state, ev.duration, TO_STR(ev.pulse)); if (!geq_margin(ev.duration, MCIR2_UNIT, MCIR2_UNIT / 2)) return 0; @@ -344,7 +344,7 @@ again: } lsc.scancode = scancode; - ir_lirc_scancode_event(dev, &lsc); + lirc_scancode_event(dev, &lsc); data->state = STATE_INACTIVE; input_event(dev->input_dev, EV_MSC, MSC_SCAN, scancode); input_sync(dev->input_dev); @@ -353,7 +353,7 @@ again: out: dev_dbg(&dev->dev, "failed at state %i (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state, ev.duration, TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c index 6a8973ae3684..b4c3e4baf34d 100644 --- a/drivers/media/rc/ir-nec-decoder.c +++ b/drivers/media/rc/ir-nec-decoder.c @@ -8,7 +8,7 @@ #include "rc-core-priv.h" #define NEC_NBITS 32 -#define NEC_UNIT 562500 /* ns */ +#define NEC_UNIT 563 /* us */ #define NEC_HEADER_PULSE (16 * NEC_UNIT) #define NECX_HEADER_PULSE (8 * NEC_UNIT) /* Less common NEC variant */ #define NEC_HEADER_SPACE (8 * NEC_UNIT) @@ -50,7 +50,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) } dev_dbg(&dev->dev, "NEC decode started at state %d (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state, ev.duration, TO_STR(ev.pulse)); switch (data->state) { @@ -163,7 +163,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) } dev_dbg(&dev->dev, "NEC decode failed at count %d state %d (%uus %s)\n", - data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->count, data->state, ev.duration, TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c index 63624654a71e..d58b6226afeb 100644 --- a/drivers/media/rc/ir-rc5-decoder.c +++ b/drivers/media/rc/ir-rc5-decoder.c @@ -16,7 +16,7 @@ #define RC5_SZ_NBITS 15 #define RC5X_NBITS 20 #define CHECK_RC5X_NBITS 8 -#define RC5_UNIT 888888 /* ns */ +#define RC5_UNIT 889 /* us */ #define RC5_BIT_START (1 * RC5_UNIT) #define RC5_BIT_END (1 * RC5_UNIT) #define RC5X_SPACE (4 * RC5_UNIT) @@ -55,7 +55,7 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev) again: dev_dbg(&dev->dev, "RC5(x/sz) decode started at state %i (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state, ev.duration, TO_STR(ev.pulse)); if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2)) return 0; @@ -164,7 +164,7 @@ again: out: dev_dbg(&dev->dev, "RC5(x/sz) decode failed at state %i count %d (%uus %s)\n", - data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state, data->count, ev.duration, TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index 0cda78f72fd8..0657ad5eef48 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -15,7 +15,7 @@ * RC6-6A-32 (MCE version with toggle bit in body) */ -#define RC6_UNIT 444444 /* nanosecs */ +#define RC6_UNIT 444 /* microseconds */ #define RC6_HEADER_NBITS 4 /* not including toggle bit */ #define RC6_0_NBITS 16 #define RC6_6A_32_NBITS 32 @@ -95,7 +95,7 @@ static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev) again: dev_dbg(&dev->dev, "RC6 decode started at state %i (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state, ev.duration, TO_STR(ev.pulse)); if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) return 0; @@ -270,7 +270,7 @@ again: out: dev_dbg(&dev->dev, "RC6 decode failed at state %i (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state, ev.duration, TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } diff --git a/drivers/media/rc/ir-rcmm-decoder.c b/drivers/media/rc/ir-rcmm-decoder.c index 028df5cb1828..fd9ec69a3718 100644 --- a/drivers/media/rc/ir-rcmm-decoder.c +++ b/drivers/media/rc/ir-rcmm-decoder.c @@ -6,12 +6,12 @@ #include "rc-core-priv.h" #include <linux/module.h> -#define RCMM_UNIT 166667 /* nanosecs */ -#define RCMM_PREFIX_PULSE 416666 /* 166666.666666666*2.5 */ -#define RCMM_PULSE_0 277777 /* 166666.666666666*(1+2/3) */ -#define RCMM_PULSE_1 444444 /* 166666.666666666*(2+2/3) */ -#define RCMM_PULSE_2 611111 /* 166666.666666666*(3+2/3) */ -#define RCMM_PULSE_3 777778 /* 166666.666666666*(4+2/3) */ +#define RCMM_UNIT 166 /* microseconds */ +#define RCMM_PREFIX_PULSE 417 /* 166.666666666666*2.5 */ +#define RCMM_PULSE_0 278 /* 166.666666666666*(1+2/3) */ +#define RCMM_PULSE_1 444 /* 166.666666666666*(2+2/3) */ +#define RCMM_PULSE_2 611 /* 166.666666666666*(3+2/3) */ +#define RCMM_PULSE_3 778 /* 166.666666666666*(4+2/3) */ enum rcmm_state { STATE_INACTIVE, @@ -64,8 +64,8 @@ static int ir_rcmm_decode(struct rc_dev *dev, struct ir_raw_event ev) int value; if (!(dev->enabled_protocols & (RC_PROTO_BIT_RCMM32 | - RC_PROTO_BIT_RCMM24 | - RC_PROTO_BIT_RCMM12))) + RC_PROTO_BIT_RCMM24 | + RC_PROTO_BIT_RCMM12))) return 0; if (!is_timing_event(ev)) { @@ -165,7 +165,7 @@ static int ir_rcmm_decode(struct rc_dev *dev, struct ir_raw_event ev) } dev_dbg(&dev->dev, "RC-MM decode failed at count %d state %d (%uus %s)\n", - data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->count, data->state, ev.duration, TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c index dd6ee1e339d6..bfc181be1044 100644 --- a/drivers/media/rc/ir-sanyo-decoder.c +++ b/drivers/media/rc/ir-sanyo-decoder.c @@ -17,7 +17,7 @@ #include "rc-core-priv.h" #define SANYO_NBITS (13+13+8+8) -#define SANYO_UNIT 562500 /* ns */ +#define SANYO_UNIT 563 /* us */ #define SANYO_HEADER_PULSE (16 * SANYO_UNIT) #define SANYO_HEADER_SPACE (8 * SANYO_UNIT) #define SANYO_BIT_PULSE (1 * SANYO_UNIT) @@ -59,7 +59,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) } dev_dbg(&dev->dev, "SANYO decode started at state %d (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state, ev.duration, TO_STR(ev.pulse)); switch (data->state) { @@ -158,7 +158,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) } dev_dbg(&dev->dev, "SANYO decode failed at count %d state %d (%uus %s)\n", - data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->count, data->state, ev.duration, TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } diff --git a/drivers/media/rc/ir-sharp-decoder.c b/drivers/media/rc/ir-sharp-decoder.c index 37fab0919131..d09c38c07dbd 100644 --- a/drivers/media/rc/ir-sharp-decoder.c +++ b/drivers/media/rc/ir-sharp-decoder.c @@ -12,7 +12,7 @@ #include "rc-core-priv.h" #define SHARP_NBITS 15 -#define SHARP_UNIT 40000 /* ns */ +#define SHARP_UNIT 40 /* us */ #define SHARP_BIT_PULSE (8 * SHARP_UNIT) /* 320us */ #define SHARP_BIT_0_PERIOD (25 * SHARP_UNIT) /* 1ms (680us space) */ #define SHARP_BIT_1_PERIOD (50 * SHARP_UNIT) /* 2ms (1680ms space) */ @@ -47,7 +47,7 @@ static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev) } dev_dbg(&dev->dev, "Sharp decode started at state %d (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state, ev.duration, TO_STR(ev.pulse)); switch (data->state) { @@ -159,7 +159,7 @@ static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev) } dev_dbg(&dev->dev, "Sharp decode failed at count %d state %d (%uus %s)\n", - data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->count, data->state, ev.duration, TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c index 7d9a7c000c75..d760d52abaa2 100644 --- a/drivers/media/rc/ir-sony-decoder.c +++ b/drivers/media/rc/ir-sony-decoder.c @@ -8,7 +8,7 @@ #include <linux/module.h> #include "rc-core-priv.h" -#define SONY_UNIT 600000 /* ns */ +#define SONY_UNIT 600 /* us */ #define SONY_HEADER_PULSE (4 * SONY_UNIT) #define SONY_HEADER_SPACE (1 * SONY_UNIT) #define SONY_BIT_0_PULSE (1 * SONY_UNIT) @@ -48,7 +48,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) goto out; dev_dbg(&dev->dev, "Sony decode started at state %d (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state, ev.duration, TO_STR(ev.pulse)); switch (data->state) { @@ -154,7 +154,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev) out: dev_dbg(&dev->dev, "Sony decode failed at state %d (%uus %s)\n", - data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state, ev.duration, TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; diff --git a/drivers/media/rc/ir-xmp-decoder.c b/drivers/media/rc/ir-xmp-decoder.c index 4c3d03876200..ff94f48bda32 100644 --- a/drivers/media/rc/ir-xmp-decoder.c +++ b/drivers/media/rc/ir-xmp-decoder.c @@ -12,11 +12,12 @@ #include <linux/module.h> #include "rc-core-priv.h" -#define XMP_UNIT 136000 /* ns */ -#define XMP_LEADER 210000 /* ns */ -#define XMP_NIBBLE_PREFIX 760000 /* ns */ -#define XMP_HALFFRAME_SPACE 13800000 /* ns */ -#define XMP_TRAILER_SPACE 20000000 /* should be 80ms but not all dureation supliers can go that high */ +#define XMP_UNIT 136 /* us */ +#define XMP_LEADER 210 /* us */ +#define XMP_NIBBLE_PREFIX 760 /* us */ +#define XMP_HALFFRAME_SPACE 13800 /* us */ +/* should be 80ms but not all duration supliers can go that high */ +#define XMP_TRAILER_SPACE 20000 enum xmp_state { STATE_INACTIVE, @@ -42,7 +43,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) } dev_dbg(&dev->dev, "XMP decode started at state %d %d (%uus %s)\n", - data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse)); + data->state, data->count, ev.duration, TO_STR(ev.pulse)); switch (data->state) { @@ -183,7 +184,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) } dev_dbg(&dev->dev, "XMP decode failed at count %d state %d (%uus %s)\n", - data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); + data->count, data->state, ev.duration, TO_STR(ev.pulse)); data->state = STATE_INACTIVE; return -EINVAL; } diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c index 5c7a7500a925..e0242c9b6aeb 100644 --- a/drivers/media/rc/ir_toy.c +++ b/drivers/media/rc/ir_toy.c @@ -38,8 +38,8 @@ static const u8 COMMAND_TXSTART[] = { 0x26, 0x24, 0x25, 0x03 }; #define LEN_SAMPLEMODEPROTO 3 #define MIN_FW_VERSION 20 -#define UNIT_NS 21333 -#define MAX_TIMEOUT_NS (UNIT_NS * U16_MAX) +#define UNIT_US 21 +#define MAX_TIMEOUT_US (UNIT_US * U16_MAX) #define MAX_PACKET 64 @@ -131,7 +131,7 @@ static void irtoy_response(struct irtoy *irtoy, u32 len) if (v == 0xffff) { rawir.pulse = false; } else { - rawir.duration = v * UNIT_NS; + rawir.duration = v * UNIT_US; ir_raw_event_store_with_timeout(irtoy->rc, &rawir); } @@ -302,7 +302,7 @@ static int irtoy_tx(struct rc_dev *rc, uint *txbuf, uint count) return -ENOMEM; for (i = 0; i < count; i++) { - u16 v = DIV_ROUND_CLOSEST(US_TO_NS(txbuf[i]), UNIT_NS); + u16 v = DIV_ROUND_CLOSEST(txbuf[i], UNIT_US); if (!v) v = 1; @@ -438,7 +438,7 @@ static int irtoy_probe(struct usb_interface *intf, rc->tx_ir = irtoy_tx; rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; rc->map_name = RC_MAP_RC6_MCE; - rc->rx_resolution = UNIT_NS; + rc->rx_resolution = UNIT_US; rc->timeout = IR_DEFAULT_TIMEOUT; /* @@ -450,8 +450,8 @@ static int irtoy_probe(struct usb_interface *intf, * * So, make timeout a largish minimum which works with most protocols. */ - rc->min_timeout = MS_TO_NS(40); - rc->max_timeout = MAX_TIMEOUT_NS; + rc->min_timeout = MS_TO_US(40); + rc->max_timeout = MAX_TIMEOUT_US; err = rc_register_device(rc); if (err) diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 07667c04c1d2..a905113fef6e 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -176,14 +176,14 @@ static void ite_decode_bytes(struct ite_dev *dev, const u8 * data, int if (next_one > 0) { ev.pulse = true; ev.duration = - ITE_BITS_TO_NS(next_one, sample_period); + ITE_BITS_TO_US(next_one, sample_period); ir_raw_event_store_with_filter(dev->rdev, &ev); } while (next_one < size) { next_zero = find_next_zero_bit_le(ldata, size, next_one + 1); ev.pulse = false; - ev.duration = ITE_BITS_TO_NS(next_zero - next_one, sample_period); + ev.duration = ITE_BITS_TO_US(next_zero - next_one, sample_period); ir_raw_event_store_with_filter(dev->rdev, &ev); if (next_zero < size) { @@ -193,7 +193,7 @@ static void ite_decode_bytes(struct ite_dev *dev, const u8 * data, int next_zero + 1); ev.pulse = true; ev.duration = - ITE_BITS_TO_NS(next_one - next_zero, + ITE_BITS_TO_US(next_one - next_zero, sample_period); ir_raw_event_store_with_filter (dev->rdev, &ev); @@ -1555,9 +1555,9 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id rdev->timeout = IR_DEFAULT_TIMEOUT; rdev->max_timeout = 10 * IR_DEFAULT_TIMEOUT; rdev->rx_resolution = ITE_BAUDRATE_DIVISOR * - itdev->params.sample_period; + itdev->params.sample_period / 1000; rdev->tx_resolution = ITE_BAUDRATE_DIVISOR * - itdev->params.sample_period; + itdev->params.sample_period / 1000; /* set up transmitter related values if needed */ if (itdev->params.hw_tx_capable) { diff --git a/drivers/media/rc/ite-cir.h b/drivers/media/rc/ite-cir.h index f04c4b34ff0c..4954470448a7 100644 --- a/drivers/media/rc/ite-cir.h +++ b/drivers/media/rc/ite-cir.h @@ -146,8 +146,8 @@ struct ite_dev { #define ITE_DEFAULT_CARRIER_FREQ 38000 /* convert bits to us */ -#define ITE_BITS_TO_NS(bits, sample_period) \ -((u32) ((bits) * ITE_BAUDRATE_DIVISOR * sample_period)) +#define ITE_BITS_TO_US(bits, sample_period) \ +((u32)((bits) * ITE_BAUDRATE_DIVISOR * (sample_period) / 1000)) /* * n in RDCR produces a tolerance of +/- n * 6.25% around the center diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 583e4f32a0da..220363b9a868 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -30,12 +30,12 @@ static DEFINE_IDA(lirc_ida); static struct class *lirc_class; /** - * ir_lirc_raw_event() - Send raw IR data to lirc to be relayed to userspace + * lirc_raw_event() - Send raw IR data to lirc to be relayed to userspace * * @dev: the struct rc_dev descriptor of the device * @ev: the struct ir_raw_event descriptor of the pulse/space */ -void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) +void lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) { unsigned long flags; struct lirc_fh *fh; @@ -67,17 +67,16 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) dev->gap = true; dev->gap_duration = ev.duration; - sample = LIRC_TIMEOUT(ev.duration / 1000); + sample = LIRC_TIMEOUT(ev.duration); dev_dbg(&dev->dev, "timeout report (duration: %d)\n", sample); /* Normal sample */ } else { if (dev->gap) { - dev->gap_duration += ktime_to_ns(ktime_sub(ktime_get(), + dev->gap_duration += ktime_to_us(ktime_sub(ktime_get(), dev->gap_start)); - /* Convert to ms and cap by LIRC_VALUE_MASK */ - do_div(dev->gap_duration, 1000); + /* Cap by LIRC_VALUE_MASK */ dev->gap_duration = min_t(u64, dev->gap_duration, LIRC_VALUE_MASK); @@ -89,10 +88,10 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) dev->gap = false; } - sample = ev.pulse ? LIRC_PULSE(ev.duration / 1000) : - LIRC_SPACE(ev.duration / 1000); + sample = ev.pulse ? LIRC_PULSE(ev.duration) : + LIRC_SPACE(ev.duration); dev_dbg(&dev->dev, "delivering %uus %s to lirc_dev\n", - TO_US(ev.duration), TO_STR(ev.pulse)); + ev.duration, TO_STR(ev.pulse)); } /* @@ -112,12 +111,12 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) } /** - * ir_lirc_scancode_event() - Send scancode data to lirc to be relayed to + * lirc_scancode_event() - Send scancode data to lirc to be relayed to * userspace. This can be called in atomic context. * @dev: the struct rc_dev descriptor of the device * @lsc: the struct lirc_scancode describing the decoded scancode */ -void ir_lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc) +void lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc) { unsigned long flags; struct lirc_fh *fh; @@ -131,9 +130,9 @@ void ir_lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc) } spin_unlock_irqrestore(&dev->lirc_fh_lock, flags); } -EXPORT_SYMBOL_GPL(ir_lirc_scancode_event); +EXPORT_SYMBOL_GPL(lirc_scancode_event); -static int ir_lirc_open(struct inode *inode, struct file *file) +static int lirc_open(struct inode *inode, struct file *file) { struct rc_dev *dev = container_of(inode->i_cdev, struct rc_dev, lirc_cdev); @@ -201,7 +200,7 @@ out_fh: return retval; } -static int ir_lirc_close(struct inode *inode, struct file *file) +static int lirc_close(struct inode *inode, struct file *file) { struct lirc_fh *fh = file->private_data; struct rc_dev *dev = fh->rc; @@ -223,8 +222,8 @@ static int ir_lirc_close(struct inode *inode, struct file *file) return 0; } -static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf, - size_t n, loff_t *ppos) +static ssize_t lirc_transmit(struct file *file, const char __user *buf, + size_t n, loff_t *ppos) { struct lirc_fh *fh = file->private_data; struct rc_dev *dev = fh->rc; @@ -296,8 +295,7 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf, } for (i = 0; i < count; i++) - /* Convert from NS to US */ - txbuf[i] = DIV_ROUND_UP(raw[i].duration, 1000); + txbuf[i] = raw[i].duration; if (dev->s_tx_carrier) { int carrier = ir_raw_encode_carrier(scan.rc_proto); @@ -325,7 +323,7 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf, } for (i = 0; i < count; i++) { - if (txbuf[i] > IR_MAX_DURATION / 1000 - duration || !txbuf[i]) { + if (txbuf[i] > IR_MAX_DURATION - duration || !txbuf[i]) { ret = -EINVAL; goto out_kfree; } @@ -365,8 +363,7 @@ out_unlock: return ret; } -static long ir_lirc_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static long lirc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct lirc_fh *fh = file->private_data; struct rc_dev *dev = fh->rc; @@ -517,7 +514,7 @@ static long ir_lirc_ioctl(struct file *file, unsigned int cmd, if (!dev->rx_resolution) ret = -ENOTTY; else - val = dev->rx_resolution / 1000; + val = dev->rx_resolution; break; case LIRC_SET_WIDEBAND_RECEIVER: @@ -539,31 +536,26 @@ static long ir_lirc_ioctl(struct file *file, unsigned int cmd, if (!dev->max_timeout) ret = -ENOTTY; else - val = DIV_ROUND_UP(dev->min_timeout, 1000); + val = dev->min_timeout; break; case LIRC_GET_MAX_TIMEOUT: if (!dev->max_timeout) ret = -ENOTTY; else - val = dev->max_timeout / 1000; + val = dev->max_timeout; break; case LIRC_SET_REC_TIMEOUT: if (!dev->max_timeout) { ret = -ENOTTY; - } else if (val > U32_MAX / 1000) { - /* Check for multiply overflow */ - ret = -EINVAL; } else { - u32 tmp = val * 1000; - - if (tmp < dev->min_timeout || tmp > dev->max_timeout) + if (val < dev->min_timeout || val > dev->max_timeout) ret = -EINVAL; else if (dev->s_timeout) - ret = dev->s_timeout(dev, tmp); + ret = dev->s_timeout(dev, val); else - dev->timeout = tmp; + dev->timeout = val; } break; @@ -571,7 +563,7 @@ static long ir_lirc_ioctl(struct file *file, unsigned int cmd, if (!dev->timeout) ret = -ENOTTY; else - val = DIV_ROUND_UP(dev->timeout, 1000); + val = dev->timeout; break; case LIRC_SET_REC_TIMEOUT_REPORTS: @@ -593,7 +585,7 @@ out: return ret; } -static __poll_t ir_lirc_poll(struct file *file, struct poll_table_struct *wait) +static __poll_t lirc_poll(struct file *file, struct poll_table_struct *wait) { struct lirc_fh *fh = file->private_data; struct rc_dev *rcdev = fh->rc; @@ -616,8 +608,8 @@ static __poll_t ir_lirc_poll(struct file *file, struct poll_table_struct *wait) return events; } -static ssize_t ir_lirc_read_mode2(struct file *file, char __user *buffer, - size_t length) +static ssize_t lirc_read_mode2(struct file *file, char __user *buffer, + size_t length) { struct lirc_fh *fh = file->private_data; struct rc_dev *rcdev = fh->rc; @@ -654,8 +646,8 @@ static ssize_t ir_lirc_read_mode2(struct file *file, char __user *buffer, return copied; } -static ssize_t ir_lirc_read_scancode(struct file *file, char __user *buffer, - size_t length) +static ssize_t lirc_read_scancode(struct file *file, char __user *buffer, + size_t length) { struct lirc_fh *fh = file->private_data; struct rc_dev *rcdev = fh->rc; @@ -693,8 +685,8 @@ static ssize_t ir_lirc_read_scancode(struct file *file, char __user *buffer, return copied; } -static ssize_t ir_lirc_read(struct file *file, char __user *buffer, - size_t length, loff_t *ppos) +static ssize_t lirc_read(struct file *file, char __user *buffer, size_t length, + loff_t *ppos) { struct lirc_fh *fh = file->private_data; struct rc_dev *rcdev = fh->rc; @@ -706,20 +698,20 @@ static ssize_t ir_lirc_read(struct file *file, char __user *buffer, return -ENODEV; if (fh->rec_mode == LIRC_MODE_MODE2) - return ir_lirc_read_mode2(file, buffer, length); + return lirc_read_mode2(file, buffer, length); else /* LIRC_MODE_SCANCODE */ - return ir_lirc_read_scancode(file, buffer, length); + return lirc_read_scancode(file, buffer, length); } static const struct file_operations lirc_fops = { .owner = THIS_MODULE, - .write = ir_lirc_transmit_ir, - .unlocked_ioctl = ir_lirc_ioctl, + .write = lirc_transmit, + .unlocked_ioctl = lirc_ioctl, .compat_ioctl = compat_ptr_ioctl, - .read = ir_lirc_read, - .poll = ir_lirc_poll, - .open = ir_lirc_open, - .release = ir_lirc_close, + .read = lirc_read, + .poll = lirc_poll, + .open = lirc_open, + .release = lirc_close, .llseek = no_llseek, }; @@ -730,7 +722,7 @@ static void lirc_release_device(struct device *ld) put_device(&rcdev->dev); } -int ir_lirc_register(struct rc_dev *dev) +int lirc_register(struct rc_dev *dev) { const char *rx_type, *tx_type; int err, minor; @@ -784,7 +776,7 @@ out_ida: return err; } -void ir_lirc_unregister(struct rc_dev *dev) +void lirc_unregister(struct rc_dev *dev) { unsigned long flags; struct lirc_fh *fh; @@ -811,8 +803,7 @@ int __init lirc_dev_init(void) return PTR_ERR(lirc_class); } - retval = alloc_chrdev_region(&lirc_base_dev, 0, RC_DEV_MAX, - "BaseRemoteCtl"); + retval = alloc_chrdev_region(&lirc_base_dev, 0, RC_DEV_MAX, "lirc"); if (retval) { class_destroy(lirc_class); pr_err("alloc_chrdev_region failed\n"); diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 98681ba10428..f1dbd059ed08 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -1070,7 +1070,7 @@ static int mceusb_set_timeout(struct rc_dev *dev, unsigned int timeout) struct mceusb_dev *ir = dev->priv; unsigned int units; - units = DIV_ROUND_CLOSEST(timeout, US_TO_NS(MCE_TIME_UNIT)); + units = DIV_ROUND_CLOSEST(timeout, MCE_TIME_UNIT); cmdbuf[2] = units >> 8; cmdbuf[3] = units; @@ -1196,7 +1196,7 @@ static void mceusb_handle_command(struct mceusb_dev *ir, u8 *buf_in) switch (subcmd) { /* 2-byte return value commands */ case MCE_RSP_EQIRTIMEOUT: - ir->rc->timeout = US_TO_NS((*hi << 8 | *lo) * MCE_TIME_UNIT); + ir->rc->timeout = (*hi << 8 | *lo) * MCE_TIME_UNIT; break; case MCE_RSP_EQIRNUMPORTS: ir->num_txports = *hi; @@ -1291,9 +1291,9 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) ir->pulse_tunit += rawir.duration; ir->pulse_count++; } - rawir.duration *= US_TO_NS(MCE_TIME_UNIT); + rawir.duration *= MCE_TIME_UNIT; - dev_dbg(ir->dev, "Storing %s %u ns (%02x)", + dev_dbg(ir->dev, "Storing %s %u us (%02x)", rawir.pulse ? "pulse" : "space", rawir.duration, ir->buf_in[i]); @@ -1605,8 +1605,8 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir) rc->dev.parent = dev; rc->priv = ir; rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; - rc->min_timeout = US_TO_NS(MCE_TIME_UNIT); - rc->timeout = MS_TO_NS(100); + rc->min_timeout = MCE_TIME_UNIT; + rc->timeout = MS_TO_US(100); if (!mceusb_model[ir->model].broken_irtimeout) { rc->s_timeout = mceusb_set_timeout; rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT; diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c index 51c6dd3406a0..dad55950dfc6 100644 --- a/drivers/media/rc/meson-ir.c +++ b/drivers/media/rc/meson-ir.c @@ -86,7 +86,7 @@ static irqreturn_t meson_ir_irq(int irqno, void *dev_id) duration = readl_relaxed(ir->reg + IR_DEC_REG1); duration = FIELD_GET(REG1_TIME_IV_MASK, duration); - rawir.duration = US_TO_NS(duration * MESON_TRATE); + rawir.duration = duration * MESON_TRATE; status = readl_relaxed(ir->reg + IR_DEC_STATUS); rawir.pulse = !!(status & STATUS_IR_DEC_IN); @@ -133,7 +133,7 @@ static int meson_ir_probe(struct platform_device *pdev) map_name = of_get_property(node, "linux,rc-map-name", NULL); ir->rc->map_name = map_name ? map_name : RC_MAP_EMPTY; ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; - ir->rc->rx_resolution = US_TO_NS(MESON_TRATE); + ir->rc->rx_resolution = MESON_TRATE; ir->rc->min_timeout = 1; ir->rc->timeout = IR_DEFAULT_TIMEOUT; ir->rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT; diff --git a/drivers/media/rc/mtk-cir.c b/drivers/media/rc/mtk-cir.c index a0c94ab322c7..5051a5e5244b 100644 --- a/drivers/media/rc/mtk-cir.c +++ b/drivers/media/rc/mtk-cir.c @@ -52,8 +52,8 @@ #define MTK_IR_END(v, p) ((v) == MTK_MAX_SAMPLES && (p) == 0) /* Number of registers to record the pulse width */ #define MTK_CHKDATA_SZ 17 -/* Sample period in ns */ -#define MTK_IR_SAMPLE 46000 +/* Sample period in us */ +#define MTK_IR_SAMPLE 46 enum mtk_fields { /* Register to setting software sampling period */ diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 52d246dc5b3d..8a37f083fe3d 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -653,8 +653,7 @@ static int nvt_ir_raw_set_wakeup_filter(struct rc_dev *dev, /* Inspect the ir samples */ for (i = 0, count = 0; i < ret && count < WAKEUP_MAX_SIZE; ++i) { - /* NS to US */ - val = DIV_ROUND_UP(raw[i].duration, 1000L) / SAMPLE_PERIOD; + val = raw[i].duration / SAMPLE_PERIOD; /* Split too large values into several smaller ones */ while (val > 0 && count < WAKEUP_MAX_SIZE) { @@ -721,8 +720,7 @@ static void nvt_process_rx_ir_data(struct nvt_dev *nvt) sample = nvt->buf[i]; rawir.pulse = ((sample & BUF_PULSE_BIT) != 0); - rawir.duration = US_TO_NS((sample & BUF_LEN_MASK) - * SAMPLE_PERIOD); + rawir.duration = (sample & BUF_LEN_MASK) * SAMPLE_PERIOD; nvt_dbg("Storing %s with duration %d", rawir.pulse ? "pulse" : "space", rawir.duration); @@ -1000,9 +998,9 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) rdev->input_id.version = nvt->chip_minor; rdev->driver_name = NVT_DRIVER_NAME; rdev->map_name = RC_MAP_RC6_MCE; - rdev->timeout = MS_TO_NS(100); + rdev->timeout = MS_TO_US(100); /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */ - rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD); + rdev->rx_resolution = CIR_SAMPLE_PERIOD; #if 0 rdev->min_timeout = XYZ; rdev->max_timeout = XYZ; diff --git a/drivers/media/rc/nuvoton-cir.h b/drivers/media/rc/nuvoton-cir.h index 0cf301d1e163..ed7d93beaa28 100644 --- a/drivers/media/rc/nuvoton-cir.h +++ b/drivers/media/rc/nuvoton-cir.h @@ -94,7 +94,7 @@ struct nvt_dev { #define CIR_IOREG_LENGTH 0x0f /* RX limit length, 8 high bits for SLCH, 8 low bits for SLCL */ -#define CIR_RX_LIMIT_COUNT (IR_DEFAULT_TIMEOUT / US_TO_NS(SAMPLE_PERIOD)) +#define CIR_RX_LIMIT_COUNT (IR_DEFAULT_TIMEOUT / SAMPLE_PERIOD) /* CIR Regs */ #define CIR_IRCON 0x00 diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 1eeab277a08e..62f032dffd33 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -193,7 +193,6 @@ static inline bool is_timing_event(struct ir_raw_event ev) return !ev.carrier_report && !ev.reset; } -#define TO_US(duration) DIV_ROUND_CLOSEST((duration), 1000) #define TO_STR(is_pulse) ((is_pulse) ? "pulse" : "space") /* functions for IR encoders */ @@ -322,20 +321,20 @@ void ir_raw_init(void); #ifdef CONFIG_LIRC int lirc_dev_init(void); void lirc_dev_exit(void); -void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev); -void ir_lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc); -int ir_lirc_register(struct rc_dev *dev); -void ir_lirc_unregister(struct rc_dev *dev); +void lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev); +void lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc); +int lirc_register(struct rc_dev *dev); +void lirc_unregister(struct rc_dev *dev); struct rc_dev *rc_dev_get_from_fd(int fd); #else static inline int lirc_dev_init(void) { return 0; } static inline void lirc_dev_exit(void) {} -static inline void ir_lirc_raw_event(struct rc_dev *dev, - struct ir_raw_event ev) { } -static inline void ir_lirc_scancode_event(struct rc_dev *dev, - struct lirc_scancode *lsc) { } -static inline int ir_lirc_register(struct rc_dev *dev) { return 0; } -static inline void ir_lirc_unregister(struct rc_dev *dev) { } +static inline void lirc_raw_event(struct rc_dev *dev, + struct ir_raw_event ev) { } +static inline void lirc_scancode_event(struct rc_dev *dev, + struct lirc_scancode *lsc) { } +static inline int lirc_register(struct rc_dev *dev) { return 0; } +static inline void lirc_unregister(struct rc_dev *dev) { } #endif /* diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c index 39dd46bbd0c1..c65bba4ec473 100644 --- a/drivers/media/rc/rc-ir-raw.c +++ b/drivers/media/rc/rc-ir-raw.c @@ -42,7 +42,7 @@ static int ir_raw_event_thread(void *data) if (dev->enabled_protocols & handler->protocols || !handler->protocols) handler->decode(dev, ev); - ir_lirc_raw_event(dev, ev); + lirc_raw_event(dev, ev); raw->prev_ev = ev; } mutex_unlock(&ir_raw_handler_lock); @@ -77,7 +77,7 @@ int ir_raw_event_store(struct rc_dev *dev, struct ir_raw_event *ev) return -EINVAL; dev_dbg(&dev->dev, "sample: (%05dus %s)\n", - TO_US(ev->duration), TO_STR(ev->pulse)); + ev->duration, TO_STR(ev->pulse)); if (!kfifo_put(&dev->raw->kfifo, *ev)) { dev_err(&dev->dev, "IR event FIFO is full!\n"); @@ -108,7 +108,7 @@ int ir_raw_event_store_edge(struct rc_dev *dev, bool pulse) return -EINVAL; now = ktime_get(); - ev.duration = ktime_to_ns(ktime_sub(now, dev->raw->last_event)); + ev.duration = ktime_to_us(ktime_sub(now, dev->raw->last_event)); ev.pulse = !pulse; return ir_raw_event_store_with_timeout(dev, &ev); @@ -275,7 +275,7 @@ static int change_protocol(struct rc_dev *dev, u64 *rc_proto) if (timeout == 0) timeout = IR_DEFAULT_TIMEOUT; else - timeout += MS_TO_NS(10); + timeout += MS_TO_US(10); if (timeout < dev->min_timeout) timeout = dev->min_timeout; @@ -561,17 +561,17 @@ static void ir_raw_edge_handle(struct timer_list *t) spin_lock_irqsave(&dev->raw->edge_spinlock, flags); interval = ktime_sub(ktime_get(), dev->raw->last_event); - if (ktime_to_ns(interval) >= dev->timeout) { + if (ktime_to_us(interval) >= dev->timeout) { struct ir_raw_event ev = { .timeout = true, - .duration = ktime_to_ns(interval) + .duration = ktime_to_us(interval) }; ir_raw_event_store(dev, &ev); } else { mod_timer(&dev->raw->edge_handle, - jiffies + nsecs_to_jiffies(dev->timeout - - ktime_to_ns(interval))); + jiffies + usecs_to_jiffies(dev->timeout - + ktime_to_us(interval))); } spin_unlock_irqrestore(&dev->raw->edge_spinlock, flags); diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c index ef8b83b707df..1ba3f96ffa7d 100644 --- a/drivers/media/rc/rc-loopback.c +++ b/drivers/media/rc/rc-loopback.c @@ -113,7 +113,7 @@ static int loop_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count) for (i = 0; i < count; i++) { rawir.pulse = i % 2 ? false : true; - rawir.duration = txbuf[i] * 1000; + rawir.duration = txbuf[i]; if (rawir.duration) ir_raw_event_store_with_filter(dev, &rawir); } @@ -219,11 +219,11 @@ static int __init loop_init(void) rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; rc->allowed_wakeup_protocols = RC_PROTO_BIT_ALL_IR_ENCODER; rc->encode_wakeup = true; - rc->timeout = 100 * 1000 * 1000; /* 100 ms */ + rc->timeout = MS_TO_US(100); /* 100 ms */ rc->min_timeout = 1; rc->max_timeout = UINT_MAX; - rc->rx_resolution = 1000; - rc->tx_resolution = 1000; + rc->rx_resolution = 1; + rc->tx_resolution = 1; rc->s_tx_mask = loop_set_tx_mask; rc->s_tx_carrier = loop_set_tx_carrier; rc->s_tx_duty_cycle = loop_set_tx_duty_cycle; diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index dee8a9f3d80a..1d811e5ffb55 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -747,7 +747,7 @@ void rc_repeat(struct rc_dev *dev) }; if (dev->allowed_protocols != RC_PROTO_BIT_CEC) - ir_lirc_scancode_event(dev, &sc); + lirc_scancode_event(dev, &sc); spin_lock_irqsave(&dev->keylock, flags); @@ -791,7 +791,7 @@ static void ir_do_keydown(struct rc_dev *dev, enum rc_proto protocol, }; if (dev->allowed_protocols != RC_PROTO_BIT_CEC) - ir_lirc_scancode_event(dev, &sc); + lirc_scancode_event(dev, &sc); if (new_event && dev->keypressed) ir_do_keyup(dev, false); @@ -1946,7 +1946,7 @@ int rc_register_device(struct rc_dev *dev) * keycodes with rc_keydown, so lirc must be registered first. */ if (dev->allowed_protocols != RC_PROTO_BIT_CEC) { - rc = ir_lirc_register(dev); + rc = lirc_register(dev); if (rc < 0) goto out_dev; } @@ -1972,7 +1972,7 @@ out_rx: rc_free_rx_device(dev); out_lirc: if (dev->allowed_protocols != RC_PROTO_BIT_CEC) - ir_lirc_unregister(dev); + lirc_unregister(dev); out_dev: device_del(&dev->dev); out_rx_free: @@ -2036,7 +2036,7 @@ void rc_unregister_device(struct rc_dev *dev) * that userspace polling will get notified. */ if (dev->allowed_protocols != RC_PROTO_BIT_CEC) - ir_lirc_unregister(dev); + lirc_unregister(dev); device_del(&dev->dev); diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index aad9526f3754..2cf3377ec63a 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -340,7 +340,7 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3) { struct ir_raw_event rawir = {}; struct device *dev; - unsigned int i, sig_size, single_len, offset, val; + unsigned int i, sig_size, offset, val; u32 mod_freq; dev = rr3->dev; @@ -361,7 +361,6 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3) for (i = 0; i < sig_size; i++) { offset = rr3->irdata.sigdata[i]; val = get_unaligned_be16(&rr3->irdata.lens[offset]); - single_len = redrat3_len_to_us(val); /* we should always get pulse/space/pulse/space samples */ if (i % 2) @@ -369,7 +368,7 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3) else rawir.pulse = true; - rawir.duration = US_TO_NS(single_len); + rawir.duration = redrat3_len_to_us(val); /* cap the value to IR_MAX_DURATION */ rawir.duration = (rawir.duration > IR_MAX_DURATION) ? IR_MAX_DURATION : rawir.duration; @@ -495,7 +494,7 @@ static u32 redrat3_get_timeout(struct redrat3_dev *rr3) return timeout; } -static int redrat3_set_timeout(struct rc_dev *rc_dev, unsigned int timeoutns) +static int redrat3_set_timeout(struct rc_dev *rc_dev, unsigned int timeoutus) { struct redrat3_dev *rr3 = rc_dev->priv; struct usb_device *udev = rr3->udev; @@ -507,7 +506,7 @@ static int redrat3_set_timeout(struct rc_dev *rc_dev, unsigned int timeoutns) if (!timeout) return -ENOMEM; - *timeout = cpu_to_be32(redrat3_us_to_len(timeoutns / 1000)); + *timeout = cpu_to_be32(redrat3_us_to_len(timeoutus)); ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), RR3_SET_IR_PARAM, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, RR3_IR_IO_SIG_TIMEOUT, 0, timeout, sizeof(*timeout), @@ -947,15 +946,15 @@ static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3) rc->dev.parent = dev; rc->priv = rr3; rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; - rc->min_timeout = MS_TO_NS(RR3_RX_MIN_TIMEOUT); - rc->max_timeout = MS_TO_NS(RR3_RX_MAX_TIMEOUT); - rc->timeout = US_TO_NS(redrat3_get_timeout(rr3)); + rc->min_timeout = MS_TO_US(RR3_RX_MIN_TIMEOUT); + rc->max_timeout = MS_TO_US(RR3_RX_MAX_TIMEOUT); + rc->timeout = redrat3_get_timeout(rr3); rc->s_timeout = redrat3_set_timeout; rc->tx_ir = redrat3_transmit_ir; rc->s_tx_carrier = redrat3_set_tx_carrier; rc->s_carrier_report = redrat3_wideband_receiver; rc->driver_name = DRIVER_NAME; - rc->rx_resolution = US_TO_NS(2); + rc->rx_resolution = 2; rc->map_name = RC_MAP_HAUPPAUGE; ret = rc_register_device(rc); diff --git a/drivers/media/rc/serial_ir.c b/drivers/media/rc/serial_ir.c index d77507ba0fb5..8cc28c92d05d 100644 --- a/drivers/media/rc/serial_ir.c +++ b/drivers/media/rc/serial_ir.c @@ -269,7 +269,7 @@ static void frbwrite(unsigned int l, bool is_pulse) if (ptr > 0 && is_pulse) { pulse += l; - if (pulse > 250000) { + if (pulse > 250) { ev.duration = space; ev.pulse = false; ir_raw_event_store_with_filter(serial_ir.rcdev, &ev); @@ -283,13 +283,13 @@ static void frbwrite(unsigned int l, bool is_pulse) } if (!is_pulse) { if (ptr == 0) { - if (l > 20000000) { + if (l > 20000) { space = l; ptr++; return; } } else { - if (l > 20000000) { + if (l > 20000) { space += pulse; if (space > IR_MAX_DURATION) space = IR_MAX_DURATION; @@ -376,7 +376,7 @@ static irqreturn_t serial_ir_irq_handler(int i, void *blah) sense = sense ? 0 : 1; } } else { - data = ktime_to_ns(delkt); + data = ktime_to_us(delkt); } frbwrite(data, !(dcd ^ sense)); serial_ir.lastkt = kt; @@ -528,7 +528,7 @@ static int serial_ir_probe(struct platform_device *dev) rcdev->min_timeout = 1; rcdev->timeout = IR_DEFAULT_TIMEOUT; rcdev->max_timeout = 10 * IR_DEFAULT_TIMEOUT; - rcdev->rx_resolution = 250000; + rcdev->rx_resolution = 250; serial_ir.rcdev = rcdev; @@ -547,7 +547,7 @@ static int serial_ir_probe(struct platform_device *dev) /* Reserve io region. */ if ((iommap && - (devm_request_mem_region(&dev->dev, iommap, 8 << ioshift, + (devm_request_mem_region(&dev->dev, iommap, 8UL << ioshift, KBUILD_MODNAME) == NULL)) || (!iommap && (devm_request_region(&dev->dev, io, 8, KBUILD_MODNAME) == NULL))) { diff --git a/drivers/media/rc/sir_ir.c b/drivers/media/rc/sir_ir.c index 80b3a6736dbd..6ec96dc34586 100644 --- a/drivers/media/rc/sir_ir.c +++ b/drivers/media/rc/sir_ir.c @@ -110,7 +110,7 @@ static void add_read_queue(int flag, unsigned long val) } else { val += TIME_CONST / 2; } - ev.duration = US_TO_NS(val); + ev.duration = val; ir_raw_event_store_with_filter(rcdev, &ev); } diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c index 1dc4e2e33705..3237fef5d502 100644 --- a/drivers/media/rc/st_rc.c +++ b/drivers/media/rc/st_rc.c @@ -134,12 +134,12 @@ static irqreturn_t st_rc_rx_interrupt(int irq, void *data) mark /= dev->sample_div; } - ev.duration = US_TO_NS(mark); + ev.duration = mark; ev.pulse = true; ir_raw_event_store(dev->rdev, &ev); if (!last_symbol) { - ev.duration = US_TO_NS(symbol); + ev.duration = symbol; ev.pulse = false; ir_raw_event_store(dev->rdev, &ev); } else { @@ -292,7 +292,7 @@ static int st_rc_probe(struct platform_device *pdev) rdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; /* rx sampling rate is 10Mhz */ rdev->rx_resolution = 100; - rdev->timeout = US_TO_NS(MAX_SYMB_TIME); + rdev->timeout = MAX_SYMB_TIME; rdev->priv = rc_dev; rdev->open = st_rc_open; rdev->close = st_rc_close; diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c index 79a41fc7161c..9f3cd9fb6b6e 100644 --- a/drivers/media/rc/streamzap.c +++ b/drivers/media/rc/streamzap.c @@ -137,7 +137,6 @@ static void sz_push_full_pulse(struct streamzap_ir *sz, } else { rawir.duration = delta; rawir.duration -= sz->sum; - rawir.duration = US_TO_NS(rawir.duration); rawir.duration = (rawir.duration > IR_MAX_DURATION) ? IR_MAX_DURATION : rawir.duration; } @@ -151,7 +150,6 @@ static void sz_push_full_pulse(struct streamzap_ir *sz, rawir.duration = ((int) value) * SZ_RESOLUTION; rawir.duration += SZ_RESOLUTION / 2; sz->sum += rawir.duration; - rawir.duration = US_TO_NS(rawir.duration); rawir.duration = (rawir.duration > IR_MAX_DURATION) ? IR_MAX_DURATION : rawir.duration; sz_push(sz, rawir); @@ -172,7 +170,6 @@ static void sz_push_full_space(struct streamzap_ir *sz, rawir.duration = ((int) value) * SZ_RESOLUTION; rawir.duration += SZ_RESOLUTION / 2; sz->sum += rawir.duration; - rawir.duration = US_TO_NS(rawir.duration); sz_push(sz, rawir); } @@ -403,13 +400,12 @@ static int streamzap_probe(struct usb_interface *intf, sz->decoder_state = PulseSpace; /* FIXME: don't yet have a way to set this */ sz->timeout_enabled = true; - sz->rdev->timeout = ((US_TO_NS(SZ_TIMEOUT * SZ_RESOLUTION) & - IR_MAX_DURATION) | 0x03000000); + sz->rdev->timeout = SZ_TIMEOUT * SZ_RESOLUTION; #if 0 /* not yet supported, depends on patches from maxim */ /* see also: LIRC_GET_REC_RESOLUTION and LIRC_SET_REC_TIMEOUT */ - sz->min_timeout = US_TO_NS(SZ_TIMEOUT * SZ_RESOLUTION); - sz->max_timeout = US_TO_NS(SZ_TIMEOUT * SZ_RESOLUTION); + sz->min_timeout = SZ_TIMEOUT * SZ_RESOLUTION; + sz->max_timeout = SZ_TIMEOUT * SZ_RESOLUTION; #endif sz->signal_start = ktime_get_real(); diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index e222b4c98be4..ddee6ee37bab 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -241,8 +241,8 @@ static int sunxi_ir_probe(struct platform_device *pdev) ir->rc->dev.parent = dev; ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; /* Frequency after IR internal divider with sample period in ns */ - ir->rc->rx_resolution = (1000000000ul / (b_clk_freq / 64)); - ir->rc->timeout = MS_TO_NS(SUNXI_IR_TIMEOUT); + ir->rc->rx_resolution = (USEC_PER_SEC / (b_clk_freq / 64)); + ir->rc->timeout = MS_TO_US(SUNXI_IR_TIMEOUT); ir->rc->driver_name = SUNXI_IR_DEV; ret = rc_register_device(ir->rc); diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c index 011a8b620d86..629787d53ee1 100644 --- a/drivers/media/rc/ttusbir.c +++ b/drivers/media/rc/ttusbir.c @@ -20,8 +20,8 @@ * messages per second (!), whether IR is idle or not. */ #define NUM_URBS 4 -#define NS_PER_BYTE 62500 -#define NS_PER_BIT (NS_PER_BYTE/8) +#define US_PER_BYTE 62 +#define US_PER_BIT (US_PER_BYTE / 8) struct ttusbir { struct rc_dev *rc; @@ -117,13 +117,13 @@ static void ttusbir_process_ir_data(struct ttusbir *tt, uint8_t *buf) switch (v) { case 0xfe: rawir.pulse = false; - rawir.duration = NS_PER_BYTE; + rawir.duration = US_PER_BYTE; if (ir_raw_event_store_with_filter(tt->rc, &rawir)) event = true; break; case 0: rawir.pulse = true; - rawir.duration = NS_PER_BYTE; + rawir.duration = US_PER_BYTE; if (ir_raw_event_store_with_filter(tt->rc, &rawir)) event = true; break; @@ -137,12 +137,12 @@ static void ttusbir_process_ir_data(struct ttusbir *tt, uint8_t *buf) rawir.pulse = false; } - rawir.duration = NS_PER_BIT * (8 - b); + rawir.duration = US_PER_BIT * (8 - b); if (ir_raw_event_store_with_filter(tt->rc, &rawir)) event = true; rawir.pulse = !rawir.pulse; - rawir.duration = NS_PER_BIT * b; + rawir.duration = US_PER_BIT * b; if (ir_raw_event_store_with_filter(tt->rc, &rawir)) event = true; break; @@ -311,10 +311,10 @@ static int ttusbir_probe(struct usb_interface *intf, rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT; /* - * The precision is NS_PER_BIT, but since every 8th bit can be - * overwritten with garbage the accuracy is at best 2 * NS_PER_BIT. + * The precision is US_PER_BIT, but since every 8th bit can be + * overwritten with garbage the accuracy is at best 2 * US_PER_BIT. */ - rc->rx_resolution = NS_PER_BIT; + rc->rx_resolution = 2 * US_PER_BIT; ret = rc_register_device(rc); if (ret) { diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 630e376d3688..aed23ca0fa6c 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -354,7 +354,6 @@ wbcir_irq_rx(struct wbcir_data *data, struct pnp_dev *device) { u8 irdata; struct ir_raw_event rawir = {}; - unsigned duration; /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */ while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) { @@ -362,13 +361,12 @@ wbcir_irq_rx(struct wbcir_data *data, struct pnp_dev *device) if (data->rxstate == WBCIR_RXSTATE_ERROR) continue; - duration = ((irdata & 0x7F) + 1) * + rawir.duration = ((irdata & 0x7F) + 1) * (data->carrier_report_enabled ? 2 : 10); rawir.pulse = irdata & 0x80 ? false : true; - rawir.duration = US_TO_NS(duration); if (rawir.pulse) - data->pulse_duration += duration; + data->pulse_duration += rawir.duration; ir_raw_event_store_with_filter(data->dev, &rawir); } @@ -519,7 +517,7 @@ wbcir_set_carrier_report(struct rc_dev *dev, int enable) /* Set a higher sampling resolution if carrier reports are enabled */ wbcir_select_bank(data, WBCIR_BANK_2); - data->dev->rx_resolution = US_TO_NS(enable ? 2 : 10); + data->dev->rx_resolution = enable ? 2 : 10; outb(enable ? 0x03 : 0x0f, data->sbase + WBCIR_REG_SP3_BGDL); outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH); @@ -1076,7 +1074,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) data->dev->min_timeout = 1; data->dev->timeout = IR_DEFAULT_TIMEOUT; data->dev->max_timeout = 10 * IR_DEFAULT_TIMEOUT; - data->dev->rx_resolution = US_TO_NS(2); + data->dev->rx_resolution = 2; data->dev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; data->dev->allowed_wakeup_protocols = RC_PROTO_BIT_NEC | RC_PROTO_BIT_NECX | RC_PROTO_BIT_NEC32 | RC_PROTO_BIT_RC5 | diff --git a/drivers/media/rc/xbox_remote.c b/drivers/media/rc/xbox_remote.c index 4a3f2cc4ef18..98d0b43608ad 100644 --- a/drivers/media/rc/xbox_remote.c +++ b/drivers/media/rc/xbox_remote.c @@ -157,7 +157,7 @@ static void xbox_remote_rc_init(struct xbox_remote *xbox_remote) rdev->device_name = xbox_remote->rc_name; rdev->input_phys = xbox_remote->rc_phys; - rdev->timeout = MS_TO_NS(10); + rdev->timeout = MS_TO_US(10); usb_to_input_id(xbox_remote->udev, &rdev->input_id); rdev->dev.parent = &xbox_remote->interface->dev; diff --git a/drivers/media/test-drivers/Kconfig b/drivers/media/test-drivers/Kconfig index 188381c85593..e27d6602545d 100644 --- a/drivers/media/test-drivers/Kconfig +++ b/drivers/media/test-drivers/Kconfig @@ -24,3 +24,19 @@ config VIDEO_VIM2M source "drivers/media/test-drivers/vicodec/Kconfig" endif #V4L_TEST_DRIVERS + +menuconfig DVB_TEST_DRIVERS + bool "DVB test drivers" + depends on DVB_CORE && MEDIA_SUPPORT && I2C + help + Enables DVB test drivers. + + This enables the DVB test drivers. They are meant as an aid for + DVB device driver writers and developers working on userspace + media applications. + +if DVB_TEST_DRIVERS + +source "drivers/media/test-drivers/vidtv/Kconfig" + +endif #DVB_TEST_DRIVERS diff --git a/drivers/media/test-drivers/Makefile b/drivers/media/test-drivers/Makefile index 74410d3a9f2d..9f0e4ebb2efe 100644 --- a/drivers/media/test-drivers/Makefile +++ b/drivers/media/test-drivers/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_VIDEO_VIMC) += vimc/ obj-$(CONFIG_VIDEO_VIVID) += vivid/ obj-$(CONFIG_VIDEO_VIM2M) += vim2m.o obj-$(CONFIG_VIDEO_VICODEC) += vicodec/ +obj-$(CONFIG_DVB_VIDTV) += vidtv/ diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c index 71928e30dae8..0e115683f8da 100644 --- a/drivers/media/test-drivers/vicodec/vicodec-core.c +++ b/drivers/media/test-drivers/vicodec/vicodec-core.c @@ -1310,7 +1310,7 @@ static int vicodec_subscribe_event(struct v4l2_fh *fh, case V4L2_EVENT_SOURCE_CHANGE: if (ctx->is_enc) return -EINVAL; - /* fall through */ + fallthrough; case V4L2_EVENT_EOS: if (ctx->is_stateless) return -EINVAL; @@ -1671,8 +1671,8 @@ static void vicodec_stop_streaming(struct vb2_queue *q) ctx->comp_size = 0; ctx->header_size = 0; ctx->comp_magic_cnt = 0; - ctx->comp_has_frame = 0; - ctx->comp_has_next_frame = 0; + ctx->comp_has_frame = false; + ctx->comp_has_next_frame = false; } } diff --git a/drivers/media/test-drivers/vidtv/Kconfig b/drivers/media/test-drivers/vidtv/Kconfig new file mode 100644 index 000000000000..22c4fd39461f --- /dev/null +++ b/drivers/media/test-drivers/vidtv/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DVB_VIDTV + tristate "Virtual DVB Driver (vidtv)" + depends on DVB_CORE && MEDIA_SUPPORT && I2C + help + The virtual DVB test driver serves as a reference DVB driver and helps + validate the existing APIs in the media subsystem. It can also aid developers + working on userspace applications. + + + When in doubt, say N. diff --git a/drivers/media/test-drivers/vidtv/Makefile b/drivers/media/test-drivers/vidtv/Makefile new file mode 100644 index 000000000000..330089e3b70c --- /dev/null +++ b/drivers/media/test-drivers/vidtv/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 + +dvb-vidtv-tuner-objs := vidtv_tuner.o +dvb-vidtv-demod-objs := vidtv_demod.o +dvb-vidtv-bridge-objs := vidtv_bridge.o vidtv_common.o vidtv_ts.o vidtv_psi.o \ + vidtv_pes.o vidtv_s302m.o vidtv_channel.o vidtv_mux.o + +obj-$(CONFIG_DVB_VIDTV) += dvb-vidtv-tuner.o dvb-vidtv-demod.o \ + dvb-vidtv-bridge.o diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c new file mode 100644 index 000000000000..74b054947bbe --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c @@ -0,0 +1,566 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The Virtual DTV test driver serves as a reference DVB driver and helps + * validate the existing APIs in the media subsystem. It can also aid + * developers working on userspace applications. + * + * When this module is loaded, it will attempt to modprobe 'dvb_vidtv_tuner' and 'dvb_vidtv_demod'. + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#include <linux/moduleparam.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/dev_printk.h> +#include <linux/time.h> +#include <linux/types.h> +#include <linux/workqueue.h> + +#include "vidtv_bridge.h" +#include "vidtv_demod.h" +#include "vidtv_tuner.h" +#include "vidtv_ts.h" +#include "vidtv_mux.h" +#include "vidtv_common.h" + +//#define MUX_BUF_MAX_SZ +//#define MUX_BUF_MIN_SZ +#define TUNER_DEFAULT_ADDR 0x68 +#define DEMOD_DEFAULT_ADDR 0x60 + +/* LNBf fake parameters: ranges used by an Universal (extended) European LNBf */ +#define LNB_CUT_FREQUENCY 11700000 +#define LNB_LOW_FREQ 9750000 +#define LNB_HIGH_FREQ 10600000 + + +static unsigned int drop_tslock_prob_on_low_snr; +module_param(drop_tslock_prob_on_low_snr, uint, 0); +MODULE_PARM_DESC(drop_tslock_prob_on_low_snr, + "Probability of losing the TS lock if the signal quality is bad"); + +static unsigned int recover_tslock_prob_on_good_snr; +module_param(recover_tslock_prob_on_good_snr, uint, 0); +MODULE_PARM_DESC(recover_tslock_prob_on_good_snr, + "Probability recovering the TS lock when the signal improves"); + +static unsigned int mock_power_up_delay_msec; +module_param(mock_power_up_delay_msec, uint, 0); +MODULE_PARM_DESC(mock_power_up_delay_msec, "Simulate a power up delay"); + +static unsigned int mock_tune_delay_msec; +module_param(mock_tune_delay_msec, uint, 0); +MODULE_PARM_DESC(mock_tune_delay_msec, "Simulate a tune delay"); + +static unsigned int vidtv_valid_dvb_t_freqs[NUM_VALID_TUNER_FREQS] = { + 474000000 +}; + +module_param_array(vidtv_valid_dvb_t_freqs, uint, NULL, 0); +MODULE_PARM_DESC(vidtv_valid_dvb_t_freqs, + "Valid DVB-T frequencies to simulate, in Hz"); + +static unsigned int vidtv_valid_dvb_c_freqs[NUM_VALID_TUNER_FREQS] = { + 474000000 +}; + +module_param_array(vidtv_valid_dvb_c_freqs, uint, NULL, 0); +MODULE_PARM_DESC(vidtv_valid_dvb_c_freqs, + "Valid DVB-C frequencies to simulate, in Hz"); + +static unsigned int vidtv_valid_dvb_s_freqs[NUM_VALID_TUNER_FREQS] = { + 11362000 +}; +module_param_array(vidtv_valid_dvb_s_freqs, uint, NULL, 0); +MODULE_PARM_DESC(vidtv_valid_dvb_s_freqs, + "Valid DVB-S/S2 frequencies to simulate at Ku-Band, in kHz"); + +static unsigned int max_frequency_shift_hz; +module_param(max_frequency_shift_hz, uint, 0); +MODULE_PARM_DESC(max_frequency_shift_hz, + "Maximum shift in HZ allowed when tuning in a channel"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nums); + +/* + * Influences the signal acquisition time. See ISO/IEC 13818-1 : 2000. p. 113. + */ +static unsigned int si_period_msec = 40; +module_param(si_period_msec, uint, 0); +MODULE_PARM_DESC(si_period_msec, "How often to send SI packets. Default: 40ms"); + +static unsigned int pcr_period_msec = 40; +module_param(pcr_period_msec, uint, 0); +MODULE_PARM_DESC(pcr_period_msec, "How often to send PCR packets. Default: 40ms"); + +static unsigned int mux_rate_kbytes_sec = 4096; +module_param(mux_rate_kbytes_sec, uint, 0); +MODULE_PARM_DESC(mux_rate_kbytes_sec, "Mux rate: will pad stream if below"); + +static unsigned int pcr_pid = 0x200; +module_param(pcr_pid, uint, 0); +MODULE_PARM_DESC(pcr_pid, "PCR PID for all channels: defaults to 0x200"); + +static unsigned int mux_buf_sz_pkts; +module_param(mux_buf_sz_pkts, uint, 0); +MODULE_PARM_DESC(mux_buf_sz_pkts, "Size for the internal mux buffer in multiples of 188 bytes"); + +#define MUX_BUF_MIN_SZ 90164 +#define MUX_BUF_MAX_SZ (MUX_BUF_MIN_SZ * 10) + +static u32 vidtv_bridge_mux_buf_sz_for_mux_rate(void) +{ + u32 max_elapsed_time_msecs = VIDTV_MAX_SLEEP_USECS / USEC_PER_MSEC; + u32 nbytes_expected; + u32 mux_buf_sz = mux_buf_sz_pkts * TS_PACKET_LEN; + + nbytes_expected = mux_rate_kbytes_sec; + nbytes_expected *= max_elapsed_time_msecs; + + mux_buf_sz = roundup(nbytes_expected, TS_PACKET_LEN); + mux_buf_sz += mux_buf_sz / 10; + + if (mux_buf_sz < MUX_BUF_MIN_SZ) + mux_buf_sz = MUX_BUF_MIN_SZ; + + if (mux_buf_sz > MUX_BUF_MAX_SZ) + mux_buf_sz = MUX_BUF_MAX_SZ; + + return mux_buf_sz; +} + +static bool vidtv_bridge_check_demod_lock(struct vidtv_dvb *dvb, u32 n) +{ + enum fe_status status; + + dvb->fe[n]->ops.read_status(dvb->fe[n], &status); + + return status == (FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_LOCK); +} + +static void +vidtv_bridge_on_new_pkts_avail(void *priv, u8 *buf, u32 npkts) +{ + /* + * called on a separate thread by the mux when new packets become + * available + */ + struct vidtv_dvb *dvb = (struct vidtv_dvb *)priv; + + /* drop packets if we lose the lock */ + if (vidtv_bridge_check_demod_lock(dvb, 0)) + dvb_dmx_swfilter_packets(&dvb->demux, buf, npkts); +} + +static int vidtv_start_streaming(struct vidtv_dvb *dvb) +{ + struct vidtv_mux_init_args mux_args = {0}; + struct device *dev = &dvb->pdev->dev; + u32 mux_buf_sz; + + if (dvb->streaming) { + dev_warn_ratelimited(dev, "Already streaming. Skipping.\n"); + return 0; + } + + mux_buf_sz = (mux_buf_sz_pkts) ? mux_buf_sz_pkts : vidtv_bridge_mux_buf_sz_for_mux_rate(); + + mux_args.mux_rate_kbytes_sec = mux_rate_kbytes_sec; + mux_args.on_new_packets_available_cb = vidtv_bridge_on_new_pkts_avail; + mux_args.mux_buf_sz = mux_buf_sz; + mux_args.pcr_period_usecs = pcr_period_msec * 1000; + mux_args.si_period_usecs = si_period_msec * 1000; + mux_args.pcr_pid = pcr_pid; + mux_args.transport_stream_id = VIDTV_DEFAULT_TS_ID; + mux_args.priv = dvb; + + dvb->streaming = true; + dvb->mux = vidtv_mux_init(dvb->fe[0], dev, mux_args); + vidtv_mux_start_thread(dvb->mux); + + dev_dbg_ratelimited(dev, "Started streaming\n"); + return 0; +} + +static int vidtv_stop_streaming(struct vidtv_dvb *dvb) +{ + struct device *dev = &dvb->pdev->dev; + + dvb->streaming = false; + vidtv_mux_stop_thread(dvb->mux); + vidtv_mux_destroy(dvb->mux); + dvb->mux = NULL; + + dev_dbg_ratelimited(dev, "Stopped streaming\n"); + return 0; +} + +static int vidtv_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct vidtv_dvb *dvb = demux->priv; + int rc; + int ret; + + if (!demux->dmx.frontend) + return -EINVAL; + + mutex_lock(&dvb->feed_lock); + + dvb->nfeeds++; + rc = dvb->nfeeds; + + if (dvb->nfeeds == 1) { + ret = vidtv_start_streaming(dvb); + if (ret < 0) + rc = ret; + } + + mutex_unlock(&dvb->feed_lock); + return rc; +} + +static int vidtv_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct vidtv_dvb *dvb = demux->priv; + int err = 0; + + mutex_lock(&dvb->feed_lock); + dvb->nfeeds--; + + if (!dvb->nfeeds) + err = vidtv_stop_streaming(dvb); + + mutex_unlock(&dvb->feed_lock); + return err; +} + +static struct dvb_frontend *vidtv_get_frontend_ptr(struct i2c_client *c) +{ + /* the demod will set this when its probe function runs */ + struct vidtv_demod_state *state = i2c_get_clientdata(c); + + return &state->frontend; +} + +static int vidtv_master_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], + int num) +{ + return 0; +} + +static u32 vidtv_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm vidtv_i2c_algorithm = { + .master_xfer = vidtv_master_xfer, + .functionality = vidtv_i2c_func, +}; + +static int vidtv_bridge_i2c_register_adap(struct vidtv_dvb *dvb) +{ + struct i2c_adapter *i2c_adapter = &dvb->i2c_adapter; + + strscpy(i2c_adapter->name, "vidtv_i2c", sizeof(i2c_adapter->name)); + i2c_adapter->owner = THIS_MODULE; + i2c_adapter->algo = &vidtv_i2c_algorithm; + i2c_adapter->algo_data = NULL; + i2c_adapter->timeout = 500; + i2c_adapter->retries = 3; + i2c_adapter->dev.parent = &dvb->pdev->dev; + + i2c_set_adapdata(i2c_adapter, dvb); + return i2c_add_adapter(&dvb->i2c_adapter); +} + +static int vidtv_bridge_register_adap(struct vidtv_dvb *dvb) +{ + int ret = 0; + + ret = dvb_register_adapter(&dvb->adapter, + KBUILD_MODNAME, + THIS_MODULE, + &dvb->i2c_adapter.dev, + adapter_nums); + + return ret; +} + +static int vidtv_bridge_dmx_init(struct vidtv_dvb *dvb) +{ + dvb->demux.dmx.capabilities = DMX_TS_FILTERING | + DMX_SECTION_FILTERING; + + dvb->demux.priv = dvb; + dvb->demux.filternum = 256; + dvb->demux.feednum = 256; + dvb->demux.start_feed = vidtv_start_feed; + dvb->demux.stop_feed = vidtv_stop_feed; + + return dvb_dmx_init(&dvb->demux); +} + +static int vidtv_bridge_dmxdev_init(struct vidtv_dvb *dvb) +{ + dvb->dmx_dev.filternum = 256; + dvb->dmx_dev.demux = &dvb->demux.dmx; + dvb->dmx_dev.capabilities = 0; + + return dvb_dmxdev_init(&dvb->dmx_dev, &dvb->adapter); +} + +static int vidtv_bridge_probe_demod(struct vidtv_dvb *dvb, u32 n) +{ + struct vidtv_demod_config cfg = {}; + + cfg.drop_tslock_prob_on_low_snr = drop_tslock_prob_on_low_snr; + cfg.recover_tslock_prob_on_good_snr = recover_tslock_prob_on_good_snr; + + dvb->i2c_client_demod[n] = dvb_module_probe("dvb_vidtv_demod", + NULL, + &dvb->i2c_adapter, + DEMOD_DEFAULT_ADDR, + &cfg); + + /* driver will not work anyways so bail out */ + if (!dvb->i2c_client_demod[n]) + return -ENODEV; + + /* retrieve a ptr to the frontend state */ + dvb->fe[n] = vidtv_get_frontend_ptr(dvb->i2c_client_demod[n]); + + return 0; +} + +static int vidtv_bridge_probe_tuner(struct vidtv_dvb *dvb, u32 n) +{ + struct vidtv_tuner_config cfg = {}; + u32 freq; + int i; + + cfg.fe = dvb->fe[n]; + cfg.mock_power_up_delay_msec = mock_power_up_delay_msec; + cfg.mock_tune_delay_msec = mock_tune_delay_msec; + + /* TODO: check if the frequencies are at a valid range */ + + memcpy(cfg.vidtv_valid_dvb_t_freqs, + vidtv_valid_dvb_t_freqs, + sizeof(vidtv_valid_dvb_t_freqs)); + + memcpy(cfg.vidtv_valid_dvb_c_freqs, + vidtv_valid_dvb_c_freqs, + sizeof(vidtv_valid_dvb_c_freqs)); + + /* + * Convert Satellite frequencies from Ku-band in kHZ into S-band + * frequencies in Hz. + */ + for (i = 0; i < ARRAY_SIZE(vidtv_valid_dvb_s_freqs); i++) { + freq = vidtv_valid_dvb_s_freqs[i]; + if (freq) { + if (freq < LNB_CUT_FREQUENCY) + freq = abs(freq - LNB_LOW_FREQ); + else + freq = abs(freq - LNB_HIGH_FREQ); + } + cfg.vidtv_valid_dvb_s_freqs[i] = freq; + } + + cfg.max_frequency_shift_hz = max_frequency_shift_hz; + + dvb->i2c_client_tuner[n] = dvb_module_probe("dvb_vidtv_tuner", + NULL, + &dvb->i2c_adapter, + TUNER_DEFAULT_ADDR, + &cfg); + + return (dvb->i2c_client_tuner[n]) ? 0 : -ENODEV; +} + +static int vidtv_bridge_dvb_init(struct vidtv_dvb *dvb) +{ + int ret; + int i; + int j; + + ret = vidtv_bridge_i2c_register_adap(dvb); + if (ret < 0) + goto fail_i2c; + + ret = vidtv_bridge_register_adap(dvb); + if (ret < 0) + goto fail_adapter; + + for (i = 0; i < NUM_FE; ++i) { + ret = vidtv_bridge_probe_demod(dvb, i); + if (ret < 0) + goto fail_demod_probe; + + ret = vidtv_bridge_probe_tuner(dvb, i); + if (ret < 0) + goto fail_tuner_probe; + + ret = dvb_register_frontend(&dvb->adapter, dvb->fe[i]); + if (ret < 0) + goto fail_fe; + } + + ret = vidtv_bridge_dmx_init(dvb); + if (ret < 0) + goto fail_dmx; + + ret = vidtv_bridge_dmxdev_init(dvb); + if (ret < 0) + goto fail_dmx_dev; + + for (j = 0; j < NUM_FE; ++j) { + ret = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, + &dvb->dmx_fe[j]); + if (ret < 0) + goto fail_dmx_conn; + + /* + * The source of the demux is a frontend connected + * to the demux. + */ + dvb->dmx_fe[j].source = DMX_FRONTEND_0; + } + + return ret; + +fail_dmx_conn: + for (j = j - 1; j >= 0; --j) + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, + &dvb->dmx_fe[j]); +fail_dmx_dev: + dvb_dmxdev_release(&dvb->dmx_dev); +fail_dmx: + dvb_dmx_release(&dvb->demux); +fail_fe: + for (j = i; j >= 0; --j) + dvb_unregister_frontend(dvb->fe[j]); +fail_tuner_probe: + for (j = i; j >= 0; --j) + if (dvb->i2c_client_tuner[j]) + dvb_module_release(dvb->i2c_client_tuner[j]); + +fail_demod_probe: + for (j = i; j >= 0; --j) + if (dvb->i2c_client_demod[j]) + dvb_module_release(dvb->i2c_client_demod[j]); + +fail_adapter: + dvb_unregister_adapter(&dvb->adapter); + +fail_i2c: + i2c_del_adapter(&dvb->i2c_adapter); + + return ret; +} + +static int vidtv_bridge_probe(struct platform_device *pdev) +{ + struct vidtv_dvb *dvb; + int ret; + + dvb = kzalloc(sizeof(*dvb), GFP_KERNEL); + if (!dvb) + return -ENOMEM; + + dvb->pdev = pdev; + + ret = vidtv_bridge_dvb_init(dvb); + if (ret < 0) + goto err_dvb; + + mutex_init(&dvb->feed_lock); + + platform_set_drvdata(pdev, dvb); + + dev_info(&pdev->dev, "Successfully initialized vidtv!\n"); + return ret; + +err_dvb: + kfree(dvb); + return ret; +} + +static int vidtv_bridge_remove(struct platform_device *pdev) +{ + struct vidtv_dvb *dvb; + u32 i; + + dvb = platform_get_drvdata(pdev); + + mutex_destroy(&dvb->feed_lock); + + for (i = 0; i < NUM_FE; ++i) { + dvb_unregister_frontend(dvb->fe[i]); + dvb_module_release(dvb->i2c_client_tuner[i]); + dvb_module_release(dvb->i2c_client_demod[i]); + } + + dvb_dmxdev_release(&dvb->dmx_dev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_adapter(&dvb->adapter); + + return 0; +} + +static void vidtv_bridge_dev_release(struct device *dev) +{ +} + +static struct platform_device vidtv_bridge_dev = { + .name = "vidtv_bridge", + .dev.release = vidtv_bridge_dev_release, +}; + +static struct platform_driver vidtv_bridge_driver = { + .driver = { + .name = "vidtv_bridge", + .suppress_bind_attrs = true, + }, + .probe = vidtv_bridge_probe, + .remove = vidtv_bridge_remove, +}; + +static void __exit vidtv_bridge_exit(void) +{ + platform_driver_unregister(&vidtv_bridge_driver); + platform_device_unregister(&vidtv_bridge_dev); +} + +static int __init vidtv_bridge_init(void) +{ + int ret; + + ret = platform_device_register(&vidtv_bridge_dev); + if (ret) + return ret; + + ret = platform_driver_register(&vidtv_bridge_driver); + if (ret) + platform_device_unregister(&vidtv_bridge_dev); + + return ret; +} + +module_init(vidtv_bridge_init); +module_exit(vidtv_bridge_exit); + +MODULE_DESCRIPTION("Virtual Digital TV Test Driver"); +MODULE_AUTHOR("Daniel W. S. Almeida"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("vidtv"); +MODULE_ALIAS("dvb_vidtv"); diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.h b/drivers/media/test-drivers/vidtv/vidtv_bridge.h new file mode 100644 index 000000000000..78fe8472fa37 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * The Virtual DTV test driver serves as a reference DVB driver and helps + * validate the existing APIs in the media subsystem. It can also aid + * developers working on userspace applications. + * + * When this module is loaded, it will attempt to modprobe 'dvb_vidtv_tuner' and 'dvb_vidtv_demod'. + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#ifndef VIDTV_BRIDGE_H +#define VIDTV_BRIDGE_H + +/* + * For now, only one frontend is supported. See vidtv_start_streaming() + */ +#define NUM_FE 1 + +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/types.h> +#include <media/dmxdev.h> +#include <media/dvb_demux.h> +#include <media/dvb_frontend.h> +#include "vidtv_mux.h" + +/** + * struct vidtv_dvb - Vidtv bridge state + * @pdev: The platform device. Obtained when the bridge is probed. + * @fe: The frontends. Obtained when probing the demodulator modules. + * @adapter: Represents a DTV adapter. See 'dvb_register_adapter'. + * @demux: The demux used by the dvb_dmx_swfilter_packets() call. + * @dmx_dev: Represents a demux device. + * @dmx_frontend: The frontends associated with the demux. + * @i2c_adapter: The i2c_adapter associated with the bridge driver. + * @i2c_client_demod: The i2c_clients associated with the demodulator modules. + * @i2c_client_tuner: The i2c_clients associated with the tuner modules. + * @nfeeds: The number of feeds active. + * @feed_lock: Protects access to the start/stop stream logic/data. + * @streaming: Whether we are streaming now. + * @mux: The abstraction responsible for delivering MPEG TS packets to the bridge. + */ +struct vidtv_dvb { + struct platform_device *pdev; + struct dvb_frontend *fe[NUM_FE]; + struct dvb_adapter adapter; + struct dvb_demux demux; + struct dmxdev dmx_dev; + struct dmx_frontend dmx_fe[NUM_FE]; + struct i2c_adapter i2c_adapter; + struct i2c_client *i2c_client_demod[NUM_FE]; + struct i2c_client *i2c_client_tuner[NUM_FE]; + + u32 nfeeds; + struct mutex feed_lock; /* Protects access to the start/stop stream logic/data. */ + + bool streaming; + + struct vidtv_mux *mux; +}; + +#endif // VIDTV_BRIDG_H diff --git a/drivers/media/test-drivers/vidtv/vidtv_channel.c b/drivers/media/test-drivers/vidtv/vidtv_channel.c new file mode 100644 index 000000000000..f2b97cf08e87 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_channel.c @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Vidtv serves as a reference DVB driver and helps validate the existing APIs + * in the media subsystem. It can also aid developers working on userspace + * applications. + * + * This file contains the code for a 'channel' abstraction. + * + * When vidtv boots, it will create some hardcoded channels. + * Their services will be concatenated to populate the SDT. + * Their programs will be concatenated to populate the PAT + * For each program in the PAT, a PMT section will be created + * The PMT section for a channel will be assigned its streams. + * Every stream will have its corresponding encoder polled to produce TS packets + * These packets may be interleaved by the mux and then delivered to the bridge + * + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/dev_printk.h> +#include <linux/ratelimit.h> + +#include "vidtv_channel.h" +#include "vidtv_psi.h" +#include "vidtv_encoder.h" +#include "vidtv_mux.h" +#include "vidtv_common.h" +#include "vidtv_s302m.h" + +static void vidtv_channel_encoder_destroy(struct vidtv_encoder *e) +{ + struct vidtv_encoder *curr = e; + struct vidtv_encoder *tmp = NULL; + + while (curr) { + /* forward the call to the derived type */ + tmp = curr; + curr = curr->next; + tmp->destroy(tmp); + } +} + +#define ENCODING_ISO8859_15 "\x0b" + +struct vidtv_channel +*vidtv_channel_s302m_init(struct vidtv_channel *head, u16 transport_stream_id) +{ + /* + * init an audio only channel with a s302m encoder + */ + const u16 s302m_service_id = 0x880; + const u16 s302m_program_num = 0x880; + const u16 s302m_program_pid = 0x101; /* packet id for PMT*/ + const u16 s302m_es_pid = 0x111; /* packet id for the ES */ + const __be32 s302m_fid = cpu_to_be32(VIDTV_S302M_FORMAT_IDENTIFIER); + + char *name = ENCODING_ISO8859_15 "Beethoven"; + char *provider = ENCODING_ISO8859_15 "LinuxTV.org"; + + struct vidtv_channel *s302m = kzalloc(sizeof(*s302m), GFP_KERNEL); + struct vidtv_s302m_encoder_init_args encoder_args = {}; + + s302m->name = kstrdup(name, GFP_KERNEL); + + s302m->service = vidtv_psi_sdt_service_init(NULL, s302m_service_id); + + s302m->service->descriptor = (struct vidtv_psi_desc *) + vidtv_psi_service_desc_init(NULL, + DIGITAL_TELEVISION_SERVICE, + name, + provider); + + s302m->transport_stream_id = transport_stream_id; + + s302m->program = vidtv_psi_pat_program_init(NULL, + s302m_service_id, + s302m_program_pid); + + s302m->program_num = s302m_program_num; + + s302m->streams = vidtv_psi_pmt_stream_init(NULL, + STREAM_PRIVATE_DATA, + s302m_es_pid); + + s302m->streams->descriptor = (struct vidtv_psi_desc *) + vidtv_psi_registration_desc_init(NULL, + s302m_fid, + NULL, + 0); + encoder_args.es_pid = s302m_es_pid; + + s302m->encoders = vidtv_s302m_encoder_init(encoder_args); + + if (head) { + while (head->next) + head = head->next; + + head->next = s302m; + } + + return s302m; +} + +static struct vidtv_psi_table_sdt_service +*vidtv_channel_sdt_serv_cat_into_new(struct vidtv_mux *m) +{ + /* Concatenate the services */ + const struct vidtv_channel *cur_chnl = m->channels; + + struct vidtv_psi_table_sdt_service *curr = NULL; + struct vidtv_psi_table_sdt_service *head = NULL; + struct vidtv_psi_table_sdt_service *tail = NULL; + + struct vidtv_psi_desc *desc = NULL; + u16 service_id; + + if (!cur_chnl) + return NULL; + + while (cur_chnl) { + curr = cur_chnl->service; + + if (!curr) + dev_warn_ratelimited(m->dev, + "No services found for channel %s\n", cur_chnl->name); + + while (curr) { + service_id = be16_to_cpu(curr->service_id); + tail = vidtv_psi_sdt_service_init(tail, service_id); + + desc = vidtv_psi_desc_clone(curr->descriptor); + vidtv_psi_desc_assign(&tail->descriptor, desc); + + if (!head) + head = tail; + + curr = curr->next; + } + + cur_chnl = cur_chnl->next; + } + + return head; +} + +static struct vidtv_psi_table_pat_program* +vidtv_channel_pat_prog_cat_into_new(struct vidtv_mux *m) +{ + /* Concatenate the programs */ + const struct vidtv_channel *cur_chnl = m->channels; + struct vidtv_psi_table_pat_program *curr = NULL; + struct vidtv_psi_table_pat_program *head = NULL; + struct vidtv_psi_table_pat_program *tail = NULL; + u16 serv_id; + u16 pid; + + if (!cur_chnl) + return NULL; + + while (cur_chnl) { + curr = cur_chnl->program; + + if (!curr) + dev_warn_ratelimited(m->dev, + "No programs found for channel %s\n", + cur_chnl->name); + + while (curr) { + serv_id = be16_to_cpu(curr->service_id); + pid = vidtv_psi_get_pat_program_pid(curr); + tail = vidtv_psi_pat_program_init(tail, + serv_id, + pid); + + if (!head) + head = tail; + + curr = curr->next; + } + + cur_chnl = cur_chnl->next; + } + + return head; +} + +static void +vidtv_channel_pmt_match_sections(struct vidtv_channel *channels, + struct vidtv_psi_table_pmt **sections, + u32 nsections) +{ + /* + * Match channels to their respective PMT sections, then assign the + * streams + */ + struct vidtv_psi_table_pmt *curr_section = NULL; + struct vidtv_channel *cur_chnl = channels; + + struct vidtv_psi_table_pmt_stream *s = NULL; + struct vidtv_psi_table_pmt_stream *head = NULL; + struct vidtv_psi_table_pmt_stream *tail = NULL; + + struct vidtv_psi_desc *desc = NULL; + u32 j; + u16 curr_id; + u16 e_pid; /* elementary stream pid */ + + while (cur_chnl) { + for (j = 0; j < nsections; ++j) { + curr_section = sections[j]; + + if (!curr_section) + continue; + + curr_id = be16_to_cpu(curr_section->header.id); + + /* we got a match */ + if (curr_id == cur_chnl->program_num) { + s = cur_chnl->streams; + + /* clone the streams for the PMT */ + while (s) { + e_pid = vidtv_psi_pmt_stream_get_elem_pid(s); + tail = vidtv_psi_pmt_stream_init(tail, + s->type, + e_pid); + + if (!head) + head = tail; + + desc = vidtv_psi_desc_clone(s->descriptor); + vidtv_psi_desc_assign(&tail->descriptor, desc); + + s = s->next; + } + + vidtv_psi_pmt_stream_assign(curr_section, head); + break; + } + } + + cur_chnl = cur_chnl->next; + } +} + +void vidtv_channel_si_init(struct vidtv_mux *m) +{ + struct vidtv_psi_table_pat_program *programs = NULL; + struct vidtv_psi_table_sdt_service *services = NULL; + + m->si.pat = vidtv_psi_pat_table_init(m->transport_stream_id); + + m->si.sdt = vidtv_psi_sdt_table_init(m->transport_stream_id); + + programs = vidtv_channel_pat_prog_cat_into_new(m); + services = vidtv_channel_sdt_serv_cat_into_new(m); + + /* assemble all programs and assign to PAT */ + vidtv_psi_pat_program_assign(m->si.pat, programs); + + /* assemble all services and assign to SDT */ + vidtv_psi_sdt_service_assign(m->si.sdt, services); + + m->si.pmt_secs = vidtv_psi_pmt_create_sec_for_each_pat_entry(m->si.pat, m->pcr_pid); + + vidtv_channel_pmt_match_sections(m->channels, + m->si.pmt_secs, + m->si.pat->programs); +} + +void vidtv_channel_si_destroy(struct vidtv_mux *m) +{ + u32 i; + u16 num_programs = m->si.pat->programs; + + vidtv_psi_pat_table_destroy(m->si.pat); + + for (i = 0; i < num_programs; ++i) + vidtv_psi_pmt_table_destroy(m->si.pmt_secs[i]); + + kfree(m->si.pmt_secs); + vidtv_psi_sdt_table_destroy(m->si.sdt); +} + +void vidtv_channels_init(struct vidtv_mux *m) +{ + /* this is the place to add new 'channels' for vidtv */ + m->channels = vidtv_channel_s302m_init(NULL, m->transport_stream_id); +} + +void vidtv_channels_destroy(struct vidtv_mux *m) +{ + struct vidtv_channel *curr = m->channels; + struct vidtv_channel *tmp = NULL; + + while (curr) { + kfree(curr->name); + vidtv_psi_sdt_service_destroy(curr->service); + vidtv_psi_pat_program_destroy(curr->program); + vidtv_psi_pmt_stream_destroy(curr->streams); + vidtv_channel_encoder_destroy(curr->encoders); + + tmp = curr; + curr = curr->next; + kfree(tmp); + } +} diff --git a/drivers/media/test-drivers/vidtv/vidtv_channel.h b/drivers/media/test-drivers/vidtv/vidtv_channel.h new file mode 100644 index 000000000000..2c3cba4313b0 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_channel.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Vidtv serves as a reference DVB driver and helps validate the existing APIs + * in the media subsystem. It can also aid developers working on userspace + * applications. + * + * This file contains the code for a 'channel' abstraction. + * + * When vidtv boots, it will create some hardcoded channels. + * Their services will be concatenated to populate the SDT. + * Their programs will be concatenated to populate the PAT + * For each program in the PAT, a PMT section will be created + * The PMT section for a channel will be assigned its streams. + * Every stream will have its corresponding encoder polled to produce TS packets + * These packets may be interleaved by the mux and then delivered to the bridge + * + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#ifndef VIDTV_CHANNEL_H +#define VIDTV_CHANNEL_H + +#include <linux/types.h> +#include "vidtv_psi.h" +#include "vidtv_encoder.h" +#include "vidtv_mux.h" + +/** + * struct vidtv_channel - A 'channel' abstraction + * + * When vidtv boots, it will create some hardcoded channels. + * Their services will be concatenated to populate the SDT. + * Their programs will be concatenated to populate the PAT + * For each program in the PAT, a PMT section will be created + * The PMT section for a channel will be assigned its streams. + * Every stream will have its corresponding encoder polled to produce TS packets + * These packets may be interleaved by the mux and then delivered to the bridge + * + * @transport_stream_id: a number to identify the TS, chosen at will. + * @service: A _single_ service. Will be concatenated into the SDT. + * @program_num: The link between PAT, PMT and SDT. + * @program: A _single_ program with one or more streams associated with it. + * Will be concatenated into the PAT. + * @streams: A stream loop used to populate the PMT section for 'program' + * @encoders: A encoder loop. There must be one encoder for each stream. + * @next: Optionally chain this channel. + */ +struct vidtv_channel { + char *name; + u16 transport_stream_id; + struct vidtv_psi_table_sdt_service *service; + u16 program_num; + struct vidtv_psi_table_pat_program *program; + struct vidtv_psi_table_pmt_stream *streams; + struct vidtv_encoder *encoders; + struct vidtv_channel *next; +}; + +/** + * vidtv_channel_si_init - Init the PSI tables from the channels in the mux + * @m: The mux containing the channels. + */ +void vidtv_channel_si_init(struct vidtv_mux *m); +void vidtv_channel_si_destroy(struct vidtv_mux *m); + +/** + * vidtv_channels_init - Init hardcoded, fake 'channels'. + * @m: The mux to store the channels into. + */ +void vidtv_channels_init(struct vidtv_mux *m); +struct vidtv_channel +*vidtv_channel_s302m_init(struct vidtv_channel *head, u16 transport_stream_id); +void vidtv_channels_destroy(struct vidtv_mux *m); + +#endif //VIDTV_CHANNEL_H diff --git a/drivers/media/test-drivers/vidtv/vidtv_common.c b/drivers/media/test-drivers/vidtv/vidtv_common.c new file mode 100644 index 000000000000..63b3055bd715 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_common.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The Virtual DVB test driver serves as a reference DVB driver and helps + * validate the existing APIs in the media subsystem. It can also aid + * developers working on userspace applications. + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ +#define pr_fmt(fmt) KBUILD_MODNAME ":%s, %d: " fmt, __func__, __LINE__ + +#include <linux/printk.h> +#include <linux/ratelimit.h> +#include <linux/string.h> +#include <linux/types.h> + +#include "vidtv_common.h" + +/** + * vidtv_memcpy() - wrapper routine to be used by MPEG-TS + * generator, in order to avoid going past the + * output buffer. + * @to: Starting element to where a MPEG-TS packet will + * be copied. + * @to_offset: Starting position of the @to buffer to be filled. + * @to_size: Size of the @to buffer. + * @from: Starting element of the buffer to be copied. + * @len: Number of elements to be copy from @from buffer + * into @to+ @to_offset buffer. + * + * Note: + * Real digital TV demod drivers should not have memcpy + * wrappers. We use it here because emulating MPEG-TS + * generation at kernelspace requires some extra care. + * + * Return: + * Returns the number of bytes written + */ +u32 vidtv_memcpy(void *to, + size_t to_offset, + size_t to_size, + const void *from, + size_t len) +{ + if (unlikely(to_offset + len > to_size)) { + pr_err_ratelimited("overflow detected, skipping. Try increasing the buffer size. Needed %zu, had %zu\n", + to_offset + len, + to_size); + return 0; + } + + memcpy(to + to_offset, from, len); + return len; +} + +/** + * vidtv_memset() - wrapper routine to be used by MPEG-TS + * generator, in order to avoid going past the + * output buffer. + * @to: Starting element to set + * @to_offset: Starting position of the @to buffer to be filled. + * @to_size: Size of the @to buffer. + * @c: The value to set the memory to. + * @len: Number of elements to be copy from @from buffer + * into @to+ @to_offset buffer. + * + * Note: + * Real digital TV demod drivers should not have memset + * wrappers. We use it here because emulating MPEG-TS + * generation at kernelspace requires some extra care. + * + * Return: + * Returns the number of bytes written + */ +u32 vidtv_memset(void *to, + size_t to_offset, + size_t to_size, + const int c, + size_t len) +{ + if (unlikely(to_offset + len > to_size)) { + pr_err_ratelimited("overflow detected, skipping. Try increasing the buffer size. Needed %zu, had %zu\n", + to_offset + len, + to_size); + return 0; + } + + memset(to + to_offset, c, len); + return len; +} diff --git a/drivers/media/test-drivers/vidtv/vidtv_common.h b/drivers/media/test-drivers/vidtv/vidtv_common.h new file mode 100644 index 000000000000..818e7f2b9ec5 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_common.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * The Virtual DVB test driver serves as a reference DVB driver and helps + * validate the existing APIs in the media subsystem. It can also aid + * developers working on userspace applications. + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#ifndef VIDTV_COMMON_H +#define VIDTV_COMMON_H + +#include <linux/types.h> + +#define CLOCK_UNIT_90KHZ 90000 +#define CLOCK_UNIT_27MHZ 27000000 +#define VIDTV_SLEEP_USECS 10000 +#define VIDTV_MAX_SLEEP_USECS (2 * VIDTV_SLEEP_USECS) +#define VIDTV_DEFAULT_TS_ID 0x744 + +u32 vidtv_memcpy(void *to, + size_t to_offset, + size_t to_size, + const void *from, + size_t len); + +u32 vidtv_memset(void *to, + size_t to_offset, + size_t to_size, + int c, + size_t len); + +#endif // VIDTV_COMMON_H diff --git a/drivers/media/test-drivers/vidtv/vidtv_demod.c b/drivers/media/test-drivers/vidtv/vidtv_demod.c new file mode 100644 index 000000000000..eba7fe1a1b48 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_demod.c @@ -0,0 +1,464 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * The Virtual DVB test driver serves as a reference DVB driver and helps + * validate the existing APIs in the media subsystem. It can also aid + * developers working on userspace applications. + * + * Copyright (C) 2020 Daniel W. S. Almeida + * Based on the example driver written by Emard <emard@softhome.net> + */ + +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/printk.h> +#include <linux/random.h> +#include <linux/ratelimit.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/workqueue.h> +#include <media/dvb_frontend.h> + +#include "vidtv_demod.h" + +#define POLL_THRD_TIME 2000 /* ms */ + +static const struct vidtv_demod_cnr_to_qual_s vidtv_demod_c_cnr_2_qual[] = { + /* from libdvbv5 source code, in milli db */ + { QAM_256, FEC_NONE, 34000, 38000}, + { QAM_64, FEC_NONE, 30000, 34000}, +}; + +static const struct vidtv_demod_cnr_to_qual_s vidtv_demod_s_cnr_2_qual[] = { + /* from libdvbv5 source code, in milli db */ + { QPSK, FEC_1_2, 7000, 10000}, + { QPSK, FEC_2_3, 9000, 12000}, + { QPSK, FEC_3_4, 10000, 13000}, + { QPSK, FEC_5_6, 11000, 14000}, + { QPSK, FEC_7_8, 12000, 15000}, +}; + +static const struct vidtv_demod_cnr_to_qual_s vidtv_demod_s2_cnr_2_qual[] = { + /* from libdvbv5 source code, in milli db */ + { QPSK, FEC_1_2, 9000, 12000}, + { QPSK, FEC_2_3, 11000, 14000}, + { QPSK, FEC_3_4, 12000, 15000}, + { QPSK, FEC_5_6, 12000, 15000}, + { QPSK, FEC_8_9, 13000, 16000}, + { QPSK, FEC_9_10, 13500, 16500}, + { PSK_8, FEC_2_3, 14500, 17500}, + { PSK_8, FEC_3_4, 16000, 19000}, + { PSK_8, FEC_5_6, 17500, 20500}, + { PSK_8, FEC_8_9, 19000, 22000}, +}; + +static const struct vidtv_demod_cnr_to_qual_s vidtv_demod_t_cnr_2_qual[] = { + /* from libdvbv5 source code, in milli db*/ + { QPSK, FEC_1_2, 4100, 5900}, + { QPSK, FEC_2_3, 6100, 9600}, + { QPSK, FEC_3_4, 7200, 12400}, + { QPSK, FEC_5_6, 8500, 15600}, + { QPSK, FEC_7_8, 9200, 17500}, + { QAM_16, FEC_1_2, 9800, 11800}, + { QAM_16, FEC_2_3, 12100, 15300}, + { QAM_16, FEC_3_4, 13400, 18100}, + { QAM_16, FEC_5_6, 14800, 21300}, + { QAM_16, FEC_7_8, 15700, 23600}, + { QAM_64, FEC_1_2, 14000, 16000}, + { QAM_64, FEC_2_3, 19900, 25400}, + { QAM_64, FEC_3_4, 24900, 27900}, + { QAM_64, FEC_5_6, 21300, 23300}, + { QAM_64, FEC_7_8, 22000, 24000}, +}; + +static const struct vidtv_demod_cnr_to_qual_s *vidtv_match_cnr_s(struct dvb_frontend *fe) +{ + const struct vidtv_demod_cnr_to_qual_s *cnr2qual = NULL; + struct device *dev = fe->dvb->device; + struct dtv_frontend_properties *c; + u32 array_size = 0; + u32 i; + + c = &fe->dtv_property_cache; + + switch (c->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: + cnr2qual = vidtv_demod_t_cnr_2_qual; + array_size = ARRAY_SIZE(vidtv_demod_t_cnr_2_qual); + break; + + case SYS_DVBS: + cnr2qual = vidtv_demod_s_cnr_2_qual; + array_size = ARRAY_SIZE(vidtv_demod_s_cnr_2_qual); + break; + + case SYS_DVBS2: + cnr2qual = vidtv_demod_s2_cnr_2_qual; + array_size = ARRAY_SIZE(vidtv_demod_s2_cnr_2_qual); + break; + + case SYS_DVBC_ANNEX_A: + cnr2qual = vidtv_demod_c_cnr_2_qual; + array_size = ARRAY_SIZE(vidtv_demod_c_cnr_2_qual); + break; + + default: + dev_warn_ratelimited(dev, + "%s: unsupported delivery system: %u\n", + __func__, + c->delivery_system); + break; + } + + for (i = 0; i < array_size; i++) + if (cnr2qual[i].modulation == c->modulation && + cnr2qual[i].fec == c->fec_inner) + return &cnr2qual[i]; + + return NULL; /* not found */ +} + +static void vidtv_clean_stats(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + /* Fill the length of each status counter */ + + /* Signal is always available */ + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_DECIBEL; + c->strength.stat[0].svalue = 0; + + /* Usually available only after Viterbi lock */ + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->cnr.stat[0].svalue = 0; + c->cnr.len = 1; + + /* Those depends on full lock */ + c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_error.stat[0].uvalue = 0; + c->pre_bit_error.len = 1; + c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[0].uvalue = 0; + c->pre_bit_count.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.stat[0].uvalue = 0; + c->post_bit_error.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].uvalue = 0; + c->post_bit_count.len = 1; + c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[0].uvalue = 0; + c->block_error.len = 1; + c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[0].uvalue = 0; + c->block_count.len = 1; +} + +static void vidtv_demod_update_stats(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct vidtv_demod_state *state = fe->demodulator_priv; + u32 scale; + + if (state->status & FE_HAS_LOCK) { + scale = FE_SCALE_COUNTER; + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + } else { + scale = FE_SCALE_NOT_AVAILABLE; + c->cnr.stat[0].scale = scale; + } + + c->pre_bit_error.stat[0].scale = scale; + c->pre_bit_count.stat[0].scale = scale; + c->post_bit_error.stat[0].scale = scale; + c->post_bit_count.stat[0].scale = scale; + c->block_error.stat[0].scale = scale; + c->block_count.stat[0].scale = scale; + + /* + * Add a 0.5% of randomness at the signal strength and CNR, + * and make them different, as we want to have something closer + * to a real case scenario. + * + * Also, usually, signal strength is a negative number in dBm. + */ + c->strength.stat[0].svalue = state->tuner_cnr; + c->strength.stat[0].svalue -= prandom_u32_max(state->tuner_cnr / 50); + c->strength.stat[0].svalue -= 68000; /* Adjust to a better range */ + + c->cnr.stat[0].svalue = state->tuner_cnr; + c->cnr.stat[0].svalue -= prandom_u32_max(state->tuner_cnr / 50); + +} + +static int vidtv_demod_read_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct vidtv_demod_state *state = fe->demodulator_priv; + const struct vidtv_demod_cnr_to_qual_s *cnr2qual = NULL; + struct vidtv_demod_config *config = &state->config; + u16 snr = 0; + + /* Simulate random lost of signal due to a bad-tuned channel */ + cnr2qual = vidtv_match_cnr_s(&state->frontend); + + if (cnr2qual && state->tuner_cnr < cnr2qual->cnr_good && + state->frontend.ops.tuner_ops.get_rf_strength) { + state->frontend.ops.tuner_ops.get_rf_strength(&state->frontend, + &snr); + + if (snr < cnr2qual->cnr_ok) { + /* eventually lose the TS lock */ + if (prandom_u32_max(100) < config->drop_tslock_prob_on_low_snr) + state->status = 0; + } else { + /* recover if the signal improves */ + if (prandom_u32_max(100) < + config->recover_tslock_prob_on_good_snr) + state->status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_LOCK; + } + } + + vidtv_demod_update_stats(&state->frontend); + + *status = state->status; + + return 0; +} + +static int vidtv_demod_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + *strength = c->strength.stat[0].uvalue; + + return 0; +} + +/* + * NOTE: + * This is implemented here just to be used as an example for real + * demod drivers. + * + * Should only be implemented if it actually reads something from the hardware. + * Also, it should check for the locks, in order to avoid report wrong data + * to userspace. + */ +static int vidtv_demod_get_frontend(struct dvb_frontend *fe, + struct dtv_frontend_properties *p) +{ + return 0; +} + +static int vidtv_demod_set_frontend(struct dvb_frontend *fe) +{ + struct vidtv_demod_state *state = fe->demodulator_priv; + u32 tuner_status = 0; + int ret; + + if (!fe->ops.tuner_ops.set_params) + return 0; + + fe->ops.tuner_ops.set_params(fe); + + /* store the CNR returned by the tuner */ + ret = fe->ops.tuner_ops.get_rf_strength(fe, &state->tuner_cnr); + if (ret < 0) + return ret; + + fe->ops.tuner_ops.get_status(fe, &tuner_status); + state->status = (state->tuner_cnr > 0) ? FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_LOCK : + 0; + + vidtv_demod_update_stats(fe); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +/* + * NOTE: + * This is implemented here just to be used as an example for real + * demod drivers. + * + * Should only be implemented if the demod has support for DVB-S or DVB-S2 + */ +static int vidtv_demod_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + return 0; +} + +/* + * NOTE: + * This is implemented here just to be used as an example for real + * demod drivers. + * + * Should only be implemented if the demod has support for DVB-S or DVB-S2 + */ +static int vidtv_demod_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + return 0; +} + +/* + * NOTE: + * This is implemented here just to be used as an example for real + * demod drivers. + * + * Should only be implemented if the demod has support for DVB-S or DVB-S2 + */ +static int vidtv_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) +{ + return 0; +} + +/* + * NOTE: + * This is implemented here just to be used as an example for real + * demod drivers. + * + * Should only be implemented if the demod has support for DVB-S or DVB-S2 + */ +static int vidtv_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd burst) +{ + return 0; +} + +static void vidtv_demod_release(struct dvb_frontend *fe) +{ + struct vidtv_demod_state *state = fe->demodulator_priv; + + kfree(state); +} + +static const struct dvb_frontend_ops vidtv_demod_ops = { + .delsys = { + SYS_DVBT, + SYS_DVBT2, + SYS_DVBC_ANNEX_A, + SYS_DVBS, + SYS_DVBS2, + }, + + .info = { + .name = "Dummy demod for DVB-T/T2/C/S/S2", + .frequency_min_hz = 51 * MHz, + .frequency_max_hz = 2150 * MHz, + .frequency_stepsize_hz = 62500, + .frequency_tolerance_hz = 29500 * kHz, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_8_9 | + FE_CAN_QAM_16 | + FE_CAN_QAM_64 | + FE_CAN_QAM_32 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO | + FE_CAN_QPSK | + FE_CAN_FEC_AUTO | + FE_CAN_INVERSION_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = vidtv_demod_release, + + .set_frontend = vidtv_demod_set_frontend, + .get_frontend = vidtv_demod_get_frontend, + + .read_status = vidtv_demod_read_status, + .read_signal_strength = vidtv_demod_read_signal_strength, + + /* For DVB-S/S2 */ + .set_voltage = vidtv_demod_set_voltage, + .set_tone = vidtv_demod_set_tone, + .diseqc_send_master_cmd = vidtv_send_diseqc_msg, + .diseqc_send_burst = vidtv_diseqc_send_burst, + +}; + +static const struct i2c_device_id vidtv_demod_i2c_id_table[] = { + {"dvb_vidtv_demod", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, vidtv_demod_i2c_id_table); + +static int vidtv_demod_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct vidtv_tuner_config *config = client->dev.platform_data; + struct vidtv_demod_state *state; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, + &vidtv_demod_ops, + sizeof(struct dvb_frontend_ops)); + + memcpy(&state->config, config, sizeof(state->config)); + + state->frontend.demodulator_priv = state; + i2c_set_clientdata(client, state); + + vidtv_clean_stats(&state->frontend); + + return 0; +} + +static int vidtv_demod_i2c_remove(struct i2c_client *client) +{ + struct vidtv_demod_state *state = i2c_get_clientdata(client); + + kfree(state); + + return 0; +} + +static struct i2c_driver vidtv_demod_i2c_driver = { + .driver = { + .name = "dvb_vidtv_demod", + .suppress_bind_attrs = true, + }, + .probe = vidtv_demod_i2c_probe, + .remove = vidtv_demod_i2c_remove, + .id_table = vidtv_demod_i2c_id_table, +}; + +module_i2c_driver(vidtv_demod_i2c_driver); + +MODULE_DESCRIPTION("Virtual DVB Demodulator Driver"); +MODULE_AUTHOR("Daniel W. S. Almeida"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/test-drivers/vidtv/vidtv_demod.h b/drivers/media/test-drivers/vidtv/vidtv_demod.h new file mode 100644 index 000000000000..87651b0193e6 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_demod.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * The Virtual DTV test driver serves as a reference DVB driver and helps + * validate the existing APIs in the media subsystem. It can also aid + * developers working on userspace applications. + * + * Copyright (C) 2020 Daniel W. S. Almeida + * Based on the example driver written by Emard <emard@softhome.net> + */ + +#ifndef VIDTV_DEMOD_H +#define VIDTV_DEMOD_H + +#include <linux/dvb/frontend.h> +#include <media/dvb_frontend.h> + +/** + * struct vidtv_demod_cnr_to_qual_s - Map CNR values to a given combination of + * modulation and fec_inner + * @modulation: see enum fe_modulation + * @fec: see enum fe_fec_rate + * + * This struct matches values for 'good' and 'ok' CNRs given the combination + * of modulation and fec_inner in use. We might simulate some noise if the + * signal quality is not too good. + * + * The values were taken from libdvbv5. + */ +struct vidtv_demod_cnr_to_qual_s { + u32 modulation; + u32 fec; + u32 cnr_ok; + u32 cnr_good; +}; + +/** + * struct vidtv_demod_config - Configuration used to init the demod + * @drop_tslock_prob_on_low_snr: probability of losing the lock due to low snr + * @recover_tslock_prob_on_good_snr: probability of recovering when the signal + * improves + * + * The configuration used to init the demodulator module, usually filled + * by a bridge driver. For vidtv, this is filled by vidtv_bridge before the + * demodulator module is probed. + */ +struct vidtv_demod_config { + u8 drop_tslock_prob_on_low_snr; + u8 recover_tslock_prob_on_good_snr; +}; + +/** + * struct vidtv_demod_state - The demodulator state + * @frontend: The frontend structure allocated by the demod. + * @config: The config used to init the demod. + * @poll_snr: The task responsible for periodically checking the simulated + * signal quality, eventually dropping or reacquiring the TS lock. + * @status: the demod status. + * @cold_start: Whether the demod has not been init yet. + * @poll_snr_thread_running: Whether the task responsible for periodically + * checking the simulated signal quality is running. + * @poll_snr_thread_restart: Whether we should restart the poll_snr task. + */ +struct vidtv_demod_state { + struct dvb_frontend frontend; + struct vidtv_demod_config config; + enum fe_status status; + u16 tuner_cnr; +}; +#endif // VIDTV_DEMOD_H diff --git a/drivers/media/test-drivers/vidtv/vidtv_encoder.h b/drivers/media/test-drivers/vidtv/vidtv_encoder.h new file mode 100644 index 000000000000..65d81daef4c3 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_encoder.h @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Vidtv serves as a reference DVB driver and helps validate the existing APIs + * in the media subsystem. It can also aid developers working on userspace + * applications. + * + * This file contains a generic encoder type that can provide data for a stream + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#ifndef VIDTV_ENCODER_H +#define VIDTV_ENCODER_H + +#include <linux/types.h> + +enum vidtv_encoder_id { + /* add IDs here when implementing new encoders */ + S302M, +}; + +struct vidtv_access_unit { + u32 num_samples; + u64 pts; + u64 dts; + u32 nbytes; + u32 offset; + struct vidtv_access_unit *next; +}; + +/* Some musical notes, used by a tone generator */ +enum musical_notes { + NOTE_SILENT = 0, + + NOTE_C_2 = 65, + NOTE_CS_2 = 69, + NOTE_D_2 = 73, + NOTE_DS_2 = 78, + NOTE_E_2 = 82, + NOTE_F_2 = 87, + NOTE_FS_2 = 93, + NOTE_G_2 = 98, + NOTE_GS_2 = 104, + NOTE_A_2 = 110, + NOTE_AS_2 = 117, + NOTE_B_2 = 123, + NOTE_C_3 = 131, + NOTE_CS_3 = 139, + NOTE_D_3 = 147, + NOTE_DS_3 = 156, + NOTE_E_3 = 165, + NOTE_F_3 = 175, + NOTE_FS_3 = 185, + NOTE_G_3 = 196, + NOTE_GS_3 = 208, + NOTE_A_3 = 220, + NOTE_AS_3 = 233, + NOTE_B_3 = 247, + NOTE_C_4 = 262, + NOTE_CS_4 = 277, + NOTE_D_4 = 294, + NOTE_DS_4 = 311, + NOTE_E_4 = 330, + NOTE_F_4 = 349, + NOTE_FS_4 = 370, + NOTE_G_4 = 392, + NOTE_GS_4 = 415, + NOTE_A_4 = 440, + NOTE_AS_4 = 466, + NOTE_B_4 = 494, + NOTE_C_5 = 523, + NOTE_CS_5 = 554, + NOTE_D_5 = 587, + NOTE_DS_5 = 622, + NOTE_E_5 = 659, + NOTE_F_5 = 698, + NOTE_FS_5 = 740, + NOTE_G_5 = 784, + NOTE_GS_5 = 831, + NOTE_A_5 = 880, + NOTE_AS_5 = 932, + NOTE_B_5 = 988, + NOTE_C_6 = 1047, + NOTE_CS_6 = 1109, + NOTE_D_6 = 1175, + NOTE_DS_6 = 1245, + NOTE_E_6 = 1319, + NOTE_F_6 = 1397, + NOTE_FS_6 = 1480, + NOTE_G_6 = 1568, + NOTE_GS_6 = 1661, + NOTE_A_6 = 1760, + NOTE_AS_6 = 1865, + NOTE_B_6 = 1976, + NOTE_C_7 = 2093 +}; + +/** + * struct vidtv_encoder - A generic encoder type. + * @id: So we can cast to a concrete implementation when needed. + * @name: Usually the same as the stream name. + * @encoder_buf: The encoder internal buffer for the access units. + * @encoder_buf_sz: The encoder buffer size, in bytes + * @encoder_buf_offset: Our byte position in the encoder buffer. + * @sample_count: How many samples we have encoded in total. + * @src_buf: The source of raw data to be encoded, encoder might set a + * default if null. + * @src_buf_offset: Our position in the source buffer. + * @is_video_encoder: Whether this a video encoder (as opposed to audio) + * @ctx: Encoder-specific state. + * @stream_id: Examples: Audio streams (0xc0-0xdf), Video streams + * (0xe0-0xef). + * @es_id: The TS PID to use for the elementary stream in this encoder. + * @encode: Prepare enough AUs for the given amount of time. + * @clear: Clear the encoder output. + * @sync: Attempt to synchronize with this encoder. + * @sampling_rate_hz: The sampling rate (or fps, if video) used. + * @last_sample_cb: Called when the encoder runs out of data.This is + * so the source can read data in a + * piecemeal fashion instead of having to + * provide it all at once. + * @destroy: Destroy this encoder, freeing allocated resources. + * @next: Next in the chain + */ +struct vidtv_encoder { + enum vidtv_encoder_id id; + char *name; + + u8 *encoder_buf; + u32 encoder_buf_sz; + u32 encoder_buf_offset; + + u64 sample_count; + int last_duration; + int note_offset; + enum musical_notes last_tone; + + struct vidtv_access_unit *access_units; + + void *src_buf; + u32 src_buf_sz; + u32 src_buf_offset; + + bool is_video_encoder; + void *ctx; + + __be16 stream_id; + + __be16 es_pid; + + void *(*encode)(struct vidtv_encoder *e); + + u32 (*clear)(struct vidtv_encoder *e); + + struct vidtv_encoder *sync; + + u32 sampling_rate_hz; + + void (*last_sample_cb)(u32 sample_no); + + void (*destroy)(struct vidtv_encoder *e); + + struct vidtv_encoder *next; +}; + +#endif /* VIDTV_ENCODER_H */ diff --git a/drivers/media/test-drivers/vidtv/vidtv_mux.c b/drivers/media/test-drivers/vidtv/vidtv_mux.c new file mode 100644 index 000000000000..082740ae9d44 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_mux.c @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Vidtv serves as a reference DVB driver and helps validate the existing APIs + * in the media subsystem. It can also aid developers working on userspace + * applications. + * + * This file contains the multiplexer logic for TS packets from different + * elementary streams + * + * Loosely based on libavcodec/mpegtsenc.c + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/dev_printk.h> +#include <linux/ratelimit.h> +#include <linux/delay.h> +#include <linux/vmalloc.h> +#include <linux/math64.h> + +#include "vidtv_mux.h" +#include "vidtv_ts.h" +#include "vidtv_pes.h" +#include "vidtv_encoder.h" +#include "vidtv_channel.h" +#include "vidtv_common.h" +#include "vidtv_psi.h" + +static struct vidtv_mux_pid_ctx +*vidtv_mux_get_pid_ctx(struct vidtv_mux *m, u16 pid) +{ + struct vidtv_mux_pid_ctx *ctx; + + hash_for_each_possible(m->pid_ctx, ctx, h, pid) + if (ctx->pid == pid) + return ctx; + return NULL; +} + +static struct vidtv_mux_pid_ctx +*vidtv_mux_create_pid_ctx_once(struct vidtv_mux *m, u16 pid) +{ + struct vidtv_mux_pid_ctx *ctx; + + ctx = vidtv_mux_get_pid_ctx(m, pid); + + if (ctx) + goto end; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + ctx->pid = pid; + ctx->cc = 0; + hash_add(m->pid_ctx, &ctx->h, pid); + +end: + return ctx; +} + +static void vidtv_mux_pid_ctx_init(struct vidtv_mux *m) +{ + struct vidtv_psi_table_pat_program *p = m->si.pat->program; + u16 pid; + + hash_init(m->pid_ctx); + /* push the pcr pid ctx */ + vidtv_mux_create_pid_ctx_once(m, m->pcr_pid); + /* push the null packet pid ctx */ + vidtv_mux_create_pid_ctx_once(m, TS_NULL_PACKET_PID); + /* push the PAT pid ctx */ + vidtv_mux_create_pid_ctx_once(m, VIDTV_PAT_PID); + /* push the SDT pid ctx */ + vidtv_mux_create_pid_ctx_once(m, VIDTV_SDT_PID); + + /* add a ctx for all PMT sections */ + while (p) { + pid = vidtv_psi_get_pat_program_pid(p); + vidtv_mux_create_pid_ctx_once(m, pid); + p = p->next; + } +} + +static void vidtv_mux_pid_ctx_destroy(struct vidtv_mux *m) +{ + int bkt; + struct vidtv_mux_pid_ctx *ctx; + struct hlist_node *tmp; + + hash_for_each_safe(m->pid_ctx, bkt, tmp, ctx, h) { + hash_del(&ctx->h); + kfree(ctx); + } +} + +static void vidtv_mux_update_clk(struct vidtv_mux *m) +{ + /* call this at every thread iteration */ + u64 elapsed_time; + + m->timing.past_jiffies = m->timing.current_jiffies; + m->timing.current_jiffies = get_jiffies_64(); + + elapsed_time = jiffies_to_usecs(m->timing.current_jiffies - + m->timing.past_jiffies); + + /* update the 27Mhz clock proportionally to the elapsed time */ + m->timing.clk += (CLOCK_UNIT_27MHZ / USEC_PER_SEC) * elapsed_time; +} + +static u32 vidtv_mux_push_si(struct vidtv_mux *m) +{ + u32 initial_offset = m->mux_buf_offset; + + struct vidtv_mux_pid_ctx *pat_ctx; + struct vidtv_mux_pid_ctx *pmt_ctx; + struct vidtv_mux_pid_ctx *sdt_ctx; + + struct vidtv_psi_pat_write_args pat_args = {}; + struct vidtv_psi_pmt_write_args pmt_args = {}; + struct vidtv_psi_sdt_write_args sdt_args = {}; + + u32 nbytes; /* the number of bytes written by this function */ + u16 pmt_pid; + u32 i; + + pat_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_PAT_PID); + sdt_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_SDT_PID); + + pat_args.buf = m->mux_buf; + pat_args.offset = m->mux_buf_offset; + pat_args.pat = m->si.pat; + pat_args.buf_sz = m->mux_buf_sz; + pat_args.continuity_counter = &pat_ctx->cc; + + m->mux_buf_offset += vidtv_psi_pat_write_into(pat_args); + + for (i = 0; i < m->si.pat->programs; ++i) { + pmt_pid = vidtv_psi_pmt_get_pid(m->si.pmt_secs[i], + m->si.pat); + + if (pmt_pid > TS_LAST_VALID_PID) { + dev_warn_ratelimited(m->dev, + "PID: %d not found\n", pmt_pid); + continue; + } + + pmt_ctx = vidtv_mux_get_pid_ctx(m, pmt_pid); + + pmt_args.buf = m->mux_buf; + pmt_args.offset = m->mux_buf_offset; + pmt_args.pmt = m->si.pmt_secs[i]; + pmt_args.pid = pmt_pid; + pmt_args.buf_sz = m->mux_buf_sz; + pmt_args.continuity_counter = &pmt_ctx->cc; + pmt_args.pcr_pid = m->pcr_pid; + + /* write each section into buffer */ + m->mux_buf_offset += vidtv_psi_pmt_write_into(pmt_args); + } + + sdt_args.buf = m->mux_buf; + sdt_args.offset = m->mux_buf_offset; + sdt_args.sdt = m->si.sdt; + sdt_args.buf_sz = m->mux_buf_sz; + sdt_args.continuity_counter = &sdt_ctx->cc; + + m->mux_buf_offset += vidtv_psi_sdt_write_into(sdt_args); + + nbytes = m->mux_buf_offset - initial_offset; + + m->num_streamed_si++; + + return nbytes; +} + +static u32 vidtv_mux_push_pcr(struct vidtv_mux *m) +{ + struct pcr_write_args args = {}; + struct vidtv_mux_pid_ctx *ctx; + u32 nbytes = 0; + + ctx = vidtv_mux_get_pid_ctx(m, m->pcr_pid); + args.dest_buf = m->mux_buf; + args.pid = m->pcr_pid; + args.buf_sz = m->mux_buf_sz; + args.continuity_counter = &ctx->cc; + + /* the 27Mhz clock will feed both parts of the PCR bitfield */ + args.pcr = m->timing.clk; + + nbytes += vidtv_ts_pcr_write_into(args); + m->mux_buf_offset += nbytes; + + m->num_streamed_pcr++; + + return nbytes; +} + +static bool vidtv_mux_should_push_pcr(struct vidtv_mux *m) +{ + u64 next_pcr_at; + + if (m->num_streamed_pcr == 0) + return true; + + next_pcr_at = m->timing.start_jiffies + + usecs_to_jiffies(m->num_streamed_pcr * + m->timing.pcr_period_usecs); + + return time_after64(m->timing.current_jiffies, next_pcr_at); +} + +static bool vidtv_mux_should_push_si(struct vidtv_mux *m) +{ + u64 next_si_at; + + if (m->num_streamed_si == 0) + return true; + + next_si_at = m->timing.start_jiffies + + usecs_to_jiffies(m->num_streamed_si * + m->timing.si_period_usecs); + + return time_after64(m->timing.current_jiffies, next_si_at); +} + +static u32 vidtv_mux_packetize_access_units(struct vidtv_mux *m, + struct vidtv_encoder *e) +{ + u32 nbytes = 0; + + struct pes_write_args args = {}; + u32 initial_offset = m->mux_buf_offset; + struct vidtv_access_unit *au = e->access_units; + + u8 *buf = NULL; + struct vidtv_mux_pid_ctx *pid_ctx = vidtv_mux_create_pid_ctx_once(m, + be16_to_cpu(e->es_pid)); + + args.dest_buf = m->mux_buf; + args.dest_buf_sz = m->mux_buf_sz; + args.pid = be16_to_cpu(e->es_pid); + args.encoder_id = e->id; + args.continuity_counter = &pid_ctx->cc; + args.stream_id = be16_to_cpu(e->stream_id); + args.send_pts = true; + + while (au) { + buf = e->encoder_buf + au->offset; + args.from = buf; + args.access_unit_len = au->nbytes; + args.dest_offset = m->mux_buf_offset; + args.pts = au->pts; + args.pcr = m->timing.clk; + + m->mux_buf_offset += vidtv_pes_write_into(args); + + au = au->next; + } + + /* + * clear the encoder state once the ES data has been written to the mux + * buffer + */ + e->clear(e); + + nbytes = m->mux_buf_offset - initial_offset; + return nbytes; +} + +static u32 vidtv_mux_poll_encoders(struct vidtv_mux *m) +{ + u32 nbytes = 0; + u32 au_nbytes; + struct vidtv_channel *cur_chnl = m->channels; + struct vidtv_encoder *e = NULL; + + while (cur_chnl) { + e = cur_chnl->encoders; + + while (e) { + e->encode(e); + /* get the TS packets into the mux buffer */ + au_nbytes = vidtv_mux_packetize_access_units(m, e); + nbytes += au_nbytes; + m->mux_buf_offset += au_nbytes; + /* grab next encoder */ + e = e->next; + } + + /* grab the next channel */ + cur_chnl = cur_chnl->next; + } + + return nbytes; +} + +static u32 vidtv_mux_pad_with_nulls(struct vidtv_mux *m, u32 npkts) +{ + struct null_packet_write_args args = {}; + u32 initial_offset = m->mux_buf_offset; + u32 nbytes; /* the number of bytes written by this function */ + u32 i; + struct vidtv_mux_pid_ctx *ctx; + + ctx = vidtv_mux_get_pid_ctx(m, TS_NULL_PACKET_PID); + + args.dest_buf = m->mux_buf; + args.buf_sz = m->mux_buf_sz; + args.continuity_counter = &ctx->cc; + args.dest_offset = m->mux_buf_offset; + + for (i = 0; i < npkts; ++i) { + m->mux_buf_offset += vidtv_ts_null_write_into(args); + args.dest_offset = m->mux_buf_offset; + } + + nbytes = m->mux_buf_offset - initial_offset; + + /* sanity check */ + if (nbytes != npkts * TS_PACKET_LEN) + dev_err_ratelimited(m->dev, "%d != %d\n", + nbytes, npkts * TS_PACKET_LEN); + + return nbytes; +} + +static void vidtv_mux_clear(struct vidtv_mux *m) +{ + /* clear the packets currently in the mux */ + memset(m->mux_buf, 0, m->mux_buf_sz * sizeof(*m->mux_buf)); + /* point to the beginning of the buffer again */ + m->mux_buf_offset = 0; +} + +#define ERR_RATE 10000000 +static void vidtv_mux_tick(struct work_struct *work) +{ + struct vidtv_mux *m = container_of(work, + struct vidtv_mux, + mpeg_thread); + struct dtv_frontend_properties *c = &m->fe->dtv_property_cache; + u32 nbytes; + u32 npkts; + u32 tot_bits = 0; + + while (m->streaming) { + nbytes = 0; + + vidtv_mux_update_clk(m); + + if (vidtv_mux_should_push_pcr(m)) + nbytes += vidtv_mux_push_pcr(m); + + if (vidtv_mux_should_push_si(m)) + nbytes += vidtv_mux_push_si(m); + + nbytes += vidtv_mux_poll_encoders(m); + nbytes += vidtv_mux_pad_with_nulls(m, 256); + + npkts = nbytes / TS_PACKET_LEN; + + /* if the buffer is not aligned there is a bug somewhere */ + if (nbytes % TS_PACKET_LEN) + dev_err_ratelimited(m->dev, "Misaligned buffer\n"); + + if (m->on_new_packets_available_cb) + m->on_new_packets_available_cb(m->priv, + m->mux_buf, + npkts); + + vidtv_mux_clear(m); + + /* + * Update bytes and packet counts at DVBv5 stats + * + * For now, both pre and post bit counts are identical, + * but post BER count can be lower than pre BER, if the error + * correction logic discards packages. + */ + c->pre_bit_count.stat[0].uvalue = nbytes * 8; + c->post_bit_count.stat[0].uvalue = nbytes * 8; + c->block_count.stat[0].uvalue += npkts; + + /* + * Even without any visible errors for the user, the pre-BER + * stats usually have an error range up to 1E-6. So, + * add some random error increment count to it. + * + * Please notice that this is a poor guy's implementation, + * as it will produce one corrected bit error every time + * ceil(total bytes / ERR_RATE) is incremented, without + * any sort of (pseudo-)randomness. + */ + tot_bits += nbytes * 8; + if (tot_bits > ERR_RATE) { + c->pre_bit_error.stat[0].uvalue++; + tot_bits -= ERR_RATE; + } + + usleep_range(VIDTV_SLEEP_USECS, VIDTV_MAX_SLEEP_USECS); + } +} + +void vidtv_mux_start_thread(struct vidtv_mux *m) +{ + if (m->streaming) { + dev_warn_ratelimited(m->dev, "Already streaming. Skipping.\n"); + return; + } + + m->streaming = true; + m->timing.start_jiffies = get_jiffies_64(); + schedule_work(&m->mpeg_thread); +} + +void vidtv_mux_stop_thread(struct vidtv_mux *m) +{ + if (m->streaming) { + m->streaming = false; /* thread will quit */ + cancel_work_sync(&m->mpeg_thread); + } +} + +struct vidtv_mux *vidtv_mux_init(struct dvb_frontend *fe, + struct device *dev, + struct vidtv_mux_init_args args) +{ + struct vidtv_mux *m = kzalloc(sizeof(*m), GFP_KERNEL); + + m->dev = dev; + m->fe = fe; + m->timing.pcr_period_usecs = args.pcr_period_usecs; + m->timing.si_period_usecs = args.si_period_usecs; + + m->mux_rate_kbytes_sec = args.mux_rate_kbytes_sec; + + m->on_new_packets_available_cb = args.on_new_packets_available_cb; + + m->mux_buf = vzalloc(args.mux_buf_sz); + m->mux_buf_sz = args.mux_buf_sz; + + m->pcr_pid = args.pcr_pid; + m->transport_stream_id = args.transport_stream_id; + m->priv = args.priv; + m->timing.current_jiffies = get_jiffies_64(); + + if (args.channels) + m->channels = args.channels; + else + vidtv_channels_init(m); + + /* will alloc data for pmt_sections after initializing pat */ + vidtv_channel_si_init(m); + + INIT_WORK(&m->mpeg_thread, vidtv_mux_tick); + + vidtv_mux_pid_ctx_init(m); + + return m; +} + +void vidtv_mux_destroy(struct vidtv_mux *m) +{ + vidtv_mux_stop_thread(m); + vidtv_mux_pid_ctx_destroy(m); + vidtv_channel_si_destroy(m); + vidtv_channels_destroy(m); + vfree(m->mux_buf); + kfree(m); +} diff --git a/drivers/media/test-drivers/vidtv/vidtv_mux.h b/drivers/media/test-drivers/vidtv/vidtv_mux.h new file mode 100644 index 000000000000..2caa60623e97 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_mux.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Vidtv serves as a reference DVB driver and helps validate the existing APIs + * in the media subsystem. It can also aid developers working on userspace + * applications. + * + * This file contains the muxer logic for TS packets from different + * elementary streams. + * + * Loosely based on libavcodec/mpegtsenc.c + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#ifndef VIDTV_MUX_H +#define VIDTV_MUX_H + +#include <linux/types.h> +#include <linux/hashtable.h> +#include <linux/workqueue.h> +#include <media/dvb_frontend.h> + +#include "vidtv_psi.h" + +/** + * struct vidtv_mux_timing - Timing related information + * + * This is used to decide when PCR or PSI packets should be sent. This will also + * provide storage for the clock, which is used to compute the value for the PCR. + * + * @start_jiffies: The value of 'jiffies' when we started the mux thread. + * @current_jiffies: The value of 'jiffies' for the current iteration. + * @past_jiffies: The value of 'jiffies' for the past iteration. + * @clk: A 27Mhz clock from which we will drive the PCR. Updated proportionally + * on every iteration. + * @pcr_period_usecs: How often we should send PCR packets. + * @si_period_usecs: How often we should send PSI packets. + */ +struct vidtv_mux_timing { + u64 start_jiffies; + u64 current_jiffies; + u64 past_jiffies; + + u64 clk; + + u64 pcr_period_usecs; + u64 si_period_usecs; +}; + +/** + * struct vidtv_mux_si - Store the PSI context. + * + * This is used to store the PAT, PMT sections and SDT in use by the muxer. + * + * The muxer acquire these by looking into the hardcoded channels in + * vidtv_channel and then periodically sends the TS packets for them> + * + * @pat: The PAT in use by the muxer. + * @pmt_secs: The PMT sections in use by the muxer. One for each program in the PAT. + * @sdt: The SDT in use by the muxer. + */ +struct vidtv_mux_si { + /* the SI tables */ + struct vidtv_psi_table_pat *pat; + struct vidtv_psi_table_pmt **pmt_secs; /* the PMT sections */ + struct vidtv_psi_table_sdt *sdt; +}; + +/** + * struct vidtv_mux_pid_ctx - Store the context for a given TS PID. + * @pid: The TS PID. + * @cc: The continuity counter for this PID. It is incremented on every TS + * pack and it will wrap around at 0xf0. If the decoder notices a sudden jump in + * this counter this will trigger a discontinuity state. + * @h: This is embedded in a hash table, mapping pid -> vidtv_mux_pid_ctx + */ +struct vidtv_mux_pid_ctx { + u16 pid; + u8 cc; /* continuity counter */ + struct hlist_node h; +}; + +/** + * struct vidtv_mux - A muxer abstraction loosely based in libavcodec/mpegtsenc.c + * @mux_rate_kbytes_sec: The bit rate for the TS, in kbytes. + * @timing: Keeps track of timing related information. + * @pid_ctx: A hash table to keep track of per-PID metadata. + * @on_new_packets_available_cb: A callback to inform of new TS packets ready. + * @mux_buf: A pointer to a buffer for this muxer. TS packets are stored there + * and then passed on to the bridge driver. + * @mux_buf_sz: The size for 'mux_buf'. + * @mux_buf_offset: The current offset into 'mux_buf'. + * @channels: The channels associated with this muxer. + * @si: Keeps track of the PSI context. + * @num_streamed_pcr: Number of PCR packets streamed. + * @num_streamed_si: The number of PSI packets streamed. + * @mpeg_thread: Thread responsible for the muxer loop. + * @streaming: whether 'mpeg_thread' is running. + * @pcr_pid: The TS PID used for the PSI packets. All channels will share the + * same PCR. + * @transport_stream_id: The transport stream ID + * @priv: Private data. + */ +struct vidtv_mux { + struct dvb_frontend *fe; + struct device *dev; + + struct vidtv_mux_timing timing; + + u32 mux_rate_kbytes_sec; + + DECLARE_HASHTABLE(pid_ctx, 3); + + void (*on_new_packets_available_cb)(void *priv, u8 *buf, u32 npackets); + + u8 *mux_buf; + u32 mux_buf_sz; + u32 mux_buf_offset; + + struct vidtv_channel *channels; + + struct vidtv_mux_si si; + u64 num_streamed_pcr; + u64 num_streamed_si; + + struct work_struct mpeg_thread; + bool streaming; + + u16 pcr_pid; + u16 transport_stream_id; + void *priv; +}; + +/** + * struct vidtv_mux_init_args - Arguments used to inix the muxer. + * @mux_rate_kbytes_sec: The bit rate for the TS, in kbytes. + * @on_new_packets_available_cb: A callback to inform of new TS packets ready. + * @mux_buf_sz: The size for 'mux_buf'. + * @pcr_period_usecs: How often we should send PCR packets. + * @si_period_usecs: How often we should send PSI packets. + * @pcr_pid: The TS PID used for the PSI packets. All channels will share the + * same PCR. + * @transport_stream_id: The transport stream ID + * @channels: an optional list of channels to use + * @priv: Private data. + */ +struct vidtv_mux_init_args { + u32 mux_rate_kbytes_sec; + void (*on_new_packets_available_cb)(void *priv, u8 *buf, u32 npackets); + u32 mux_buf_sz; + u64 pcr_period_usecs; + u64 si_period_usecs; + u16 pcr_pid; + u16 transport_stream_id; + struct vidtv_channel *channels; + void *priv; +}; + +struct vidtv_mux *vidtv_mux_init(struct dvb_frontend *fe, + struct device *dev, + struct vidtv_mux_init_args args); +void vidtv_mux_destroy(struct vidtv_mux *m); + +void vidtv_mux_start_thread(struct vidtv_mux *m); +void vidtv_mux_stop_thread(struct vidtv_mux *m); + +#endif //VIDTV_MUX_H diff --git a/drivers/media/test-drivers/vidtv/vidtv_pes.c b/drivers/media/test-drivers/vidtv/vidtv_pes.c new file mode 100644 index 000000000000..1c75f88070e9 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_pes.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Vidtv serves as a reference DVB driver and helps validate the existing APIs + * in the media subsystem. It can also aid developers working on userspace + * applications. + * + * This file contains the logic to translate the ES data for one access unit + * from an encoder into MPEG TS packets. It does so by first encapsulating it + * with a PES header and then splitting it into TS packets. + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s, %d: " fmt, __func__, __LINE__ + +#include <linux/types.h> +#include <linux/printk.h> +#include <linux/ratelimit.h> +#include <asm/byteorder.h> + +#include "vidtv_pes.h" +#include "vidtv_common.h" +#include "vidtv_encoder.h" +#include "vidtv_ts.h" + +#define PRIVATE_STREAM_1_ID 0xbd /* private_stream_1. See SMPTE 302M-2007 p.6 */ +#define PES_HEADER_MAX_STUFFING_BYTES 32 +#define PES_TS_HEADER_MAX_STUFFING_BYTES 182 + +static u32 vidtv_pes_op_get_len(bool send_pts, bool send_dts) +{ + u32 len = 0; + + /* the flags must always be sent */ + len += sizeof(struct vidtv_pes_optional); + + /* From all optionals, we might send these for now */ + if (send_pts && send_dts) + len += sizeof(struct vidtv_pes_optional_pts_dts); + else if (send_pts) + len += sizeof(struct vidtv_pes_optional_pts); + + return len; +} + +#define SIZE_PCR (6 + sizeof(struct vidtv_mpeg_ts_adaption)) + +static u32 vidtv_pes_h_get_len(bool send_pts, bool send_dts) +{ + u32 len = 0; + + /* PES header length notwithstanding stuffing bytes */ + + len += sizeof(struct vidtv_mpeg_pes); + len += vidtv_pes_op_get_len(send_pts, send_dts); + + return len; +} + +static u32 vidtv_pes_write_header_stuffing(struct pes_header_write_args args) +{ + /* + * This is a fixed 8-bit value equal to '0xFF' that can be inserted + * by the encoder, for example to meet the requirements of the channel. + * It is discarded by the decoder. No more than 32 stuffing bytes shall + * be present in one PES packet header. + */ + if (args.n_pes_h_s_bytes > PES_HEADER_MAX_STUFFING_BYTES) { + pr_warn_ratelimited("More than %d stuffing bytes in PES packet header\n", + PES_HEADER_MAX_STUFFING_BYTES); + args.n_pes_h_s_bytes = PES_HEADER_MAX_STUFFING_BYTES; + } + + return vidtv_memset(args.dest_buf, + args.dest_offset, + args.dest_buf_sz, + TS_FILL_BYTE, + args.n_pes_h_s_bytes); +} + +static u32 vidtv_pes_write_pts_dts(struct pes_header_write_args args) +{ + u32 nbytes = 0; /* the number of bytes written by this function */ + + struct vidtv_pes_optional_pts pts = {}; + struct vidtv_pes_optional_pts_dts pts_dts = {}; + void *op = NULL; + size_t op_sz = 0; + u64 mask1; + u64 mask2; + u64 mask3; + + if (!args.send_pts && args.send_dts) + return 0; + + mask1 = GENMASK_ULL(32, 30); + mask2 = GENMASK_ULL(29, 15); + mask3 = GENMASK_ULL(14, 0); + + /* see ISO/IEC 13818-1 : 2000 p. 32 */ + if (args.send_pts && args.send_dts) { + pts_dts.pts1 = (0x3 << 4) | ((args.pts & mask1) >> 29) | 0x1; + pts_dts.pts2 = cpu_to_be16(((args.pts & mask2) >> 14) | 0x1); + pts_dts.pts3 = cpu_to_be16(((args.pts & mask3) << 1) | 0x1); + + pts_dts.dts1 = (0x1 << 4) | ((args.dts & mask1) >> 29) | 0x1; + pts_dts.dts2 = cpu_to_be16(((args.dts & mask2) >> 14) | 0x1); + pts_dts.dts3 = cpu_to_be16(((args.dts & mask3) << 1) | 0x1); + + op = &pts_dts; + op_sz = sizeof(pts_dts); + + } else if (args.send_pts) { + pts.pts1 = (0x1 << 5) | ((args.pts & mask1) >> 29) | 0x1; + pts.pts2 = cpu_to_be16(((args.pts & mask2) >> 14) | 0x1); + pts.pts3 = cpu_to_be16(((args.pts & mask3) << 1) | 0x1); + + op = &pts; + op_sz = sizeof(pts); + } + + /* copy PTS/DTS optional */ + nbytes += vidtv_memcpy(args.dest_buf, + args.dest_offset + nbytes, + args.dest_buf_sz, + op, + op_sz); + + return nbytes; +} + +static u32 vidtv_pes_write_h(struct pes_header_write_args args) +{ + u32 nbytes = 0; /* the number of bytes written by this function */ + + struct vidtv_mpeg_pes pes_header = {}; + struct vidtv_pes_optional pes_optional = {}; + struct pes_header_write_args pts_dts_args = args; + u32 stream_id = (args.encoder_id == S302M) ? PRIVATE_STREAM_1_ID : args.stream_id; + u16 pes_opt_bitfield = 0x01 << 15; + + pes_header.bitfield = cpu_to_be32((PES_START_CODE_PREFIX << 8) | stream_id); + + pes_header.length = cpu_to_be16(vidtv_pes_op_get_len(args.send_pts, + args.send_dts) + + args.access_unit_len); + + if (args.send_pts && args.send_dts) + pes_opt_bitfield |= (0x3 << 6); + else if (args.send_pts) + pes_opt_bitfield |= (0x1 << 7); + + pes_optional.bitfield = cpu_to_be16(pes_opt_bitfield); + pes_optional.length = vidtv_pes_op_get_len(args.send_pts, args.send_dts) + + args.n_pes_h_s_bytes - + sizeof(struct vidtv_pes_optional); + + /* copy header */ + nbytes += vidtv_memcpy(args.dest_buf, + args.dest_offset + nbytes, + args.dest_buf_sz, + &pes_header, + sizeof(pes_header)); + + /* copy optional header bits */ + nbytes += vidtv_memcpy(args.dest_buf, + args.dest_offset + nbytes, + args.dest_buf_sz, + &pes_optional, + sizeof(pes_optional)); + + /* copy the timing information */ + pts_dts_args.dest_offset = args.dest_offset + nbytes; + nbytes += vidtv_pes_write_pts_dts(pts_dts_args); + + /* write any PES header stuffing */ + nbytes += vidtv_pes_write_header_stuffing(args); + + return nbytes; +} + +static u32 vidtv_pes_write_pcr_bits(u8 *to, u32 to_offset, u64 pcr) +{ + /* Exact same from ffmpeg. PCR is a counter driven by a 27Mhz clock */ + u64 div; + u64 rem; + u8 *buf = to + to_offset; + u64 pcr_low; + u64 pcr_high; + + div = div64_u64_rem(pcr, 300, &rem); + + pcr_low = rem; /* pcr_low = pcr % 300 */ + pcr_high = div; /* pcr_high = pcr / 300 */ + + *buf++ = pcr_high >> 25; + *buf++ = pcr_high >> 17; + *buf++ = pcr_high >> 9; + *buf++ = pcr_high >> 1; + *buf++ = pcr_high << 7 | pcr_low >> 8 | 0x7e; + *buf++ = pcr_low; + + return 6; +} + +static u32 vidtv_pes_write_stuffing(struct pes_ts_header_write_args *args, + u32 dest_offset, bool need_pcr, + u64 *last_pcr) +{ + struct vidtv_mpeg_ts_adaption ts_adap = {}; + int stuff_nbytes; + u32 nbytes = 0; + + if (!args->n_stuffing_bytes) + return 0; + + ts_adap.random_access = 1; + + /* length _immediately_ following 'adaptation_field_length' */ + if (need_pcr) { + ts_adap.PCR = 1; + ts_adap.length = SIZE_PCR; + } else { + ts_adap.length = sizeof(ts_adap); + } + stuff_nbytes = args->n_stuffing_bytes - ts_adap.length; + + ts_adap.length -= sizeof(ts_adap.length); + + if (unlikely(stuff_nbytes < 0)) + stuff_nbytes = 0; + + ts_adap.length += stuff_nbytes; + + /* write the adap after the TS header */ + nbytes += vidtv_memcpy(args->dest_buf, + dest_offset + nbytes, + args->dest_buf_sz, + &ts_adap, + sizeof(ts_adap)); + + /* write the optional PCR */ + if (need_pcr) { + nbytes += vidtv_pes_write_pcr_bits(args->dest_buf, + dest_offset + nbytes, + args->pcr); + + *last_pcr = args->pcr; + } + + /* write the stuffing bytes, if are there anything left */ + if (stuff_nbytes) + nbytes += vidtv_memset(args->dest_buf, + dest_offset + nbytes, + args->dest_buf_sz, + TS_FILL_BYTE, + stuff_nbytes); + + /* + * The n_stuffing_bytes contain a pre-calculated value of + * the amount of data that this function would read, made from + * vidtv_pes_h_get_len(). If something went wrong, print a warning + */ + if (nbytes != args->n_stuffing_bytes) + pr_warn_ratelimited("write size was %d, expected %d\n", + nbytes, args->n_stuffing_bytes); + + return nbytes; +} + +static u32 vidtv_pes_write_ts_h(struct pes_ts_header_write_args args, + bool need_pcr, u64 *last_pcr) +{ + /* number of bytes written by this function */ + u32 nbytes = 0; + struct vidtv_mpeg_ts ts_header = {}; + u16 payload_start = !args.wrote_pes_header; + + ts_header.sync_byte = TS_SYNC_BYTE; + ts_header.bitfield = cpu_to_be16((payload_start << 14) | args.pid); + ts_header.scrambling = 0; + ts_header.adaptation_field = (args.n_stuffing_bytes) > 0; + ts_header.payload = (args.n_stuffing_bytes) < PES_TS_HEADER_MAX_STUFFING_BYTES; + + ts_header.continuity_counter = *args.continuity_counter; + + vidtv_ts_inc_cc(args.continuity_counter); + + /* write the TS header */ + nbytes += vidtv_memcpy(args.dest_buf, + args.dest_offset + nbytes, + args.dest_buf_sz, + &ts_header, + sizeof(ts_header)); + + /* write stuffing, if any */ + nbytes += vidtv_pes_write_stuffing(&args, args.dest_offset + nbytes, + need_pcr, last_pcr); + + return nbytes; +} + +u32 vidtv_pes_write_into(struct pes_write_args args) +{ + u32 unaligned_bytes = (args.dest_offset % TS_PACKET_LEN); + struct pes_ts_header_write_args ts_header_args = {}; + struct pes_header_write_args pes_header_args = {}; + u32 remaining_len = args.access_unit_len; + bool wrote_pes_header = false; + u64 last_pcr = args.pcr; + bool need_pcr = true; + u32 available_space; + u32 payload_size; + u32 stuff_bytes; + u32 nbytes = 0; + + if (unaligned_bytes) { + pr_warn_ratelimited("buffer is misaligned, while starting PES\n"); + + /* forcibly align and hope for the best */ + nbytes += vidtv_memset(args.dest_buf, + args.dest_offset + nbytes, + args.dest_buf_sz, + TS_FILL_BYTE, + TS_PACKET_LEN - unaligned_bytes); + } + + if (args.send_dts && !args.send_pts) { + pr_warn_ratelimited("forbidden value '01' for PTS_DTS flags\n"); + args.send_pts = true; + args.pts = args.dts; + } + + /* see SMPTE 302M clause 6.4 */ + if (args.encoder_id == S302M) { + args.send_dts = false; + args.send_pts = true; + } + + while (remaining_len) { + available_space = TS_PAYLOAD_LEN; + /* + * The amount of space initially available in the TS packet. + * if this is the beginning of the PES packet, take into account + * the space needed for the TS header _and_ for the PES header + */ + if (!wrote_pes_header) + available_space -= vidtv_pes_h_get_len(args.send_pts, + args.send_dts); + + /* + * if the encoder has inserted stuffing bytes in the PES + * header, account for them. + */ + available_space -= args.n_pes_h_s_bytes; + + /* Take the extra adaptation into account if need to send PCR */ + if (need_pcr) { + available_space -= SIZE_PCR; + stuff_bytes = SIZE_PCR; + } else { + stuff_bytes = 0; + } + + /* + * how much of the _actual_ payload should be written in this + * packet. + */ + if (remaining_len >= available_space) { + payload_size = available_space; + } else { + /* Last frame should ensure 188-bytes PS alignment */ + payload_size = remaining_len; + stuff_bytes += available_space - payload_size; + + /* + * Ensure that the stuff bytes will be within the + * allowed range, decrementing the number of payload + * bytes to write if needed. + */ + if (stuff_bytes > PES_TS_HEADER_MAX_STUFFING_BYTES) { + u32 tmp = stuff_bytes - PES_TS_HEADER_MAX_STUFFING_BYTES; + + stuff_bytes = PES_TS_HEADER_MAX_STUFFING_BYTES; + payload_size -= tmp; + } + } + + /* write ts header */ + ts_header_args.dest_buf = args.dest_buf; + ts_header_args.dest_offset = args.dest_offset + nbytes; + ts_header_args.dest_buf_sz = args.dest_buf_sz; + ts_header_args.pid = args.pid; + ts_header_args.pcr = args.pcr; + ts_header_args.continuity_counter = args.continuity_counter; + ts_header_args.wrote_pes_header = wrote_pes_header; + ts_header_args.n_stuffing_bytes = stuff_bytes; + + nbytes += vidtv_pes_write_ts_h(ts_header_args, need_pcr, + &last_pcr); + + need_pcr = false; + + if (!wrote_pes_header) { + /* write the PES header only once */ + pes_header_args.dest_buf = args.dest_buf; + + pes_header_args.dest_offset = args.dest_offset + + nbytes; + + pes_header_args.dest_buf_sz = args.dest_buf_sz; + pes_header_args.encoder_id = args.encoder_id; + pes_header_args.send_pts = args.send_pts; + pes_header_args.pts = args.pts; + pes_header_args.send_dts = args.send_dts; + pes_header_args.dts = args.dts; + pes_header_args.stream_id = args.stream_id; + pes_header_args.n_pes_h_s_bytes = args.n_pes_h_s_bytes; + pes_header_args.access_unit_len = args.access_unit_len; + + nbytes += vidtv_pes_write_h(pes_header_args); + wrote_pes_header = true; + } + + /* write as much of the payload as we possibly can */ + nbytes += vidtv_memcpy(args.dest_buf, + args.dest_offset + nbytes, + args.dest_buf_sz, + args.from, + payload_size); + + args.from += payload_size; + + remaining_len -= payload_size; + } + + return nbytes; +} diff --git a/drivers/media/test-drivers/vidtv/vidtv_pes.h b/drivers/media/test-drivers/vidtv/vidtv_pes.h new file mode 100644 index 000000000000..0ea9e863024d --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_pes.h @@ -0,0 +1,191 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Vidtv serves as a reference DVB driver and helps validate the existing APIs + * in the media subsystem. It can also aid developers working on userspace + * applications. + * + * This file contains the logic to translate the ES data for one access unit + * from an encoder into MPEG TS packets. It does so by first encapsulating it + * with a PES header and then splitting it into TS packets. + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#ifndef VIDTV_PES_H +#define VIDTV_PES_H + +#include <asm/byteorder.h> +#include <linux/types.h> + +#include "vidtv_common.h" + +#define PES_MAX_LEN 65536 /* Set 'length' to 0 if greater. Only possible for video. */ +#define PES_START_CODE_PREFIX 0x001 /* 00 00 01 */ + +/* Used when sending PTS, but not DTS */ +struct vidtv_pes_optional_pts { + u8 pts1; + __be16 pts2; + __be16 pts3; +} __packed; + +/* Used when sending both PTS and DTS */ +struct vidtv_pes_optional_pts_dts { + u8 pts1; + __be16 pts2; + __be16 pts3; + + u8 dts1; + __be16 dts2; + __be16 dts3; +} __packed; + +/* PES optional flags */ +struct vidtv_pes_optional { + /* + * These flags show which components are actually + * present in the "optional fields" in the optional PES + * header and which are not + * + * u16 two:2; //0x2 + * u16 PES_scrambling_control:2; + * u16 PES_priority:1; + * u16 data_alignment_indicator:1; // unused + * u16 copyright:1; + * u16 original_or_copy:1; + * u16 PTS_DTS:2; + * u16 ESCR:1; + * u16 ES_rate:1; + * u16 DSM_trick_mode:1; + * u16 additional_copy_info:1; + * u16 PES_CRC:1; + * u16 PES_extension:1; + */ + __be16 bitfield; + u8 length; +} __packed; + +/* The PES header */ +struct vidtv_mpeg_pes { + __be32 bitfield; /* packet_start_code_prefix:24, stream_id: 8 */ + /* after this field until the end of the PES data payload */ + __be16 length; + struct vidtv_pes_optional optional[]; +} __packed; + +/** + * struct pes_header_write_args - Arguments to write a PES header. + * @dest_buf: The buffer to write into. + * @dest_offset: where to start writing in the dest_buffer. + * @dest_buf_sz: The size of the dest_buffer + * @encoder_id: Encoder id (see vidtv_encoder.h) + * @send_pts: Should we send PTS? + * @pts: PTS value to send. + * @send_dts: Should we send DTS? + * @dts: DTS value to send. + * @stream_id: The stream id to use. Ex: Audio streams (0xc0-0xdf), Video + * streams (0xe0-0xef). + * @n_pes_h_s_bytes: Padding bytes. Might be used by an encoder if needed, gets + * discarded by the decoder. + * @access_unit_len: The size of _one_ access unit (with any headers it might need) + */ +struct pes_header_write_args { + void *dest_buf; + u32 dest_offset; + u32 dest_buf_sz; + u32 encoder_id; + + bool send_pts; + u64 pts; + + bool send_dts; + u64 dts; + + u16 stream_id; + /* might be used by an encoder if needed, gets discarded by decoder */ + u32 n_pes_h_s_bytes; + u32 access_unit_len; +}; + +/** + * struct pes_ts_header_write_args - Arguments to write a TS header. + * @dest_buf: The buffer to write into. + * @dest_offset: where to start writing in the dest_buffer. + * @dest_buf_sz: The size of the dest_buffer + * @pid: The PID to use for the TS packets. + * @continuity_counter: Incremented on every new TS packet. + * @n_pes_h_s_bytes: Padding bytes. Might be used by an encoder if needed, gets + * discarded by the decoder. + */ +struct pes_ts_header_write_args { + void *dest_buf; + u32 dest_offset; + u32 dest_buf_sz; + u16 pid; + u8 *continuity_counter; + bool wrote_pes_header; + u32 n_stuffing_bytes; + u64 pcr; +}; + +/** + * struct pes_write_args - Arguments for the packetizer. + * @dest_buf: The buffer to write into. + * @from: A pointer to the encoder buffer containing one access unit. + * @access_unit_len: The size of _one_ access unit (with any headers it might need) + * @dest_offset: where to start writing in the dest_buffer. + * @dest_buf_sz: The size of the dest_buffer + * @pid: The PID to use for the TS packets. + * @encoder_id: Encoder id (see vidtv_encoder.h) + * @continuity_counter: Incremented on every new TS packet. + * @stream_id: The stream id to use. Ex: Audio streams (0xc0-0xdf), Video + * streams (0xe0-0xef). + * @send_pts: Should we send PTS? + * @pts: PTS value to send. + * @send_dts: Should we send DTS? + * @dts: DTS value to send. + * @n_pes_h_s_bytes: Padding bytes. Might be used by an encoder if needed, gets + * discarded by the decoder. + */ +struct pes_write_args { + void *dest_buf; + void *from; + u32 access_unit_len; + + u32 dest_offset; + u32 dest_buf_sz; + u16 pid; + + u32 encoder_id; + + u8 *continuity_counter; + + u16 stream_id; + + bool send_pts; + u64 pts; + + bool send_dts; + u64 dts; + + u32 n_pes_h_s_bytes; + u64 pcr; +}; + +/** + * vidtv_pes_write_into - Write a PES packet as MPEG-TS packets into a buffer. + * @args: The args to use when writing + * + * This function translate the ES data for one access unit + * from an encoder into MPEG TS packets. It does so by first encapsulating it + * with a PES header and then splitting it into TS packets. + * + * The data is then written into the buffer pointed to by 'args.buf' + * + * Return: The number of bytes written into the buffer. This is usually NOT + * equal to the size of the access unit, since we need space for PES headers, TS headers + * and padding bytes, if any. + */ +u32 vidtv_pes_write_into(struct pes_write_args args); + +#endif // VIDTV_PES_H diff --git a/drivers/media/test-drivers/vidtv/vidtv_psi.c b/drivers/media/test-drivers/vidtv/vidtv_psi.c new file mode 100644 index 000000000000..82cf67dd27c0 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_psi.c @@ -0,0 +1,1322 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file contains the logic to work with MPEG Program-Specific Information. + * These are defined both in ISO/IEC 13818-1 (systems) and ETSI EN 300 468. + * PSI is carried in the form of table structures, and although each table might + * technically be broken into one or more sections, we do not do this here, + * hence 'table' and 'section' are interchangeable for vidtv. + * + * This code currently supports three tables: PAT, PMT and SDT. These are the + * bare minimum to get userspace to recognize our MPEG transport stream. It can + * be extended to support more PSI tables in the future. + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s, %d: " fmt, __func__, __LINE__ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/crc32.h> +#include <linux/string.h> +#include <linux/printk.h> +#include <linux/ratelimit.h> +#include <linux/string.h> +#include <asm/byteorder.h> + +#include "vidtv_psi.h" +#include "vidtv_common.h" +#include "vidtv_ts.h" + +#define CRC_SIZE_IN_BYTES 4 +#define MAX_VERSION_NUM 32 + +static const u32 CRC_LUT[256] = { + /* from libdvbv5 */ + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +static inline u32 dvb_crc32(u32 crc, u8 *data, u32 len) +{ + /* from libdvbv5 */ + while (len--) + crc = (crc << 8) ^ CRC_LUT[((crc >> 24) ^ *data++) & 0xff]; + return crc; +} + +static void vidtv_psi_update_version_num(struct vidtv_psi_table_header *h) +{ + h->version++; +} + +static inline u16 vidtv_psi_sdt_serv_get_desc_loop_len(struct vidtv_psi_table_sdt_service *s) +{ + u16 mask; + u16 ret; + + mask = GENMASK(11, 0); + + ret = be16_to_cpu(s->bitfield) & mask; + return ret; +} + +static inline u16 vidtv_psi_pmt_stream_get_desc_loop_len(struct vidtv_psi_table_pmt_stream *s) +{ + u16 mask; + u16 ret; + + mask = GENMASK(9, 0); + + ret = be16_to_cpu(s->bitfield2) & mask; + return ret; +} + +static inline u16 vidtv_psi_pmt_get_desc_loop_len(struct vidtv_psi_table_pmt *p) +{ + u16 mask; + u16 ret; + + mask = GENMASK(9, 0); + + ret = be16_to_cpu(p->bitfield2) & mask; + return ret; +} + +static inline u16 vidtv_psi_get_sec_len(struct vidtv_psi_table_header *h) +{ + u16 mask; + u16 ret; + + mask = GENMASK(11, 0); + + ret = be16_to_cpu(h->bitfield) & mask; + return ret; +} + +inline u16 vidtv_psi_get_pat_program_pid(struct vidtv_psi_table_pat_program *p) +{ + u16 mask; + u16 ret; + + mask = GENMASK(12, 0); + + ret = be16_to_cpu(p->bitfield) & mask; + return ret; +} + +inline u16 vidtv_psi_pmt_stream_get_elem_pid(struct vidtv_psi_table_pmt_stream *s) +{ + u16 mask; + u16 ret; + + mask = GENMASK(12, 0); + + ret = be16_to_cpu(s->bitfield) & mask; + return ret; +} + +static inline void vidtv_psi_set_desc_loop_len(__be16 *bitfield, u16 new_len, u8 desc_len_nbits) +{ + u16 mask; + __be16 new; + + mask = GENMASK(15, desc_len_nbits); + + new = cpu_to_be16((be16_to_cpu(*bitfield) & mask) | new_len); + *bitfield = new; +} + +static void vidtv_psi_set_sec_len(struct vidtv_psi_table_header *h, u16 new_len) +{ + u16 old_len = vidtv_psi_get_sec_len(h); + __be16 new; + u16 mask; + + mask = GENMASK(15, 13); + + new = cpu_to_be16((be16_to_cpu(h->bitfield) & mask) | new_len); + + if (old_len > MAX_SECTION_LEN) + pr_warn_ratelimited("section length: %d > %d, old len was %d\n", + new_len, + MAX_SECTION_LEN, + old_len); + + h->bitfield = new; +} + +static u32 vidtv_psi_ts_psi_write_into(struct psi_write_args args) +{ + /* + * Packetize PSI sections into TS packets: + * push a TS header (4bytes) every 184 bytes + * manage the continuity_counter + * add stuffing (i.e. padding bytes) after the CRC + */ + + u32 nbytes_past_boundary = (args.dest_offset % TS_PACKET_LEN); + bool aligned = (nbytes_past_boundary == 0); + struct vidtv_mpeg_ts ts_header = {}; + + /* number of bytes written by this function */ + u32 nbytes = 0; + /* how much there is left to write */ + u32 remaining_len = args.len; + /* how much we can be written in this packet */ + u32 payload_write_len = 0; + /* where we are in the source */ + u32 payload_offset = 0; + + const u16 PAYLOAD_START = args.new_psi_section; + + if (!args.crc && !args.is_crc) + pr_warn_ratelimited("Missing CRC for chunk\n"); + + if (args.crc) + *args.crc = dvb_crc32(*args.crc, args.from, args.len); + + if (args.new_psi_section && !aligned) { + pr_warn_ratelimited("Cannot write a new PSI section in a misaligned buffer\n"); + + /* forcibly align and hope for the best */ + nbytes += vidtv_memset(args.dest_buf, + args.dest_offset + nbytes, + args.dest_buf_sz, + TS_FILL_BYTE, + TS_PACKET_LEN - nbytes_past_boundary); + } + + while (remaining_len) { + nbytes_past_boundary = (args.dest_offset + nbytes) % TS_PACKET_LEN; + aligned = (nbytes_past_boundary == 0); + + if (aligned) { + /* if at a packet boundary, write a new TS header */ + ts_header.sync_byte = TS_SYNC_BYTE; + ts_header.bitfield = cpu_to_be16((PAYLOAD_START << 14) | args.pid); + ts_header.scrambling = 0; + ts_header.continuity_counter = *args.continuity_counter; + ts_header.payload = 1; + /* no adaptation field */ + ts_header.adaptation_field = 0; + + /* copy the header */ + nbytes += vidtv_memcpy(args.dest_buf, + args.dest_offset + nbytes, + args.dest_buf_sz, + &ts_header, + sizeof(ts_header)); + /* + * This will trigger a discontinuity if the buffer is full, + * effectively dropping the packet. + */ + vidtv_ts_inc_cc(args.continuity_counter); + } + + /* write the pointer_field in the first byte of the payload */ + if (args.new_psi_section) + nbytes += vidtv_memset(args.dest_buf, + args.dest_offset + nbytes, + args.dest_buf_sz, + 0x0, + 1); + + /* write as much of the payload as possible */ + nbytes_past_boundary = (args.dest_offset + nbytes) % TS_PACKET_LEN; + payload_write_len = min(TS_PACKET_LEN - nbytes_past_boundary, remaining_len); + + nbytes += vidtv_memcpy(args.dest_buf, + args.dest_offset + nbytes, + args.dest_buf_sz, + args.from + payload_offset, + payload_write_len); + + /* 'payload_write_len' written from a total of 'len' requested*/ + remaining_len -= payload_write_len; + payload_offset += payload_write_len; + } + + /* + * fill the rest of the packet if there is any remaining space unused + */ + + nbytes_past_boundary = (args.dest_offset + nbytes) % TS_PACKET_LEN; + + if (args.is_crc) + nbytes += vidtv_memset(args.dest_buf, + args.dest_offset + nbytes, + args.dest_buf_sz, + TS_FILL_BYTE, + TS_PACKET_LEN - nbytes_past_boundary); + + return nbytes; +} + +static u32 table_section_crc32_write_into(struct crc32_write_args args) +{ + /* the CRC is the last entry in the section */ + u32 nbytes = 0; + struct psi_write_args psi_args = {}; + + psi_args.dest_buf = args.dest_buf; + psi_args.from = &args.crc; + psi_args.len = CRC_SIZE_IN_BYTES; + psi_args.dest_offset = args.dest_offset; + psi_args.pid = args.pid; + psi_args.new_psi_section = false; + psi_args.continuity_counter = args.continuity_counter; + psi_args.is_crc = true; + psi_args.dest_buf_sz = args.dest_buf_sz; + + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + + return nbytes; +} + +struct vidtv_psi_desc_service *vidtv_psi_service_desc_init(struct vidtv_psi_desc *head, + enum service_type service_type, + char *service_name, + char *provider_name) +{ + struct vidtv_psi_desc_service *desc; + u32 service_name_len = service_name ? strlen(service_name) : 0; + u32 provider_name_len = provider_name ? strlen(provider_name) : 0; + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + + desc->type = SERVICE_DESCRIPTOR; + + desc->length = sizeof_field(struct vidtv_psi_desc_service, service_type) + + sizeof_field(struct vidtv_psi_desc_service, provider_name_len) + + provider_name_len + + sizeof_field(struct vidtv_psi_desc_service, service_name_len) + + service_name_len; + + desc->service_type = service_type; + + desc->service_name_len = service_name_len; + + if (service_name && service_name_len) + desc->service_name = kstrdup(service_name, GFP_KERNEL); + + desc->provider_name_len = provider_name_len; + + if (provider_name && provider_name_len) + desc->provider_name = kstrdup(provider_name, GFP_KERNEL); + + if (head) { + while (head->next) + head = head->next; + + head->next = (struct vidtv_psi_desc *)desc; + } + return desc; +} + +struct vidtv_psi_desc_registration +*vidtv_psi_registration_desc_init(struct vidtv_psi_desc *head, + __be32 format_id, + u8 *additional_ident_info, + u32 additional_info_len) +{ + struct vidtv_psi_desc_registration *desc; + + desc = kzalloc(sizeof(*desc) + sizeof(format_id) + additional_info_len, GFP_KERNEL); + + desc->type = REGISTRATION_DESCRIPTOR; + + desc->length = sizeof_field(struct vidtv_psi_desc_registration, format_id) + + additional_info_len; + + desc->format_id = format_id; + + if (additional_ident_info && additional_info_len) + memcpy(desc->additional_identification_info, + additional_ident_info, + additional_info_len); + + if (head) { + while (head->next) + head = head->next; + + head->next = (struct vidtv_psi_desc *)desc; + } + + return desc; +} + +struct vidtv_psi_desc *vidtv_psi_desc_clone(struct vidtv_psi_desc *desc) +{ + struct vidtv_psi_desc *head = NULL; + struct vidtv_psi_desc *prev = NULL; + struct vidtv_psi_desc *curr = NULL; + + struct vidtv_psi_desc_service *service; + + while (desc) { + switch (desc->type) { + case SERVICE_DESCRIPTOR: + service = (struct vidtv_psi_desc_service *)desc; + curr = (struct vidtv_psi_desc *) + vidtv_psi_service_desc_init(head, + service->service_type, + service->service_name, + service->provider_name); + break; + + case REGISTRATION_DESCRIPTOR: + default: + curr = kzalloc(sizeof(*desc) + desc->length, GFP_KERNEL); + memcpy(curr, desc, sizeof(*desc) + desc->length); + break; + } + + if (curr) + curr->next = NULL; + if (!head) + head = curr; + if (prev) + prev->next = curr; + + prev = curr; + desc = desc->next; + } + + return head; +} + +void vidtv_psi_desc_destroy(struct vidtv_psi_desc *desc) +{ + struct vidtv_psi_desc *curr = desc; + struct vidtv_psi_desc *tmp = NULL; + + while (curr) { + tmp = curr; + curr = curr->next; + + switch (tmp->type) { + case SERVICE_DESCRIPTOR: + kfree(((struct vidtv_psi_desc_service *)tmp)->provider_name); + kfree(((struct vidtv_psi_desc_service *)tmp)->service_name); + + break; + case REGISTRATION_DESCRIPTOR: + /* nothing to do */ + break; + + default: + pr_warn_ratelimited("Possible leak: not handling descriptor type %d\n", + tmp->type); + break; + } + + kfree(tmp); + } +} + +static u16 +vidtv_psi_desc_comp_loop_len(struct vidtv_psi_desc *desc) +{ + u32 length = 0; + + if (!desc) + return 0; + + while (desc) { + length += sizeof_field(struct vidtv_psi_desc, type); + length += sizeof_field(struct vidtv_psi_desc, length); + length += desc->length; /* from 'length' field until the end of the descriptor */ + desc = desc->next; + } + + return length; +} + +void vidtv_psi_desc_assign(struct vidtv_psi_desc **to, + struct vidtv_psi_desc *desc) +{ + if (desc == *to) + return; + + if (*to) + vidtv_psi_desc_destroy(*to); + + *to = desc; +} + +void vidtv_pmt_desc_assign(struct vidtv_psi_table_pmt *pmt, + struct vidtv_psi_desc **to, + struct vidtv_psi_desc *desc) +{ + vidtv_psi_desc_assign(to, desc); + vidtv_psi_pmt_table_update_sec_len(pmt); + + if (vidtv_psi_get_sec_len(&pmt->header) > MAX_SECTION_LEN) + vidtv_psi_desc_assign(to, NULL); + + vidtv_psi_update_version_num(&pmt->header); +} + +void vidtv_sdt_desc_assign(struct vidtv_psi_table_sdt *sdt, + struct vidtv_psi_desc **to, + struct vidtv_psi_desc *desc) +{ + vidtv_psi_desc_assign(to, desc); + vidtv_psi_sdt_table_update_sec_len(sdt); + + if (vidtv_psi_get_sec_len(&sdt->header) > MAX_SECTION_LEN) + vidtv_psi_desc_assign(to, NULL); + + vidtv_psi_update_version_num(&sdt->header); +} + +static u32 vidtv_psi_desc_write_into(struct desc_write_args args) +{ + /* the number of bytes written by this function */ + u32 nbytes = 0; + struct psi_write_args psi_args = {}; + + psi_args.dest_buf = args.dest_buf; + psi_args.from = &args.desc->type; + + psi_args.len = sizeof_field(struct vidtv_psi_desc, type) + + sizeof_field(struct vidtv_psi_desc, length); + + psi_args.dest_offset = args.dest_offset + nbytes; + psi_args.pid = args.pid; + psi_args.new_psi_section = false; + psi_args.continuity_counter = args.continuity_counter; + psi_args.is_crc = false; + psi_args.dest_buf_sz = args.dest_buf_sz; + psi_args.crc = args.crc; + + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + + switch (args.desc->type) { + case SERVICE_DESCRIPTOR: + psi_args.dest_offset = args.dest_offset + nbytes; + psi_args.len = sizeof_field(struct vidtv_psi_desc_service, service_type) + + sizeof_field(struct vidtv_psi_desc_service, provider_name_len); + psi_args.from = &((struct vidtv_psi_desc_service *)args.desc)->service_type; + + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + + psi_args.dest_offset = args.dest_offset + nbytes; + psi_args.len = ((struct vidtv_psi_desc_service *)args.desc)->provider_name_len; + psi_args.from = ((struct vidtv_psi_desc_service *)args.desc)->provider_name; + + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + + psi_args.dest_offset = args.dest_offset + nbytes; + psi_args.len = sizeof_field(struct vidtv_psi_desc_service, service_name_len); + psi_args.from = &((struct vidtv_psi_desc_service *)args.desc)->service_name_len; + + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + + psi_args.dest_offset = args.dest_offset + nbytes; + psi_args.len = ((struct vidtv_psi_desc_service *)args.desc)->service_name_len; + psi_args.from = ((struct vidtv_psi_desc_service *)args.desc)->service_name; + + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + break; + + case REGISTRATION_DESCRIPTOR: + default: + psi_args.dest_offset = args.dest_offset + nbytes; + psi_args.len = args.desc->length; + psi_args.from = &args.desc->data; + + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + break; + } + + return nbytes; +} + +static u32 +vidtv_psi_table_header_write_into(struct header_write_args args) +{ + /* the number of bytes written by this function */ + u32 nbytes = 0; + struct psi_write_args psi_args = {}; + + psi_args.dest_buf = args.dest_buf; + psi_args.from = args.h; + psi_args.len = sizeof(struct vidtv_psi_table_header); + psi_args.dest_offset = args.dest_offset; + psi_args.pid = args.pid; + psi_args.new_psi_section = true; + psi_args.continuity_counter = args.continuity_counter; + psi_args.is_crc = false; + psi_args.dest_buf_sz = args.dest_buf_sz; + psi_args.crc = args.crc; + + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + + return nbytes; +} + +void +vidtv_psi_pat_table_update_sec_len(struct vidtv_psi_table_pat *pat) +{ + /* see ISO/IEC 13818-1 : 2000 p.43 */ + u16 length = 0; + u32 i; + + /* from immediately after 'section_length' until 'last_section_number'*/ + length += PAT_LEN_UNTIL_LAST_SECTION_NUMBER; + + /* do not count the pointer */ + for (i = 0; i < pat->programs; ++i) + length += sizeof(struct vidtv_psi_table_pat_program) - + sizeof(struct vidtv_psi_table_pat_program *); + + length += CRC_SIZE_IN_BYTES; + + vidtv_psi_set_sec_len(&pat->header, length); +} + +void vidtv_psi_pmt_table_update_sec_len(struct vidtv_psi_table_pmt *pmt) +{ + /* see ISO/IEC 13818-1 : 2000 p.46 */ + u16 length = 0; + struct vidtv_psi_table_pmt_stream *s = pmt->stream; + u16 desc_loop_len; + + /* from immediately after 'section_length' until 'program_info_length'*/ + length += PMT_LEN_UNTIL_PROGRAM_INFO_LENGTH; + + desc_loop_len = vidtv_psi_desc_comp_loop_len(pmt->descriptor); + vidtv_psi_set_desc_loop_len(&pmt->bitfield2, desc_loop_len, 10); + + length += desc_loop_len; + + while (s) { + /* skip both pointers at the end */ + length += sizeof(struct vidtv_psi_table_pmt_stream) - + sizeof(struct vidtv_psi_desc *) - + sizeof(struct vidtv_psi_table_pmt_stream *); + + desc_loop_len = vidtv_psi_desc_comp_loop_len(s->descriptor); + vidtv_psi_set_desc_loop_len(&s->bitfield2, desc_loop_len, 10); + + length += desc_loop_len; + + s = s->next; + } + + length += CRC_SIZE_IN_BYTES; + + vidtv_psi_set_sec_len(&pmt->header, length); +} + +void vidtv_psi_sdt_table_update_sec_len(struct vidtv_psi_table_sdt *sdt) +{ + /* see ETSI EN 300 468 V 1.10.1 p.24 */ + u16 length = 0; + struct vidtv_psi_table_sdt_service *s = sdt->service; + u16 desc_loop_len; + + /* + * from immediately after 'section_length' until + * 'reserved_for_future_use' + */ + length += SDT_LEN_UNTIL_RESERVED_FOR_FUTURE_USE; + + while (s) { + /* skip both pointers at the end */ + length += sizeof(struct vidtv_psi_table_sdt_service) - + sizeof(struct vidtv_psi_desc *) - + sizeof(struct vidtv_psi_table_sdt_service *); + + desc_loop_len = vidtv_psi_desc_comp_loop_len(s->descriptor); + vidtv_psi_set_desc_loop_len(&s->bitfield, desc_loop_len, 12); + + length += desc_loop_len; + + s = s->next; + } + + length += CRC_SIZE_IN_BYTES; + + vidtv_psi_set_sec_len(&sdt->header, length); +} + +struct vidtv_psi_table_pat_program* +vidtv_psi_pat_program_init(struct vidtv_psi_table_pat_program *head, + u16 service_id, + u16 program_map_pid) +{ + struct vidtv_psi_table_pat_program *program; + const u16 RESERVED = 0x07; + + program = kzalloc(sizeof(*program), GFP_KERNEL); + + program->service_id = cpu_to_be16(service_id); + + /* pid for the PMT section in the TS */ + program->bitfield = cpu_to_be16((RESERVED << 13) | program_map_pid); + program->next = NULL; + + if (head) { + while (head->next) + head = head->next; + + head->next = program; + } + + return program; +} + +void +vidtv_psi_pat_program_destroy(struct vidtv_psi_table_pat_program *p) +{ + struct vidtv_psi_table_pat_program *curr = p; + struct vidtv_psi_table_pat_program *tmp = NULL; + + while (curr) { + tmp = curr; + curr = curr->next; + kfree(tmp); + } +} + +void +vidtv_psi_pat_program_assign(struct vidtv_psi_table_pat *pat, + struct vidtv_psi_table_pat_program *p) +{ + /* This function transfers ownership of p to the table */ + + u16 program_count = 0; + struct vidtv_psi_table_pat_program *program = p; + + if (p == pat->program) + return; + + while (program) { + ++program_count; + program = program->next; + } + + pat->programs = program_count; + pat->program = p; + + /* Recompute section length */ + vidtv_psi_pat_table_update_sec_len(pat); + + if (vidtv_psi_get_sec_len(&pat->header) > MAX_SECTION_LEN) + vidtv_psi_pat_program_assign(pat, NULL); + + vidtv_psi_update_version_num(&pat->header); +} + +struct vidtv_psi_table_pat *vidtv_psi_pat_table_init(u16 transport_stream_id) +{ + struct vidtv_psi_table_pat *pat = kzalloc(sizeof(*pat), GFP_KERNEL); + const u16 SYNTAX = 0x1; + const u16 ZERO = 0x0; + const u16 ONES = 0x03; + + pat->header.table_id = 0x0; + + pat->header.bitfield = cpu_to_be16((SYNTAX << 15) | (ZERO << 14) | (ONES << 12)); + pat->header.id = cpu_to_be16(transport_stream_id); + pat->header.current_next = 0x1; + + pat->header.version = 0x1f; + + pat->header.one2 = 0x03; + pat->header.section_id = 0x0; + pat->header.last_section = 0x0; + + pat->programs = 0; + + vidtv_psi_pat_table_update_sec_len(pat); + + return pat; +} + +u32 vidtv_psi_pat_write_into(struct vidtv_psi_pat_write_args args) +{ + /* the number of bytes written by this function */ + u32 nbytes = 0; + const u16 pat_pid = VIDTV_PAT_PID; + u32 crc = 0xffffffff; + + struct vidtv_psi_table_pat_program *p = args.pat->program; + + struct header_write_args h_args = {}; + struct psi_write_args psi_args = {}; + struct crc32_write_args c_args = {}; + + vidtv_psi_pat_table_update_sec_len(args.pat); + + h_args.dest_buf = args.buf; + h_args.dest_offset = args.offset; + h_args.h = &args.pat->header; + h_args.pid = pat_pid; + h_args.continuity_counter = args.continuity_counter; + h_args.dest_buf_sz = args.buf_sz; + h_args.crc = &crc; + + nbytes += vidtv_psi_table_header_write_into(h_args); + + /* note that the field 'u16 programs' is not really part of the PAT */ + + psi_args.dest_buf = args.buf; + psi_args.pid = pat_pid; + psi_args.new_psi_section = false; + psi_args.continuity_counter = args.continuity_counter; + psi_args.is_crc = false; + psi_args.dest_buf_sz = args.buf_sz; + psi_args.crc = &crc; + + while (p) { + /* copy the PAT programs */ + psi_args.from = p; + /* skip the pointer */ + psi_args.len = sizeof(*p) - + sizeof(struct vidtv_psi_table_pat_program *); + psi_args.dest_offset = args.offset + nbytes; + + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + + p = p->next; + } + + c_args.dest_buf = args.buf; + c_args.dest_offset = args.offset + nbytes; + c_args.crc = cpu_to_be32(crc); + c_args.pid = pat_pid; + c_args.continuity_counter = args.continuity_counter; + c_args.dest_buf_sz = args.buf_sz; + + /* Write the CRC32 at the end */ + nbytes += table_section_crc32_write_into(c_args); + + return nbytes; +} + +void +vidtv_psi_pat_table_destroy(struct vidtv_psi_table_pat *p) +{ + vidtv_psi_pat_program_destroy(p->program); + kfree(p); +} + +struct vidtv_psi_table_pmt_stream* +vidtv_psi_pmt_stream_init(struct vidtv_psi_table_pmt_stream *head, + enum vidtv_psi_stream_types stream_type, + u16 es_pid) +{ + struct vidtv_psi_table_pmt_stream *stream; + const u16 RESERVED1 = 0x07; + const u16 RESERVED2 = 0x0f; + const u16 ZERO = 0x0; + u16 desc_loop_len; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + + stream->type = stream_type; + + stream->bitfield = cpu_to_be16((RESERVED1 << 13) | es_pid); + + desc_loop_len = vidtv_psi_desc_comp_loop_len(stream->descriptor); + + stream->bitfield2 = cpu_to_be16((RESERVED2 << 12) | + (ZERO << 10) | + desc_loop_len); + stream->next = NULL; + + if (head) { + while (head->next) + head = head->next; + + head->next = stream; + } + + return stream; +} + +void vidtv_psi_pmt_stream_destroy(struct vidtv_psi_table_pmt_stream *s) +{ + struct vidtv_psi_table_pmt_stream *curr_stream = s; + struct vidtv_psi_table_pmt_stream *tmp_stream = NULL; + + while (curr_stream) { + tmp_stream = curr_stream; + curr_stream = curr_stream->next; + vidtv_psi_desc_destroy(tmp_stream->descriptor); + kfree(tmp_stream); + } +} + +void vidtv_psi_pmt_stream_assign(struct vidtv_psi_table_pmt *pmt, + struct vidtv_psi_table_pmt_stream *s) +{ + /* This function transfers ownership of s to the table */ + if (s == pmt->stream) + return; + + pmt->stream = s; + vidtv_psi_pmt_table_update_sec_len(pmt); + + if (vidtv_psi_get_sec_len(&pmt->header) > MAX_SECTION_LEN) + vidtv_psi_pmt_stream_assign(pmt, NULL); + + vidtv_psi_update_version_num(&pmt->header); +} + +u16 vidtv_psi_pmt_get_pid(struct vidtv_psi_table_pmt *section, + struct vidtv_psi_table_pat *pat) +{ + struct vidtv_psi_table_pat_program *program = pat->program; + + /* + * service_id is the same as program_number in the + * corresponding program_map_section + * see ETSI EN 300 468 v1.15.1 p. 24 + */ + while (program) { + if (program->service_id == section->header.id) + return vidtv_psi_get_pat_program_pid(program); + + program = program->next; + } + + return TS_LAST_VALID_PID + 1; /* not found */ +} + +struct vidtv_psi_table_pmt *vidtv_psi_pmt_table_init(u16 program_number, + u16 pcr_pid) +{ + struct vidtv_psi_table_pmt *pmt = kzalloc(sizeof(*pmt), GFP_KERNEL); + const u16 SYNTAX = 0x1; + const u16 ZERO = 0x0; + const u16 ONES = 0x03; + const u16 RESERVED1 = 0x07; + const u16 RESERVED2 = 0x0f; + u16 desc_loop_len; + + if (!pcr_pid) + pcr_pid = 0x1fff; + + pmt->header.table_id = 0x2; + + pmt->header.bitfield = cpu_to_be16((SYNTAX << 15) | (ZERO << 14) | (ONES << 12)); + + pmt->header.id = cpu_to_be16(program_number); + pmt->header.current_next = 0x1; + + pmt->header.version = 0x1f; + + pmt->header.one2 = ONES; + pmt->header.section_id = 0; + pmt->header.last_section = 0; + + pmt->bitfield = cpu_to_be16((RESERVED1 << 13) | pcr_pid); + + desc_loop_len = vidtv_psi_desc_comp_loop_len(pmt->descriptor); + + pmt->bitfield2 = cpu_to_be16((RESERVED2 << 12) | + (ZERO << 10) | + desc_loop_len); + + vidtv_psi_pmt_table_update_sec_len(pmt); + + return pmt; +} + +u32 vidtv_psi_pmt_write_into(struct vidtv_psi_pmt_write_args args) +{ + /* the number of bytes written by this function */ + u32 nbytes = 0; + u32 crc = 0xffffffff; + + struct vidtv_psi_desc *table_descriptor = args.pmt->descriptor; + struct vidtv_psi_table_pmt_stream *stream = args.pmt->stream; + struct vidtv_psi_desc *stream_descriptor = (stream) ? + args.pmt->stream->descriptor : + NULL; + + struct header_write_args h_args = {}; + struct psi_write_args psi_args = {}; + struct desc_write_args d_args = {}; + struct crc32_write_args c_args = {}; + + vidtv_psi_pmt_table_update_sec_len(args.pmt); + + h_args.dest_buf = args.buf; + h_args.dest_offset = args.offset; + h_args.h = &args.pmt->header; + h_args.pid = args.pid; + h_args.continuity_counter = args.continuity_counter; + h_args.dest_buf_sz = args.buf_sz; + h_args.crc = &crc; + + nbytes += vidtv_psi_table_header_write_into(h_args); + + /* write the two bitfields */ + psi_args.dest_buf = args.buf; + psi_args.from = &args.pmt->bitfield; + psi_args.len = sizeof_field(struct vidtv_psi_table_pmt, bitfield) + + sizeof_field(struct vidtv_psi_table_pmt, bitfield2); + + psi_args.dest_offset = args.offset + nbytes; + psi_args.pid = args.pid; + psi_args.new_psi_section = false; + psi_args.continuity_counter = args.continuity_counter; + psi_args.is_crc = false; + psi_args.dest_buf_sz = args.buf_sz; + psi_args.crc = &crc; + + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + + while (table_descriptor) { + /* write the descriptors, if any */ + d_args.dest_buf = args.buf; + d_args.dest_offset = args.offset + nbytes; + d_args.desc = table_descriptor; + d_args.pid = args.pid; + d_args.continuity_counter = args.continuity_counter; + d_args.dest_buf_sz = args.buf_sz; + d_args.crc = &crc; + + nbytes += vidtv_psi_desc_write_into(d_args); + + table_descriptor = table_descriptor->next; + } + + while (stream) { + /* write the streams, if any */ + psi_args.from = stream; + psi_args.len = sizeof_field(struct vidtv_psi_table_pmt_stream, type) + + sizeof_field(struct vidtv_psi_table_pmt_stream, bitfield) + + sizeof_field(struct vidtv_psi_table_pmt_stream, bitfield2); + psi_args.dest_offset = args.offset + nbytes; + + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + + while (stream_descriptor) { + /* write the stream descriptors, if any */ + d_args.dest_buf = args.buf; + d_args.dest_offset = args.offset + nbytes; + d_args.desc = stream_descriptor; + d_args.pid = args.pid; + d_args.continuity_counter = args.continuity_counter; + d_args.dest_buf_sz = args.buf_sz; + d_args.crc = &crc; + + nbytes += vidtv_psi_desc_write_into(d_args); + + stream_descriptor = stream_descriptor->next; + } + + stream = stream->next; + } + + c_args.dest_buf = args.buf; + c_args.dest_offset = args.offset + nbytes; + c_args.crc = cpu_to_be32(crc); + c_args.pid = args.pid; + c_args.continuity_counter = args.continuity_counter; + c_args.dest_buf_sz = args.buf_sz; + + /* Write the CRC32 at the end */ + nbytes += table_section_crc32_write_into(c_args); + + return nbytes; +} + +void vidtv_psi_pmt_table_destroy(struct vidtv_psi_table_pmt *pmt) +{ + vidtv_psi_desc_destroy(pmt->descriptor); + vidtv_psi_pmt_stream_destroy(pmt->stream); + kfree(pmt); +} + +struct vidtv_psi_table_sdt *vidtv_psi_sdt_table_init(u16 transport_stream_id) +{ + struct vidtv_psi_table_sdt *sdt = kzalloc(sizeof(*sdt), GFP_KERNEL); + const u16 SYNTAX = 0x1; + const u16 ONE = 0x1; + const u16 ONES = 0x03; + const u16 RESERVED = 0xff; + + sdt->header.table_id = 0x42; + + sdt->header.bitfield = cpu_to_be16((SYNTAX << 15) | (ONE << 14) | (ONES << 12)); + + /* + * This is a 16-bit field which serves as a label for identification + * of the TS, about which the SDT informs, from any other multiplex + * within the delivery system. + */ + sdt->header.id = cpu_to_be16(transport_stream_id); + sdt->header.current_next = ONE; + + sdt->header.version = 0x1f; + + sdt->header.one2 = ONES; + sdt->header.section_id = 0; + sdt->header.last_section = 0; + + /* + * FIXME: The network_id range from 0xFF01 to 0xFFFF is used to + * indicate temporary private use. For now, let's use the first + * value. + * This can be changed to something more useful, when support for + * NIT gets added + */ + sdt->network_id = cpu_to_be16(0xff01); + sdt->reserved = RESERVED; + + vidtv_psi_sdt_table_update_sec_len(sdt); + + return sdt; +} + +u32 vidtv_psi_sdt_write_into(struct vidtv_psi_sdt_write_args args) +{ + u32 nbytes = 0; + u16 sdt_pid = VIDTV_SDT_PID; /* see ETSI EN 300 468 v1.15.1 p. 11 */ + + u32 crc = 0xffffffff; + + struct vidtv_psi_table_sdt_service *service = args.sdt->service; + struct vidtv_psi_desc *service_desc = (args.sdt->service) ? + args.sdt->service->descriptor : + NULL; + + struct header_write_args h_args = {}; + struct psi_write_args psi_args = {}; + struct desc_write_args d_args = {}; + struct crc32_write_args c_args = {}; + + vidtv_psi_sdt_table_update_sec_len(args.sdt); + + h_args.dest_buf = args.buf; + h_args.dest_offset = args.offset; + h_args.h = &args.sdt->header; + h_args.pid = sdt_pid; + h_args.continuity_counter = args.continuity_counter; + h_args.dest_buf_sz = args.buf_sz; + h_args.crc = &crc; + + nbytes += vidtv_psi_table_header_write_into(h_args); + + psi_args.dest_buf = args.buf; + psi_args.from = &args.sdt->network_id; + + psi_args.len = sizeof_field(struct vidtv_psi_table_sdt, network_id) + + sizeof_field(struct vidtv_psi_table_sdt, reserved); + + psi_args.dest_offset = args.offset + nbytes; + psi_args.pid = sdt_pid; + psi_args.new_psi_section = false; + psi_args.continuity_counter = args.continuity_counter; + psi_args.is_crc = false; + psi_args.dest_buf_sz = args.buf_sz; + psi_args.crc = &crc; + + /* copy u16 network_id + u8 reserved)*/ + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + + while (service) { + /* copy the services, if any */ + psi_args.from = service; + /* skip both pointers at the end */ + psi_args.len = sizeof(struct vidtv_psi_table_sdt_service) - + sizeof(struct vidtv_psi_desc *) - + sizeof(struct vidtv_psi_table_sdt_service *); + psi_args.dest_offset = args.offset + nbytes; + + nbytes += vidtv_psi_ts_psi_write_into(psi_args); + + while (service_desc) { + /* copy the service descriptors, if any */ + d_args.dest_buf = args.buf; + d_args.dest_offset = args.offset + nbytes; + d_args.desc = service_desc; + d_args.pid = sdt_pid; + d_args.continuity_counter = args.continuity_counter; + d_args.dest_buf_sz = args.buf_sz; + d_args.crc = &crc; + + nbytes += vidtv_psi_desc_write_into(d_args); + + service_desc = service_desc->next; + } + + service = service->next; + } + + c_args.dest_buf = args.buf; + c_args.dest_offset = args.offset + nbytes; + c_args.crc = cpu_to_be32(crc); + c_args.pid = sdt_pid; + c_args.continuity_counter = args.continuity_counter; + c_args.dest_buf_sz = args.buf_sz; + + /* Write the CRC at the end */ + nbytes += table_section_crc32_write_into(c_args); + + return nbytes; +} + +void vidtv_psi_sdt_table_destroy(struct vidtv_psi_table_sdt *sdt) +{ + vidtv_psi_sdt_service_destroy(sdt->service); + kfree(sdt); +} + +struct vidtv_psi_table_sdt_service +*vidtv_psi_sdt_service_init(struct vidtv_psi_table_sdt_service *head, + u16 service_id) +{ + struct vidtv_psi_table_sdt_service *service; + + service = kzalloc(sizeof(*service), GFP_KERNEL); + + /* + * ETSI 300 468: this is a 16bit field which serves as a label to + * identify this service from any other service within the TS. + * The service id is the same as the program number in the + * corresponding program_map_section + */ + service->service_id = cpu_to_be16(service_id); + service->EIT_schedule = 0x0; + service->EIT_present_following = 0x0; + service->reserved = 0x3f; + + service->bitfield = cpu_to_be16(RUNNING << 13); + + if (head) { + while (head->next) + head = head->next; + + head->next = service; + } + + return service; +} + +void +vidtv_psi_sdt_service_destroy(struct vidtv_psi_table_sdt_service *service) +{ + struct vidtv_psi_table_sdt_service *curr = service; + struct vidtv_psi_table_sdt_service *tmp = NULL; + + while (curr) { + tmp = curr; + curr = curr->next; + vidtv_psi_desc_destroy(tmp->descriptor); + kfree(tmp); + } +} + +void +vidtv_psi_sdt_service_assign(struct vidtv_psi_table_sdt *sdt, + struct vidtv_psi_table_sdt_service *service) +{ + if (service == sdt->service) + return; + + sdt->service = service; + + /* recompute section length */ + vidtv_psi_sdt_table_update_sec_len(sdt); + + if (vidtv_psi_get_sec_len(&sdt->header) > MAX_SECTION_LEN) + vidtv_psi_sdt_service_assign(sdt, NULL); + + vidtv_psi_update_version_num(&sdt->header); +} + +struct vidtv_psi_table_pmt** +vidtv_psi_pmt_create_sec_for_each_pat_entry(struct vidtv_psi_table_pat *pat, u16 pcr_pid) + +{ + /* + * PMTs contain information about programs. For each program, + * there is one PMT section. This function will create a section + * for each program found in the PAT + */ + struct vidtv_psi_table_pat_program *program = pat->program; + struct vidtv_psi_table_pmt **pmt_secs; + u32 i = 0; + + /* a section for each program_id */ + pmt_secs = kcalloc(pat->programs, + sizeof(struct vidtv_psi_table_pmt *), + GFP_KERNEL); + + while (program) { + pmt_secs[i] = vidtv_psi_pmt_table_init(be16_to_cpu(program->service_id), pcr_pid); + ++i; + program = program->next; + } + + return pmt_secs; +} + +struct vidtv_psi_table_pmt +*vidtv_psi_find_pmt_sec(struct vidtv_psi_table_pmt **pmt_sections, + u16 nsections, + u16 program_num) +{ + /* find the PMT section associated with 'program_num' */ + struct vidtv_psi_table_pmt *sec = NULL; + u32 i; + + for (i = 0; i < nsections; ++i) { + sec = pmt_sections[i]; + if (be16_to_cpu(sec->header.id) == program_num) + return sec; + } + + return NULL; /* not found */ +} diff --git a/drivers/media/test-drivers/vidtv/vidtv_psi.h b/drivers/media/test-drivers/vidtv/vidtv_psi.h new file mode 100644 index 000000000000..3f962cc78278 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_psi.h @@ -0,0 +1,577 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file contains the logic to work with MPEG Program-Specific Information. + * These are defined both in ISO/IEC 13818-1 (systems) and ETSI EN 300 468. + * PSI is carried in the form of table structures, and although each table might + * technically be broken into one or more sections, we do not do this here, + * hence 'table' and 'section' are interchangeable for vidtv. + * + * This code currently supports three tables: PAT, PMT and SDT. These are the + * bare minimum to get userspace to recognize our MPEG transport stream. It can + * be extended to support more PSI tables in the future. + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#ifndef VIDTV_PSI_H +#define VIDTV_PSI_H + +#include <linux/types.h> +#include <asm/byteorder.h> + +/* + * all section lengths start immediately after the 'section_length' field + * see ISO/IEC 13818-1 : 2000 and ETSI EN 300 468 V 1.10.1 for + * reference + */ +#define PAT_LEN_UNTIL_LAST_SECTION_NUMBER 5 +#define PMT_LEN_UNTIL_PROGRAM_INFO_LENGTH 9 +#define SDT_LEN_UNTIL_RESERVED_FOR_FUTURE_USE 8 +#define MAX_SECTION_LEN 1021 +#define VIDTV_PAT_PID 0 /* mandated by the specs */ +#define VIDTV_SDT_PID 0x0011 /* mandated by the specs */ + +enum vidtv_psi_descriptors { + REGISTRATION_DESCRIPTOR = 0x05, /* See ISO/IEC 13818-1 section 2.6.8 */ + SERVICE_DESCRIPTOR = 0x48, /* See ETSI EN 300 468 section 6.2.33 */ +}; + +enum vidtv_psi_stream_types { + STREAM_PRIVATE_DATA = 0x06, /* see ISO/IEC 13818-1 2000 p. 48 */ +}; + +/** + * struct vidtv_psi_desc - A generic PSI descriptor type. + * The descriptor length is an 8-bit field specifying the total number of bytes of the data portion + * of the descriptor following the byte defining the value of this field. + */ +struct vidtv_psi_desc { + struct vidtv_psi_desc *next; + u8 type; + u8 length; + u8 data[]; +} __packed; + +/** + * struct vidtv_psi_desc_service - Service descriptor. + * See ETSI EN 300 468 section 6.2.33. + */ +struct vidtv_psi_desc_service { + struct vidtv_psi_desc *next; + u8 type; + u8 length; + + u8 service_type; + u8 provider_name_len; + char *provider_name; + u8 service_name_len; + char *service_name; +} __packed; + +/** + * struct vidtv_psi_desc_registration - A registration descriptor. + * See ISO/IEC 13818-1 section 2.6.8 + */ +struct vidtv_psi_desc_registration { + struct vidtv_psi_desc *next; + u8 type; + u8 length; + + /* + * The format_identifier is a 32-bit value obtained from a Registration + * Authority as designated by ISO/IEC JTC 1/SC 29. + */ + __be32 format_id; + /* + * The meaning of additional_identification_info bytes, if any, are + * defined by the assignee of that format_identifier, and once defined + * they shall not change. + */ + u8 additional_identification_info[]; +} __packed; + +/** + * struct vidtv_psi_table_header - A header that is present for all PSI tables. + */ +struct vidtv_psi_table_header { + u8 table_id; + + __be16 bitfield; /* syntax: 1, zero: 1, one: 2, section_length: 13 */ + + __be16 id; /* TS ID */ + u8 current_next:1; + u8 version:5; + u8 one2:2; + u8 section_id; /* section_number */ + u8 last_section; /* last_section_number */ +} __packed; + +/** + * struct vidtv_psi_table_pat_program - A single program in the PAT + * See ISO/IEC 13818-1 : 2000 p.43 + */ +struct vidtv_psi_table_pat_program { + __be16 service_id; + __be16 bitfield; /* reserved: 3, program_map_pid/network_pid: 13 */ + struct vidtv_psi_table_pat_program *next; +} __packed; + +/** + * struct vidtv_psi_table_pat - The Program Allocation Table (PAT) + * See ISO/IEC 13818-1 : 2000 p.43 + */ +struct vidtv_psi_table_pat { + struct vidtv_psi_table_header header; + u16 programs; /* Included by libdvbv5, not part of the table and not actually serialized */ + struct vidtv_psi_table_pat_program *program; +} __packed; + +/** + * struct vidtv_psi_table_sdt_service - Represents a service in the SDT. + * see ETSI EN 300 468 v1.15.1 section 5.2.3. + */ +struct vidtv_psi_table_sdt_service { + __be16 service_id; + u8 EIT_present_following:1; + u8 EIT_schedule:1; + u8 reserved:6; + __be16 bitfield; /* running_status: 3, free_ca:1, desc_loop_len:12 */ + struct vidtv_psi_desc *descriptor; + struct vidtv_psi_table_sdt_service *next; +} __packed; + +/** + * struct vidtv_psi_table_sdt - Represents the Service Description Table + * see ETSI EN 300 468 v1.15.1 section 5.2.3. + */ + +struct vidtv_psi_table_sdt { + struct vidtv_psi_table_header header; + __be16 network_id; /* original_network_id */ + u8 reserved; + struct vidtv_psi_table_sdt_service *service; +} __packed; + +/** + * enum service_running_status - Status of a SDT service. + * see ETSI EN 300 468 v1.15.1 section 5.2.3 table 6. + */ +enum service_running_status { + RUNNING = 0x4, +}; + +/** + * enum service_type - The type of a SDT service. + * see ETSI EN 300 468 v1.15.1 section 6.2.33, table 81. + */ +enum service_type { + /* see ETSI EN 300 468 v1.15.1 p. 77 */ + DIGITAL_TELEVISION_SERVICE = 0x1, +}; + +/** + * struct vidtv_psi_table_pmt_stream - A single stream in the PMT. + * See ISO/IEC 13818-1 : 2000 p.46. + */ +struct vidtv_psi_table_pmt_stream { + u8 type; + __be16 bitfield; /* reserved: 3, elementary_pid: 13 */ + __be16 bitfield2; /*reserved: 4, zero: 2, desc_length: 10 */ + struct vidtv_psi_desc *descriptor; + struct vidtv_psi_table_pmt_stream *next; +} __packed; + +/** + * struct vidtv_psi_table_pmt - The Program Map Table (PMT). + * See ISO/IEC 13818-1 : 2000 p.46. + */ +struct vidtv_psi_table_pmt { + struct vidtv_psi_table_header header; + __be16 bitfield; /* reserved:3, pcr_pid: 13 */ + __be16 bitfield2; /* reserved: 4, zero: 2, desc_len: 10 */ + struct vidtv_psi_desc *descriptor; + struct vidtv_psi_table_pmt_stream *stream; +} __packed; + +/** + * struct psi_write_args - Arguments for the PSI packetizer. + * @dest_buf: The buffer to write into. + * @from: PSI data to be copied. + * @len: How much to write. + * @dest_offset: where to start writing in the dest_buffer. + * @pid: TS packet ID. + * @new_psi_section: Set when starting a table section. + * @continuity_counter: Incremented on every new packet. + * @is_crc: Set when writing the CRC at the end. + * @dest_buf_sz: The size of the dest_buffer + * @crc: a pointer to store the crc for this chunk + */ +struct psi_write_args { + void *dest_buf; + void *from; + size_t len; + u32 dest_offset; + u16 pid; + bool new_psi_section; + u8 *continuity_counter; + bool is_crc; + u32 dest_buf_sz; + u32 *crc; +}; + +/** + * struct desc_write_args - Arguments in order to write a descriptor. + * @dest_buf: The buffer to write into. + * @dest_offset: where to start writing in the dest_buffer. + * @desc: A pointer to the descriptor + * @pid: TS packet ID. + * @continuity_counter: Incremented on every new packet. + * @dest_buf_sz: The size of the dest_buffer + * @crc: a pointer to store the crc for this chunk + */ +struct desc_write_args { + void *dest_buf; + u32 dest_offset; + struct vidtv_psi_desc *desc; + u16 pid; + u8 *continuity_counter; + u32 dest_buf_sz; + u32 *crc; +}; + +/** + * struct crc32_write_args - Arguments in order to write the CRC at the end of + * the PSI tables. + * @dest_buf: The buffer to write into. + * @dest_offset: where to start writing in the dest_buffer. + * @crc: the CRC value to write + * @pid: TS packet ID. + * @continuity_counter: Incremented on every new packet. + * @dest_buf_sz: The size of the dest_buffer + */ +struct crc32_write_args { + void *dest_buf; + u32 dest_offset; + __be32 crc; + u16 pid; + u8 *continuity_counter; + u32 dest_buf_sz; +}; + +/** + * struct header_write_args - Arguments in order to write the common table + * header + * @dest_buf: The buffer to write into. + * @dest_offset: where to start writing in the dest_buffer. + * @h: a pointer to the header. + * @pid: TS packet ID. + * @continuity_counter: Incremented on every new packet. + * @dest_buf_sz: The size of the dest_buffer + * @crc: a pointer to store the crc for this chunk + */ +struct header_write_args { + void *dest_buf; + u32 dest_offset; + struct vidtv_psi_table_header *h; + u16 pid; + u8 *continuity_counter; + u32 dest_buf_sz; + u32 *crc; +}; + +struct vidtv_psi_desc_service *vidtv_psi_service_desc_init(struct vidtv_psi_desc *head, + enum service_type service_type, + char *service_name, + char *provider_name); + +struct vidtv_psi_desc_registration +*vidtv_psi_registration_desc_init(struct vidtv_psi_desc *head, + __be32 format_id, + u8 *additional_ident_info, + u32 additional_info_len); + +struct vidtv_psi_table_pat_program +*vidtv_psi_pat_program_init(struct vidtv_psi_table_pat_program *head, + u16 service_id, + u16 program_map_pid); + +struct vidtv_psi_table_pmt_stream* +vidtv_psi_pmt_stream_init(struct vidtv_psi_table_pmt_stream *head, + enum vidtv_psi_stream_types stream_type, + u16 es_pid); + +struct vidtv_psi_table_pat *vidtv_psi_pat_table_init(u16 transport_stream_id); + +struct vidtv_psi_table_pmt *vidtv_psi_pmt_table_init(u16 program_number, + u16 pcr_pid); + +struct vidtv_psi_table_sdt *vidtv_psi_sdt_table_init(u16 transport_stream_id); + +struct vidtv_psi_table_sdt_service* +vidtv_psi_sdt_service_init(struct vidtv_psi_table_sdt_service *head, + u16 service_id); + +void +vidtv_psi_desc_destroy(struct vidtv_psi_desc *desc); + +void +vidtv_psi_pat_program_destroy(struct vidtv_psi_table_pat_program *p); + +void +vidtv_psi_pat_table_destroy(struct vidtv_psi_table_pat *p); + +void +vidtv_psi_pmt_stream_destroy(struct vidtv_psi_table_pmt_stream *s); + +void +vidtv_psi_pmt_table_destroy(struct vidtv_psi_table_pmt *pmt); + +void +vidtv_psi_sdt_table_destroy(struct vidtv_psi_table_sdt *sdt); + +void +vidtv_psi_sdt_service_destroy(struct vidtv_psi_table_sdt_service *service); + +/** + * vidtv_psi_sdt_service_assign - Assigns the service loop to the SDT. + * @sdt: The SDT to assign to. + * @service: The service loop (one or more services) + * + * This will free the previous service loop in the table. + * This will assign ownership of the service loop to the table, i.e. the table + * will free this service loop when a call to its destroy function is made. + */ +void +vidtv_psi_sdt_service_assign(struct vidtv_psi_table_sdt *sdt, + struct vidtv_psi_table_sdt_service *service); + +/** + * vidtv_psi_desc_assign - Assigns a descriptor loop at some point + * @to: Where to assign this descriptor loop to + * @desc: The descriptor loop that will be assigned. + * + * This will free the loop in 'to', if any. + */ +void vidtv_psi_desc_assign(struct vidtv_psi_desc **to, + struct vidtv_psi_desc *desc); + +/** + * vidtv_psi_pmt_desc_assign - Assigns a descriptor loop at some point in a PMT section. + * @pmt: The PMT section that will contain the descriptor loop + * @to: Where in the PMT to assign this descriptor loop to + * @desc: The descriptor loop that will be assigned. + * + * This will free the loop in 'to', if any. + * This will assign ownership of the loop to the table, i.e. the table + * will free this loop when a call to its destroy function is made. + */ +void vidtv_pmt_desc_assign(struct vidtv_psi_table_pmt *pmt, + struct vidtv_psi_desc **to, + struct vidtv_psi_desc *desc); + +/** + * vidtv_psi_sdt_desc_assign - Assigns a descriptor loop at some point in a SDT. + * @sdt: The SDT that will contain the descriptor loop + * @to: Where in the PMT to assign this descriptor loop to + * @desc: The descriptor loop that will be assigned. + * + * This will free the loop in 'to', if any. + * This will assign ownership of the loop to the table, i.e. the table + * will free this loop when a call to its destroy function is made. + */ +void vidtv_sdt_desc_assign(struct vidtv_psi_table_sdt *sdt, + struct vidtv_psi_desc **to, + struct vidtv_psi_desc *desc); + +/** + * vidtv_psi_pat_program_assign - Assigns the program loop to the PAT. + * @pat: The PAT to assign to. + * @p: The program loop (one or more programs) + * + * This will free the previous program loop in the table. + * This will assign ownership of the program loop to the table, i.e. the table + * will free this program loop when a call to its destroy function is made. + */ +void vidtv_psi_pat_program_assign(struct vidtv_psi_table_pat *pat, + struct vidtv_psi_table_pat_program *p); + +/** + * vidtv_psi_pmt_stream_assign - Assigns the stream loop to the PAT. + * @pmt: The PMT to assign to. + * @s: The stream loop (one or more streams) + * + * This will free the previous stream loop in the table. + * This will assign ownership of the stream loop to the table, i.e. the table + * will free this stream loop when a call to its destroy function is made. + */ +void vidtv_psi_pmt_stream_assign(struct vidtv_psi_table_pmt *pmt, + struct vidtv_psi_table_pmt_stream *s); + +struct vidtv_psi_desc *vidtv_psi_desc_clone(struct vidtv_psi_desc *desc); + +/** + * vidtv_psi_create_sec_for_each_pat_entry - Create a PMT section for each + * program found in the PAT + * @pat: The PAT to look for programs. + * @s: The stream loop (one or more streams) + * @pcr_pid: packet ID for the PCR to be used for the program described in this + * PMT section + */ +struct vidtv_psi_table_pmt** +vidtv_psi_pmt_create_sec_for_each_pat_entry(struct vidtv_psi_table_pat *pat, u16 pcr_pid); + +/** + * vidtv_psi_pmt_get_pid - Get the TS PID for a PMT section. + * @section: The PMT section whose PID we want to retrieve. + * @pat: The PAT table to look into. + * + * Returns: the TS PID for 'section' + */ +u16 vidtv_psi_pmt_get_pid(struct vidtv_psi_table_pmt *section, + struct vidtv_psi_table_pat *pat); + +/** + * vidtv_psi_pat_table_update_sec_len - Recompute and update the PAT section length. + * @pat: The PAT whose length is to be updated. + * + * This will traverse the table and accumulate the length of its components, + * which is then used to replace the 'section_length' field. + * + * If section_length > MAX_SECTION_LEN, the operation fails. + */ +void vidtv_psi_pat_table_update_sec_len(struct vidtv_psi_table_pat *pat); + +/** + * vidtv_psi_pmt_table_update_sec_len - Recompute and update the PMT section length. + * @pmt: The PMT whose length is to be updated. + * + * This will traverse the table and accumulate the length of its components, + * which is then used to replace the 'section_length' field. + * + * If section_length > MAX_SECTION_LEN, the operation fails. + */ +void vidtv_psi_pmt_table_update_sec_len(struct vidtv_psi_table_pmt *pmt); + +/** + * vidtv_psi_sdt_table_update_sec_len - Recompute and update the SDT section length. + * @sdt: The SDT whose length is to be updated. + * + * This will traverse the table and accumulate the length of its components, + * which is then used to replace the 'section_length' field. + * + * If section_length > MAX_SECTION_LEN, the operation fails. + */ +void vidtv_psi_sdt_table_update_sec_len(struct vidtv_psi_table_sdt *sdt); + +/** + * struct vidtv_psi_pat_write_args - Arguments for writing a PAT table + * @buf: The destination buffer. + * @offset: The offset into the destination buffer. + * @pat: A pointer to the PAT. + * @buf_sz: The size of the destination buffer. + * @continuity_counter: A pointer to the CC. Incremented on every new packet. + * + */ +struct vidtv_psi_pat_write_args { + char *buf; + u32 offset; + struct vidtv_psi_table_pat *pat; + u32 buf_sz; + u8 *continuity_counter; +}; + +/** + * vidtv_psi_pat_write_into - Write PAT as MPEG-TS packets into a buffer. + * @args: An instance of struct vidtv_psi_pat_write_args + * + * This function writes the MPEG TS packets for a PAT table into a buffer. + * Calling code will usually generate the PAT via a call to its init function + * and thus is responsible for freeing it. + * + * Return: The number of bytes written into the buffer. This is NOT + * equal to the size of the PAT, since more space is needed for TS headers during TS + * encapsulation. + */ +u32 vidtv_psi_pat_write_into(struct vidtv_psi_pat_write_args args); + +/** + * struct vidtv_psi_sdt_write_args - Arguments for writing a SDT table + * @buf: The destination buffer. + * @offset: The offset into the destination buffer. + * @sdt: A pointer to the SDT. + * @buf_sz: The size of the destination buffer. + * @continuity_counter: A pointer to the CC. Incremented on every new packet. + * + */ + +struct vidtv_psi_sdt_write_args { + char *buf; + u32 offset; + struct vidtv_psi_table_sdt *sdt; + u32 buf_sz; + u8 *continuity_counter; +}; + +/** + * vidtv_psi_sdt_write_into - Write SDT as MPEG-TS packets into a buffer. + * @args: an instance of struct vidtv_psi_sdt_write_args + * + * This function writes the MPEG TS packets for a SDT table into a buffer. + * Calling code will usually generate the SDT via a call to its init function + * and thus is responsible for freeing it. + * + * Return: The number of bytes written into the buffer. This is NOT + * equal to the size of the SDT, since more space is needed for TS headers during TS + * encapsulation. + */ +u32 vidtv_psi_sdt_write_into(struct vidtv_psi_sdt_write_args args); + +/** + * struct vidtv_psi_pmt_write_args - Arguments for writing a PMT section + * @buf: The destination buffer. + * @offset: The offset into the destination buffer. + * @pmt: A pointer to the PMT. + * @buf_sz: The size of the destination buffer. + * @continuity_counter: A pointer to the CC. Incremented on every new packet. + * + */ +struct vidtv_psi_pmt_write_args { + char *buf; + u32 offset; + struct vidtv_psi_table_pmt *pmt; + u16 pid; + u32 buf_sz; + u8 *continuity_counter; + u16 pcr_pid; +}; + +/** + * vidtv_psi_pmt_write_into - Write PMT as MPEG-TS packets into a buffer. + * @args: an instance of struct vidtv_psi_pmt_write_args + * + * This function writes the MPEG TS packets for a PMT section into a buffer. + * Calling code will usually generate the PMT section via a call to its init function + * and thus is responsible for freeing it. + * + * Return: The number of bytes written into the buffer. This is NOT + * equal to the size of the PMT section, since more space is needed for TS headers + * during TS encapsulation. + */ +u32 vidtv_psi_pmt_write_into(struct vidtv_psi_pmt_write_args args); + +/** + * vidtv_psi_find_pmt_sec - Finds the PMT section for 'program_num' + * @pmt_sections: The sections to look into. + * @nsections: The number of sections. + * @program_num: The 'program_num' from PAT pointing to a PMT section. + * + * Return: A pointer to the PMT, if found, or NULL. + */ +struct vidtv_psi_table_pmt *vidtv_psi_find_pmt_sec(struct vidtv_psi_table_pmt **pmt_sections, + u16 nsections, + u16 program_num); + +u16 vidtv_psi_get_pat_program_pid(struct vidtv_psi_table_pat_program *p); +u16 vidtv_psi_pmt_stream_get_elem_pid(struct vidtv_psi_table_pmt_stream *s); + +#endif // VIDTV_PSI_H diff --git a/drivers/media/test-drivers/vidtv/vidtv_s302m.c b/drivers/media/test-drivers/vidtv/vidtv_s302m.c new file mode 100644 index 000000000000..a447ccbd68d5 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_s302m.c @@ -0,0 +1,502 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Vidtv serves as a reference DVB driver and helps validate the existing APIs + * in the media subsystem. It can also aid developers working on userspace + * applications. + * + * This file contains the code for an AES3 (also known as AES/EBU) encoder. + * It is based on EBU Tech 3250 and SMPTE 302M technical documents. + * + * This encoder currently supports 16bit AES3 subframes using 16bit signed + * integers. + * + * Note: AU stands for Access Unit, and AAU stands for Audio Access Unit + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s, %d: " fmt, __func__, __LINE__ + +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/crc32.h> +#include <linux/vmalloc.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/jiffies.h> +#include <linux/printk.h> +#include <linux/ratelimit.h> +#include <linux/fixp-arith.h> + +#include <linux/math64.h> +#include <asm/byteorder.h> + +#include "vidtv_s302m.h" +#include "vidtv_encoder.h" +#include "vidtv_common.h" + +#define S302M_SAMPLING_RATE_HZ 48000 +#define PES_PRIVATE_STREAM_1 0xbd /* PES: private_stream_1 */ +#define S302M_BLOCK_SZ 192 +#define S302M_SIN_LUT_NUM_ELEM 1024 + +/* these are retrieved empirically from ffmpeg/libavcodec */ +#define FF_S302M_DEFAULT_NUM_FRAMES 1115 +#define FF_S302M_DEFAULT_PTS_INCREMENT 2090 +#define FF_S302M_DEFAULT_PTS_OFFSET 100000 + +/* Used by the tone generator: number of samples for PI */ +#define PI 180 + +static const u8 reverse[256] = { + /* from ffmpeg */ + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, + 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, + 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, + 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, + 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, + 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, + 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, + 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, + 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, + 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, + 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, + 0x3F, 0xBF, 0x7F, 0xFF, +}; + +struct tone_duration { + enum musical_notes note; + int duration; +}; + +#define COMPASS 120 /* beats per minute (Allegro) */ +static const struct tone_duration beethoven_5th_symphony[] = { + { NOTE_E_6, 128}, { NOTE_DS_6, 128}, { NOTE_E_6, 128}, + { NOTE_DS_6, 128}, { NOTE_E_6, 128}, { NOTE_B_5, 128}, + { NOTE_D_6, 128}, { NOTE_C_6, 128}, { NOTE_A_3, 128}, + { NOTE_E_4, 128}, { NOTE_A_4, 128}, { NOTE_C_5, 128}, + { NOTE_E_5, 128}, { NOTE_A_5, 128}, { NOTE_E_3, 128}, + { NOTE_E_4, 128}, { NOTE_GS_4, 128}, { NOTE_E_5, 128}, + { NOTE_GS_5, 128}, { NOTE_B_5, 128}, { NOTE_A_3, 128}, + { NOTE_E_4, 128}, { NOTE_A_4, 128}, { NOTE_E_5, 128}, + { NOTE_E_6, 128}, { NOTE_DS_6, 128}, { NOTE_E_6, 128}, + { NOTE_DS_6, 128}, { NOTE_E_6, 128}, { NOTE_B_5, 128}, + { NOTE_D_6, 128}, { NOTE_C_6, 128}, { NOTE_A_3, 128}, + { NOTE_E_4, 128}, { NOTE_A_4, 128}, { NOTE_C_5, 128}, + { NOTE_E_5, 128}, { NOTE_A_5, 128}, { NOTE_E_3, 128}, + { NOTE_E_4, 128}, { NOTE_GS_4, 128}, { NOTE_E_5, 128}, + { NOTE_C_6, 128}, { NOTE_B_5, 128}, { NOTE_A_3, 128}, + { NOTE_E_4, 128}, { NOTE_A_4, 128}, { NOTE_SILENT, 128}, + + { NOTE_E_6, 128}, { NOTE_DS_6, 128}, { NOTE_E_6, 128}, + { NOTE_DS_6, 128}, { NOTE_E_6, 128}, { NOTE_B_5, 128}, + { NOTE_D_6, 128}, { NOTE_C_6, 128}, { NOTE_A_3, 128}, + { NOTE_E_4, 128}, { NOTE_A_4, 128}, { NOTE_C_5, 128}, + { NOTE_E_5, 128}, { NOTE_A_5, 128}, { NOTE_E_3, 128}, + { NOTE_E_4, 128}, { NOTE_GS_4, 128}, { NOTE_E_5, 128}, + { NOTE_GS_5, 128}, { NOTE_B_5, 128}, { NOTE_A_3, 128}, + { NOTE_E_4, 128}, { NOTE_A_4, 128}, { NOTE_E_5, 128}, + { NOTE_E_6, 128}, { NOTE_DS_6, 128}, { NOTE_E_6, 128}, + { NOTE_DS_6, 128}, { NOTE_E_6, 128}, { NOTE_B_5, 128}, + { NOTE_D_6, 128}, { NOTE_C_6, 128}, { NOTE_A_3, 128}, + { NOTE_E_4, 128}, { NOTE_A_4, 128}, { NOTE_C_5, 128}, + { NOTE_E_5, 128}, { NOTE_A_5, 128}, { NOTE_E_3, 128}, + { NOTE_E_4, 128}, { NOTE_GS_4, 128}, { NOTE_E_5, 128}, + { NOTE_C_6, 128}, { NOTE_B_5, 128}, { NOTE_A_3, 128}, + { NOTE_E_4, 128}, { NOTE_A_4, 128}, { NOTE_B_4, 128}, + { NOTE_C_5, 128}, { NOTE_D_5, 128}, { NOTE_C_4, 128}, + { NOTE_G_4, 128}, { NOTE_C_5, 128}, { NOTE_G_4, 128}, + { NOTE_F_5, 128}, { NOTE_E_5, 128}, { NOTE_G_3, 128}, + { NOTE_G_4, 128}, { NOTE_B_3, 128}, { NOTE_F_4, 128}, + { NOTE_E_5, 128}, { NOTE_D_5, 128}, { NOTE_A_3, 128}, + { NOTE_E_4, 128}, { NOTE_A_4, 128}, { NOTE_E_4, 128}, + { NOTE_D_5, 128}, { NOTE_C_5, 128}, { NOTE_E_3, 128}, + { NOTE_E_4, 128}, { NOTE_E_5, 255}, { NOTE_E_6, 128}, + { NOTE_E_5, 128}, { NOTE_E_6, 128}, { NOTE_E_5, 255}, + { NOTE_DS_5, 128}, { NOTE_E_5, 128}, { NOTE_DS_6, 128}, + { NOTE_E_6, 128}, { NOTE_DS_5, 128}, { NOTE_E_5, 128}, + { NOTE_DS_6, 128}, { NOTE_E_6, 128}, { NOTE_DS_6, 128}, + { NOTE_E_6, 128}, { NOTE_DS_6, 128}, { NOTE_E_6, 128}, + { NOTE_B_5, 128}, { NOTE_D_6, 128}, { NOTE_C_6, 128}, + { NOTE_A_3, 128}, { NOTE_E_4, 128}, { NOTE_A_4, 128}, + { NOTE_C_5, 128}, { NOTE_E_5, 128}, { NOTE_A_5, 128}, + { NOTE_E_3, 128}, { NOTE_E_4, 128}, { NOTE_GS_4, 128}, + { NOTE_E_5, 128}, { NOTE_GS_5, 128}, { NOTE_B_5, 128}, + { NOTE_A_3, 128}, { NOTE_E_4, 128}, { NOTE_A_4, 128}, + { NOTE_E_5, 128}, { NOTE_E_6, 128}, { NOTE_DS_6, 128}, + { NOTE_E_6, 128}, { NOTE_DS_6, 128}, { NOTE_E_6, 128}, + { NOTE_B_5, 128}, { NOTE_D_6, 128}, { NOTE_C_6, 128}, + { NOTE_A_3, 128}, { NOTE_E_4, 128}, { NOTE_A_4, 128}, + { NOTE_C_5, 128}, { NOTE_E_5, 128}, { NOTE_A_5, 128}, + { NOTE_E_3, 128}, { NOTE_E_4, 128}, { NOTE_GS_4, 128}, + { NOTE_E_5, 128}, { NOTE_C_6, 128}, { NOTE_B_5, 128}, + { NOTE_C_5, 255}, { NOTE_C_5, 255}, { NOTE_SILENT, 512}, +}; + +static struct vidtv_access_unit *vidtv_s302m_access_unit_init(struct vidtv_access_unit *head) +{ + struct vidtv_access_unit *au = kzalloc(sizeof(*au), GFP_KERNEL); + + if (head) { + while (head->next) + head = head->next; + + head->next = au; + } + + return au; +} + +static void vidtv_s302m_access_unit_destroy(struct vidtv_encoder *e) +{ + struct vidtv_access_unit *head = e->access_units; + struct vidtv_access_unit *tmp = NULL; + + while (head) { + tmp = head; + head = head->next; + kfree(tmp); + } + + e->access_units = NULL; +} + +static void vidtv_s302m_alloc_au(struct vidtv_encoder *e) +{ + struct vidtv_access_unit *sync_au = NULL; + struct vidtv_access_unit *temp = NULL; + + if (e->sync && e->sync->is_video_encoder) { + sync_au = e->sync->access_units; + + while (sync_au) { + temp = vidtv_s302m_access_unit_init(e->access_units); + if (!e->access_units) + e->access_units = temp; + + sync_au = sync_au->next; + } + + return; + } + + e->access_units = vidtv_s302m_access_unit_init(NULL); +} + +static void +vidtv_s302m_compute_sample_count_from_video(struct vidtv_encoder *e) +{ + struct vidtv_access_unit *au = e->access_units; + struct vidtv_access_unit *sync_au = e->sync->access_units; + u32 vau_duration_usecs; + u32 sample_duration_usecs; + u32 s; + + vau_duration_usecs = USEC_PER_SEC / e->sync->sampling_rate_hz; + sample_duration_usecs = USEC_PER_SEC / e->sampling_rate_hz; + + while (au && sync_au) { + s = DIV_ROUND_UP(vau_duration_usecs, sample_duration_usecs); + au->num_samples = s; + au = au->next; + sync_au = sync_au->next; + } +} + +static void vidtv_s302m_compute_pts_from_video(struct vidtv_encoder *e) +{ + struct vidtv_access_unit *au = e->access_units; + struct vidtv_access_unit *sync_au = e->sync->access_units; + + /* use the same pts from the video access unit*/ + while (au && sync_au) { + au->pts = sync_au->pts; + au = au->next; + sync_au = sync_au->next; + } +} + +static u16 vidtv_s302m_get_sample(struct vidtv_encoder *e) +{ + u16 sample; + int pos; + + if (!e->src_buf) { + /* + * Simple tone generator: play the tones at the + * beethoven_5th_symphony array. + */ + if (e->last_duration <= 0) { + if (e->src_buf_offset >= ARRAY_SIZE(beethoven_5th_symphony)) + e->src_buf_offset = 0; + + e->last_tone = beethoven_5th_symphony[e->src_buf_offset].note; + e->last_duration = beethoven_5th_symphony[e->src_buf_offset].duration * S302M_SAMPLING_RATE_HZ / COMPASS / 5; + e->src_buf_offset++; + e->note_offset = 0; + } else { + e->last_duration--; + } + + /* Handle silent */ + if (!e->last_tone) { + e->src_buf_offset = 0; + return 0x8000; + } + + pos = (2 * PI * e->note_offset * e->last_tone / S302M_SAMPLING_RATE_HZ); + + if (pos == 360) + e->note_offset = 0; + else + e->note_offset++; + + return (fixp_sin32(pos % (2 * PI)) >> 16) + 0x8000; + } + + /* bug somewhere */ + if (e->src_buf_offset > e->src_buf_sz) { + pr_err_ratelimited("overflow detected: %d > %d, wrapping.\n", + e->src_buf_offset, + e->src_buf_sz); + + e->src_buf_offset = 0; + } + + if (e->src_buf_offset >= e->src_buf_sz) { + /* let the source know we are out of data */ + if (e->last_sample_cb) + e->last_sample_cb(e->sample_count); + + e->src_buf_offset = 0; + } + + sample = *(u16 *)(e->src_buf + e->src_buf_offset); + + return sample; +} + +static u32 vidtv_s302m_write_frame(struct vidtv_encoder *e, + u16 sample) +{ + u32 nbytes = 0; + struct vidtv_s302m_frame_16 f = {}; + struct vidtv_s302m_ctx *ctx = e->ctx; + + /* from ffmpeg: see s302enc.c */ + + u8 vucf = ctx->frame_index == 0 ? 0x10 : 0; + + f.data[0] = sample & 0xFF; + f.data[1] = (sample & 0xFF00) >> 8; + f.data[2] = ((sample & 0x0F) << 4) | vucf; + f.data[3] = (sample & 0x0FF0) >> 4; + f.data[4] = (sample & 0xF000) >> 12; + + f.data[0] = reverse[f.data[0]]; + f.data[1] = reverse[f.data[1]]; + f.data[2] = reverse[f.data[2]]; + f.data[3] = reverse[f.data[3]]; + f.data[4] = reverse[f.data[4]]; + + nbytes += vidtv_memcpy(e->encoder_buf, + e->encoder_buf_offset, + VIDTV_S302M_BUF_SZ, + &f, + sizeof(f)); + + e->encoder_buf_offset += nbytes; + + ctx->frame_index++; + if (ctx->frame_index >= S302M_BLOCK_SZ) + ctx->frame_index = 0; + + return nbytes; +} + +static u32 vidtv_s302m_write_h(struct vidtv_encoder *e, u32 p_sz) +{ + struct vidtv_smpte_s302m_es h = {}; + u32 nbytes = 0; + + /* 2 channels, ident: 0, 16 bits per sample */ + h.bitfield = cpu_to_be32((p_sz << 16)); + + nbytes += vidtv_memcpy(e->encoder_buf, + e->encoder_buf_offset, + e->encoder_buf_sz, + &h, + sizeof(h)); + + e->encoder_buf_offset += nbytes; + return nbytes; +} + +static void vidtv_s302m_write_frames(struct vidtv_encoder *e) +{ + struct vidtv_access_unit *au = e->access_units; + struct vidtv_s302m_ctx *ctx = e->ctx; + u32 nbytes_per_unit = 0; + u32 nbytes = 0; + u32 au_sz = 0; + u16 sample; + u32 j; + + while (au) { + au_sz = au->num_samples * + sizeof(struct vidtv_s302m_frame_16); + + nbytes_per_unit = vidtv_s302m_write_h(e, au_sz); + + for (j = 0; j < au->num_samples; ++j) { + sample = vidtv_s302m_get_sample(e); + nbytes_per_unit += vidtv_s302m_write_frame(e, sample); + + if (e->src_buf) + e->src_buf_offset += sizeof(u16); + + e->sample_count++; + } + + au->nbytes = nbytes_per_unit; + + if (au_sz + sizeof(struct vidtv_smpte_s302m_es) != nbytes_per_unit) { + pr_warn_ratelimited("write size was %u, expected %zu\n", + nbytes_per_unit, + au_sz + sizeof(struct vidtv_smpte_s302m_es)); + } + + nbytes += nbytes_per_unit; + au->offset = nbytes - nbytes_per_unit; + + nbytes_per_unit = 0; + ctx->au_count++; + + au = au->next; + } +} + +static void *vidtv_s302m_encode(struct vidtv_encoder *e) +{ + /* + * According to SMPTE 302M, an audio access unit is specified as those + * AES3 words that are associated with a corresponding video frame. + * Therefore, there is one audio access unit for every video access unit + * in the corresponding video encoder ('sync'), using the same values + * for PTS as used by the video encoder. + * + * Assuming that it is also possible to send audio without any + * associated video, as in a radio-like service, a single audio access unit + * is created with values for 'num_samples' and 'pts' taken empirically from + * ffmpeg + */ + + struct vidtv_s302m_ctx *ctx = e->ctx; + + vidtv_s302m_access_unit_destroy(e); + vidtv_s302m_alloc_au(e); + + if (e->sync && e->sync->is_video_encoder) { + vidtv_s302m_compute_sample_count_from_video(e); + vidtv_s302m_compute_pts_from_video(e); + } else { + e->access_units->num_samples = FF_S302M_DEFAULT_NUM_FRAMES; + e->access_units->pts = (ctx->au_count * FF_S302M_DEFAULT_PTS_INCREMENT) + + FF_S302M_DEFAULT_PTS_OFFSET; + } + + vidtv_s302m_write_frames(e); + + return e->encoder_buf; +} + +static u32 vidtv_s302m_clear(struct vidtv_encoder *e) +{ + struct vidtv_access_unit *au = e->access_units; + u32 count = 0; + + while (au) { + count++; + au = au->next; + } + + vidtv_s302m_access_unit_destroy(e); + memset(e->encoder_buf, 0, VIDTV_S302M_BUF_SZ); + e->encoder_buf_offset = 0; + + return count; +} + +struct vidtv_encoder +*vidtv_s302m_encoder_init(struct vidtv_s302m_encoder_init_args args) +{ + struct vidtv_encoder *e = kzalloc(sizeof(*e), GFP_KERNEL); + u32 priv_sz = sizeof(struct vidtv_s302m_ctx); + + e->id = S302M; + + if (args.name) + e->name = kstrdup(args.name, GFP_KERNEL); + + e->encoder_buf = vzalloc(VIDTV_S302M_BUF_SZ); + e->encoder_buf_sz = VIDTV_S302M_BUF_SZ; + e->encoder_buf_offset = 0; + + e->sample_count = 0; + e->last_duration = 0; + + e->src_buf = (args.src_buf) ? args.src_buf : NULL; + e->src_buf_sz = (args.src_buf) ? args.src_buf_sz : 0; + e->src_buf_offset = 0; + + e->is_video_encoder = false; + e->ctx = kzalloc(priv_sz, GFP_KERNEL); + + e->encode = vidtv_s302m_encode; + e->clear = vidtv_s302m_clear; + + e->es_pid = cpu_to_be16(args.es_pid); + e->stream_id = cpu_to_be16(PES_PRIVATE_STREAM_1); + + e->sync = args.sync; + e->sampling_rate_hz = S302M_SAMPLING_RATE_HZ; + + e->last_sample_cb = args.last_sample_cb; + + e->destroy = vidtv_s302m_encoder_destroy; + + if (args.head) { + while (args.head->next) + args.head = args.head->next; + + args.head->next = e; + } + + e->next = NULL; + + return e; +} + +void vidtv_s302m_encoder_destroy(struct vidtv_encoder *e) +{ + if (e->id != S302M) { + pr_err_ratelimited("Encoder type mismatch, skipping.\n"); + return; + } + + vidtv_s302m_access_unit_destroy(e); + kfree(e->name); + vfree(e->encoder_buf); + kfree(e->ctx); + kfree(e); +} diff --git a/drivers/media/test-drivers/vidtv/vidtv_s302m.h b/drivers/media/test-drivers/vidtv/vidtv_s302m.h new file mode 100644 index 000000000000..eca5e3150ede --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_s302m.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Vidtv serves as a reference DVB driver and helps validate the existing APIs + * in the media subsystem. It can also aid developers working on userspace + * applications. + * + * This file contains the code for an AES3 (also known as AES/EBU) encoder. + * It is based on EBU Tech 3250 and SMPTE 302M technical documents. + * + * This encoder currently supports 16bit AES3 subframes using 16bit signed + * integers. + * + * Note: AU stands for Access Unit, and AAU stands for Audio Access Unit + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#ifndef VIDTV_S302M_H +#define VIDTV_S302M_H + +#include <linux/types.h> +#include <asm/byteorder.h> + +#include "vidtv_encoder.h" + +/* see SMPTE 302M 2007 clause 7.3 */ +#define VIDTV_S302M_BUF_SZ 65024 + +/* see ETSI TS 102 154 v.1.2.1 clause 7.3.5 */ +#define VIDTV_S302M_FORMAT_IDENTIFIER 0x42535344 + +/** + * struct vidtv_s302m_ctx - s302m encoder context. + * @enc: A pointer to the containing encoder structure. + * @frame_index: The current frame in a block + * @au_count: The total number of access units encoded up to now + */ +struct vidtv_s302m_ctx { + struct vidtv_encoder *enc; + u32 frame_index; + u32 au_count; +}; + +/** + * struct vidtv_smpte_s302m_es - s302m MPEG Elementary Stream header. + * + * See SMPTE 302M 2007 table 1. + */ +struct vidtv_smpte_s302m_es { + /* + * + * audio_packet_size:16; + * num_channels:2; + * channel_identification:8; + * bits_per_sample:2; // 0x0 for 16bits + * zero:4; + */ + __be32 bitfield; +} __packed; + +struct vidtv_s302m_frame_16 { + u8 data[5]; +} __packed; + +/** + * struct vidtv_s302m_encoder_init_args - Args for the s302m encoder. + * + * @name: A name to identify this particular instance + * @src_buf: The source buffer, encoder will default to a sine wave if this is NULL. + * @src_buf_sz: The size of the source buffer. + * @es_pid: The MPEG Elementary Stream PID to use. + * @sync: Attempt to synchronize audio with this video encoder, if not NULL. + * @last_sample_cb: A callback called when the encoder runs out of data. + * @head: Add to this chain + */ +struct vidtv_s302m_encoder_init_args { + char *name; + void *src_buf; + u32 src_buf_sz; + u16 es_pid; + struct vidtv_encoder *sync; + void (*last_sample_cb)(u32 sample_no); + + struct vidtv_encoder *head; +}; + +struct vidtv_encoder +*vidtv_s302m_encoder_init(struct vidtv_s302m_encoder_init_args args); + +void vidtv_s302m_encoder_destroy(struct vidtv_encoder *encoder); + +#endif /* VIDTV_S302M_H */ diff --git a/drivers/media/test-drivers/vidtv/vidtv_ts.c b/drivers/media/test-drivers/vidtv/vidtv_ts.c new file mode 100644 index 000000000000..190b9e4438dc --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_ts.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The Virtual DVB test driver serves as a reference DVB driver and helps + * validate the existing APIs in the media subsystem. It can also aid + * developers working on userspace applications. + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s, %d: " fmt, __func__, __LINE__ + +#include <linux/printk.h> +#include <linux/ratelimit.h> +#include <linux/types.h> +#include <linux/math64.h> +#include <asm/byteorder.h> + +#include "vidtv_ts.h" +#include "vidtv_common.h" + +static u32 vidtv_ts_write_pcr_bits(u8 *to, u32 to_offset, u64 pcr) +{ + /* Exact same from ffmpeg. PCR is a counter driven by a 27Mhz clock */ + u64 div; + u64 rem; + u8 *buf = to + to_offset; + u64 pcr_low; + u64 pcr_high; + + div = div64_u64_rem(pcr, 300, &rem); + + pcr_low = rem; /* pcr_low = pcr % 300 */ + pcr_high = div; /* pcr_high = pcr / 300 */ + + *buf++ = pcr_high >> 25; + *buf++ = pcr_high >> 17; + *buf++ = pcr_high >> 9; + *buf++ = pcr_high >> 1; + *buf++ = pcr_high << 7 | pcr_low >> 8 | 0x7e; + *buf++ = pcr_low; + + return 6; +} + +void vidtv_ts_inc_cc(u8 *continuity_counter) +{ + ++*continuity_counter; + if (*continuity_counter > TS_CC_MAX_VAL) + *continuity_counter = 0; +} + +u32 vidtv_ts_null_write_into(struct null_packet_write_args args) +{ + u32 nbytes = 0; + struct vidtv_mpeg_ts ts_header = {}; + + ts_header.sync_byte = TS_SYNC_BYTE; + ts_header.bitfield = cpu_to_be16(TS_NULL_PACKET_PID); + ts_header.payload = 1; + ts_header.continuity_counter = *args.continuity_counter; + + /* copy TS header */ + nbytes += vidtv_memcpy(args.dest_buf, + args.dest_offset + nbytes, + args.buf_sz, + &ts_header, + sizeof(ts_header)); + + vidtv_ts_inc_cc(args.continuity_counter); + + /* fill the rest with empty data */ + nbytes += vidtv_memset(args.dest_buf, + args.dest_offset + nbytes, + args.buf_sz, + TS_FILL_BYTE, + TS_PACKET_LEN - nbytes); + + /* we should have written exactly _one_ 188byte packet */ + if (nbytes != TS_PACKET_LEN) + pr_warn_ratelimited("Expected exactly %d bytes, got %d\n", + TS_PACKET_LEN, + nbytes); + + return nbytes; +} + +u32 vidtv_ts_pcr_write_into(struct pcr_write_args args) +{ + u32 nbytes = 0; + struct vidtv_mpeg_ts ts_header = {}; + struct vidtv_mpeg_ts_adaption ts_adap = {}; + + ts_header.sync_byte = TS_SYNC_BYTE; + ts_header.bitfield = cpu_to_be16(args.pid); + ts_header.scrambling = 0; + /* cc is not incremented, but it is needed. see 13818-1 clause 2.4.3.3 */ + ts_header.continuity_counter = *args.continuity_counter; + ts_header.payload = 0; + ts_header.adaptation_field = 1; + + /* 13818-1 clause 2.4.3.5 */ + ts_adap.length = 183; + ts_adap.PCR = 1; + + /* copy TS header */ + nbytes += vidtv_memcpy(args.dest_buf, + args.dest_offset + nbytes, + args.buf_sz, + &ts_header, + sizeof(ts_header)); + + /* write the adap after the TS header */ + nbytes += vidtv_memcpy(args.dest_buf, + args.dest_offset + nbytes, + args.buf_sz, + &ts_adap, + sizeof(ts_adap)); + + /* write the PCR optional */ + nbytes += vidtv_ts_write_pcr_bits(args.dest_buf, + args.dest_offset + nbytes, + args.pcr); + + nbytes += vidtv_memset(args.dest_buf, + args.dest_offset + nbytes, + args.buf_sz, + TS_FILL_BYTE, + TS_PACKET_LEN - nbytes); + + /* we should have written exactly _one_ 188byte packet */ + if (nbytes != TS_PACKET_LEN) + pr_warn_ratelimited("Expected exactly %d bytes, got %d\n", + TS_PACKET_LEN, + nbytes); + + return nbytes; +} diff --git a/drivers/media/test-drivers/vidtv/vidtv_ts.h b/drivers/media/test-drivers/vidtv/vidtv_ts.h new file mode 100644 index 000000000000..83dcc9183b45 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_ts.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * The Virtual DVB test driver serves as a reference DVB driver and helps + * validate the existing APIs in the media subsystem. It can also aid + * developers working on userspace applications. + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#ifndef VIDTV_TS_H +#define VIDTV_TS_H + +#include <linux/types.h> +#include <asm/byteorder.h> + +#define TS_SYNC_BYTE 0x47 +#define TS_PACKET_LEN 188 +#define TS_PAYLOAD_LEN 184 +#define TS_NULL_PACKET_PID 0x1fff +#define TS_CC_MAX_VAL 0x0f /* 4 bits */ +#define TS_LAST_VALID_PID 8191 +#define TS_FILL_BYTE 0xff /* the byte used in packet stuffing */ + +struct vidtv_mpeg_ts_adaption { + u8 length; + struct { + u8 extension:1; + u8 private_data:1; + u8 splicing_point:1; + u8 OPCR:1; + u8 PCR:1; + u8 priority:1; + u8 random_access:1; + u8 discontinued:1; + } __packed; + u8 data[]; +} __packed; + +struct vidtv_mpeg_ts { + u8 sync_byte; + __be16 bitfield; /* tei: 1, payload_start:1 priority: 1, pid:13 */ + struct { + u8 continuity_counter:4; + u8 payload:1; + u8 adaptation_field:1; + u8 scrambling:2; + } __packed; + struct vidtv_mpeg_ts_adaption adaption[]; +} __packed; + +/** + * struct pcr_write_args - Arguments for the pcr_write_into function. + * @dest_buf: The buffer to write into. + * @dest_offset: The byte offset into the buffer. + * @pid: The TS PID for the PCR packets. + * @buf_sz: The size of the buffer in bytes. + * @countinuity_counter: The TS continuity_counter. + * @pcr: A sample from the system clock. + */ +struct pcr_write_args { + void *dest_buf; + u32 dest_offset; + u16 pid; + u32 buf_sz; + u8 *continuity_counter; + u64 pcr; +}; + +/** + * struct null_packet_write_args - Arguments for the null_write_into function + * @dest_buf: The buffer to write into. + * @dest_offset: The byte offset into the buffer. + * @buf_sz: The size of the buffer in bytes. + * @countinuity_counter: The TS continuity_counter. + */ +struct null_packet_write_args { + void *dest_buf; + u32 dest_offset; + u32 buf_sz; + u8 *continuity_counter; +}; + +/* Increment the continuity counter */ +void vidtv_ts_inc_cc(u8 *continuity_counter); + +/** + * vidtv_ts_null_write_into - Write a TS null packet into a buffer. + * @args: the arguments to use when writing. + * + * This function will write a null packet into a buffer. This is usually used to + * pad TS streams. + * + * Return: The number of bytes written into the buffer. + */ +u32 vidtv_ts_null_write_into(struct null_packet_write_args args); + +/** + * vidtv_ts_pcr_write_into - Write a PCR packet into a buffer. + * @args: the arguments to use when writing. + * + * This function will write a PCR packet into a buffer. This is used to + * synchronize the clocks between encoders and decoders. + * + * Return: The number of bytes written into the buffer. + */ +u32 vidtv_ts_pcr_write_into(struct pcr_write_args args); + +#endif //VIDTV_TS_H diff --git a/drivers/media/test-drivers/vidtv/vidtv_tuner.c b/drivers/media/test-drivers/vidtv/vidtv_tuner.c new file mode 100644 index 000000000000..9bc49e099f65 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_tuner.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The Virtual DVB test driver serves as a reference DVB driver and helps + * validate the existing APIs in the media subsystem. It can also aid + * developers working on userspace applications. + * + * The vidtv tuner should support common TV standards such as + * DVB-T/T2/S/S2, ISDB-T and ATSC when completed. + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <media/dvb_frontend.h> +#include <linux/printk.h> +#include <linux/ratelimit.h> + +#include "vidtv_tuner.h" + +struct vidtv_tuner_cnr_to_qual_s { + /* attempt to use the same values as libdvbv5 */ + u32 modulation; + u32 fec; + u32 cnr_ok; + u32 cnr_good; +}; + +static const struct vidtv_tuner_cnr_to_qual_s vidtv_tuner_c_cnr_2_qual[] = { + /* from libdvbv5 source code, in milli db */ + { QAM_256, FEC_NONE, 34000, 38000}, + { QAM_64, FEC_NONE, 30000, 34000}, +}; + +static const struct vidtv_tuner_cnr_to_qual_s vidtv_tuner_s_cnr_2_qual[] = { + /* from libdvbv5 source code, in milli db */ + { QPSK, FEC_1_2, 7000, 10000}, + { QPSK, FEC_2_3, 9000, 12000}, + { QPSK, FEC_3_4, 10000, 13000}, + { QPSK, FEC_5_6, 11000, 14000}, + { QPSK, FEC_7_8, 12000, 15000}, +}; + +static const struct vidtv_tuner_cnr_to_qual_s vidtv_tuner_s2_cnr_2_qual[] = { + /* from libdvbv5 source code, in milli db */ + { QPSK, FEC_1_2, 9000, 12000}, + { QPSK, FEC_2_3, 11000, 14000}, + { QPSK, FEC_3_4, 12000, 15000}, + { QPSK, FEC_5_6, 12000, 15000}, + { QPSK, FEC_8_9, 13000, 16000}, + { QPSK, FEC_9_10, 13500, 16500}, + { PSK_8, FEC_2_3, 14500, 17500}, + { PSK_8, FEC_3_4, 16000, 19000}, + { PSK_8, FEC_5_6, 17500, 20500}, + { PSK_8, FEC_8_9, 19000, 22000}, +}; + +static const struct vidtv_tuner_cnr_to_qual_s vidtv_tuner_t_cnr_2_qual[] = { + /* from libdvbv5 source code, in milli db*/ + { QPSK, FEC_1_2, 4100, 5900}, + { QPSK, FEC_2_3, 6100, 9600}, + { QPSK, FEC_3_4, 7200, 12400}, + { QPSK, FEC_5_6, 8500, 15600}, + { QPSK, FEC_7_8, 9200, 17500}, + { QAM_16, FEC_1_2, 9800, 11800}, + { QAM_16, FEC_2_3, 12100, 15300}, + { QAM_16, FEC_3_4, 13400, 18100}, + { QAM_16, FEC_5_6, 14800, 21300}, + { QAM_16, FEC_7_8, 15700, 23600}, + { QAM_64, FEC_1_2, 14000, 16000}, + { QAM_64, FEC_2_3, 19900, 25400}, + { QAM_64, FEC_3_4, 24900, 27900}, + { QAM_64, FEC_5_6, 21300, 23300}, + { QAM_64, FEC_7_8, 22000, 24000}, +}; + +/** + * struct vidtv_tuner_hardware_state - Simulate the tuner hardware status + * @asleep: whether the tuner is asleep, i.e whether _sleep() or _suspend() was + * called. + * @lock_status: Whether the tuner has managed to lock on the requested + * frequency. + * @if_frequency: The tuner's intermediate frequency. Hardcoded for the purposes + * of simulation. + * @tuned_frequency: The actual tuned frequency. + * @bandwidth: The actual bandwidth. + * + * This structure is meant to simulate the status of the tuner hardware, as if + * we had a physical tuner hardware. + */ +struct vidtv_tuner_hardware_state { + bool asleep; + u32 lock_status; + u32 if_frequency; + u32 tuned_frequency; + u32 bandwidth; +}; + +/** + * struct vidtv_tuner_dev - The tuner struct + * @fe: A pointer to the dvb_frontend structure allocated by vidtv_demod + * @hw_state: A struct to simulate the tuner's hardware state as if we had a + * physical tuner hardware. + * @config: The configuration used to start the tuner module, usually filled + * by a bridge driver. For vidtv, this is filled by vidtv_bridge before the + * tuner module is probed. + */ +struct vidtv_tuner_dev { + struct dvb_frontend *fe; + struct vidtv_tuner_hardware_state hw_state; + struct vidtv_tuner_config config; +}; + +static struct vidtv_tuner_dev* +vidtv_tuner_get_dev(struct dvb_frontend *fe) +{ + return i2c_get_clientdata(fe->tuner_priv); +} + +static int vidtv_tuner_check_frequency_shift(struct dvb_frontend *fe) +{ + struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct vidtv_tuner_config config = tuner_dev->config; + u32 *valid_freqs = NULL; + u32 array_sz = 0; + u32 i; + u32 shift; + + switch (c->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: + valid_freqs = config.vidtv_valid_dvb_t_freqs; + array_sz = ARRAY_SIZE(config.vidtv_valid_dvb_t_freqs); + break; + case SYS_DVBS: + case SYS_DVBS2: + valid_freqs = config.vidtv_valid_dvb_s_freqs; + array_sz = ARRAY_SIZE(config.vidtv_valid_dvb_s_freqs); + break; + case SYS_DVBC_ANNEX_A: + valid_freqs = config.vidtv_valid_dvb_c_freqs; + array_sz = ARRAY_SIZE(config.vidtv_valid_dvb_c_freqs); + break; + + default: + dev_warn(fe->dvb->device, + "%s: unsupported delivery system: %u\n", + __func__, + c->delivery_system); + + return -EINVAL; + } + + for (i = 0; i < array_sz; i++) { + if (!valid_freqs[i]) + break; + shift = abs(c->frequency - valid_freqs[i]); + + if (!shift) + return 0; + + /* + * This will provide a value from 0 to 100 that would + * indicate how far is the tuned frequency from the + * right one. + */ + if (shift < config.max_frequency_shift_hz) + return shift * 100 / config.max_frequency_shift_hz; + } + + return -EINVAL; +} + +static int +vidtv_tuner_get_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); + const struct vidtv_tuner_cnr_to_qual_s *cnr2qual = NULL; + struct device *dev = fe->dvb->device; + u32 array_size = 0; + s32 shift; + u32 i; + + shift = vidtv_tuner_check_frequency_shift(fe); + if (shift < 0) { + tuner_dev->hw_state.lock_status = 0; + *strength = 0; + return 0; + } + + switch (c->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: + cnr2qual = vidtv_tuner_t_cnr_2_qual; + array_size = ARRAY_SIZE(vidtv_tuner_t_cnr_2_qual); + break; + + case SYS_DVBS: + cnr2qual = vidtv_tuner_s_cnr_2_qual; + array_size = ARRAY_SIZE(vidtv_tuner_s_cnr_2_qual); + break; + + case SYS_DVBS2: + cnr2qual = vidtv_tuner_s2_cnr_2_qual; + array_size = ARRAY_SIZE(vidtv_tuner_s2_cnr_2_qual); + break; + + case SYS_DVBC_ANNEX_A: + cnr2qual = vidtv_tuner_c_cnr_2_qual; + array_size = ARRAY_SIZE(vidtv_tuner_c_cnr_2_qual); + break; + + default: + dev_warn_ratelimited(dev, + "%s: unsupported delivery system: %u\n", + __func__, + c->delivery_system); + return -EINVAL; + } + + for (i = 0; i < array_size; i++) { + if (cnr2qual[i].modulation != c->modulation || + cnr2qual[i].fec != c->fec_inner) + continue; + + if (!shift) { + *strength = cnr2qual[i].cnr_good; + return 0; + } + /* + * Channel tuned at wrong frequency. Simulate that the + * Carrier S/N ratio is not too good. + */ + + *strength = cnr2qual[i].cnr_ok - + (cnr2qual[i].cnr_good - cnr2qual[i].cnr_ok); + return 0; + } + + /* + * do a linear interpolation between 34dB and 10dB if we can't + * match against the table + */ + *strength = 34000 - 24000 * shift / 100; + return 0; +} + +static int vidtv_tuner_init(struct dvb_frontend *fe) +{ + struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); + struct vidtv_tuner_config config = tuner_dev->config; + + msleep_interruptible(config.mock_power_up_delay_msec); + + tuner_dev->hw_state.asleep = false; + tuner_dev->hw_state.if_frequency = 5000; + + return 0; +} + +static int vidtv_tuner_sleep(struct dvb_frontend *fe) +{ + struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); + + tuner_dev->hw_state.asleep = true; + return 0; +} + +static int vidtv_tuner_suspend(struct dvb_frontend *fe) +{ + struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); + + tuner_dev->hw_state.asleep = true; + return 0; +} + +static int vidtv_tuner_resume(struct dvb_frontend *fe) +{ + struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); + + tuner_dev->hw_state.asleep = false; + return 0; +} + +static int vidtv_tuner_set_params(struct dvb_frontend *fe) +{ + struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); + struct vidtv_tuner_config config = tuner_dev->config; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + s32 shift; + + u32 min_freq = fe->ops.tuner_ops.info.frequency_min_hz; + u32 max_freq = fe->ops.tuner_ops.info.frequency_max_hz; + u32 min_bw = fe->ops.tuner_ops.info.bandwidth_min; + u32 max_bw = fe->ops.tuner_ops.info.bandwidth_max; + + if (c->frequency < min_freq || c->frequency > max_freq || + c->bandwidth_hz < min_bw || c->bandwidth_hz > max_bw) { + tuner_dev->hw_state.lock_status = 0; + return -EINVAL; + } + + tuner_dev->hw_state.tuned_frequency = c->frequency; + tuner_dev->hw_state.bandwidth = c->bandwidth_hz; + tuner_dev->hw_state.lock_status = TUNER_STATUS_LOCKED; + + msleep_interruptible(config.mock_tune_delay_msec); + + shift = vidtv_tuner_check_frequency_shift(fe); + if (shift < 0) { + tuner_dev->hw_state.lock_status = 0; + return shift; + } + + return 0; +} + +static int vidtv_tuner_set_config(struct dvb_frontend *fe, + void *priv_cfg) +{ + struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); + + memcpy(&tuner_dev->config, priv_cfg, sizeof(tuner_dev->config)); + + return 0; +} + +static int vidtv_tuner_get_frequency(struct dvb_frontend *fe, + u32 *frequency) +{ + struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); + + *frequency = tuner_dev->hw_state.tuned_frequency; + + return 0; +} + +static int vidtv_tuner_get_bandwidth(struct dvb_frontend *fe, + u32 *bandwidth) +{ + struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); + + *bandwidth = tuner_dev->hw_state.bandwidth; + + return 0; +} + +static int vidtv_tuner_get_if_frequency(struct dvb_frontend *fe, + u32 *frequency) +{ + struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); + + *frequency = tuner_dev->hw_state.if_frequency; + + return 0; +} + +static int vidtv_tuner_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); + + *status = tuner_dev->hw_state.lock_status; + + return 0; +} + +static const struct dvb_tuner_ops vidtv_tuner_ops = { + .init = vidtv_tuner_init, + .sleep = vidtv_tuner_sleep, + .suspend = vidtv_tuner_suspend, + .resume = vidtv_tuner_resume, + .set_params = vidtv_tuner_set_params, + .set_config = vidtv_tuner_set_config, + .get_bandwidth = vidtv_tuner_get_bandwidth, + .get_frequency = vidtv_tuner_get_frequency, + .get_if_frequency = vidtv_tuner_get_if_frequency, + .get_status = vidtv_tuner_get_status, + .get_rf_strength = vidtv_tuner_get_signal_strength +}; + +static const struct i2c_device_id vidtv_tuner_i2c_id_table[] = { + {"dvb_vidtv_tuner", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, vidtv_tuner_i2c_id_table); + +static int vidtv_tuner_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct vidtv_tuner_config *config = client->dev.platform_data; + struct dvb_frontend *fe = config->fe; + struct vidtv_tuner_dev *tuner_dev = NULL; + + tuner_dev = kzalloc(sizeof(*tuner_dev), GFP_KERNEL); + if (!tuner_dev) + return -ENOMEM; + + tuner_dev->fe = config->fe; + i2c_set_clientdata(client, tuner_dev); + + memcpy(&fe->ops.tuner_ops, + &vidtv_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + memcpy(&tuner_dev->config, config, sizeof(tuner_dev->config)); + fe->tuner_priv = client; + + return 0; +} + +static int vidtv_tuner_i2c_remove(struct i2c_client *client) +{ + struct vidtv_tuner_dev *tuner_dev = i2c_get_clientdata(client); + + kfree(tuner_dev); + + return 0; +} + +static struct i2c_driver vidtv_tuner_i2c_driver = { + .driver = { + .name = "dvb_vidtv_tuner", + .suppress_bind_attrs = true, + }, + .probe = vidtv_tuner_i2c_probe, + .remove = vidtv_tuner_i2c_remove, + .id_table = vidtv_tuner_i2c_id_table, +}; +module_i2c_driver(vidtv_tuner_i2c_driver); + +MODULE_DESCRIPTION("Virtual DVB Tuner"); +MODULE_AUTHOR("Daniel W. S. Almeida"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/test-drivers/vidtv/vidtv_tuner.h b/drivers/media/test-drivers/vidtv/vidtv_tuner.h new file mode 100644 index 000000000000..8455b2d564b3 --- /dev/null +++ b/drivers/media/test-drivers/vidtv/vidtv_tuner.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * The Virtual DTV test driver serves as a reference DVB driver and helps + * validate the existing APIs in the media subsystem. It can also aid + * developers working on userspace applications. + * + * Copyright (C) 2020 Daniel W. S. Almeida + */ + +#ifndef VIDTV_TUNER_H +#define VIDTV_TUNER_H + +#include <linux/types.h> +#include <media/dvb_frontend.h> + +#define NUM_VALID_TUNER_FREQS 8 + +/** + * struct vidtv_tuner_config - Configuration used to init the tuner. + * @fe: A pointer to the dvb_frontend structure allocated by vidtv_demod. + * @mock_power_up_delay_msec: Simulate a power-up delay. + * @mock_tune_delay_msec: Simulate a tune delay. + * @vidtv_valid_dvb_t_freqs: The valid DVB-T frequencies to simulate. + * @vidtv_valid_dvb_c_freqs: The valid DVB-C frequencies to simulate. + * @vidtv_valid_dvb_s_freqs: The valid DVB-S frequencies to simulate. + * @max_frequency_shift_hz: The maximum frequency shift in HZ allowed when + * tuning in a channel + * + * The configuration used to init the tuner module, usually filled + * by a bridge driver. For vidtv, this is filled by vidtv_bridge before the + * tuner module is probed. + */ +struct vidtv_tuner_config { + struct dvb_frontend *fe; + u32 mock_power_up_delay_msec; + u32 mock_tune_delay_msec; + u32 vidtv_valid_dvb_t_freqs[NUM_VALID_TUNER_FREQS]; + u32 vidtv_valid_dvb_c_freqs[NUM_VALID_TUNER_FREQS]; + u32 vidtv_valid_dvb_s_freqs[NUM_VALID_TUNER_FREQS]; + u8 max_frequency_shift_hz; +}; + +#endif //VIDTV_TUNER_H diff --git a/drivers/media/test-drivers/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c index c63496b17b9a..5e9fd902cd37 100644 --- a/drivers/media/test-drivers/vimc/vimc-capture.c +++ b/drivers/media/test-drivers/vimc/vimc-capture.c @@ -351,8 +351,7 @@ static void vimc_cap_unregister(struct vimc_ent_device *ved) struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device, ved); - vb2_queue_release(&vcap->queue); - video_unregister_device(&vcap->vdev); + vb2_video_unregister_device(&vcap->vdev); } static void *vimc_cap_process_frame(struct vimc_ent_device *ved, @@ -477,13 +476,11 @@ static struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc, if (ret) { dev_err(vimc->mdev.dev, "%s: video register failed (err=%d)\n", vcap->vdev.name, ret); - goto err_release_queue; + goto err_clean_m_ent; } return &vcap->ved; -err_release_queue: - vb2_queue_release(q); err_clean_m_ent: media_entity_cleanup(&vcap->vdev.entity); err_free_vcap: diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index f7ee37e9508d..aa8d350fd682 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -832,56 +832,16 @@ static int vivid_create_queue(struct vivid_dev *dev, return vb2_queue_init(q); } -static int vivid_create_instance(struct platform_device *pdev, int inst) +static int vivid_detect_feature_set(struct vivid_dev *dev, int inst, + unsigned node_type, + bool *has_tuner, + bool *has_modulator, + int *ccs_cap, + int *ccs_out, + unsigned in_type_counter[4], + unsigned out_type_counter[4]) { - static const struct v4l2_dv_timings def_dv_timings = - V4L2_DV_BT_CEA_1280X720P60; - unsigned in_type_counter[4] = { 0, 0, 0, 0 }; - unsigned out_type_counter[4] = { 0, 0, 0, 0 }; - int ccs_cap = ccs_cap_mode[inst]; - int ccs_out = ccs_out_mode[inst]; - bool has_tuner; - bool has_modulator; - struct vivid_dev *dev; - struct video_device *vfd; - unsigned node_type = node_types[inst]; - v4l2_std_id tvnorms_cap = 0, tvnorms_out = 0; - int ret; int i; -#ifdef CONFIG_VIDEO_VIVID_CEC - unsigned int cec_tx_bus_cnt = 0; -#endif - - /* allocate main vivid state structure */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - dev->inst = inst; - -#ifdef CONFIG_MEDIA_CONTROLLER - dev->v4l2_dev.mdev = &dev->mdev; - - /* Initialize media device */ - strscpy(dev->mdev.model, VIVID_MODULE_NAME, sizeof(dev->mdev.model)); - snprintf(dev->mdev.bus_info, sizeof(dev->mdev.bus_info), - "platform:%s-%03d", VIVID_MODULE_NAME, inst); - dev->mdev.dev = &pdev->dev; - media_device_init(&dev->mdev); - dev->mdev.ops = &vivid_media_ops; -#endif - - /* register v4l2_device */ - snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), - "%s-%03d", VIVID_MODULE_NAME, inst); - ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); - if (ret) { - kfree(dev); - return ret; - } - dev->v4l2_dev.release = vivid_dev_release; - - /* start detecting feature set */ /* do we use single- or multi-planar? */ dev->multiplanar = multiplanar[inst] > 1; @@ -947,14 +907,12 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) !dev->has_vid_cap && !dev->has_meta_cap) { v4l2_warn(&dev->v4l2_dev, "Webcam or HDMI input without video or metadata nodes\n"); - kfree(dev); return -EINVAL; } if ((in_type_counter[TV] || in_type_counter[SVID]) && !dev->has_vid_cap && !dev->has_vbi_cap && !dev->has_meta_cap) { v4l2_warn(&dev->v4l2_dev, "TV or S-Video input without video, VBI or metadata nodes\n"); - kfree(dev); return -EINVAL; } @@ -976,13 +934,11 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) !dev->has_vid_out && !dev->has_vbi_out && !dev->has_meta_out) { v4l2_warn(&dev->v4l2_dev, "S-Video output without video, VBI or metadata nodes\n"); - kfree(dev); return -EINVAL; } if (out_type_counter[HDMI] && !dev->has_vid_out && !dev->has_meta_out) { v4l2_warn(&dev->v4l2_dev, "HDMI output without video or metadata nodes\n"); - kfree(dev); return -EINVAL; } @@ -999,25 +955,25 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->has_tv_tuner = in_type_counter[TV]; /* do we have a tuner? */ - has_tuner = ((dev->has_vid_cap || dev->has_vbi_cap) && in_type_counter[TV]) || - dev->has_radio_rx || dev->has_sdr_cap; + *has_tuner = ((dev->has_vid_cap || dev->has_vbi_cap) && in_type_counter[TV]) || + dev->has_radio_rx || dev->has_sdr_cap; /* do we have a modulator? */ - has_modulator = dev->has_radio_tx; + *has_modulator = dev->has_radio_tx; if (dev->has_vid_cap) /* do we have a framebuffer for overlay testing? */ dev->has_fb = node_type & 0x10000; /* can we do crop/compose/scaling while capturing? */ - if (no_error_inj && ccs_cap == -1) - ccs_cap = 7; + if (no_error_inj && *ccs_cap == -1) + *ccs_cap = 7; /* if ccs_cap == -1, then the user can select it using controls */ - if (ccs_cap != -1) { - dev->has_crop_cap = ccs_cap & 1; - dev->has_compose_cap = ccs_cap & 2; - dev->has_scaler_cap = ccs_cap & 4; + if (*ccs_cap != -1) { + dev->has_crop_cap = *ccs_cap & 1; + dev->has_compose_cap = *ccs_cap & 2; + dev->has_scaler_cap = *ccs_cap & 4; v4l2_info(&dev->v4l2_dev, "Capture Crop: %c Compose: %c Scaler: %c\n", dev->has_crop_cap ? 'Y' : 'N', dev->has_compose_cap ? 'Y' : 'N', @@ -1025,14 +981,14 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) } /* can we do crop/compose/scaling with video output? */ - if (no_error_inj && ccs_out == -1) - ccs_out = 7; + if (no_error_inj && *ccs_out == -1) + *ccs_out = 7; /* if ccs_out == -1, then the user can select it using controls */ - if (ccs_out != -1) { - dev->has_crop_out = ccs_out & 1; - dev->has_compose_out = ccs_out & 2; - dev->has_scaler_out = ccs_out & 4; + if (*ccs_out != -1) { + dev->has_crop_out = *ccs_out & 1; + dev->has_compose_out = *ccs_out & 2; + dev->has_scaler_out = *ccs_out & 4; v4l2_info(&dev->v4l2_dev, "Output Crop: %c Compose: %c Scaler: %c\n", dev->has_crop_out ? 'Y' : 'N', dev->has_compose_out ? 'Y' : 'N', @@ -1042,8 +998,11 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) /* do we create a touch capture device */ dev->has_touch_cap = node_type & 0x80000; - /* end detecting feature set */ + return 0; +} +static void vivid_set_capabilities(struct vivid_dev *dev) +{ if (dev->has_vid_cap) { /* set up the capabilities of the video capture device */ dev->vid_cap_caps = dev->multiplanar ? @@ -1122,58 +1081,14 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->touch_cap_caps |= dev->multiplanar ? V4L2_CAP_VIDEO_CAPTURE_MPLANE : V4L2_CAP_VIDEO_CAPTURE; } +} - ret = -ENOMEM; - /* initialize the test pattern generator */ - tpg_init(&dev->tpg, 640, 360); - if (tpg_alloc(&dev->tpg, array_size(MAX_WIDTH, MAX_ZOOM))) - goto free_dev; - dev->scaled_line = vzalloc(array_size(MAX_WIDTH, MAX_ZOOM)); - if (!dev->scaled_line) - goto free_dev; - dev->blended_line = vzalloc(array_size(MAX_WIDTH, MAX_ZOOM)); - if (!dev->blended_line) - goto free_dev; - - /* load the edid */ - dev->edid = vmalloc(array_size(256, 128)); - if (!dev->edid) - goto free_dev; - - while (v4l2_dv_timings_presets[dev->query_dv_timings_size].bt.width) - dev->query_dv_timings_size++; - - /* - * Create a char pointer array that points to the names of all the - * preset timings - */ - dev->query_dv_timings_qmenu = kmalloc_array(dev->query_dv_timings_size, - sizeof(char *), GFP_KERNEL); - /* - * Create a string array containing the names of all the preset - * timings. Each name is max 31 chars long (+ terminating 0). - */ - dev->query_dv_timings_qmenu_strings = - kmalloc_array(dev->query_dv_timings_size, 32, GFP_KERNEL); - - if (!dev->query_dv_timings_qmenu || - !dev->query_dv_timings_qmenu_strings) - goto free_dev; - - for (i = 0; i < dev->query_dv_timings_size; i++) { - const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt; - char *p = dev->query_dv_timings_qmenu_strings + i * 32; - u32 htot, vtot; - - dev->query_dv_timings_qmenu[i] = p; - - htot = V4L2_DV_BT_FRAME_WIDTH(bt); - vtot = V4L2_DV_BT_FRAME_HEIGHT(bt); - snprintf(p, 32, "%ux%u%s%u", - bt->width, bt->height, bt->interlaced ? "i" : "p", - (u32)bt->pixelclock / (htot * vtot)); - } - +static void vivid_disable_unused_ioctls(struct vivid_dev *dev, + bool has_tuner, + bool has_modulator, + unsigned in_type_counter[4], + unsigned out_type_counter[4]) +{ /* disable invalid ioctls based on the feature set */ if (!dev->has_audio_inputs) { v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_AUDIO); @@ -1260,112 +1175,52 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) v4l2_disable_ioctl(&dev->touch_cap_dev, VIDIOC_S_PARM); v4l2_disable_ioctl(&dev->touch_cap_dev, VIDIOC_ENUM_FRAMESIZES); v4l2_disable_ioctl(&dev->touch_cap_dev, VIDIOC_ENUM_FRAMEINTERVALS); +} - /* configure internal data */ - dev->fmt_cap = &vivid_formats[0]; - dev->fmt_out = &vivid_formats[0]; - if (!dev->multiplanar) - vivid_formats[0].data_offset[0] = 0; - dev->webcam_size_idx = 1; - dev->webcam_ival_idx = 3; - tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc); - dev->std_out = V4L2_STD_PAL; - if (dev->input_type[0] == TV || dev->input_type[0] == SVID) - tvnorms_cap = V4L2_STD_ALL; - if (dev->output_type[0] == SVID) - tvnorms_out = V4L2_STD_ALL; - for (i = 0; i < MAX_INPUTS; i++) { - dev->dv_timings_cap[i] = def_dv_timings; - dev->std_cap[i] = V4L2_STD_PAL; - } - dev->dv_timings_out = def_dv_timings; - dev->tv_freq = 2804 /* 175.25 * 16 */; - dev->tv_audmode = V4L2_TUNER_MODE_STEREO; - dev->tv_field_cap = V4L2_FIELD_INTERLACED; - dev->tv_field_out = V4L2_FIELD_INTERLACED; - dev->radio_rx_freq = 95000 * 16; - dev->radio_rx_audmode = V4L2_TUNER_MODE_STEREO; - if (dev->has_radio_tx) { - dev->radio_tx_freq = 95500 * 16; - dev->radio_rds_loop = false; - } - dev->radio_tx_subchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_RDS; - dev->sdr_adc_freq = 300000; - dev->sdr_fm_freq = 50000000; - dev->sdr_pixelformat = V4L2_SDR_FMT_CU8; - dev->sdr_buffersize = SDR_CAP_SAMPLES_PER_BUF * 2; - - dev->edid_max_blocks = dev->edid_blocks = 2; - memcpy(dev->edid, vivid_hdmi_edid, sizeof(vivid_hdmi_edid)); - dev->radio_rds_init_time = ktime_get(); - - /* create all controls */ - ret = vivid_create_controls(dev, ccs_cap == -1, ccs_out == -1, no_error_inj, - in_type_counter[TV] || in_type_counter[SVID] || - out_type_counter[SVID], - in_type_counter[HDMI] || out_type_counter[HDMI]); - if (ret) - goto unreg_dev; +static int vivid_init_dv_timings(struct vivid_dev *dev) +{ + int i; - /* 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); - } else if (dev->num_inputs && dev->input_type[0] == HDMI) { - v4l2_ctrl_activate(dev->ctrl_std_signal_mode, false); - v4l2_ctrl_activate(dev->ctrl_standard, false); - } + while (v4l2_dv_timings_presets[dev->query_dv_timings_size].bt.width) + dev->query_dv_timings_size++; /* - * update the capture and output formats to do a proper initial - * configuration. + * Create a char pointer array that points to the names of all the + * preset timings */ - vivid_update_format_cap(dev, false); - vivid_update_format_out(dev); - - /* initialize overlay */ - dev->fb_cap.fmt.width = dev->src_rect.width; - dev->fb_cap.fmt.height = dev->src_rect.height; - dev->fb_cap.fmt.pixelformat = dev->fmt_cap->fourcc; - dev->fb_cap.fmt.bytesperline = dev->src_rect.width * tpg_g_twopixelsize(&dev->tpg, 0) / 2; - dev->fb_cap.fmt.sizeimage = dev->src_rect.height * dev->fb_cap.fmt.bytesperline; + dev->query_dv_timings_qmenu = kmalloc_array(dev->query_dv_timings_size, + sizeof(char *), GFP_KERNEL); + /* + * Create a string array containing the names of all the preset + * timings. Each name is max 31 chars long (+ terminating 0). + */ + dev->query_dv_timings_qmenu_strings = + kmalloc_array(dev->query_dv_timings_size, 32, GFP_KERNEL); - /* update touch configuration */ - dev->timeperframe_tch_cap.numerator = 1; - dev->timeperframe_tch_cap.denominator = 10; - vivid_set_touch(dev, 0); + if (!dev->query_dv_timings_qmenu || + !dev->query_dv_timings_qmenu_strings) + return -ENOMEM; - /* initialize locks */ - spin_lock_init(&dev->slock); - mutex_init(&dev->mutex); + for (i = 0; i < dev->query_dv_timings_size; i++) { + const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt; + char *p = dev->query_dv_timings_qmenu_strings + i * 32; + u32 htot, vtot; - /* init dma queues */ - INIT_LIST_HEAD(&dev->vid_cap_active); - INIT_LIST_HEAD(&dev->vid_out_active); - INIT_LIST_HEAD(&dev->vbi_cap_active); - INIT_LIST_HEAD(&dev->vbi_out_active); - INIT_LIST_HEAD(&dev->sdr_cap_active); - INIT_LIST_HEAD(&dev->meta_cap_active); - INIT_LIST_HEAD(&dev->meta_out_active); - INIT_LIST_HEAD(&dev->touch_cap_active); + dev->query_dv_timings_qmenu[i] = p; - INIT_LIST_HEAD(&dev->cec_work_list); - spin_lock_init(&dev->cec_slock); - /* - * Same as create_singlethread_workqueue, but now I can use the - * string formatting of alloc_ordered_workqueue. - */ - dev->cec_workqueue = - alloc_ordered_workqueue("vivid-%03d-cec", WQ_MEM_RECLAIM, inst); - if (!dev->cec_workqueue) { - ret = -ENOMEM; - goto unreg_dev; + htot = V4L2_DV_BT_FRAME_WIDTH(bt); + vtot = V4L2_DV_BT_FRAME_HEIGHT(bt); + snprintf(p, 32, "%ux%u%s%u", + bt->width, bt->height, bt->interlaced ? "i" : "p", + (u32)bt->pixelclock / (htot * vtot)); } - if (allocators[inst] == 1) - dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + return 0; +} + +static int vivid_create_queues(struct vivid_dev *dev) +{ + int ret; /* start creating the vb2 queues */ if (dev->has_vid_cap) { @@ -1374,7 +1229,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) V4L2_BUF_TYPE_VIDEO_CAPTURE, 2, &vivid_vid_cap_qops); if (ret) - goto unreg_dev; + return ret; } if (dev->has_vid_out) { @@ -1383,7 +1238,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) V4L2_BUF_TYPE_VIDEO_OUTPUT, 2, &vivid_vid_out_qops); if (ret) - goto unreg_dev; + return ret; } if (dev->has_vbi_cap) { @@ -1392,7 +1247,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) V4L2_BUF_TYPE_VBI_CAPTURE, 2, &vivid_vbi_cap_qops); if (ret) - goto unreg_dev; + return ret; } if (dev->has_vbi_out) { @@ -1401,7 +1256,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) V4L2_BUF_TYPE_VBI_OUTPUT, 2, &vivid_vbi_out_qops); if (ret) - goto unreg_dev; + return ret; } if (dev->has_sdr_cap) { @@ -1410,7 +1265,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) V4L2_BUF_TYPE_SDR_CAPTURE, 8, &vivid_sdr_cap_qops); if (ret) - goto unreg_dev; + return ret; } if (dev->has_meta_cap) { @@ -1419,7 +1274,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) V4L2_BUF_TYPE_META_CAPTURE, 2, &vivid_meta_cap_qops); if (ret) - goto unreg_dev; + return ret; } if (dev->has_meta_out) { @@ -1428,7 +1283,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) V4L2_BUF_TYPE_META_OUTPUT, 1, &vivid_meta_out_qops); if (ret) - goto unreg_dev; + return ret; } if (dev->has_touch_cap) { @@ -1437,63 +1292,31 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) V4L2_BUF_TYPE_VIDEO_CAPTURE, 1, &vivid_touch_cap_qops); if (ret) - goto unreg_dev; + return ret; } if (dev->has_fb) { /* Create framebuffer for testing capture/output overlay */ ret = vivid_fb_init(dev); if (ret) - goto unreg_dev; + return ret; v4l2_info(&dev->v4l2_dev, "Framebuffer device registered as fb%d\n", dev->fb_info.node); } + return 0; +} -#ifdef CONFIG_VIDEO_VIVID_CEC - if (dev->has_vid_cap && in_type_counter[HDMI]) { - struct cec_adapter *adap; - - adap = vivid_cec_alloc_adap(dev, 0, false); - ret = PTR_ERR_OR_ZERO(adap); - if (ret < 0) - goto unreg_dev; - dev->cec_rx_adap = adap; - } - - if (dev->has_vid_out) { - for (i = 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); - 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]); - goto unreg_dev; - } - - dev->cec_tx_adap[cec_tx_bus_cnt] = adap; - cec_tx_bus_cnt++; - } - } -#endif - - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_cap); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_out); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_cap); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_out); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_rx); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_tx); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_sdr_cap); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_meta_cap); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_meta_out); - v4l2_ctrl_handler_setup(&dev->ctrl_hdl_touch_cap); +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], + unsigned out_type_counter[4]) +{ + struct video_device *vfd; + int ret; - /* finally start creating the device nodes */ if (dev->has_vid_cap) { vfd = &dev->vid_cap_dev; snprintf(vfd->name, sizeof(vfd->name), @@ -1517,7 +1340,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->vid_cap_pad.flags = MEDIA_PAD_FL_SINK; ret = media_entity_pads_init(&vfd->entity, 1, &dev->vid_cap_pad); if (ret) - goto unreg_dev; + return ret; #endif #ifdef CONFIG_VIDEO_VIVID_CEC @@ -1526,7 +1349,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) if (ret < 0) { cec_delete_adapter(dev->cec_rx_adap); dev->cec_rx_adap = NULL; - goto unreg_dev; + 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", @@ -1536,12 +1359,15 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) ret = video_register_device(vfd, VFL_TYPE_VIDEO, vid_cap_nr[inst]); if (ret < 0) - goto unreg_dev; + return ret; v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s\n", video_device_node_name(vfd)); } if (dev->has_vid_out) { +#ifdef CONFIG_VIDEO_VIVID_CEC + int i; +#endif vfd = &dev->vid_out_dev; snprintf(vfd->name, sizeof(vfd->name), "vivid-%03d-vid-out", inst); @@ -1565,7 +1391,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->vid_out_pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&vfd->entity, 1, &dev->vid_out_pad); if (ret) - goto unreg_dev; + return ret; #endif #ifdef CONFIG_VIDEO_VIVID_CEC @@ -1576,7 +1402,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) cec_delete_adapter(dev->cec_tx_adap[i]); dev->cec_tx_adap[i] = NULL; } - goto unreg_dev; + return ret; } v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI output %d\n", dev_name(&dev->cec_tx_adap[i]->devnode.dev), i); @@ -1589,7 +1415,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) ret = video_register_device(vfd, VFL_TYPE_VIDEO, vid_out_nr[inst]); if (ret < 0) - goto unreg_dev; + return ret; v4l2_info(&dev->v4l2_dev, "V4L2 output device registered as %s\n", video_device_node_name(vfd)); } @@ -1612,12 +1438,12 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->vbi_cap_pad.flags = MEDIA_PAD_FL_SINK; ret = media_entity_pads_init(&vfd->entity, 1, &dev->vbi_cap_pad); if (ret) - goto unreg_dev; + return ret; #endif ret = video_register_device(vfd, VFL_TYPE_VBI, vbi_cap_nr[inst]); if (ret < 0) - goto unreg_dev; + return ret; v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s, supports %s VBI\n", video_device_node_name(vfd), (dev->has_raw_vbi_cap && dev->has_sliced_vbi_cap) ? @@ -1644,12 +1470,12 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->vbi_out_pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&vfd->entity, 1, &dev->vbi_out_pad); if (ret) - goto unreg_dev; + return ret; #endif ret = video_register_device(vfd, VFL_TYPE_VBI, vbi_out_nr[inst]); if (ret < 0) - goto unreg_dev; + return ret; v4l2_info(&dev->v4l2_dev, "V4L2 output device registered as %s, supports %s VBI\n", video_device_node_name(vfd), (dev->has_raw_vbi_out && dev->has_sliced_vbi_out) ? @@ -1674,12 +1500,12 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->sdr_cap_pad.flags = MEDIA_PAD_FL_SINK; ret = media_entity_pads_init(&vfd->entity, 1, &dev->sdr_cap_pad); if (ret) - goto unreg_dev; + return ret; #endif ret = video_register_device(vfd, VFL_TYPE_SDR, sdr_cap_nr[inst]); if (ret < 0) - goto unreg_dev; + return ret; v4l2_info(&dev->v4l2_dev, "V4L2 capture device registered as %s\n", video_device_node_name(vfd)); } @@ -1698,7 +1524,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) ret = video_register_device(vfd, VFL_TYPE_RADIO, radio_rx_nr[inst]); if (ret < 0) - goto unreg_dev; + return ret; v4l2_info(&dev->v4l2_dev, "V4L2 receiver device registered as %s\n", video_device_node_name(vfd)); } @@ -1718,7 +1544,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) ret = video_register_device(vfd, VFL_TYPE_RADIO, radio_tx_nr[inst]); if (ret < 0) - goto unreg_dev; + return ret; v4l2_info(&dev->v4l2_dev, "V4L2 transmitter device registered as %s\n", video_device_node_name(vfd)); } @@ -1741,12 +1567,12 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) ret = media_entity_pads_init(&vfd->entity, 1, &dev->meta_cap_pad); if (ret) - goto unreg_dev; + return ret; #endif ret = video_register_device(vfd, VFL_TYPE_VIDEO, meta_cap_nr[inst]); if (ret < 0) - goto unreg_dev; + return ret; v4l2_info(&dev->v4l2_dev, "V4L2 metadata capture device registered as %s\n", video_device_node_name(vfd)); @@ -1771,12 +1597,12 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) ret = media_entity_pads_init(&vfd->entity, 1, &dev->meta_out_pad); if (ret) - goto unreg_dev; + return ret; #endif ret = video_register_device(vfd, VFL_TYPE_VIDEO, meta_out_nr[inst]); if (ret < 0) - goto unreg_dev; + return ret; v4l2_info(&dev->v4l2_dev, "V4L2 metadata output device registered as %s\n", video_device_node_name(vfd)); @@ -1800,12 +1626,12 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) ret = media_entity_pads_init(&vfd->entity, 1, &dev->touch_cap_pad); if (ret) - goto unreg_dev; + return ret; #endif ret = video_register_device(vfd, VFL_TYPE_TOUCH, touch_cap_nr[inst]); if (ret < 0) - goto unreg_dev; + return ret; v4l2_info(&dev->v4l2_dev, "V4L2 touch capture device registered as %s\n", video_device_node_name(vfd)); @@ -1817,26 +1643,268 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) if (ret) { dev_err(dev->mdev.dev, "media device register failed (err=%d)\n", ret); + return ret; + } +#endif + return 0; +} + +static int vivid_create_instance(struct platform_device *pdev, int inst) +{ + static const struct v4l2_dv_timings def_dv_timings = + V4L2_DV_BT_CEA_1280X720P60; + unsigned in_type_counter[4] = { 0, 0, 0, 0 }; + unsigned out_type_counter[4] = { 0, 0, 0, 0 }; + int ccs_cap = ccs_cap_mode[inst]; + int ccs_out = ccs_out_mode[inst]; + bool has_tuner; + bool has_modulator; + 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; + + /* allocate main vivid state structure */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->inst = inst; + +#ifdef CONFIG_MEDIA_CONTROLLER + dev->v4l2_dev.mdev = &dev->mdev; + + /* Initialize media device */ + strscpy(dev->mdev.model, VIVID_MODULE_NAME, sizeof(dev->mdev.model)); + snprintf(dev->mdev.bus_info, sizeof(dev->mdev.bus_info), + "platform:%s-%03d", VIVID_MODULE_NAME, inst); + dev->mdev.dev = &pdev->dev; + media_device_init(&dev->mdev); + dev->mdev.ops = &vivid_media_ops; +#endif + + /* register v4l2_device */ + snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), + "%s-%03d", VIVID_MODULE_NAME, inst); + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + kfree(dev); + return ret; + } + dev->v4l2_dev.release = vivid_dev_release; + + ret = vivid_detect_feature_set(dev, inst, node_type, + &has_tuner, &has_modulator, + &ccs_cap, &ccs_out, + in_type_counter, out_type_counter); + if (ret) + goto free_dev; + + vivid_set_capabilities(dev); + + ret = -ENOMEM; + /* initialize the test pattern generator */ + tpg_init(&dev->tpg, 640, 360); + if (tpg_alloc(&dev->tpg, array_size(MAX_WIDTH, MAX_ZOOM))) + goto free_dev; + dev->scaled_line = vzalloc(array_size(MAX_WIDTH, MAX_ZOOM)); + if (!dev->scaled_line) + goto free_dev; + dev->blended_line = vzalloc(array_size(MAX_WIDTH, MAX_ZOOM)); + if (!dev->blended_line) + goto free_dev; + + /* load the edid */ + dev->edid = vmalloc(array_size(256, 128)); + if (!dev->edid) + goto free_dev; + + ret = vivid_init_dv_timings(dev); + if (ret < 0) + goto free_dev; + + vivid_disable_unused_ioctls(dev, has_tuner, has_modulator, + in_type_counter, out_type_counter); + + /* configure internal data */ + dev->fmt_cap = &vivid_formats[0]; + dev->fmt_out = &vivid_formats[0]; + if (!dev->multiplanar) + vivid_formats[0].data_offset[0] = 0; + dev->webcam_size_idx = 1; + dev->webcam_ival_idx = 3; + tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc); + dev->std_out = V4L2_STD_PAL; + if (dev->input_type[0] == TV || dev->input_type[0] == SVID) + tvnorms_cap = V4L2_STD_ALL; + if (dev->output_type[0] == SVID) + tvnorms_out = V4L2_STD_ALL; + for (i = 0; i < MAX_INPUTS; i++) { + dev->dv_timings_cap[i] = def_dv_timings; + dev->std_cap[i] = V4L2_STD_PAL; + } + dev->dv_timings_out = def_dv_timings; + dev->tv_freq = 2804 /* 175.25 * 16 */; + dev->tv_audmode = V4L2_TUNER_MODE_STEREO; + dev->tv_field_cap = V4L2_FIELD_INTERLACED; + dev->tv_field_out = V4L2_FIELD_INTERLACED; + dev->radio_rx_freq = 95000 * 16; + dev->radio_rx_audmode = V4L2_TUNER_MODE_STEREO; + if (dev->has_radio_tx) { + dev->radio_tx_freq = 95500 * 16; + dev->radio_rds_loop = false; + } + dev->radio_tx_subchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_RDS; + dev->sdr_adc_freq = 300000; + dev->sdr_fm_freq = 50000000; + dev->sdr_pixelformat = V4L2_SDR_FMT_CU8; + dev->sdr_buffersize = SDR_CAP_SAMPLES_PER_BUF * 2; + + dev->edid_max_blocks = dev->edid_blocks = 2; + memcpy(dev->edid, vivid_hdmi_edid, sizeof(vivid_hdmi_edid)); + dev->radio_rds_init_time = ktime_get(); + + /* create all controls */ + ret = vivid_create_controls(dev, ccs_cap == -1, ccs_out == -1, no_error_inj, + in_type_counter[TV] || in_type_counter[SVID] || + out_type_counter[SVID], + in_type_counter[HDMI] || out_type_counter[HDMI]); + if (ret) 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); + } else if (dev->num_inputs && dev->input_type[0] == HDMI) { + v4l2_ctrl_activate(dev->ctrl_std_signal_mode, false); + v4l2_ctrl_activate(dev->ctrl_standard, false); + } + + /* + * update the capture and output formats to do a proper initial + * configuration. + */ + vivid_update_format_cap(dev, false); + vivid_update_format_out(dev); + + /* initialize overlay */ + dev->fb_cap.fmt.width = dev->src_rect.width; + dev->fb_cap.fmt.height = dev->src_rect.height; + dev->fb_cap.fmt.pixelformat = dev->fmt_cap->fourcc; + dev->fb_cap.fmt.bytesperline = dev->src_rect.width * tpg_g_twopixelsize(&dev->tpg, 0) / 2; + dev->fb_cap.fmt.sizeimage = dev->src_rect.height * dev->fb_cap.fmt.bytesperline; + + /* update touch configuration */ + dev->timeperframe_tch_cap.numerator = 1; + dev->timeperframe_tch_cap.denominator = 10; + vivid_set_touch(dev, 0); + + /* initialize locks */ + spin_lock_init(&dev->slock); + mutex_init(&dev->mutex); + + /* init dma queues */ + INIT_LIST_HEAD(&dev->vid_cap_active); + INIT_LIST_HEAD(&dev->vid_out_active); + INIT_LIST_HEAD(&dev->vbi_cap_active); + INIT_LIST_HEAD(&dev->vbi_out_active); + INIT_LIST_HEAD(&dev->sdr_cap_active); + INIT_LIST_HEAD(&dev->meta_cap_active); + INIT_LIST_HEAD(&dev->meta_out_active); + INIT_LIST_HEAD(&dev->touch_cap_active); + + INIT_LIST_HEAD(&dev->cec_work_list); + spin_lock_init(&dev->cec_slock); + /* + * Same as create_singlethread_workqueue, but now I can use the + * string formatting of alloc_ordered_workqueue. + */ + dev->cec_workqueue = alloc_ordered_workqueue("vivid-%03d-cec", + WQ_MEM_RECLAIM, inst); + if (!dev->cec_workqueue) { + ret = -ENOMEM; + goto unreg_dev; + } + + if (allocators[inst] == 1) + dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + + ret = vivid_create_queues(dev); + if (ret) + goto unreg_dev; + +#ifdef CONFIG_VIDEO_VIVID_CEC + if (dev->has_vid_cap && in_type_counter[HDMI]) { + struct cec_adapter *adap; + + adap = vivid_cec_alloc_adap(dev, 0, false); + ret = PTR_ERR_OR_ZERO(adap); + if (ret < 0) + goto unreg_dev; + dev->cec_rx_adap = adap; + } + + if (dev->has_vid_out) { + for (i = 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); + 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]); + goto unreg_dev; + } + + dev->cec_tx_adap[cec_tx_bus_cnt] = adap; + cec_tx_bus_cnt++; + } } #endif + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_cap); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vid_out); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_cap); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_vbi_out); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_rx); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_tx); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_sdr_cap); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_meta_cap); + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_meta_out); + 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, + tvnorms_cap, tvnorms_out, + in_type_counter, out_type_counter); + if (ret) + goto unreg_dev; + /* Now that everything is fine, let's add it to device list */ vivid_devs[inst] = dev; return 0; unreg_dev: - video_unregister_device(&dev->touch_cap_dev); - video_unregister_device(&dev->meta_out_dev); - video_unregister_device(&dev->meta_cap_dev); + vb2_video_unregister_device(&dev->touch_cap_dev); + vb2_video_unregister_device(&dev->meta_out_dev); + vb2_video_unregister_device(&dev->meta_cap_dev); video_unregister_device(&dev->radio_tx_dev); video_unregister_device(&dev->radio_rx_dev); - video_unregister_device(&dev->sdr_cap_dev); - video_unregister_device(&dev->vbi_out_dev); - video_unregister_device(&dev->vbi_cap_dev); - video_unregister_device(&dev->vid_out_dev); - video_unregister_device(&dev->vid_cap_dev); + vb2_video_unregister_device(&dev->sdr_cap_dev); + vb2_video_unregister_device(&dev->vbi_out_dev); + vb2_video_unregister_device(&dev->vbi_cap_dev); + vb2_video_unregister_device(&dev->vid_out_dev); + vb2_video_unregister_device(&dev->vid_cap_dev); cec_unregister_adapter(dev->cec_rx_adap); for (i = 0; i < MAX_OUTPUTS; i++) cec_unregister_adapter(dev->cec_tx_adap[i]); @@ -1907,27 +1975,27 @@ static int vivid_remove(struct platform_device *pdev) if (dev->has_vid_cap) { v4l2_info(&dev->v4l2_dev, "unregistering %s\n", video_device_node_name(&dev->vid_cap_dev)); - video_unregister_device(&dev->vid_cap_dev); + vb2_video_unregister_device(&dev->vid_cap_dev); } if (dev->has_vid_out) { v4l2_info(&dev->v4l2_dev, "unregistering %s\n", video_device_node_name(&dev->vid_out_dev)); - video_unregister_device(&dev->vid_out_dev); + vb2_video_unregister_device(&dev->vid_out_dev); } if (dev->has_vbi_cap) { v4l2_info(&dev->v4l2_dev, "unregistering %s\n", video_device_node_name(&dev->vbi_cap_dev)); - video_unregister_device(&dev->vbi_cap_dev); + vb2_video_unregister_device(&dev->vbi_cap_dev); } if (dev->has_vbi_out) { v4l2_info(&dev->v4l2_dev, "unregistering %s\n", video_device_node_name(&dev->vbi_out_dev)); - video_unregister_device(&dev->vbi_out_dev); + vb2_video_unregister_device(&dev->vbi_out_dev); } if (dev->has_sdr_cap) { v4l2_info(&dev->v4l2_dev, "unregistering %s\n", video_device_node_name(&dev->sdr_cap_dev)); - video_unregister_device(&dev->sdr_cap_dev); + vb2_video_unregister_device(&dev->sdr_cap_dev); } if (dev->has_radio_rx) { v4l2_info(&dev->v4l2_dev, "unregistering %s\n", @@ -1948,17 +2016,17 @@ static int vivid_remove(struct platform_device *pdev) if (dev->has_meta_cap) { v4l2_info(&dev->v4l2_dev, "unregistering %s\n", video_device_node_name(&dev->meta_cap_dev)); - video_unregister_device(&dev->meta_cap_dev); + vb2_video_unregister_device(&dev->meta_cap_dev); } if (dev->has_meta_out) { v4l2_info(&dev->v4l2_dev, "unregistering %s\n", video_device_node_name(&dev->meta_out_dev)); - video_unregister_device(&dev->meta_out_dev); + vb2_video_unregister_device(&dev->meta_out_dev); } if (dev->has_touch_cap) { v4l2_info(&dev->v4l2_dev, "unregistering %s\n", video_device_node_name(&dev->touch_cap_dev)); - video_unregister_device(&dev->touch_cap_dev); + vb2_video_unregister_device(&dev->touch_cap_dev); } cec_unregister_adapter(dev->cec_rx_adap); for (j = 0; j < MAX_OUTPUTS; j++) diff --git a/drivers/media/test-drivers/vivid/vivid-meta-out.c b/drivers/media/test-drivers/vivid/vivid-meta-out.c index ff8a039aba72..95835b52b58f 100644 --- a/drivers/media/test-drivers/vivid/vivid-meta-out.c +++ b/drivers/media/test-drivers/vivid/vivid-meta-out.c @@ -164,10 +164,11 @@ void vivid_meta_out_process(struct vivid_dev *dev, { struct vivid_meta_out_buf *meta = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); - tpg_s_brightness(&dev->tpg, meta->brightness); - tpg_s_contrast(&dev->tpg, meta->contrast); - tpg_s_saturation(&dev->tpg, meta->saturation); - tpg_s_hue(&dev->tpg, meta->hue); + v4l2_ctrl_s_ctrl(dev->brightness, meta->brightness); + v4l2_ctrl_s_ctrl(dev->contrast, meta->contrast); + v4l2_ctrl_s_ctrl(dev->saturation, meta->saturation); + v4l2_ctrl_s_ctrl(dev->hue, meta->hue); + dprintk(dev, 2, " %s brightness %u contrast %u saturation %u hue %d\n", __func__, meta->brightness, meta->contrast, meta->saturation, meta->hue); diff --git a/drivers/media/test-drivers/vivid/vivid-vbi-gen.c b/drivers/media/test-drivers/vivid/vivid-vbi-gen.c index acc98445a1fa..a141369a7a63 100644 --- a/drivers/media/test-drivers/vivid/vivid-vbi-gen.c +++ b/drivers/media/test-drivers/vivid/vivid-vbi-gen.c @@ -298,7 +298,7 @@ void vivid_vbi_gen_sliced(struct vivid_vbi_gen_data *vbi, switch (frame) { case 0: vivid_vbi_gen_set_time_of_day(vbi->time_of_day_packet); - /* fall through */ + fallthrough; case 1 ... 7: data1->data[0] = vbi->time_of_day_packet[frame * 2]; data1->data[1] = vbi->time_of_day_packet[frame * 2 + 1]; diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index e94beef008c8..eadf28ab1e39 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -560,6 +560,7 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv, unsigned factor = 1; unsigned w, h; unsigned p; + bool user_set_csc = !!(mp->flags & V4L2_PIX_FMT_FLAG_SET_CSC); fmt = vivid_get_format(dev, mp->pixelformat); if (!fmt) { @@ -633,13 +634,30 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv, (fmt->bit_depth[p] / fmt->vdownsampling[p])) / (fmt->bit_depth[0] / fmt->vdownsampling[0]); - mp->colorspace = vivid_colorspace_cap(dev); - if (fmt->color_enc == TGP_COLOR_ENC_HSV) - mp->hsv_enc = vivid_hsv_enc_cap(dev); - else + if (!user_set_csc || !v4l2_is_colorspace_valid(mp->colorspace)) + mp->colorspace = vivid_colorspace_cap(dev); + + if (!user_set_csc || !v4l2_is_xfer_func_valid(mp->xfer_func)) + mp->xfer_func = vivid_xfer_func_cap(dev); + + if (fmt->color_enc == TGP_COLOR_ENC_HSV) { + if (!user_set_csc || !v4l2_is_hsv_enc_valid(mp->hsv_enc)) + mp->hsv_enc = vivid_hsv_enc_cap(dev); + } else if (fmt->color_enc == TGP_COLOR_ENC_YCBCR) { + if (!user_set_csc || !v4l2_is_ycbcr_enc_valid(mp->ycbcr_enc)) + mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev); + } else { mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev); - mp->xfer_func = vivid_xfer_func_cap(dev); - mp->quantization = vivid_quantization_cap(dev); + } + + if (fmt->color_enc == TGP_COLOR_ENC_YCBCR || + fmt->color_enc == TGP_COLOR_ENC_RGB) { + if (!user_set_csc || !v4l2_is_quant_valid(mp->quantization)) + mp->quantization = vivid_quantization_cap(dev); + } else { + mp->quantization = vivid_quantization_cap(dev); + } + memset(mp->reserved, 0, sizeof(mp->reserved)); return 0; } @@ -769,6 +787,14 @@ int vivid_s_fmt_vid_cap(struct file *file, void *priv, if (vivid_is_sdtv_cap(dev)) dev->tv_field_cap = mp->field; tpg_update_mv_step(&dev->tpg); + dev->tpg.colorspace = mp->colorspace; + dev->tpg.xfer_func = mp->xfer_func; + if (dev->fmt_cap->color_enc == TGP_COLOR_ENC_YCBCR) + dev->tpg.ycbcr_enc = mp->ycbcr_enc; + else + dev->tpg.hsv_enc = mp->hsv_enc; + dev->tpg.quantization = mp->quantization; + return 0; } diff --git a/drivers/media/test-drivers/vivid/vivid-vid-common.c b/drivers/media/test-drivers/vivid/vivid-vid-common.c index 76b0be670ebb..19701fe72030 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-common.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-common.c @@ -920,6 +920,31 @@ int vivid_enum_fmt_vid(struct file *file, void *priv, fmt = &vivid_formats[f->index]; f->pixelformat = fmt->fourcc; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return 0; + /* + * For capture devices, we support the CSC API. + * We allow userspace to: + * 1. set the colorspace + * 2. set the xfer_func + * 3. set the ycbcr_enc on YUV formats + * 4. set the hsv_enc on HSV formats + * 5. set the quantization on YUV and RGB formats + */ + f->flags |= V4L2_FMT_FLAG_CSC_COLORSPACE; + f->flags |= V4L2_FMT_FLAG_CSC_XFER_FUNC; + + if (fmt->color_enc == TGP_COLOR_ENC_YCBCR) { + f->flags |= V4L2_FMT_FLAG_CSC_YCBCR_ENC; + f->flags |= V4L2_FMT_FLAG_CSC_QUANTIZATION; + } else if (fmt->color_enc == TGP_COLOR_ENC_HSV) { + f->flags |= V4L2_FMT_FLAG_CSC_HSV_ENC; + } else if (fmt->color_enc == TGP_COLOR_ENC_RGB) { + f->flags |= V4L2_FMT_FLAG_CSC_QUANTIZATION; + } + return 0; } diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c index b7b5b33b11f4..eaa3bbc903d7 100644 --- a/drivers/media/tuners/fc0011.c +++ b/drivers/media/tuners/fc0011.c @@ -250,7 +250,7 @@ static int fc0011_set_params(struct dvb_frontend *fe) dev_warn(&priv->i2c->dev, "Unsupported bandwidth %u kHz. Using 6000 kHz.\n", bandwidth); bandwidth = 6000; - /* fallthrough */ + fallthrough; case 6000: regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW6M; break; diff --git a/drivers/media/tuners/qt1010.c b/drivers/media/tuners/qt1010.c index e48faf942830..3853a3d43d4f 100644 --- a/drivers/media/tuners/qt1010.c +++ b/drivers/media/tuners/qt1010.c @@ -222,23 +222,24 @@ static int qt1010_init_meas1(struct qt1010_priv *priv, { QT1010_WR, reg, reg_init_val }, { QT1010_WR, 0x1e, 0x00 }, { QT1010_WR, 0x1e, oper }, - { QT1010_RD, reg, 0xff } }; for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { - if (i2c_data[i].oper == QT1010_WR) { - err = qt1010_writereg(priv, i2c_data[i].reg, - i2c_data[i].val); - } else { - err = qt1010_readreg(priv, i2c_data[i].reg, &val2); - } - if (err) return err; + err = qt1010_writereg(priv, i2c_data[i].reg, + i2c_data[i].val); + if (err) + return err; } + err = qt1010_readreg(priv, reg, &val2); + if (err) + return err; do { val1 = val2; err = qt1010_readreg(priv, reg, &val2); - if (err) return err; + if (err) + return err; + dev_dbg(&priv->i2c->dev, "%s: compare reg:%02x %02x %02x\n", __func__, reg, val1, val2); } while (val1 != val2); @@ -250,7 +251,7 @@ static int qt1010_init_meas1(struct qt1010_priv *priv, static int qt1010_init_meas2(struct qt1010_priv *priv, u8 reg_init_val, u8 *retval) { - u8 i, val; + u8 i, val = 0xff; int err; qt1010_i2c_oper_t i2c_data[] = { { QT1010_WR, 0x07, reg_init_val }, @@ -261,6 +262,7 @@ static int qt1010_init_meas2(struct qt1010_priv *priv, { QT1010_WR, 0x1e, 0x00 }, { QT1010_WR, 0x22, 0xff } }; + for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { if (i2c_data[i].oper == QT1010_WR) { err = qt1010_writereg(priv, i2c_data[i].reg, @@ -268,7 +270,8 @@ static int qt1010_init_meas2(struct qt1010_priv *priv, } else { err = qt1010_readreg(priv, i2c_data[i].reg, &val); } - if (err) return err; + if (err) + return err; } *retval = val; return 0; diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c index 471aaf71fdef..f0371d004b36 100644 --- a/drivers/media/tuners/tda18271-fe.c +++ b/drivers/media/tuners/tda18271-fe.c @@ -948,7 +948,7 @@ static int tda18271_set_params(struct dvb_frontend *fe) break; case SYS_DVBC_ANNEX_B: bw = 6000000; - /* fall through */ + fallthrough; case SYS_DVBC_ANNEX_A: case SYS_DVBC_ANNEX_C: if (bw <= 6000000) { diff --git a/drivers/media/tuners/tuner-simple.c b/drivers/media/tuners/tuner-simple.c index b6e70fada3fb..8fb186b25d6a 100644 --- a/drivers/media/tuners/tuner-simple.c +++ b/drivers/media/tuners/tuner-simple.c @@ -500,7 +500,7 @@ static int simple_radio_bandswitch(struct dvb_frontend *fe, u8 *buffer) case TUNER_TENA_9533_DI: case TUNER_YMEC_TVF_5533MF: tuner_dbg("This tuner doesn't have FM. Most cards have a TEA5767 for FM\n"); - return 0; + return -EINVAL; case TUNER_PHILIPS_FM1216ME_MK3: case TUNER_PHILIPS_FM1236_MK3: case TUNER_PHILIPS_FMD1216ME_MK3: @@ -702,7 +702,8 @@ static int simple_set_radio_freq(struct dvb_frontend *fe, TUNER_RATIO_SELECT_50; /* 50 kHz step */ /* Bandswitch byte */ - simple_radio_bandswitch(fe, &buffer[0]); + if (simple_radio_bandswitch(fe, &buffer[0])) + return 0; /* Convert from 1/16 kHz V4L steps to 1/20 MHz (=50 kHz) PLL steps freq * (1 Mhz / 16000 V4L steps) * (20 PLL steps / 1 MHz) = diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c index 4befa920246c..3d3368202cd0 100644 --- a/drivers/media/usb/au0828/au0828-input.c +++ b/drivers/media/usb/au0828/au0828-input.c @@ -104,11 +104,11 @@ static int au8522_rc_andor(struct au0828_rc *ir, u16 reg, u8 mask, u8 value) /* Remote Controller time units */ -#define AU8522_UNIT 200000 /* ns */ -#define NEC_START_SPACE (4500000 / AU8522_UNIT) -#define NEC_START_PULSE (562500 * 16) +#define AU8522_UNIT 200 /* us */ +#define NEC_START_SPACE (4500 / AU8522_UNIT) +#define NEC_START_PULSE (563 * 16) #define RC5_START_SPACE (4 * AU8522_UNIT) -#define RC5_START_PULSE 888888 +#define RC5_START_PULSE 889 static int au0828_get_key_au8522(struct au0828_rc *ir) { diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 51b8d14fb4dc..aa5bc6a2ae20 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -938,8 +938,8 @@ int au0828_analog_unregister(struct au0828_dev *dev) return 0; mutex_lock(&au0828_sysfs_lock); - video_unregister_device(&dev->vdev); - video_unregister_device(&dev->vbi_dev); + vb2_video_unregister_device(&dev->vdev); + vb2_video_unregister_device(&dev->vbi_dev); mutex_unlock(&au0828_sysfs_lock); v4l2_device_disconnect(&dev->v4l2_dev); @@ -2011,8 +2011,7 @@ int au0828_analog_register(struct au0828_dev *dev, if (retval != 0) { dprintk(1, "unable to register video device (error = %d).\n", retval); - ret = -ENODEV; - goto err_reg_vdev; + return -ENODEV; } /* Register the vbi device */ @@ -2040,10 +2039,7 @@ int au0828_analog_register(struct au0828_dev *dev, return 0; err_reg_vbi_dev: - video_unregister_device(&dev->vdev); -err_reg_vdev: - vb2_queue_release(&dev->vb_vidq); - vb2_queue_release(&dev->vb_vbiq); + vb2_video_unregister_device(&dev->vdev); return ret; } diff --git a/drivers/media/usb/b2c2/flexcop-usb.c b/drivers/media/usb/b2c2/flexcop-usb.c index e3234d169065..e731243267e4 100644 --- a/drivers/media/usb/b2c2/flexcop-usb.c +++ b/drivers/media/usb/b2c2/flexcop-usb.c @@ -419,10 +419,9 @@ static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb) usb_free_urb(fc_usb->iso_urb[i]); } - if (fc_usb->iso_buffer != NULL) - usb_free_coherent(fc_usb->udev, - fc_usb->buffer_size, fc_usb->iso_buffer, - fc_usb->dma_addr); + usb_free_coherent(fc_usb->udev, fc_usb->buffer_size, + fc_usb->iso_buffer, fc_usb->dma_addr); + } static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb) @@ -513,6 +512,8 @@ static int flexcop_usb_init(struct flexcop_usb *fc_usb) if (fc_usb->uintf->cur_altsetting->desc.bNumEndpoints < 1) return -ENODEV; + if (!usb_endpoint_is_isoc_in(&fc_usb->uintf->cur_altsetting->endpoint[1].desc)) + return -ENODEV; switch (fc_usb->udev->speed) { case USB_SPEED_LOW: diff --git a/drivers/media/usb/b2c2/flexcop-usb.h b/drivers/media/usb/b2c2/flexcop-usb.h index e86faa0e06ca..2f230bf72252 100644 --- a/drivers/media/usb/b2c2/flexcop-usb.h +++ b/drivers/media/usb/b2c2/flexcop-usb.h @@ -15,7 +15,7 @@ #define B2C2_USB_CTRL_PIPE_IN usb_rcvctrlpipe(fc_usb->udev, 0) #define B2C2_USB_CTRL_PIPE_OUT usb_sndctrlpipe(fc_usb->udev, 0) -#define B2C2_USB_DATA_PIPE usb_rcvisocpipe(fc_usb->udev, 0x81) +#define B2C2_USB_DATA_PIPE usb_rcvisocpipe(fc_usb->udev, 1) struct flexcop_usb { struct usb_device *udev; diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c index 982cb56e97e9..05d91caaed0c 100644 --- a/drivers/media/usb/cx231xx/cx231xx-core.c +++ b/drivers/media/usb/cx231xx/cx231xx-core.c @@ -115,11 +115,9 @@ void cx231xx_init_extension(struct cx231xx *dev) struct cx231xx_ops *ops = NULL; mutex_lock(&cx231xx_devlist_mutex); - if (!list_empty(&cx231xx_extension_devlist)) { - list_for_each_entry(ops, &cx231xx_extension_devlist, next) { - if (ops->init) - ops->init(dev); - } + list_for_each_entry(ops, &cx231xx_extension_devlist, next) { + if (ops->init) + ops->init(dev); } mutex_unlock(&cx231xx_devlist_mutex); } @@ -129,11 +127,9 @@ void cx231xx_close_extension(struct cx231xx *dev) struct cx231xx_ops *ops = NULL; mutex_lock(&cx231xx_devlist_mutex); - if (!list_empty(&cx231xx_extension_devlist)) { - list_for_each_entry(ops, &cx231xx_extension_devlist, next) { - if (ops->fini) - ops->fini(dev); - } + list_for_each_entry(ops, &cx231xx_extension_devlist, next) { + if (ops->fini) + ops->fini(dev); } mutex_unlock(&cx231xx_devlist_mutex); } diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index c427b9031e42..c70b3cef3176 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -43,7 +43,7 @@ static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) case READ_I2C: write = 0; state->buf[2] |= 0x01; /* set I2C direction */ - /* fall through */ + fallthrough; case WRITE_I2C: state->buf[0] = READ_WRITE_I2C; break; diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c index b7ca236174f3..0c434259c36f 100644 --- a/drivers/media/usb/dvb-usb-v2/gl861.c +++ b/drivers/media/usb/dvb-usb-v2/gl861.c @@ -41,7 +41,7 @@ static int gl861_ctrl_msg(struct dvb_usb_device *d, u8 request, u16 value, switch (request) { case CMD_WRITE: memcpy(ctx->buf, data, size); - /* Fall through */ + fallthrough; case CMD_WRITE_SHORT: pipe = usb_sndctrlpipe(d->udev, 0); requesttype = USB_TYPE_VENDOR | USB_DIR_OUT; diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 8a3c0eeed959..5a7a9522d46d 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -687,7 +687,7 @@ static const char *lme_firmware_switch(struct dvb_usb_device *d, int cold) cold = 0; break; } - /* fall through */ + fallthrough; case TUNER_LG: fw_lme = fw_lg; ret = request_firmware(&fw, fw_lme, &udev->dev); @@ -710,7 +710,7 @@ static const char *lme_firmware_switch(struct dvb_usb_device *d, int cold) cold = 0; break; } - /* fall through */ + fallthrough; case TUNER_LG: fw_lme = fw_c_lg; ret = request_firmware(&fw, fw_lme, &udev->dev); @@ -718,7 +718,7 @@ static const char *lme_firmware_switch(struct dvb_usb_device *d, int cold) st->dvb_usb_lme2510_firmware = TUNER_LG; break; } - /* fall through */ + fallthrough; case TUNER_S0194: fw_lme = fw_c_s0194; ret = request_firmware(&fw, fw_lme, &udev->dev); @@ -1018,7 +1018,7 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) } break; } - /* fall through */ + fallthrough; case 0x22f0: st->i2c_gate = 5; adap->fe[0] = dvb_attach(m88rs2000_attach, diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c index 0b7dda99e410..ef489c566b75 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c @@ -632,7 +632,7 @@ int mxl111sf_set_gpio(struct mxl111sf_state *state, int gpio, int val) default: mxl_printk(KERN_ERR, "gpio_port_expander undefined, assuming PCA9534"); - /* fall-thru */ + fallthrough; case mxl111sf_PCA9534: return pca9534_set_gpio(state, gpio, val); case mxl111sf_gpio_hw: @@ -693,7 +693,7 @@ int mxl111sf_init_port_expander(struct mxl111sf_state *state) default: mxl_printk(KERN_ERR, "gpio_port_expander undefined, assuming PCA9534"); - /* fall-thru */ + fallthrough; case mxl111sf_PCA9534: return pca9534_init_port_expander(state); case mxl111sf_gpio_hw: diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 2080f6ef4be1..91460e4d0c30 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1781,7 +1781,7 @@ static int rtl2832u_rc_query(struct dvb_usb_device *d) /* pass data to Kernel IR decoder */ for (i = 0; i < len; i++) { ev.pulse = buf[i] >> 7; - ev.duration = 50800 * (buf[i] & 0x7f); + ev.duration = 51 * (buf[i] & 0x7f); ir_raw_event_store_with_filter(d->rc_dev, &ev); } @@ -1809,7 +1809,7 @@ static int rtl2832u_get_rc_config(struct dvb_usb_device *d, rc->query = rtl2832u_rc_query; rc->interval = 200; /* we program idle len to 0xc0, set timeout to one less */ - rc->timeout = 0xbf * 50800; + rc->timeout = 0xbf * 51; return 0; } diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index 25ba03edcb5c..7498110142e4 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -279,6 +279,7 @@ config DVB_USB_PCTV452E tristate "Pinnacle PCTV HDTV Pro USB device/TT Connect S2-3600" depends on DVB_USB select TTPCI_EEPROM + select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT select DVB_LNBP22 if MEDIA_SUBDRV_AUTOSELECT select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/usb/dvb-usb/cxusb-analog.c b/drivers/media/usb/dvb-usb/cxusb-analog.c index 001cae648797..e93183ddd797 100644 --- a/drivers/media/usb/dvb-usb/cxusb-analog.c +++ b/drivers/media/usb/dvb-usb/cxusb-analog.c @@ -1615,8 +1615,6 @@ static void cxusb_medion_videodev_release(struct video_device *vdev) cxusb_vprintk(dvbdev, OPS, "video device release\n"); - vb2_queue_release(vdev->queue); - video_device_release(vdev); } @@ -1647,8 +1645,7 @@ static int cxusb_medion_register_analog_video(struct dvb_usb_device *dvbdev) cxdev->videodev = video_device_alloc(); if (!cxdev->videodev) { dev_err(&dvbdev->udev->dev, "video device alloc failed\n"); - ret = -ENOMEM; - goto ret_qrelease; + return -ENOMEM; } cxdev->videodev->device_caps = videocaps; @@ -1674,10 +1671,6 @@ static int cxusb_medion_register_analog_video(struct dvb_usb_device *dvbdev) ret_vrelease: video_device_release(cxdev->videodev); - -ret_qrelease: - vb2_queue_release(&cxdev->videoqueue); - return ret; } @@ -1820,7 +1813,7 @@ int cxusb_medion_register_analog(struct dvb_usb_device *dvbdev) return 0; ret_vunreg: - video_unregister_device(cxdev->videodev); + vb2_video_unregister_device(cxdev->videodev); ret_unregister: v4l2_device_put(&cxdev->v4l2dev); @@ -1836,7 +1829,7 @@ void cxusb_medion_unregister_analog(struct dvb_usb_device *dvbdev) cxusb_vprintk(dvbdev, OPS, "unregistering analog\n"); video_unregister_device(cxdev->radiodev); - video_unregister_device(cxdev->videodev); + vb2_video_unregister_device(cxdev->videodev); v4l2_device_put(&cxdev->v4l2dev); wait_for_completion(&cxdev->v4l2_release); diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c index 52e648e2713a..d3288c107906 100644 --- a/drivers/media/usb/dvb-usb/dib0700_devices.c +++ b/drivers/media/usb/dvb-usb/dib0700_devices.c @@ -1738,14 +1738,9 @@ static int dib809x_tuner_attach(struct dvb_usb_adapter *adap) struct dib0700_adapter_state *st = adap->priv; struct i2c_adapter *tun_i2c = st->dib8000_ops.get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); - if (adap->id == 0) { - if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL) - return -ENODEV; - } else { - /* FIXME: check if it is fe_adap[1] */ - if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL) - return -ENODEV; - } + /* FIXME: if adap->id != 0, check if it is fe_adap[1] */ + if (!dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config)) + return -ENODEV; st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib8096_set_param_override; diff --git a/drivers/media/usb/dvb-usb/pctv452e.c b/drivers/media/usb/dvb-usb/pctv452e.c index 441d878fc22c..9b78b40abc6d 100644 --- a/drivers/media/usb/dvb-usb/pctv452e.c +++ b/drivers/media/usb/dvb-usb/pctv452e.c @@ -20,6 +20,7 @@ #include "stb6100.h" #include "stb6100_cfg.h" /* FE Power */ +#include "isl6423.h" #include "lnbp22.h" #include <media/dvb_ca_en50221.h> @@ -83,6 +84,13 @@ static struct stb0899_postproc pctv45e_postproc[] = { { 0, 0 } }; +static struct isl6423_config pctv452e_isl6423_config = { + .current_max = SEC_CURRENT_515m, + .curlim = SEC_CURRENT_LIM_ON, + .mod_extern = 1, + .addr = 0x08, +}; + /* * stores all private variables for communication with the PCTV452e DVB-S2 */ @@ -909,15 +917,23 @@ static int pctv452e_frontend_attach(struct dvb_usb_adapter *a) &a->dev->i2c_adap); if (!a->fe_adap[0].fe) return -ENODEV; - if ((dvb_attach(lnbp22_attach, a->fe_adap[0].fe, - &a->dev->i2c_adap)) == NULL) - err("Cannot attach lnbp22\n"); id = a->dev->desc->warm_ids[0]; - if (USB_VID_TECHNOTREND == id->idVendor - && USB_PID_TECHNOTREND_CONNECT_S2_3650_CI == id->idProduct) + if (id->idVendor == USB_VID_TECHNOTREND && + id->idProduct == USB_PID_TECHNOTREND_CONNECT_S2_3650_CI) { + if (dvb_attach(lnbp22_attach, + a->fe_adap[0].fe, + &a->dev->i2c_adap) == NULL) { + err("Cannot attach lnbp22\n"); + } /* Error ignored. */ tt3650_ci_init(a); + } else if (dvb_attach(isl6423_attach, + a->fe_adap[0].fe, + &a->dev->i2c_adap, + &pctv452e_isl6423_config) == NULL) { + err("Cannot attach isl6423\n"); + } return 0; } diff --git a/drivers/media/usb/dvb-usb/technisat-usb2.c b/drivers/media/usb/dvb-usb/technisat-usb2.c index f172120db2aa..a9ed26ce1be6 100644 --- a/drivers/media/usb/dvb-usb/technisat-usb2.c +++ b/drivers/media/usb/dvb-usb/technisat-usb2.c @@ -656,14 +656,14 @@ unlock: for (i = 1; i < ARRAY_SIZE(state->buf); i++) { if (buf[i] == 0xff) { ev.pulse = 0; - ev.duration = 888888*2; + ev.duration = 889 * 2; ir_raw_event_store(d->rc_dev, &ev); break; } ev.pulse = !ev.pulse; ev.duration = (buf[i] * FIRMWARE_CLOCK_DIVISOR * - FIRMWARE_CLOCK_TICK) / 1000; + FIRMWARE_CLOCK_TICK) / (1000 * 1000); ir_raw_event_store(d->rc_dev, &ev); } diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 6833b5bfe293..dc968fd5ace9 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -362,13 +362,13 @@ static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, return -ENODEV; switch (cmd) { - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ - case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_START: atomic_set(&dev->adev.stream_started, 1); break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */ - case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */ + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: atomic_set(&dev->adev.stream_started, 0); break; diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index a8c321d11827..5144888ae36f 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2519,6 +2519,26 @@ const struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + /* + * 1f4d:1abe MyGica iGrabber + * (same as several other EM2860 devices) + * Empia EM2860, Philips SAA7113, Empia EMP202, No Tuner + */ + [EM2860_BOARD_MYGICA_IGRABBER] = { + .name = "MyGica iGrabber", + .vchannels = 2, + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_COMPOSITE, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, }; EXPORT_SYMBOL_GPL(em28xx_boards); @@ -2698,6 +2718,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2860_BOARD_EASYCAP }, { USB_DEVICE(0x1b80, 0xe425), .driver_info = EM2874_BOARD_MAXMEDIA_UB425_TC }, + { USB_DEVICE(0x1f4d, 0x1abe), + .driver_info = EM2860_BOARD_MYGICA_IGRABBER }, { USB_DEVICE(0x2304, 0x0242), .driver_info = EM2884_BOARD_PCTV_510E }, { USB_DEVICE(0x2013, 0x0251), diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index acbb62397314..55a46faaf7b7 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -151,6 +151,7 @@ #define EM2882_BOARD_ZOLID_HYBRID_TV_STICK 102 #define EM2861_BOARD_MAGIX_VIDEOWANDLER2 103 #define EM28178_BOARD_PCTV_461E_V2 104 +#define EM2860_BOARD_MYGICA_IGRABBER 105 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 diff --git a/drivers/media/usb/go7007/go7007-driver.c b/drivers/media/usb/go7007/go7007-driver.c index 153a0c3e3da6..f1767be9d868 100644 --- a/drivers/media/usb/go7007/go7007-driver.c +++ b/drivers/media/usb/go7007/go7007-driver.c @@ -643,7 +643,7 @@ void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) case 0xD8: if (go->format == V4L2_PIX_FMT_MJPEG) vb = frame_boundary(go, vb); - /* fall through */ + fallthrough; default: store_byte(vb, 0xFF); store_byte(vb, buf[i]); diff --git a/drivers/media/usb/gspca/mr97310a.c b/drivers/media/usb/gspca/mr97310a.c index 464aa61cd914..3553788e8542 100644 --- a/drivers/media/usb/gspca/mr97310a.c +++ b/drivers/media/usb/gspca/mr97310a.c @@ -510,7 +510,7 @@ static int start_cif_cam(struct gspca_dev *gspca_dev) switch (gspca_dev->pixfmt.width) { case 160: data[9] |= 0x04; /* reg 8, 2:1 scale down from 320 */ - /* fall through */ + fallthrough; case 320: default: data[3] = 0x28; /* reg 2, H size/8 */ @@ -520,7 +520,7 @@ static int start_cif_cam(struct gspca_dev *gspca_dev) break; case 176: data[9] |= 0x04; /* reg 8, 2:1 scale down from 352 */ - /* fall through */ + fallthrough; case 352: data[3] = 0x2c; /* reg 2, H size/8 */ data[4] = 0x48; /* reg 3, V size/4 */ @@ -607,10 +607,10 @@ static int start_vga_cam(struct gspca_dev *gspca_dev) switch (gspca_dev->pixfmt.width) { case 160: data[9] |= 0x0c; /* reg 8, 4:1 scale down */ - /* fall through */ + fallthrough; case 320: data[9] |= 0x04; /* reg 8, 2:1 scale down */ - /* fall through */ + fallthrough; case 640: default: data[3] = 0x50; /* reg 2, H size/8 */ @@ -627,7 +627,7 @@ static int start_vga_cam(struct gspca_dev *gspca_dev) case 176: data[9] |= 0x04; /* reg 8, 2:1 scale down */ - /* fall through */ + fallthrough; case 352: data[3] = 0x2c; /* reg 2, H size */ data[4] = 0x48; /* reg 3, V size */ diff --git a/drivers/media/usb/gspca/nw80x.c b/drivers/media/usb/gspca/nw80x.c index 880f569bda30..0f5f2464ac7a 100644 --- a/drivers/media/usb/gspca/nw80x.c +++ b/drivers/media/usb/gspca/nw80x.c @@ -2019,7 +2019,7 @@ static int sd_init_controls(struct gspca_dev *gspca_dev) V4L2_CID_AUTOGAIN, 0, 1, 1, 1); gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAIN, 0, 253, 1, 128); - /* fall through */ + fallthrough; case Cvideopro: case DvcV6: case Kritter: diff --git a/drivers/media/usb/gspca/ov519.c b/drivers/media/usb/gspca/ov519.c index 0afe70a3f9a2..cd6776c3163b 100644 --- a/drivers/media/usb/gspca/ov519.c +++ b/drivers/media/usb/gspca/ov519.c @@ -2004,7 +2004,7 @@ static void reg_w(struct sd *sd, u16 index, u16 value) break; case BRIDGE_OVFX2: req = 0x0a; - /* fall through */ + fallthrough; case BRIDGE_W9968CF: gspca_dbg(gspca_dev, D_USBO, "SET %02x %04x %04x\n", req, value, index); @@ -3528,7 +3528,7 @@ static void ov511_mode_init_regs(struct sd *sd) case SEN_OV76BE: if (sd->gspca_dev.pixfmt.width == 320) interlaced = 1; - /* Fall through */ + fallthrough; case SEN_OV6630: case SEN_OV7610: case SEN_OV7670: @@ -3541,7 +3541,7 @@ static void ov511_mode_init_regs(struct sd *sd) break; } /* For 640x480 case */ - /* fall through */ + fallthrough; default: /* case 20: */ /* case 15: */ diff --git a/drivers/media/usb/gspca/sn9c20x.c b/drivers/media/usb/gspca/sn9c20x.c index 2a6d0a1265a7..bfd194c61819 100644 --- a/drivers/media/usb/gspca/sn9c20x.c +++ b/drivers/media/usb/gspca/sn9c20x.c @@ -1637,7 +1637,7 @@ static int sd_config(struct gspca_dev *gspca_dev, break; case SENSOR_HV7131R: sd->i2c_intf = 0x81; /* i2c 400 Kb/s */ - /* fall through */ + fallthrough; default: cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); diff --git a/drivers/media/usb/gspca/sunplus.c b/drivers/media/usb/gspca/sunplus.c index f4a4222f0d2e..ace3da40006e 100644 --- a/drivers/media/usb/gspca/sunplus.c +++ b/drivers/media/usb/gspca/sunplus.c @@ -551,7 +551,7 @@ static void init_ctl_reg(struct gspca_dev *gspca_dev) case BRIDGE_SPCA504: case BRIDGE_SPCA504C: pollreg = 0; - /* fall through */ + fallthrough; default: /* case BRIDGE_SPCA533: */ /* case BRIDGE_SPCA504B: */ @@ -634,7 +634,7 @@ static int sd_init(struct gspca_dev *gspca_dev) reg_w_riv(gspca_dev, 0x00, 0x2000, 0x00); reg_w_riv(gspca_dev, 0x00, 0x2301, 0x13); reg_w_riv(gspca_dev, 0x00, 0x2306, 0x00); - /* fall through */ + fallthrough; case BRIDGE_SPCA533: spca504B_PollingDataReady(gspca_dev); spca50x_GetFirmware(gspca_dev); diff --git a/drivers/media/usb/gspca/xirlink_cit.c b/drivers/media/usb/gspca/xirlink_cit.c index c579b100f066..cc87c24dd24c 100644 --- a/drivers/media/usb/gspca/xirlink_cit.c +++ b/drivers/media/usb/gspca/xirlink_cit.c @@ -1409,7 +1409,7 @@ static int cit_restart_stream(struct gspca_dev *gspca_dev) case CIT_MODEL0: case CIT_MODEL1: cit_write_reg(gspca_dev, 0x0001, 0x0114); - /* Fall through */ + fallthrough; case CIT_MODEL2: case CIT_MODEL4: cit_write_reg(gspca_dev, 0x00c0, 0x010c); /* Go! */ @@ -2725,7 +2725,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) break; case CIT_MODEL2: v4l2_ctrl_grab(sd->lighting, false); - /* Fall through! */ + fallthrough; case CIT_MODEL4: cit_model2_Packet1(gspca_dev, 0x0030, 0x0004); diff --git a/drivers/media/usb/gspca/zc3xx.c b/drivers/media/usb/gspca/zc3xx.c index 15a2449d536f..aa285d5d6c0d 100644 --- a/drivers/media/usb/gspca/zc3xx.c +++ b/drivers/media/usb/gspca/zc3xx.c @@ -6766,7 +6766,7 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_HV7131R: case SENSOR_TAS5130C: reg_r(gspca_dev, 0x0008); - /* fall through */ + fallthrough; case SENSOR_PO2030: reg_w(gspca_dev, 0x03, 0x0008); break; @@ -6815,7 +6815,7 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_TAS5130C: reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */ reg_w(gspca_dev, 0x15, 0x01ae); - /* fall through */ + fallthrough; case SENSOR_PAS202B: case SENSOR_PO2030: /* reg_w(gspca_dev, 0x40, ZC3XX_R117_GGAIN); in win traces */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c index 1cfb7cf64131..f4a727918e35 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -864,10 +864,9 @@ static int ctrl_std_sym_to_val(struct pvr2_ctrl *cptr, const char *bufPtr,unsigned int bufSize, int *mskp,int *valp) { - int ret; v4l2_std_id id; - ret = pvr2_std_str_to_id(&id,bufPtr,bufSize); - if (ret < 0) return ret; + if (!pvr2_std_str_to_id(&id, bufPtr, bufSize)) + return -EINVAL; if (mskp) *mskp = id; if (valp) *valp = id; return 0; diff --git a/drivers/media/usb/pwc/pwc-v4l.c b/drivers/media/usb/pwc/pwc-v4l.c index 2f135d533af6..71b719d363a5 100644 --- a/drivers/media/usb/pwc/pwc-v4l.c +++ b/drivers/media/usb/pwc/pwc-v4l.c @@ -554,7 +554,7 @@ static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) if (!DEVICE_USE_CODEC3(pdev->type)) break; /* For CODEC3 where autogain also controls expo */ - /* fall through */ + fallthrough; case V4L2_CID_EXPOSURE_AUTO: if (pdev->exposure_valid && time_before(jiffies, pdev->last_exposure_update + HZ / 4)) { diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c index 9ba3a2ae36e5..df4c5dcba39c 100644 --- a/drivers/media/usb/siano/smsusb.c +++ b/drivers/media/usb/siano/smsusb.c @@ -430,7 +430,7 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id) break; case SMS_UNKNOWN_TYPE: pr_err("Unspecified sms device type!\n"); - /* fall-thru */ + fallthrough; default: dev->buffer_size = USB2_BUFFER_SIZE; dev->response_alignment = align; diff --git a/drivers/media/usb/tm6000/tm6000-alsa.c b/drivers/media/usb/tm6000/tm6000-alsa.c index c26a0ff60a64..3a2df36ef1db 100644 --- a/drivers/media/usb/tm6000/tm6000-alsa.c +++ b/drivers/media/usb/tm6000/tm6000-alsa.c @@ -272,13 +272,13 @@ static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd) int err = 0; switch (cmd) { - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ - case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_START: atomic_set(&core->stream_started, 1); break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */ - case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */ + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: atomic_set(&core->stream_started, 0); break; diff --git a/drivers/media/usb/tm6000/tm6000-core.c b/drivers/media/usb/tm6000/tm6000-core.c index 2c723706f8c8..5c8cbc5d6f72 100644 --- a/drivers/media/usb/tm6000/tm6000-core.c +++ b/drivers/media/usb/tm6000/tm6000-core.c @@ -853,11 +853,9 @@ int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type, /* FIXME: tm6000_extension_devlist_lock should be a spinlock */ - if (!list_empty(&tm6000_extension_devlist)) { - list_for_each_entry(ops, &tm6000_extension_devlist, next) { - if (ops->fillbuf && ops->type == type) - ops->fillbuf(dev, buf, size); - } + list_for_each_entry(ops, &tm6000_extension_devlist, next) { + if (ops->fillbuf && ops->type == type) + ops->fillbuf(dev, buf, size); } return 0; @@ -898,11 +896,9 @@ void tm6000_init_extension(struct tm6000_core *dev) struct tm6000_ops *ops = NULL; mutex_lock(&tm6000_devlist_mutex); - if (!list_empty(&tm6000_extension_devlist)) { - list_for_each_entry(ops, &tm6000_extension_devlist, next) { - if (ops->init) - ops->init(dev); - } + list_for_each_entry(ops, &tm6000_extension_devlist, next) { + if (ops->init) + ops->init(dev); } mutex_unlock(&tm6000_devlist_mutex); } @@ -912,11 +908,9 @@ void tm6000_close_extension(struct tm6000_core *dev) struct tm6000_ops *ops = NULL; mutex_lock(&tm6000_devlist_mutex); - if (!list_empty(&tm6000_extension_devlist)) { - list_for_each_entry(ops, &tm6000_extension_devlist, next) { - if (ops->fini) - ops->fini(dev); - } + list_for_each_entry(ops, &tm6000_extension_devlist, next) { + if (ops->fini) + ops->fini(dev); } mutex_unlock(&tm6000_devlist_mutex); } diff --git a/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c index 4e56ff83566b..9e016b71aa91 100644 --- a/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c +++ b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c @@ -5,6 +5,9 @@ * Copyright (c) 2002 Holger Waechtler <holger@convergence.de> * Copyright (c) 2003 Felix Domke <tmbinc@elitedvb.net> */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/slab.h> #include <linux/wait.h> @@ -59,7 +62,12 @@ MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -#define dprintk(x...) do { if (debug) printk(KERN_DEBUG x); } while (0) +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_DEBUG pr_fmt("%s: " fmt), \ + __func__, ##arg); \ +} while (0) + #define ISO_BUF_COUNT 4 #define FRAMES_PER_ISO_BUF 4 @@ -72,6 +80,9 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define TTUSB_REV_2_2 0x22 #define TTUSB_BUDGET_NAME "ttusb_stc_fw" +#define MAX_SEND 0x28 +#define MAX_RCV 0x20 + /* * since we're casting (struct ttusb*) <-> (struct dvb_demux*) around * the dvb_demux field must be the first in struct!! @@ -119,87 +130,70 @@ struct ttusb { int cc; /* MuxCounter - will increment on EVERY MUX PACKET */ /* (including stuffing. yes. really.) */ - u8 last_result[32]; + u8 send_buf[MAX_SEND]; + u8 last_result[MAX_RCV]; int revision; struct dvb_frontend* fe; }; -/* ugly workaround ... don't know why it's necessary to read */ -/* all result codes. */ - -static int ttusb_cmd(struct ttusb *ttusb, - const u8 * data, int len, int needresult) +static int ttusb_cmd(struct ttusb *ttusb, u8 *data, int len, int len_result) { int actual_len; int err; - int i; - - if (debug >= 3) { - printk(KERN_DEBUG ">"); - for (i = 0; i < len; ++i) - printk(KERN_CONT " %02x", data[i]); - printk(KERN_CONT "\n"); - } if (mutex_lock_interruptible(&ttusb->semusb) < 0) return -EAGAIN; + if (debug >= 3) + dprintk("> %*ph\n", len, data); + + memcpy(data, ttusb->send_buf, len); + err = usb_bulk_msg(ttusb->dev, ttusb->bulk_out_pipe, - (u8 *) data, len, &actual_len, 1000); + ttusb->send_buf, len, &actual_len, 1000); if (err != 0) { - dprintk("%s: usb_bulk_msg(send) failed, err == %i!\n", - __func__, err); - mutex_unlock(&ttusb->semusb); - return err; + dprintk("usb_bulk_msg(send) failed, err == %i!\n", err); + goto err; } if (actual_len != len) { - dprintk("%s: only wrote %d of %d bytes\n", __func__, + err = -EIO; + dprintk("only wrote %d of %d bytes\n", actual_len, len); - mutex_unlock(&ttusb->semusb); - return -1; + goto err; } err = usb_bulk_msg(ttusb->dev, ttusb->bulk_in_pipe, - ttusb->last_result, 32, &actual_len, 1000); + ttusb->last_result, MAX_RCV, &actual_len, 1000); if (err != 0) { - printk("%s: failed, receive error %d\n", __func__, - err); - mutex_unlock(&ttusb->semusb); - return err; + pr_err("cmd xter failed, receive error %d\n", err); + goto err; } if (debug >= 3) { actual_len = ttusb->last_result[3] + 4; - printk(KERN_DEBUG "<"); - for (i = 0; i < actual_len; ++i) - printk(KERN_CONT " %02x", ttusb->last_result[i]); - printk(KERN_CONT "\n"); + dprintk("< %*ph\n", actual_len, ttusb->last_result); } - if (!needresult) - mutex_unlock(&ttusb->semusb); - return 0; -} + if (len_result) + memcpy(ttusb->send_buf, ttusb->last_result, len_result); -static int ttusb_result(struct ttusb *ttusb, u8 * data, int len) -{ - memcpy(data, ttusb->last_result, len); +err: mutex_unlock(&ttusb->semusb); - return 0; + return err; } static int ttusb_i2c_msg(struct ttusb *ttusb, u8 addr, u8 * snd_buf, u8 snd_len, u8 * rcv_buf, u8 rcv_len) { - u8 b[0x28]; + u8 b[MAX_SEND]; u8 id = ++ttusb->c; int i, err; - if (snd_len > 0x28 - 7 || rcv_len > 0x20 - 7) + if (snd_len > MAX_SEND - 7 || rcv_len > MAX_RCV - 7) return -EINVAL; b[0] = 0xaa; @@ -213,22 +207,19 @@ static int ttusb_i2c_msg(struct ttusb *ttusb, for (i = 0; i < snd_len; i++) b[7 + i] = snd_buf[i]; - err = ttusb_cmd(ttusb, b, snd_len + 7, 1); + err = ttusb_cmd(ttusb, b, snd_len + 7, MAX_RCV); if (err) return -EREMOTEIO; - err = ttusb_result(ttusb, b, 0x20); - /* check if the i2c transaction was successful */ if ((snd_len != b[5]) || (rcv_len != b[6])) return -EREMOTEIO; if (rcv_len > 0) { if (err || b[0] != 0x55 || b[1] != id) { - dprintk - ("%s: usb_bulk_msg(recv) failed, err == %i, id == %02x, b == ", - __func__, err, id); + dprintk("usb_bulk_msg(recv) failed, err == %i, id == %02x, b == ", + err, id); return -EREMOTEIO; } @@ -272,7 +263,7 @@ static int master_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, int num snd_buf, snd_len, rcv_buf, rcv_len); if (err < rcv_len) { - dprintk("%s: i == %i\n", __func__, i); + dprintk("i == %i\n", i); break; } @@ -292,7 +283,7 @@ static int ttusb_boot_dsp(struct ttusb *ttusb) err = request_firmware(&fw, "ttusb-budget/dspbootcode.bin", &ttusb->dev->dev); if (err) { - printk(KERN_ERR "ttusb-budget: failed to request firmware\n"); + pr_err("failed to request firmware\n"); return err; } @@ -332,8 +323,7 @@ static int ttusb_boot_dsp(struct ttusb *ttusb) done: release_firmware(fw); if (err) { - dprintk("%s: usb_bulk_msg() failed, return value %i!\n", - __func__, err); + dprintk("usb_bulk_msg() failed, return value %i!\n", err); } return err; @@ -400,8 +390,6 @@ static int ttusb_init_controller(struct ttusb *ttusb) /* i2c write read: 5 bytes, addr 0x10, 0x02 bytes write, 1 bytes read. */ u8 b3[] = { 0xaa, ++ttusb->c, 0x31, 5, 0x10, 0x02, 0x01, 0x00, 0x1e }; - u8 b4[] = - { 0x55, ttusb->c, 0x31, 4, 0x10, 0x02, 0x01, 0x00, 0x1e }; u8 get_version[] = { 0xaa, ++ttusb->c, 0x17, 5, 0, 0, 0, 0, 0 }; u8 get_dsp_version[0x20] = @@ -422,44 +410,35 @@ static int ttusb_init_controller(struct ttusb *ttusb) if ((err = ttusb_cmd(ttusb, b2, sizeof(b2), 0))) return err; - if ((err = ttusb_cmd(ttusb, b3, sizeof(b3), 1))) - return err; - - err = ttusb_result(ttusb, b4, sizeof(b4)); - - if ((err = ttusb_cmd(ttusb, get_version, sizeof(get_version), 1))) + if ((err = ttusb_cmd(ttusb, b3, sizeof(b3), 0))) return err; - if ((err = ttusb_result(ttusb, get_version, sizeof(get_version)))) + if ((err = ttusb_cmd(ttusb, get_version, + sizeof(get_version), sizeof(get_version)))) return err; - dprintk("%s: stc-version: %c%c%c%c%c\n", __func__, - get_version[4], get_version[5], get_version[6], - get_version[7], get_version[8]); + dprintk("stc-version: %c%c%c%c%c\n", get_version[4], get_version[5], + get_version[6], get_version[7], get_version[8]); if (memcmp(get_version + 4, "V 0.0", 5) && memcmp(get_version + 4, "V 1.1", 5) && memcmp(get_version + 4, "V 2.1", 5) && memcmp(get_version + 4, "V 2.2", 5)) { - printk - ("%s: unknown STC version %c%c%c%c%c, please report!\n", - __func__, get_version[4], get_version[5], - get_version[6], get_version[7], get_version[8]); + pr_err("unknown STC version %c%c%c%c%c, please report!\n", + get_version[4], get_version[5], + get_version[6], get_version[7], get_version[8]); } ttusb->revision = ((get_version[6] - '0') << 4) | (get_version[8] - '0'); err = - ttusb_cmd(ttusb, get_dsp_version, sizeof(get_dsp_version), 1); + ttusb_cmd(ttusb, get_dsp_version, + sizeof(get_dsp_version), sizeof(get_dsp_version)); if (err) return err; - err = - ttusb_result(ttusb, get_dsp_version, sizeof(get_dsp_version)); - if (err) - return err; - printk("%s: dsp-version: %c%c%c\n", __func__, + pr_info("dsp-version: %c%c%c\n", get_dsp_version[4], get_dsp_version[5], get_dsp_version[6]); return 0; } @@ -481,8 +460,7 @@ static int ttusb_send_diseqc(struct dvb_frontend* fe, /* Diseqc */ if ((err = ttusb_cmd(ttusb, b, 4 + b[3], 0))) { - dprintk("%s: usb_bulk_msg() failed, return value %i!\n", - __func__, err); + dprintk("usb_bulk_msg() failed, return value %i!\n", err); } return err; @@ -499,8 +477,7 @@ static int ttusb_update_lnb(struct ttusb *ttusb) /* SetLNB */ if ((err = ttusb_cmd(ttusb, b, sizeof(b), 0))) { - dprintk("%s: usb_bulk_msg() failed, return value %i!\n", - __func__, err); + dprintk("usb_bulk_msg() failed, return value %i!\n", err); } return err; @@ -534,8 +511,7 @@ static void ttusb_set_led_freq(struct ttusb *ttusb, u8 freq) err = ttusb_cmd(ttusb, b, sizeof(b), 0); if (err) { - dprintk("%s: usb_bulk_msg() failed, return value %i!\n", - __func__, err); + dprintk("usb_bulk_msg() failed, return value %i!\n", err); } } #endif @@ -559,7 +535,7 @@ static void ttusb_process_muxpack(struct ttusb *ttusb, const u8 * muxpack, int i; if (len < 4 || len & 0x1) { - pr_warn("%s: muxpack has invalid len %d\n", __func__, len); + pr_warn("muxpack has invalid len %d\n", len); numinvalid++; return; } @@ -567,8 +543,7 @@ static void ttusb_process_muxpack(struct ttusb *ttusb, const u8 * muxpack, for (i = 0; i < len; i += 2) csum ^= le16_to_cpup((__le16 *) (muxpack + i)); if (csum) { - printk("%s: muxpack with incorrect checksum, ignoring\n", - __func__); + pr_warn("muxpack with incorrect checksum, ignoring\n"); numinvalid++; return; } @@ -576,8 +551,8 @@ static void ttusb_process_muxpack(struct ttusb *ttusb, const u8 * muxpack, cc = (muxpack[len - 4] << 8) | muxpack[len - 3]; cc &= 0x7FFF; if ((cc != ttusb->cc) && (ttusb->cc != -1)) - printk("%s: cc discontinuity (%d frames missing)\n", - __func__, (cc - ttusb->cc) & 0x7FFF); + pr_warn("cc discontinuity (%d frames missing)\n", + (cc - ttusb->cc) & 0x7FFF); ttusb->cc = (cc + 1) & 0x7FFF; if (muxpack[0] & 0x80) { #ifdef TTUSB_HWSECTIONS @@ -598,7 +573,7 @@ static void ttusb_process_muxpack(struct ttusb *ttusb, const u8 * muxpack, !!(ttusb->muxpack[1] & 1)) data++; #warning TODO: pusi - printk("cc: %04x\n", (data[0] << 8) | data[1]); + dprintk("cc: %04x\n", (data[0] << 8) | data[1]); #endif numsec++; } else if (muxpack[0] == 0x47) { @@ -617,7 +592,7 @@ static void ttusb_process_muxpack(struct ttusb *ttusb, const u8 * muxpack, dvb_dmx_swfilter_packets(&ttusb->dvb_demux, muxpack, 1); } else if (muxpack[0] != 0) { numinvalid++; - printk("illegal muxpack type %02x\n", muxpack[0]); + pr_err("illegal muxpack type %02x\n", muxpack[0]); } else numstuff++; } @@ -627,7 +602,7 @@ static void ttusb_process_frame(struct ttusb *ttusb, u8 * data, int len) int maxwork = 1024; while (len) { if (!(maxwork--)) { - printk("%s: too much work\n", __func__); + pr_err("too much work\n"); break; } @@ -641,10 +616,7 @@ static void ttusb_process_frame(struct ttusb *ttusb, u8 * data, int len) else { ttusb->mux_state = 0; if (ttusb->insync) { - dprintk("%s: %02x\n", - __func__, data[-1]); - printk(KERN_INFO "%s: lost sync.\n", - __func__); + pr_info("lost sync.\n"); ttusb->insync = 0; } } @@ -700,10 +672,8 @@ static void ttusb_process_frame(struct ttusb *ttusb, u8 * data, int len) ttusb->muxpack[1] + 2 + 4; else { - dprintk - ("%s: invalid state: first byte is %x\n", - __func__, - ttusb->muxpack[0]); + dprintk("invalid state: first byte is %x\n", + ttusb->muxpack[0]); ttusb->mux_state = 0; } } @@ -752,12 +722,6 @@ static void ttusb_iso_irq(struct urb *urb) if (!ttusb->iso_streaming) return; -#if 0 - printk("%s: status %d, errcount == %d, length == %i\n", - __func__, - urb->status, urb->error_count, urb->actual_length); -#endif - if (!urb->status) { for (i = 0; i < urb->number_of_packets; ++i) { numpkt++; @@ -830,7 +794,7 @@ static int ttusb_start_iso_xfer(struct ttusb *ttusb) int i, j, err, buffer_offset = 0; if (ttusb->iso_streaming) { - printk("%s: iso xfer already running!\n", __func__); + pr_err("iso xfer already running!\n"); return 0; } @@ -864,9 +828,8 @@ static int ttusb_start_iso_xfer(struct ttusb *ttusb) for (i = 0; i < ISO_BUF_COUNT; i++) { if ((err = usb_submit_urb(ttusb->iso_urb[i], GFP_ATOMIC))) { ttusb_stop_iso_xfer(ttusb); - printk - ("%s: failed urb submission (%i: err = %i)!\n", - __func__, i, err); + pr_err("failed urb submission (%i: err = %i)!\n", + i, err); return err; } } @@ -1426,7 +1389,7 @@ static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) { - printk("dvb-ttusb-budget: dvbc_philips_tdm1316l_pll_set Error 1\n"); + pr_err("dvbc_philips_tdm1316l_pll_set Error 1\n"); return -EIO; } @@ -1435,7 +1398,7 @@ static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) { - printk("dvb-ttusb-budget: dvbc_philips_tdm1316l_pll_set Error 2\n"); + pr_err("dvbc_philips_tdm1316l_pll_set Error 2\n"); return -EIO; } @@ -1612,12 +1575,12 @@ static void frontend_init(struct ttusb* ttusb) } if (ttusb->fe == NULL) { - printk("dvb-ttusb-budget: A frontend driver was not found for device [%04x:%04x]\n", + pr_err("no frontend driver found for device [%04x:%04x]\n", le16_to_cpu(ttusb->dev->descriptor.idVendor), le16_to_cpu(ttusb->dev->descriptor.idProduct)); } else { if (dvb_register_frontend(&ttusb->adapter, ttusb->fe)) { - printk("dvb-ttusb-budget: Frontend registration failed!\n"); + pr_err("Frontend registration failed!\n"); dvb_frontend_detach(ttusb->fe); ttusb->fe = NULL; } @@ -1637,7 +1600,7 @@ static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *i struct ttusb *ttusb; int result; - dprintk("%s: TTUSB DVB connected\n", __func__); + dprintk("TTUSB DVB connected\n"); udev = interface_to_usbdev(intf); @@ -1659,14 +1622,14 @@ static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *i result = ttusb_alloc_iso_urbs(ttusb); if (result < 0) { - dprintk("%s: ttusb_alloc_iso_urbs - failed\n", __func__); + dprintk("ttusb_alloc_iso_urbs - failed\n"); mutex_unlock(&ttusb->semi2c); kfree(ttusb); return result; } if (ttusb_init_controller(ttusb)) - printk("ttusb_init_controller: error\n"); + pr_err("ttusb_init_controller: error\n"); mutex_unlock(&ttusb->semi2c); @@ -1711,7 +1674,7 @@ static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *i result = dvb_dmx_init(&ttusb->dvb_demux); if (result < 0) { - printk("ttusb_dvb: dvb_dmx_init failed (errno = %d)\n", result); + pr_err("dvb_dmx_init failed (errno = %d)\n", result); result = -ENODEV; goto err_i2c_del_adapter; } @@ -1722,14 +1685,14 @@ static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *i result = dvb_dmxdev_init(&ttusb->dmxdev, &ttusb->adapter); if (result < 0) { - printk("ttusb_dvb: dvb_dmxdev_init failed (errno = %d)\n", + pr_err("dvb_dmxdev_init failed (errno = %d)\n", result); result = -ENODEV; goto err_release_dmx; } if (dvb_net_init(&ttusb->adapter, &ttusb->dvbnet, &ttusb->dvb_demux.dmx)) { - printk("ttusb_dvb: dvb_net_init failed!\n"); + pr_err("dvb_net_init failed!\n"); result = -ENODEV; goto err_release_dmxdev; } @@ -1778,7 +1741,7 @@ static void ttusb_disconnect(struct usb_interface *intf) kfree(ttusb); - dprintk("%s: TTUSB DVB disconnected\n", __func__); + dprintk("TTUSB DVB disconnected\n"); } static const struct usb_device_id ttusb_table[] = { diff --git a/drivers/media/usb/ttusb-dec/ttusb_dec.c b/drivers/media/usb/ttusb-dec/ttusb_dec.c index b8d39b2f777f..df6c5e4a0f05 100644 --- a/drivers/media/usb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/usb/ttusb-dec/ttusb_dec.c @@ -769,9 +769,9 @@ static void ttusb_dec_process_urb_frame(struct ttusb_dec *dec, u8 *b, } } -static void ttusb_dec_process_urb_frame_list(unsigned long data) +static void ttusb_dec_process_urb_frame_list(struct tasklet_struct *t) { - struct ttusb_dec *dec = (struct ttusb_dec *)data; + struct ttusb_dec *dec = from_tasklet(dec, t, urb_tasklet); struct list_head *item; struct urb_frame *frame; unsigned long flags; @@ -1209,8 +1209,7 @@ static void ttusb_dec_init_tasklet(struct ttusb_dec *dec) { spin_lock_init(&dec->urb_frame_list_lock); INIT_LIST_HEAD(&dec->urb_frame_list); - tasklet_init(&dec->urb_tasklet, ttusb_dec_process_urb_frame_list, - (unsigned long)dec); + tasklet_setup(&dec->urb_tasklet, ttusb_dec_process_urb_frame_list); } static int ttusb_init_rc( struct ttusb_dec *dec) diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c index ee9c656d121f..2308c0b4f5e7 100644 --- a/drivers/media/usb/usbtv/usbtv-core.c +++ b/drivers/media/usb/usbtv/usbtv-core.c @@ -113,7 +113,8 @@ static int usbtv_probe(struct usb_interface *intf, usbtv_audio_fail: /* we must not free at this point */ - usb_get_dev(usbtv->udev); + v4l2_device_get(&usbtv->v4l2_dev); + /* this will undo the v4l2_device_get() */ usbtv_video_free(usbtv); usbtv_video_fail: diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index c89efcd46163..3b4a2e769230 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -872,7 +872,6 @@ static void usbtv_release(struct v4l2_device *v4l2_dev) v4l2_device_unregister(&usbtv->v4l2_dev); v4l2_ctrl_handler_free(&usbtv->ctrl); - vb2_queue_release(&usbtv->vb2q); kfree(usbtv); } @@ -954,7 +953,6 @@ vdev_fail: v4l2_fail: ctrl_fail: v4l2_ctrl_handler_free(&usbtv->ctrl); - vb2_queue_release(&usbtv->vb2q); return ret; } @@ -965,7 +963,7 @@ void usbtv_video_free(struct usbtv *usbtv) mutex_lock(&usbtv->v4l2_lock); usbtv_stop(usbtv); - video_unregister_device(&usbtv->vdev); + vb2_video_unregister_device(&usbtv->vdev); v4l2_device_disconnect(&usbtv->v4l2_dev); mutex_unlock(&usbtv->v4l2_lock); diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index e399b9fad757..f479d8971dfb 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -773,12 +773,16 @@ static s32 uvc_get_le_value(struct uvc_control_mapping *mapping, offset &= 7; mask = ((1LL << bits) - 1) << offset; - for (; bits > 0; data++) { + while (1) { u8 byte = *data & mask; value |= offset > 0 ? (byte >> offset) : (byte << (-offset)); bits -= 8 - (offset > 0 ? offset : 0); + if (bits <= 0) + break; + offset -= 8; mask = (1 << bits) - 1; + data++; } /* Sign-extend the value if needed. */ @@ -1844,30 +1848,35 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, { struct uvc_entity *entity; struct uvc_control *ctrl; - unsigned int i, found = 0; + unsigned int i; + bool found; u32 reqflags; u16 size; u8 *data = NULL; int ret; /* Find the extension unit. */ + found = false; list_for_each_entry(entity, &chain->entities, chain) { if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT && - entity->id == xqry->unit) + entity->id == xqry->unit) { + found = true; break; + } } - if (entity->id != xqry->unit) { + if (!found) { uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n", xqry->unit); return -ENOENT; } /* Find the control and perform delayed initialization if needed. */ + found = false; for (i = 0; i < entity->ncontrols; ++i) { ctrl = &entity->controls[i]; if (ctrl->index == xqry->selector - 1) { - found = 1; + found = true; break; } } @@ -2011,25 +2020,14 @@ int uvc_ctrl_restore_values(struct uvc_device *dev) static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl, const struct uvc_control_info *info) { - int ret = 0; - ctrl->info = *info; INIT_LIST_HEAD(&ctrl->info.mappings); /* Allocate an array to save control values (cur, def, max, etc.) */ ctrl->uvc_data = kzalloc(ctrl->info.size * UVC_CTRL_DATA_LAST + 1, GFP_KERNEL); - if (ctrl->uvc_data == NULL) { - ret = -ENOMEM; - goto done; - } - - /* - * Retrieve control flags from the device. Ignore errors and work with - * default flag values from the uvc_ctrl array when the device doesn't - * properly implement GET_INFO on standard controls. - */ - uvc_ctrl_get_flags(dev, ctrl, &ctrl->info); + if (!ctrl->uvc_data) + return -ENOMEM; ctrl->initialized = 1; @@ -2037,10 +2035,7 @@ static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl, "entity %u\n", ctrl->info.entity, ctrl->info.selector, dev->udev->devpath, ctrl->entity->id); -done: - if (ret < 0) - kfree(ctrl->uvc_data); - return ret; + return 0; } /* @@ -2253,6 +2248,13 @@ static void uvc_ctrl_init_ctrl(struct uvc_device *dev, struct uvc_control *ctrl) if (uvc_entity_match_guid(ctrl->entity, info->entity) && ctrl->index == info->index) { uvc_ctrl_add_info(dev, ctrl, info); + /* + * Retrieve control flags from the device. Ignore errors + * and work with default flag values from the uvc_ctrl + * array when the device doesn't properly implement + * GET_INFO on standard controls. + */ + uvc_ctrl_get_flags(dev, ctrl, &ctrl->info); break; } } diff --git a/drivers/media/usb/uvc/uvc_debugfs.c b/drivers/media/usb/uvc/uvc_debugfs.c index 2b8af4b54117..1a1258d4ffca 100644 --- a/drivers/media/usb/uvc/uvc_debugfs.c +++ b/drivers/media/usb/uvc/uvc_debugfs.c @@ -73,7 +73,6 @@ static struct dentry *uvc_debugfs_root_dir; void uvc_debugfs_init_stream(struct uvc_streaming *stream) { struct usb_device *udev = stream->dev->udev; - struct dentry *dent; char dir_name[33]; if (uvc_debugfs_root_dir == NULL) @@ -82,22 +81,11 @@ void uvc_debugfs_init_stream(struct uvc_streaming *stream) snprintf(dir_name, sizeof(dir_name), "%u-%u-%u", udev->bus->busnum, udev->devnum, stream->intfnum); - dent = debugfs_create_dir(dir_name, uvc_debugfs_root_dir); - if (IS_ERR_OR_NULL(dent)) { - uvc_printk(KERN_INFO, "Unable to create debugfs %s " - "directory.\n", dir_name); - return; - } - - stream->debugfs_dir = dent; + stream->debugfs_dir = debugfs_create_dir(dir_name, + uvc_debugfs_root_dir); - dent = debugfs_create_file("stats", 0444, stream->debugfs_dir, - stream, &uvc_debugfs_stats_fops); - if (IS_ERR_OR_NULL(dent)) { - uvc_printk(KERN_INFO, "Unable to create debugfs stats file.\n"); - uvc_debugfs_cleanup_stream(stream); - return; - } + debugfs_create_file("stats", 0444, stream->debugfs_dir, stream, + &uvc_debugfs_stats_fops); } void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 431d86e1c94b..ddb9eaa11be7 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -214,6 +214,11 @@ static struct uvc_format_desc uvc_fmts[] = { .guid = UVC_GUID_FORMAT_CNF4, .fcc = V4L2_PIX_FMT_CNF4, }, + { + .name = "HEVC", + .guid = UVC_GUID_FORMAT_HEVC, + .fcc = V4L2_PIX_FMT_HEVC, + }, }; /* ------------------------------------------------------------------------ @@ -248,10 +253,10 @@ static struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16]) return NULL; } -static u32 uvc_colorspace(const u8 primaries) +static enum v4l2_colorspace uvc_colorspace(const u8 primaries) { - static const u8 colorprimaries[] = { - 0, + static const enum v4l2_colorspace colorprimaries[] = { + V4L2_COLORSPACE_DEFAULT, /* Unspecified */ V4L2_COLORSPACE_SRGB, V4L2_COLORSPACE_470_SYSTEM_M, V4L2_COLORSPACE_470_SYSTEM_BG, @@ -262,7 +267,61 @@ static u32 uvc_colorspace(const u8 primaries) if (primaries < ARRAY_SIZE(colorprimaries)) return colorprimaries[primaries]; - return 0; + return V4L2_COLORSPACE_DEFAULT; /* Reserved */ +} + +static enum v4l2_xfer_func uvc_xfer_func(const u8 transfer_characteristics) +{ + /* + * V4L2 does not currently have definitions for all possible values of + * UVC transfer characteristics. If v4l2_xfer_func is extended with new + * values, the mapping below should be updated. + * + * Substitutions are taken from the mapping given for + * V4L2_XFER_FUNC_DEFAULT documented in videodev2.h. + */ + static const enum v4l2_xfer_func xfer_funcs[] = { + V4L2_XFER_FUNC_DEFAULT, /* Unspecified */ + V4L2_XFER_FUNC_709, + V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 M */ + V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 B, G */ + V4L2_XFER_FUNC_709, /* Substitution for SMPTE 170M */ + V4L2_XFER_FUNC_SMPTE240M, + V4L2_XFER_FUNC_NONE, + V4L2_XFER_FUNC_SRGB, + }; + + if (transfer_characteristics < ARRAY_SIZE(xfer_funcs)) + return xfer_funcs[transfer_characteristics]; + + return V4L2_XFER_FUNC_DEFAULT; /* Reserved */ +} + +static enum v4l2_ycbcr_encoding uvc_ycbcr_enc(const u8 matrix_coefficients) +{ + /* + * V4L2 does not currently have definitions for all possible values of + * UVC matrix coefficients. If v4l2_ycbcr_encoding is extended with new + * values, the mapping below should be updated. + * + * Substitutions are taken from the mapping given for + * V4L2_YCBCR_ENC_DEFAULT documented in videodev2.h. + * + * FCC is assumed to be close enough to 601. + */ + static const enum v4l2_ycbcr_encoding ycbcr_encs[] = { + V4L2_YCBCR_ENC_DEFAULT, /* Unspecified */ + V4L2_YCBCR_ENC_709, + V4L2_YCBCR_ENC_601, /* Substitution for FCC */ + V4L2_YCBCR_ENC_601, /* Substitution for BT.470-2 B, G */ + V4L2_YCBCR_ENC_601, + V4L2_YCBCR_ENC_SMPTE240M, + }; + + if (matrix_coefficients < ARRAY_SIZE(ycbcr_encs)) + return ycbcr_encs[matrix_coefficients]; + + return V4L2_YCBCR_ENC_DEFAULT; /* Reserved */ } /* Simplify a fraction using a simple continued fraction decomposition. The @@ -284,7 +343,7 @@ void uvc_simplify_fraction(u32 *numerator, u32 *denominator, return; /* Convert the fraction to a simple continued fraction. See - * http://mathforum.org/dr.math/faq/faq.fractions.html + * https://mathforum.org/dr.math/faq/faq.fractions.html * Stop if the current term is bigger than or equal to the given * threshold. */ @@ -704,6 +763,8 @@ static int uvc_parse_format(struct uvc_device *dev, } format->colorspace = uvc_colorspace(buffer[3]); + format->xfer_func = uvc_xfer_func(buffer[4]); + format->ycbcr_enc = uvc_ycbcr_enc(buffer[5]); buflen -= buffer[0]; buffer += buffer[0]; diff --git a/drivers/media/usb/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c index b4499cddeffe..ca3a9c2eec27 100644 --- a/drivers/media/usb/uvc/uvc_entity.c +++ b/drivers/media/usb/uvc/uvc_entity.c @@ -73,10 +73,45 @@ static int uvc_mc_init_entity(struct uvc_video_chain *chain, int ret; if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) { + u32 function; + v4l2_subdev_init(&entity->subdev, &uvc_subdev_ops); strscpy(entity->subdev.name, entity->name, sizeof(entity->subdev.name)); + switch (UVC_ENTITY_TYPE(entity)) { + case UVC_VC_SELECTOR_UNIT: + function = MEDIA_ENT_F_VID_MUX; + break; + case UVC_VC_PROCESSING_UNIT: + case UVC_VC_EXTENSION_UNIT: + /* For lack of a better option. */ + function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + break; + case UVC_COMPOSITE_CONNECTOR: + case UVC_COMPONENT_CONNECTOR: + function = MEDIA_ENT_F_CONN_COMPOSITE; + break; + case UVC_SVIDEO_CONNECTOR: + function = MEDIA_ENT_F_CONN_SVIDEO; + break; + case UVC_ITT_CAMERA: + function = MEDIA_ENT_F_CAM_SENSOR; + break; + case UVC_TT_VENDOR_SPECIFIC: + case UVC_ITT_VENDOR_SPECIFIC: + case UVC_ITT_MEDIA_TRANSPORT_INPUT: + case UVC_OTT_VENDOR_SPECIFIC: + case UVC_OTT_DISPLAY: + case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: + case UVC_EXTERNAL_VENDOR_SPECIFIC: + default: + function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN; + break; + } + + entity->subdev.entity.function = function; + ret = media_entity_pads_init(&entity->subdev.entity, entity->num_pads, entity->pads); diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 0335e69b70ab..fa06bfa174ad 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -247,12 +247,44 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream, if (ret < 0) goto done; + /* After the probe, update fmt with the values returned from + * negotiation with the device. + */ + for (i = 0; i < stream->nformats; ++i) { + if (probe->bFormatIndex == stream->format[i].index) { + format = &stream->format[i]; + break; + } + } + + if (i == stream->nformats) { + uvc_trace(UVC_TRACE_FORMAT, "Unknown bFormatIndex %u\n", + probe->bFormatIndex); + return -EINVAL; + } + + for (i = 0; i < format->nframes; ++i) { + if (probe->bFrameIndex == format->frame[i].bFrameIndex) { + frame = &format->frame[i]; + break; + } + } + + if (i == format->nframes) { + uvc_trace(UVC_TRACE_FORMAT, "Unknown bFrameIndex %u\n", + probe->bFrameIndex); + return -EINVAL; + } + fmt->fmt.pix.width = frame->wWidth; fmt->fmt.pix.height = frame->wHeight; fmt->fmt.pix.field = V4L2_FIELD_NONE; fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame); fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize; + fmt->fmt.pix.pixelformat = format->fcc; fmt->fmt.pix.colorspace = format->colorspace; + fmt->fmt.pix.xfer_func = format->xfer_func; + fmt->fmt.pix.ycbcr_enc = format->ycbcr_enc; if (uvc_format != NULL) *uvc_format = format; @@ -289,6 +321,8 @@ static int uvc_v4l2_get_format(struct uvc_streaming *stream, fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame); fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize; fmt->fmt.pix.colorspace = format->colorspace; + fmt->fmt.pix.xfer_func = format->xfer_func; + fmt->fmt.pix.ycbcr_enc = format->ycbcr_enc; done: mutex_unlock(&stream->mutex); diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index a65d5353a441..a6a441d92b94 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -622,7 +622,7 @@ static u16 uvc_video_clock_host_sof(const struct uvc_clock_sample *sample) * to avoid losing precision in the division. Similarly, the host timestamp is * computed with * - * TS = ((TS2 - TS1) * PTS + TS1 * SOF2 - TS2 * SOF1) / (SOF2 - SOF1) (2) + * TS = ((TS2 - TS1) * SOF + TS1 * SOF2 - TS2 * SOF1) / (SOF2 - SOF1) (2) * * SOF values are coded on 11 bits by USB. We extend their precision with 16 * decimal bits, leading to a 11.16 coding. @@ -1509,11 +1509,11 @@ static void uvc_video_complete(struct urb *urb) default: uvc_printk(KERN_WARNING, "Non-zero status (%d) in video " "completion handler.\n", urb->status); - /* fall through */ + fallthrough; case -ENOENT: /* usb_poison_urb() called. */ if (stream->frozen) return; - /* fall through */ + fallthrough; case -ECONNRESET: /* usb_unlink_urb() called. */ case -ESHUTDOWN: /* The endpoint is being disabled. */ uvc_queue_cancel(queue, urb->status == -ESHUTDOWN); diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 6ab972c643e3..a3dfacf069c4 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -165,6 +165,10 @@ {0x32, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_HEVC \ + { 'H', 'E', 'V', 'C', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} + /* ------------------------------------------------------------------------ * Driver specific constants. @@ -370,7 +374,9 @@ struct uvc_format { u8 type; u8 index; u8 bpp; - u8 colorspace; + enum v4l2_colorspace colorspace; + enum v4l2_xfer_func xfer_func; + enum v4l2_ycbcr_encoding ycbcr_enc; u32 fcc; u32 flags; diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 593bcf6c3735..a99e82ec9ab6 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -246,9 +246,6 @@ struct v4l2_format32 { * @memory: buffer memory type * @format: frame format, for which buffers are requested * @capabilities: capabilities of this buffer type. - * @flags: additional buffer management attributes (ignored unless the - * queue has V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS capability and - * configured for MMAP streaming I/O). * @reserved: future extensions */ struct v4l2_create_buffers32 { @@ -257,8 +254,7 @@ struct v4l2_create_buffers32 { __u32 memory; /* enum v4l2_memory */ struct v4l2_format32 format; __u32 capabilities; - __u32 flags; - __u32 reserved[6]; + __u32 reserved[7]; }; static int __bufsize_v4l2_format(struct v4l2_format32 __user *p32, u32 *size) @@ -359,8 +355,7 @@ static int get_v4l2_create32(struct v4l2_create_buffers __user *p64, { if (!access_ok(p32, sizeof(*p32)) || copy_in_user(p64, p32, - offsetof(struct v4l2_create_buffers32, format)) || - assign_in_user(&p64->flags, &p32->flags)) + offsetof(struct v4l2_create_buffers32, format))) return -EFAULT; return __get_v4l2_format32(&p64->format, &p32->format, aux_buf, aux_space); @@ -422,7 +417,6 @@ static int put_v4l2_create32(struct v4l2_create_buffers __user *p64, copy_in_user(p32, p64, offsetof(struct v4l2_create_buffers32, format)) || assign_in_user(&p32->capabilities, &p64->capabilities) || - assign_in_user(&p32->flags, &p64->flags) || copy_in_user(p32->reserved, p64->reserved, sizeof(p64->reserved))) return -EFAULT; return __put_v4l2_format32(&p64->format, &p32->format); diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 45a2403aa039..bd7f330c941c 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -200,6 +200,7 @@ const char * const *v4l2_ctrl_get_menu(u32 id) static const char * const mpeg_video_bitrate_mode[] = { "Variable Bitrate", "Constant Bitrate", + "Constant Quality", NULL }; static const char * const mpeg_stream_type[] = { @@ -474,6 +475,23 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "3", NULL, }; + static const char * const vp9_level[] = { + "1", + "1.1", + "2", + "2.1", + "3", + "3.1", + "4", + "4.1", + "5", + "5.1", + "5.2", + "6", + "6.1", + "6.2", + NULL, + }; static const char * const flash_led_mode[] = { "Off", @@ -590,6 +608,12 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "External", NULL, }; + static const char * const mpeg_video_frame_skip[] = { + "Disabled", + "Level Limit", + "VBV/CPB Limit", + NULL, + }; switch (id) { case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: @@ -651,6 +675,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return flash_strobe_source; case V4L2_CID_MPEG_VIDEO_HEADER_MODE: return header_mode; + case V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE: + return mpeg_video_frame_skip; case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: return multi_slice; case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: @@ -685,6 +711,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return vp8_profile; case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: return vp9_profile; + case V4L2_CID_MPEG_VIDEO_VP9_LEVEL: + return vp9_level; case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return jpeg_chroma_subsampling; case V4L2_CID_DV_TX_MODE: @@ -832,6 +860,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure"; case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown"; case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode"; + case V4L2_CID_MPEG_VIDEO_CONSTANT_QUALITY: return "Constant Quality"; case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate"; case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate"; case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation"; @@ -844,6 +873,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: return "H264 MB Level Rate Control"; case V4L2_CID_MPEG_VIDEO_HEADER_MODE: return "Sequence Header Mode"; case V4L2_CID_MPEG_VIDEO_MAX_REF_PIC: return "Max Number of Reference Pics"; + case V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE: return "Frame Skip Mode"; case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: return "H263 I-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: return "H263 P-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: return "H263 B-Frame QP Value"; @@ -897,6 +927,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS: return "H264 Decode Parameters"; case V4L2_CID_MPEG_VIDEO_H264_DECODE_MODE: return "H264 Decode Mode"; case V4L2_CID_MPEG_VIDEO_H264_START_CODE: return "H264 Start Code"; + case V4L2_CID_MPEG_VIDEO_H264_PRED_WEIGHTS: return "H264 Prediction Weight Table"; case V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL: return "MPEG2 Level"; case V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE: return "MPEG2 Profile"; case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: return "MPEG4 I-Frame QP Value"; @@ -938,6 +969,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_VPX_P_FRAME_QP: return "VPX P-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: return "VP8 Profile"; case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: return "VP9 Profile"; + case V4L2_CID_MPEG_VIDEO_VP9_LEVEL: return "VP9 Level"; case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HEADER: return "VP8 Frame Header"; /* HEVC controls */ @@ -1265,6 +1297,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_FLASH_LED_MODE: case V4L2_CID_FLASH_STROBE_SOURCE: case V4L2_CID_MPEG_VIDEO_HEADER_MODE: + case V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE: case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: case V4L2_CID_MPEG_VIDEO_H264_LEVEL: @@ -1294,6 +1327,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: case V4L2_CID_MPEG_VIDEO_VP9_PROFILE: + case V4L2_CID_MPEG_VIDEO_VP9_LEVEL: case V4L2_CID_DETECT_MD_MODE: case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: @@ -1412,6 +1446,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS: *type = V4L2_CTRL_TYPE_H264_DECODE_PARAMS; break; + case V4L2_CID_MPEG_VIDEO_H264_PRED_WEIGHTS: + *type = V4L2_CTRL_TYPE_H264_PRED_WEIGHTS; + break; case V4L2_CID_MPEG_VIDEO_VP8_FRAME_HEADER: *type = V4L2_CTRL_TYPE_VP8_FRAME_HEADER; break; @@ -1721,6 +1758,8 @@ static void std_log(const struct v4l2_ctrl *ctrl) #define zero_padding(s) \ memset(&(s).padding, 0, sizeof((s).padding)) +#define zero_reserved(s) \ + memset(&(s).reserved, 0, sizeof((s).reserved)) /* * Compound controls validation requires setting unused fields/flags to zero @@ -1731,6 +1770,8 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, { struct v4l2_ctrl_mpeg2_slice_params *p_mpeg2_slice_params; struct v4l2_ctrl_vp8_frame_header *p_vp8_frame_header; + struct v4l2_ctrl_h264_slice_params *p_h264_slice_params; + struct v4l2_ctrl_h264_decode_params *p_h264_dec_params; struct v4l2_ctrl_hevc_sps *p_hevc_sps; struct v4l2_ctrl_hevc_pps *p_hevc_pps; struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params; @@ -1790,8 +1831,25 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, case V4L2_CTRL_TYPE_H264_SPS: case V4L2_CTRL_TYPE_H264_PPS: case V4L2_CTRL_TYPE_H264_SCALING_MATRIX: + case V4L2_CTRL_TYPE_H264_PRED_WEIGHTS: + break; + case V4L2_CTRL_TYPE_H264_SLICE_PARAMS: + p_h264_slice_params = p; + + zero_reserved(*p_h264_slice_params); + break; + case V4L2_CTRL_TYPE_H264_DECODE_PARAMS: + p_h264_dec_params = p; + + for (i = 0; i < V4L2_H264_NUM_DPB_ENTRIES; i++) { + struct v4l2_h264_dpb_entry *dpb_entry = + &p_h264_dec_params->dpb[i]; + + zero_reserved(*dpb_entry); + } + zero_reserved(*p_h264_dec_params); break; case V4L2_CTRL_TYPE_VP8_FRAME_HEADER: @@ -2553,6 +2611,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, case V4L2_CTRL_TYPE_H264_DECODE_PARAMS: elem_size = sizeof(struct v4l2_ctrl_h264_decode_params); break; + case V4L2_CTRL_TYPE_H264_PRED_WEIGHTS: + elem_size = sizeof(struct v4l2_ctrl_h264_pred_weights); + break; case V4L2_CTRL_TYPE_VP8_FRAME_HEADER: elem_size = sizeof(struct v4l2_ctrl_vp8_frame_header); break; diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index a4c3c77c1894..d7bbe33840cb 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -547,8 +547,8 @@ int v4l2_fwnode_endpoint_alloc_parse(struct fwnode_handle *fwnode, } for (i = 0; i < vep->nr_of_link_frequencies; i++) - pr_info("link-frequencies %u value %llu\n", i, - vep->link_frequencies[i]); + pr_debug("link-frequencies %u value %llu\n", i, + vep->link_frequencies[i]); } pr_debug("===== end parsing endpoint %pfw\n", fwnode); diff --git a/drivers/media/v4l2-core/v4l2-h264.c b/drivers/media/v4l2-core/v4l2-h264.c index edf6225f0522..5633a242520a 100644 --- a/drivers/media/v4l2-core/v4l2-h264.c +++ b/drivers/media/v4l2-core/v4l2-h264.c @@ -18,14 +18,12 @@ * * @b: the builder context to initialize * @dec_params: decode parameters control - * @slice_params: first slice parameters control * @sps: SPS control * @dpb: DPB to use when creating the reference list */ void v4l2_h264_init_reflist_builder(struct v4l2_h264_reflist_builder *b, const struct v4l2_ctrl_h264_decode_params *dec_params, - const struct v4l2_ctrl_h264_slice_params *slice_params, const struct v4l2_ctrl_h264_sps *sps, const struct v4l2_h264_dpb_entry dpb[V4L2_H264_NUM_DPB_ENTRIES]) { @@ -33,13 +31,13 @@ v4l2_h264_init_reflist_builder(struct v4l2_h264_reflist_builder *b, unsigned int i; max_frame_num = 1 << (sps->log2_max_frame_num_minus4 + 4); - cur_frame_num = slice_params->frame_num; + cur_frame_num = dec_params->frame_num; memset(b, 0, sizeof(*b)); - if (!(slice_params->flags & V4L2_H264_SLICE_FLAG_FIELD_PIC)) + if (!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)) b->cur_pic_order_count = min(dec_params->bottom_field_order_cnt, dec_params->top_field_order_cnt); - else if (slice_params->flags & V4L2_H264_SLICE_FLAG_BOTTOM_FIELD) + else if (dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD) b->cur_pic_order_count = dec_params->bottom_field_order_cnt; else b->cur_pic_order_count = dec_params->top_field_order_cnt; @@ -66,10 +64,10 @@ v4l2_h264_init_reflist_builder(struct v4l2_h264_reflist_builder *b, else b->refs[i].frame_num = dpb[i].frame_num; - if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD)) + if (dpb[i].fields == V4L2_H264_FRAME_REF) pic_order_count = min(dpb[i].top_field_order_cnt, dpb[i].bottom_field_order_cnt); - else if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_BOTTOM_FIELD) + else if (dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF) pic_order_count = dpb[i].bottom_field_order_cnt; else pic_order_count = dpb[i].top_field_order_cnt; diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index f74b42280892..eeff398fbdcc 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2042,6 +2042,9 @@ static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops, if (ret) return ret; + + CLEAR_AFTER_FIELD(p, capabilities); + return ops->vidioc_reqbufs(file, fh, p); } @@ -2081,7 +2084,7 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops, if (ret) return ret; - CLEAR_AFTER_FIELD(create, flags); + CLEAR_AFTER_FIELD(create, capabilities); v4l_sanitize_format(&create->format); diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 95a8f2dc5341..b221b4e438a1 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -43,6 +43,10 @@ module_param(debug, bool, 0644); #define TRANS_ABORT (1 << 2) +/* The job queue is not running new jobs */ +#define QUEUE_PAUSED (1 << 0) + + /* Offset base for buffers on the destination queue - used to distinguish * between source and destination buffers when mmapping - they receive the same * offsets but for different queues */ @@ -84,6 +88,7 @@ static const char * const m2m_entity_name[] = { * @job_queue: instances queued to run * @job_spinlock: protects job_queue * @job_work: worker to run queued jobs. + * @job_queue_flags: flags of the queue status, %QUEUE_PAUSED. * @m2m_ops: driver callbacks */ struct v4l2_m2m_dev { @@ -101,6 +106,7 @@ struct v4l2_m2m_dev { struct list_head job_queue; spinlock_t job_spinlock; struct work_struct job_work; + unsigned long job_queue_flags; const struct v4l2_m2m_ops *m2m_ops; }; @@ -263,6 +269,12 @@ static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev) return; } + if (m2m_dev->job_queue_flags & QUEUE_PAUSED) { + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + dprintk("Running new jobs is paused\n"); + return; + } + m2m_dev->curr_ctx = list_first_entry(&m2m_dev->job_queue, struct v4l2_m2m_ctx, queue); m2m_dev->curr_ctx->job_flags |= TRANS_RUNNING; @@ -528,6 +540,34 @@ unlock: } EXPORT_SYMBOL(v4l2_m2m_buf_done_and_job_finish); +void v4l2_m2m_suspend(struct v4l2_m2m_dev *m2m_dev) +{ + unsigned long flags; + struct v4l2_m2m_ctx *curr_ctx; + + spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + m2m_dev->job_queue_flags |= QUEUE_PAUSED; + curr_ctx = m2m_dev->curr_ctx; + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + + if (curr_ctx) + wait_event(curr_ctx->finished, + !(curr_ctx->job_flags & TRANS_RUNNING)); +} +EXPORT_SYMBOL(v4l2_m2m_suspend); + +void v4l2_m2m_resume(struct v4l2_m2m_dev *m2m_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&m2m_dev->job_spinlock, flags); + m2m_dev->job_queue_flags &= ~QUEUE_PAUSED; + spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); + + v4l2_m2m_try_run(m2m_dev); +} +EXPORT_SYMBOL(v4l2_m2m_resume); + int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_requestbuffers *reqbufs) { @@ -841,7 +881,6 @@ static __poll_t v4l2_m2m_poll_for_data(struct file *file, struct poll_table_struct *wait) { struct vb2_queue *src_q, *dst_q; - struct vb2_buffer *src_vb = NULL, *dst_vb = NULL; __poll_t rc = 0; unsigned long flags; @@ -862,34 +901,17 @@ static __poll_t v4l2_m2m_poll_for_data(struct file *file, list_empty(&dst_q->queued_list))) return EPOLLERR; - spin_lock_irqsave(&dst_q->done_lock, flags); - if (list_empty(&dst_q->done_list)) { - /* - * If the last buffer was dequeued from the capture queue, - * return immediately. DQBUF will return -EPIPE. - */ - if (dst_q->last_buffer_dequeued) { - spin_unlock_irqrestore(&dst_q->done_lock, flags); - return EPOLLIN | EPOLLRDNORM; - } - } - spin_unlock_irqrestore(&dst_q->done_lock, flags); - spin_lock_irqsave(&src_q->done_lock, flags); if (!list_empty(&src_q->done_list)) - src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer, - done_entry); - if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE - || src_vb->state == VB2_BUF_STATE_ERROR)) rc |= EPOLLOUT | EPOLLWRNORM; spin_unlock_irqrestore(&src_q->done_lock, flags); spin_lock_irqsave(&dst_q->done_lock, flags); - if (!list_empty(&dst_q->done_list)) - dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer, - done_entry); - if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE - || dst_vb->state == VB2_BUF_STATE_ERROR)) + /* + * If the last buffer was dequeued from the capture queue, signal + * userspace. DQBUF(CAPTURE) will return -EPIPE. + */ + if (!list_empty(&dst_q->done_list) || dst_q->last_buffer_dequeued) rc |= EPOLLIN | EPOLLRDNORM; spin_unlock_irqrestore(&dst_q->done_lock, flags); diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 6b989fe5a0a9..a7d508e74d6b 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -309,6 +309,20 @@ static int call_enum_dv_timings(struct v4l2_subdev *sd, sd->ops->pad->enum_dv_timings(sd, dvt); } +static int call_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_config *config) +{ + return check_pad(sd, pad) ? : + sd->ops->pad->get_mbus_config(sd, pad, config); +} + +static int call_set_mbus_config(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_config *config) +{ + return check_pad(sd, pad) ? : + sd->ops->pad->get_mbus_config(sd, pad, config); +} + static const struct v4l2_subdev_pad_ops v4l2_subdev_call_pad_wrappers = { .get_fmt = call_get_fmt, .set_fmt = call_set_fmt, @@ -321,6 +335,8 @@ static const struct v4l2_subdev_pad_ops v4l2_subdev_call_pad_wrappers = { .set_edid = call_set_edid, .dv_timings_cap = call_dv_timings_cap, .enum_dv_timings = call_enum_dv_timings, + .get_mbus_config = call_get_mbus_config, + .set_mbus_config = call_set_mbus_config, }; static const struct v4l2_subdev_video_ops v4l2_subdev_call_video_wrappers = { diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c index 46ff19df9f53..8dd0562de287 100644 --- a/drivers/media/v4l2-core/videobuf-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf-dma-sg.c @@ -180,7 +180,7 @@ static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma, if (rw == READ) flags |= FOLL_WRITE; - dprintk(1, "init user [0x%lx+0x%lx => %d pages]\n", + dprintk(1, "init user [0x%lx+0x%lx => %lu pages]\n", data, size, dma->nr_pages); err = pin_user_pages(data & PAGE_MASK, dma->nr_pages, @@ -188,7 +188,7 @@ static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma, if (err != dma->nr_pages) { dma->nr_pages = (err >= 0) ? err : 0; - dprintk(1, "pin_user_pages: err=%d [%d]\n", err, + dprintk(1, "pin_user_pages: err=%d [%lu]\n", err, dma->nr_pages); return err < 0 ? err : -EINVAL; } @@ -208,11 +208,11 @@ static int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, } static int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, - int nr_pages) + unsigned long nr_pages) { int i; - dprintk(1, "init kernel [%d pages]\n", nr_pages); + dprintk(1, "init kernel [%lu pages]\n", nr_pages); dma->direction = direction; dma->vaddr_pages = kcalloc(nr_pages, sizeof(*dma->vaddr_pages), @@ -238,11 +238,11 @@ static int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, dma->vaddr = vmap(dma->vaddr_pages, nr_pages, VM_MAP | VM_IOREMAP, PAGE_KERNEL); if (NULL == dma->vaddr) { - dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); + dprintk(1, "vmalloc_32(%lu pages) failed\n", nr_pages); goto out_free_pages; } - dprintk(1, "vmalloc is at addr %p, size=%d\n", + dprintk(1, "vmalloc is at addr %p, size=%lu\n", dma->vaddr, nr_pages << PAGE_SHIFT); memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT); @@ -267,9 +267,9 @@ out_free_pages: } static int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction, - dma_addr_t addr, int nr_pages) + dma_addr_t addr, unsigned long nr_pages) { - dprintk(1, "init overlay [%d pages @ bus 0x%lx]\n", + dprintk(1, "init overlay [%lu pages @ bus 0x%lx]\n", nr_pages, (unsigned long)addr); dma->direction = direction; @@ -500,9 +500,11 @@ static int __videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb, struct v4l2_framebuffer *fbuf) { - int err, pages; - dma_addr_t bus; struct videobuf_dma_sg_memory *mem = vb->priv; + unsigned long pages; + dma_addr_t bus; + int err; + BUG_ON(!mem); MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); |