diff options
Diffstat (limited to 'drivers/media')
332 files changed, 15534 insertions, 18777 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 37124c3b8c2a..8add62a18293 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -78,13 +78,13 @@ config MEDIA_SDR_SUPPORT Say Y when you have a software defined radio device. config MEDIA_CEC_SUPPORT - bool "HDMI CEC support" - ---help--- - Enable support for HDMI CEC (Consumer Electronics Control), - which is an optional HDMI feature. + bool "HDMI CEC support" + ---help--- + Enable support for HDMI CEC (Consumer Electronics Control), + which is an optional HDMI feature. - Say Y when you have an HDMI receiver, transmitter or a USB CEC - adapter that supports HDMI CEC. + Say Y when you have an HDMI receiver, transmitter or a USB CEC + adapter that supports HDMI CEC. source "drivers/media/cec/Kconfig" diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index 002ed4c90371..b7fad0ec5710 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -339,12 +339,19 @@ static void cec_data_cancel(struct cec_data *data) data->adap->transmit_queue_sz--; } - /* Mark it as an error */ - data->msg.tx_ts = ktime_get_ns(); - data->msg.tx_status |= CEC_TX_STATUS_ERROR | - CEC_TX_STATUS_MAX_RETRIES; - data->msg.tx_error_cnt++; - data->attempts = 0; + if (data->msg.tx_status & CEC_TX_STATUS_OK) { + /* Mark the canceled RX as a timeout */ + data->msg.rx_ts = ktime_get_ns(); + data->msg.rx_status = CEC_RX_STATUS_TIMEOUT; + } else { + /* Mark the canceled TX as an error */ + data->msg.tx_ts = ktime_get_ns(); + data->msg.tx_status |= CEC_TX_STATUS_ERROR | + CEC_TX_STATUS_MAX_RETRIES; + data->msg.tx_error_cnt++; + data->attempts = 0; + } + /* Queue transmitted message for monitoring purposes */ cec_queue_msg_monitor(data->adap, &data->msg, 1); diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c index b0c87f9ea08f..b278ab90b387 100644 --- a/drivers/media/cec/cec-core.c +++ b/drivers/media/cec/cec-core.c @@ -322,7 +322,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(100); + adap->rc->timeout = MS_TO_NS(550); #endif return adap; } diff --git a/drivers/media/cec/cec-pin-error-inj.c b/drivers/media/cec/cec-pin-error-inj.c index aaa899a175ce..c0088d3b8e3d 100644 --- a/drivers/media/cec/cec-pin-error-inj.c +++ b/drivers/media/cec/cec-pin-error-inj.c @@ -81,10 +81,9 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line) u64 *error; u8 *args; bool has_op; - u32 op; + u8 op; u8 mode; u8 pos; - u8 v; p = skip_spaces(p); token = strsep(&p, delims); @@ -146,12 +145,18 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line) comma = strchr(token, ','); if (comma) *comma++ = '\0'; - if (!strcmp(token, "any")) - op = CEC_ERROR_INJ_OP_ANY; - else if (!kstrtou8(token, 0, &v)) - op = v; - else + if (!strcmp(token, "any")) { + has_op = false; + error = pin->error_inj + CEC_ERROR_INJ_OP_ANY; + args = pin->error_inj_args[CEC_ERROR_INJ_OP_ANY]; + } else if (!kstrtou8(token, 0, &op)) { + has_op = true; + error = pin->error_inj + op; + args = pin->error_inj_args[op]; + } else { return false; + } + mode = CEC_ERROR_INJ_MODE_ONCE; if (comma) { if (!strcmp(comma, "off")) @@ -166,10 +171,6 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line) return false; } - error = pin->error_inj + op; - args = pin->error_inj_args[op]; - has_op = op <= 0xff; - token = strsep(&p, delims); if (p) { p = skip_spaces(p); @@ -203,16 +204,18 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line) mode_mask = CEC_ERROR_INJ_MODE_MASK << mode_offset; arg_idx = cec_error_inj_cmds[i].arg_idx; - if (mode_offset == CEC_ERROR_INJ_RX_ARB_LOST_OFFSET || - mode_offset == CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET) - is_bit_pos = false; - if (mode_offset == CEC_ERROR_INJ_RX_ARB_LOST_OFFSET) { if (has_op) return false; if (!has_pos) pos = 0x0f; + is_bit_pos = false; + } else if (mode_offset == CEC_ERROR_INJ_TX_ADD_BYTES_OFFSET) { + if (!has_pos || !pos) + return false; + is_bit_pos = false; } + if (arg_idx >= 0 && is_bit_pos) { if (!has_pos || pos >= 160) return false; diff --git a/drivers/media/cec/cec-pin.c b/drivers/media/cec/cec-pin.c index 2a5df99735fa..6e311424f0dc 100644 --- a/drivers/media/cec/cec-pin.c +++ b/drivers/media/cec/cec-pin.c @@ -119,7 +119,7 @@ static void cec_pin_update(struct cec_pin *pin, bool v, bool force) if (pin->work_pin_events_dropped) { pin->work_pin_events_dropped = false; - v |= CEC_PIN_EVENT_FL_DROPPED; + ev |= CEC_PIN_EVENT_FL_DROPPED; } pin->work_pin_events[pin->work_pin_events_wr] = ev; pin->work_pin_ts[pin->work_pin_events_wr] = ktime_get(); diff --git a/drivers/media/common/b2c2/flexcop-fe-tuner.c b/drivers/media/common/b2c2/flexcop-fe-tuner.c index a1ce3e8eb1d3..aac1aadb0cb1 100644 --- a/drivers/media/common/b2c2/flexcop-fe-tuner.c +++ b/drivers/media/common/b2c2/flexcop-fe-tuner.c @@ -495,7 +495,6 @@ static int airstar_atsc2_attach(struct flexcop_device *fc, /* AirStar ATSC 3rd generation */ #if FE_SUPPORTED(LGDT330X) static struct lgdt330x_config air2pc_atsc_hd5000_config = { - .demod_address = 0x59, .demod_chip = LGDT3303, .serial_mpeg = 0x04, .clock_polarity_flip = 1, @@ -504,7 +503,8 @@ static struct lgdt330x_config air2pc_atsc_hd5000_config = { static int airstar_atsc3_attach(struct flexcop_device *fc, struct i2c_adapter *i2c) { - fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, i2c); + fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, + 0x59, i2c); if (!fc->fe) return 0; diff --git a/drivers/media/common/b2c2/flexcop-i2c.c b/drivers/media/common/b2c2/flexcop-i2c.c index 564da6fa900d..6675b605eb6f 100644 --- a/drivers/media/common/b2c2/flexcop-i2c.c +++ b/drivers/media/common/b2c2/flexcop-i2c.c @@ -105,40 +105,36 @@ static int flexcop_i2c_write4(struct flexcop_device *fc, } int flexcop_i2c_request(struct flexcop_i2c_adapter *i2c, - flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len) + flexcop_access_op_t op, u8 chipaddr, + u8 start_addr, u8 *buf, u16 size) { int ret; - -#ifdef DUMP_I2C_MESSAGES - int i; -#endif + int len = size; + u8 *p; + u8 addr = start_addr; u16 bytes_to_transfer; flexcop_ibi_value r100; - deb_i2c("op = %d\n",op); + deb_i2c("port %d %s(%02x): register %02x, size: %d\n", + i2c->port, + op == FC_READ ? "rd" : "wr", + chipaddr, start_addr, size); r100.raw = 0; r100.tw_sm_c_100.chipaddr = chipaddr; r100.tw_sm_c_100.twoWS_rw = op; r100.tw_sm_c_100.twoWS_port_reg = i2c->port; -#ifdef DUMP_I2C_MESSAGES - printk(KERN_DEBUG "%d ", i2c->port); - if (op == FC_READ) - printk(KERN_CONT "rd("); - else - printk(KERN_CONT "wr("); - printk(KERN_CONT "%02x): %02x ", chipaddr, addr); -#endif - /* in that case addr is the only value -> * we write it twice as baseaddr and val0 * BBTI is doing it like that for ISL6421 at least */ if (i2c->no_base_addr && len == 0 && op == FC_WRITE) { - buf = &addr; + buf = &start_addr; len = 1; } + p = buf; + while (len != 0) { bytes_to_transfer = len > 4 ? 4 : len; @@ -146,26 +142,21 @@ int flexcop_i2c_request(struct flexcop_i2c_adapter *i2c, r100.tw_sm_c_100.baseaddr = addr; if (op == FC_READ) - ret = flexcop_i2c_read4(i2c, r100, buf); + ret = flexcop_i2c_read4(i2c, r100, p); else - ret = flexcop_i2c_write4(i2c->fc, r100, buf); - -#ifdef DUMP_I2C_MESSAGES - for (i = 0; i < bytes_to_transfer; i++) - printk(KERN_CONT "%02x ", buf[i]); -#endif + ret = flexcop_i2c_write4(i2c->fc, r100, p); if (ret < 0) return ret; - buf += bytes_to_transfer; + p += bytes_to_transfer; addr += bytes_to_transfer; len -= bytes_to_transfer; } - -#ifdef DUMP_I2C_MESSAGES - printk(KERN_CONT "\n"); -#endif + deb_i2c_dump("port %d %s(%02x): register %02x: %*ph\n", + i2c->port, + op == FC_READ ? "rd" : "wr", + chipaddr, start_addr, size, buf); return 0; } diff --git a/drivers/media/common/b2c2/flexcop.c b/drivers/media/common/b2c2/flexcop.c index 2e0ab55cd67e..cbaa61f10d5f 100644 --- a/drivers/media/common/b2c2/flexcop.c +++ b/drivers/media/common/b2c2/flexcop.c @@ -42,7 +42,7 @@ int b2c2_flexcop_debug; EXPORT_SYMBOL_GPL(b2c2_flexcop_debug); module_param_named(debug, b2c2_flexcop_debug, int, 0644); MODULE_PARM_DESC(debug, - "set debug level (1=info,2=tuner,4=i2c,8=ts,16=sram,32=reg (|-able))." + "set debug level (1=info,2=tuner,4=i2c,8=ts,16=sram,32=reg,64=i2cdump (|-able))." DEBSTATUS); #undef DEBSTATUS diff --git a/drivers/media/common/b2c2/flexcop.h b/drivers/media/common/b2c2/flexcop.h index 911ece59ea02..486fe2380b92 100644 --- a/drivers/media/common/b2c2/flexcop.h +++ b/drivers/media/common/b2c2/flexcop.h @@ -26,5 +26,6 @@ extern int b2c2_flexcop_debug; #define deb_ts(args...) dprintk(0x08, args) #define deb_sram(args...) dprintk(0x10, args) #define deb_rdump(args...) dprintk(0x20, args) +#define deb_i2c_dump(args...) dprintk(0x40, args) #endif diff --git a/drivers/media/common/saa7146/saa7146_i2c.c b/drivers/media/common/saa7146/saa7146_i2c.c index f9e099d812c8..3feddc52c446 100644 --- a/drivers/media/common/saa7146/saa7146_i2c.c +++ b/drivers/media/common/saa7146/saa7146_i2c.c @@ -308,7 +308,7 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m /* prepare the message(s), get number of u32s to transfer */ count = saa7146_i2c_msg_prepare(msgs, num, buffer); if ( 0 > count ) { - err = -1; + err = -EIO; goto out; } @@ -360,7 +360,7 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m /* if any things had to be read, get the results */ if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) { DEB_I2C("could not cleanup i2c-message\n"); - err = -1; + err = -EIO; goto out; } diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c index b5dcc6d1fe90..3b02cb570a6e 100644 --- a/drivers/media/common/siano/smscoreapi.c +++ b/drivers/media/common/siano/smscoreapi.c @@ -415,8 +415,8 @@ EXPORT_SYMBOL_GPL(smscore_get_board_id); struct smscore_registry_entry_t { struct list_head entry; - char devpath[32]; - int mode; + char devpath[32]; + int mode; enum sms_device_type_st type; }; @@ -442,7 +442,7 @@ static struct smscore_registry_entry_t *smscore_find_registry(char *devpath) next != &g_smscore_registry; next = next->next) { entry = (struct smscore_registry_entry_t *) next; - if (!strcmp(entry->devpath, devpath)) { + if (!strncmp(entry->devpath, devpath, sizeof(entry->devpath))) { kmutex_unlock(&g_smscore_registrylock); return entry; } @@ -450,7 +450,7 @@ static struct smscore_registry_entry_t *smscore_find_registry(char *devpath) entry = kmalloc(sizeof(*entry), GFP_KERNEL); if (entry) { entry->mode = default_mode; - strcpy(entry->devpath, devpath); + strlcpy(entry->devpath, devpath, sizeof(entry->devpath)); list_add(&entry->entry, &g_smscore_registry); } else pr_err("failed to create smscore_registry.\n"); @@ -649,6 +649,7 @@ smscore_buffer_t *smscore_createbuffer(u8 *buffer, void *common_buffer, */ int smscore_register_device(struct smsdevice_params_t *params, struct smscore_device_t **coredev, + gfp_t gfp_buf_flags, void *mdev) { struct smscore_device_t *dev; @@ -661,6 +662,7 @@ int smscore_register_device(struct smsdevice_params_t *params, #ifdef CONFIG_MEDIA_CONTROLLER_DVB dev->media_dev = mdev; #endif + dev->gfp_buf_flags = gfp_buf_flags; /* init list entry so it could be safe in smscore_unregister_device */ INIT_LIST_HEAD(&dev->entry); @@ -697,7 +699,7 @@ int smscore_register_device(struct smsdevice_params_t *params, buffer = dma_alloc_coherent(params->device, dev->common_buffer_size, &dev->common_buffer_phys, - GFP_KERNEL | GFP_DMA); + GFP_KERNEL | dev->gfp_buf_flags); if (!buffer) { smscore_unregister_device(dev); return -ENOMEM; @@ -733,7 +735,7 @@ int smscore_register_device(struct smsdevice_params_t *params, dev->postload_handler = params->postload_handler; dev->device_flags = params->flags; - strcpy(dev->devpath, params->devpath); + strlcpy(dev->devpath, params->devpath, sizeof(dev->devpath)); smscore_registry_settype(dev->devpath, params->device_type); @@ -792,7 +794,7 @@ static int smscore_init_ir(struct smscore_device_t *coredev) else { buffer = kmalloc(sizeof(struct sms_msg_data2) + SMS_DMA_ALIGNMENT, - GFP_KERNEL | GFP_DMA); + GFP_KERNEL | coredev->gfp_buf_flags); if (buffer) { struct sms_msg_data2 *msg = (struct sms_msg_data2 *) @@ -933,7 +935,7 @@ static int smscore_load_firmware_family2(struct smscore_device_t *coredev, } /* PAGE_SIZE buffer shall be enough and dma aligned */ - msg = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA); + msg = kmalloc(PAGE_SIZE, GFP_KERNEL | coredev->gfp_buf_flags); if (!msg) return -ENOMEM; @@ -1168,7 +1170,7 @@ static int smscore_load_firmware_from_file(struct smscore_device_t *coredev, } pr_debug("read fw %s, buffer size=0x%zx\n", fw_filename, fw->size); fw_buf = kmalloc(ALIGN(fw->size + sizeof(struct sms_firmware), - SMS_ALLOC_ALIGNMENT), GFP_KERNEL | GFP_DMA); + SMS_ALLOC_ALIGNMENT), GFP_KERNEL | coredev->gfp_buf_flags); if (!fw_buf) { pr_err("failed to allocate firmware buffer\n"); rc = -ENOMEM; @@ -1260,7 +1262,7 @@ EXPORT_SYMBOL_GPL(smscore_unregister_device); static int smscore_detect_mode(struct smscore_device_t *coredev) { void *buffer = kmalloc(sizeof(struct sms_msg_hdr) + SMS_DMA_ALIGNMENT, - GFP_KERNEL | GFP_DMA); + GFP_KERNEL | coredev->gfp_buf_flags); struct sms_msg_hdr *msg = (struct sms_msg_hdr *) SMS_ALIGN_ADDRESS(buffer); int rc; @@ -1309,7 +1311,7 @@ static int smscore_init_device(struct smscore_device_t *coredev, int mode) int rc = 0; buffer = kmalloc(sizeof(struct sms_msg_data) + - SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); + SMS_DMA_ALIGNMENT, GFP_KERNEL | coredev->gfp_buf_flags); if (!buffer) return -ENOMEM; @@ -1398,7 +1400,7 @@ int smscore_set_device_mode(struct smscore_device_t *coredev, int mode) coredev->device_flags &= ~SMS_DEVICE_NOT_READY; buffer = kmalloc(sizeof(struct sms_msg_data) + - SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); + SMS_DMA_ALIGNMENT, GFP_KERNEL | coredev->gfp_buf_flags); if (buffer) { struct sms_msg_data *msg = (struct sms_msg_data *) SMS_ALIGN_ADDRESS(buffer); @@ -1971,7 +1973,7 @@ int smscore_gpio_configure(struct smscore_device_t *coredev, u8 pin_num, total_len = sizeof(struct sms_msg_hdr) + (sizeof(u32) * 6); buffer = kmalloc(total_len + SMS_DMA_ALIGNMENT, - GFP_KERNEL | GFP_DMA); + GFP_KERNEL | coredev->gfp_buf_flags); if (!buffer) return -ENOMEM; @@ -2043,7 +2045,7 @@ int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 pin_num, (3 * sizeof(u32)); /* keep it 3 ! */ buffer = kmalloc(total_len + SMS_DMA_ALIGNMENT, - GFP_KERNEL | GFP_DMA); + GFP_KERNEL | coredev->gfp_buf_flags); if (!buffer) return -ENOMEM; @@ -2091,7 +2093,7 @@ int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 pin_num, total_len = sizeof(struct sms_msg_hdr) + (2 * sizeof(u32)); buffer = kmalloc(total_len + SMS_DMA_ALIGNMENT, - GFP_KERNEL | GFP_DMA); + GFP_KERNEL | coredev->gfp_buf_flags); if (!buffer) return -ENOMEM; diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h index 134c69f7ea7b..eb58853008c9 100644 --- a/drivers/media/common/siano/smscoreapi.h +++ b/drivers/media/common/siano/smscoreapi.h @@ -190,6 +190,8 @@ struct smscore_device_t { int mode, modes_supported; + gfp_t gfp_buf_flags; + /* host <--> device messages */ struct completion version_ex_done, data_download_done, trigger_done; struct completion data_validity_done, device_ready_done; @@ -1125,6 +1127,7 @@ extern void smscore_unregister_hotplug(hotplug_t hotplug); extern int smscore_register_device(struct smsdevice_params_t *params, struct smscore_device_t **coredev, + gfp_t gfp_buf_flags, void *mdev); extern void smscore_unregister_device(struct smscore_device_t *coredev); diff --git a/drivers/media/common/siano/smsendian.c b/drivers/media/common/siano/smsendian.c index bfe831c10b1c..b95a631f23f9 100644 --- a/drivers/media/common/siano/smsendian.c +++ b/drivers/media/common/siano/smsendian.c @@ -35,7 +35,7 @@ void smsendian_handle_tx_message(void *buffer) switch (msg->x_msg_header.msg_type) { case MSG_SMS_DATA_DOWNLOAD_REQ: { - msg->msg_data[0] = le32_to_cpu(msg->msg_data[0]); + msg->msg_data[0] = le32_to_cpu((__force __le32)(msg->msg_data[0])); break; } @@ -44,7 +44,7 @@ void smsendian_handle_tx_message(void *buffer) sizeof(struct sms_msg_hdr))/4; for (i = 0; i < msg_words; i++) - msg->msg_data[i] = le32_to_cpu(msg->msg_data[i]); + msg->msg_data[i] = le32_to_cpu((__force __le32)msg->msg_data[i]); break; } @@ -64,7 +64,7 @@ void smsendian_handle_rx_message(void *buffer) { struct sms_version_res *ver = (struct sms_version_res *) msg; - ver->chip_model = le16_to_cpu(ver->chip_model); + ver->chip_model = le16_to_cpu((__force __le16)ver->chip_model); break; } @@ -81,7 +81,7 @@ void smsendian_handle_rx_message(void *buffer) sizeof(struct sms_msg_hdr))/4; for (i = 0; i < msg_words; i++) - msg->msg_data[i] = le32_to_cpu(msg->msg_data[i]); + msg->msg_data[i] = le32_to_cpu((__force __le32)msg->msg_data[i]); break; } @@ -95,9 +95,9 @@ void smsendian_handle_message_header(void *msg) #ifdef __BIG_ENDIAN struct sms_msg_hdr *phdr = (struct sms_msg_hdr *)msg; - phdr->msg_type = le16_to_cpu(phdr->msg_type); - phdr->msg_length = le16_to_cpu(phdr->msg_length); - phdr->msg_flags = le16_to_cpu(phdr->msg_flags); + phdr->msg_type = le16_to_cpu((__force __le16)phdr->msg_type); + phdr->msg_length = le16_to_cpu((__force __le16)phdr->msg_length); + phdr->msg_flags = le16_to_cpu((__force __le16)phdr->msg_flags); #endif /* __BIG_ENDIAN */ } EXPORT_SYMBOL_GPL(smsendian_handle_message_header); diff --git a/drivers/media/common/videobuf2/Kconfig b/drivers/media/common/videobuf2/Kconfig index 17c32ea58395..4ed11b46676a 100644 --- a/drivers/media/common/videobuf2/Kconfig +++ b/drivers/media/common/videobuf2/Kconfig @@ -12,7 +12,6 @@ config VIDEOBUF2_MEMOPS config VIDEOBUF2_DMA_CONTIG tristate - depends on HAS_DMA select VIDEOBUF2_CORE select VIDEOBUF2_MEMOPS select DMA_SHARED_BUFFER @@ -25,7 +24,6 @@ config VIDEOBUF2_VMALLOC config VIDEOBUF2_DMA_SG tristate - depends on HAS_DMA select VIDEOBUF2_CORE select VIDEOBUF2_MEMOPS diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index d3f7bb33a54d..f32ec7342ef0 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -916,9 +916,12 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) dprintk(4, "done processing on buffer %d, state: %d\n", vb->index, state); - /* sync buffers */ - for (plane = 0; plane < vb->num_planes; ++plane) - call_void_memop(vb, finish, vb->planes[plane].mem_priv); + if (state != VB2_BUF_STATE_QUEUED && + state != VB2_BUF_STATE_REQUEUEING) { + /* sync buffers */ + for (plane = 0; plane < vb->num_planes; ++plane) + call_void_memop(vb, finish, vb->planes[plane].mem_priv); + } spin_lock_irqsave(&q->done_lock, flags); if (state == VB2_BUF_STATE_QUEUED || diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c index 61a750fae465..cb078d688c70 100644 --- a/drivers/media/dvb-core/dmxdev.c +++ b/drivers/media/dvb-core/dmxdev.c @@ -622,7 +622,7 @@ static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev, struct dmxdev_filter *filter, struct dmxdev_feed *feed) { - ktime_t timeout = 0; + ktime_t timeout = ktime_set(0, 0); struct dmx_pes_filter_params *para = &filter->params.pes; enum dmx_output otype; int ret; diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index 97365a863519..1310526b0d49 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -31,6 +31,7 @@ #include <linux/slab.h> #include <linux/list.h> #include <linux/module.h> +#include <linux/nospec.h> #include <linux/vmalloc.h> #include <linux/delay.h> #include <linux/spinlock.h> @@ -1476,6 +1477,7 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file, if (slot >= ca->slot_count) return -EINVAL; + slot = array_index_nospec(slot, ca->slot_count); sl = &ca->slot_info[slot]; /* check if the slot is actually running */ diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index e33414975065..ce25aef39008 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -190,7 +190,7 @@ dtv_property_legacy_params_sync(struct dvb_frontend *fe, static bool has_get_frontend(struct dvb_frontend *fe) { - return fe->ops.get_frontend != NULL; + return fe->ops.get_frontend; } /* @@ -272,11 +272,23 @@ static void dvb_frontend_add_event(struct dvb_frontend *fe, mutex_unlock(&events->mtx); - wake_up_interruptible (&events->wait_queue); + wake_up_interruptible(&events->wait_queue); +} + +static int dvb_frontend_test_event(struct dvb_frontend_private *fepriv, + struct dvb_fe_events *events) +{ + int ret; + + up(&fepriv->sem); + ret = events->eventw != events->eventr; + down(&fepriv->sem); + + return ret; } static int dvb_frontend_get_event(struct dvb_frontend *fe, - struct dvb_frontend_event *event, int flags) + struct dvb_frontend_event *event, int flags) { struct dvb_frontend_private *fepriv = fe->frontend_priv; struct dvb_fe_events *events = &fepriv->events; @@ -294,13 +306,8 @@ static int dvb_frontend_get_event(struct dvb_frontend *fe, if (flags & O_NONBLOCK) return -EWOULDBLOCK; - up(&fepriv->sem); - - ret = wait_event_interruptible (events->wait_queue, - events->eventw != events->eventr); - - if (down_interruptible (&fepriv->sem)) - return -ERESTARTSYS; + ret = wait_event_interruptible(events->wait_queue, + dvb_frontend_test_event(fepriv, events)); if (ret < 0) return ret; @@ -327,8 +334,8 @@ static void dvb_frontend_clear_events(struct dvb_frontend *fe) static void dvb_frontend_init(struct dvb_frontend *fe) { dev_dbg(fe->dvb->device, - "%s: initialising adapter %i frontend %i (%s)...\n", - __func__, fe->dvb->num, fe->id, fe->ops.info.name); + "%s: initialising adapter %i frontend %i (%s)...\n", + __func__, fe->dvb->num, fe->id, fe->ops.info.name); if (fe->ops.init) fe->ops.init(fe); @@ -358,14 +365,14 @@ static void dvb_frontend_swzigzag_update_delay(struct dvb_frontend_private *fepr dev_dbg(fe->dvb->device, "%s:\n", __func__); if (locked) - (fepriv->quality) = (fepriv->quality * 220 + 36*256) / 256; + (fepriv->quality) = (fepriv->quality * 220 + 36 * 256) / 256; else (fepriv->quality) = (fepriv->quality * 220 + 0) / 256; q2 = fepriv->quality - 128; q2 *= q2; - fepriv->delay = fepriv->min_delay + q2 * HZ / (128*128); + fepriv->delay = fepriv->min_delay + q2 * HZ / (128 * 128); } /** @@ -393,7 +400,7 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra (c->inversion == INVERSION_AUTO)); /* setup parameters correctly */ - while(!ready) { + while (!ready) { /* calculate the lnb_drift */ fepriv->lnb_drift = fepriv->auto_step * fepriv->step_size; @@ -405,7 +412,7 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra } /* perform inversion and +/- zigzag */ - switch(fepriv->auto_sub_step) { + switch (fepriv->auto_sub_step) { case 0: /* try with the current inversion and current drift setting */ ready = 1; @@ -450,11 +457,11 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra return 1; } - dev_dbg(fe->dvb->device, "%s: drift:%i inversion:%i auto_step:%i " \ - "auto_sub_step:%i started_auto_step:%i\n", - __func__, fepriv->lnb_drift, fepriv->inversion, - fepriv->auto_step, fepriv->auto_sub_step, - fepriv->started_auto_step); + dev_dbg(fe->dvb->device, + "%s: drift:%i inversion:%i auto_step:%i auto_sub_step:%i started_auto_step:%i\n", + __func__, fepriv->lnb_drift, fepriv->inversion, + fepriv->auto_step, fepriv->auto_sub_step, + fepriv->started_auto_step); /* set the frontend itself */ c->frequency += fepriv->lnb_drift; @@ -485,7 +492,7 @@ static void dvb_frontend_swzigzag(struct dvb_frontend *fe) /* if we've got no parameters, just keep idling */ if (fepriv->state & FESTATE_IDLE) { - fepriv->delay = 3*HZ; + fepriv->delay = 3 * HZ; fepriv->quality = 0; return; } @@ -502,7 +509,7 @@ static void dvb_frontend_swzigzag(struct dvb_frontend *fe) else fepriv->state = FESTATE_TUNED; } - fepriv->delay = 3*HZ; + fepriv->delay = 3 * HZ; fepriv->quality = 0; return; } @@ -591,7 +598,7 @@ static void dvb_frontend_swzigzag(struct dvb_frontend *fe) } fepriv->check_wrapped = 1; - /* if we've just retuned, enter the ZIGZAG_FAST state. + /* if we've just re-tuned, enter the ZIGZAG_FAST state. * This ensures we cannot return from an * FE_SET_FRONTEND ioctl before the first frontend tune * occurs */ @@ -658,7 +665,7 @@ static int dvb_frontend_thread(void *data) fepriv->check_wrapped = 0; fepriv->quality = 0; - fepriv->delay = 3*HZ; + fepriv->delay = 3 * HZ; fepriv->status = 0; fepriv->wakeup = 0; fepriv->reinitialise = 0; @@ -670,8 +677,9 @@ static int dvb_frontend_thread(void *data) up(&fepriv->sem); /* is locked when we enter the thread... */ restart: wait_event_interruptible_timeout(fepriv->wait_queue, - dvb_frontend_should_wakeup(fe) || kthread_should_stop() - || freezing(current), + dvb_frontend_should_wakeup(fe) || + kthread_should_stop() || + freezing(current), fepriv->delay); if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) { @@ -820,8 +828,8 @@ static void dvb_frontend_stop(struct dvb_frontend *fe) /* paranoia check in case a signal arrived */ if (fepriv->thread) dev_warn(fe->dvb->device, - "dvb_frontend_stop: warning: thread %p won't exit\n", - fepriv->thread); + "dvb_frontend_stop: warning: thread %p won't exit\n", + fepriv->thread); } /* @@ -858,12 +866,12 @@ static int dvb_frontend_start(struct dvb_frontend *fe) if (fe->exit == DVB_FE_NO_EXIT) return 0; else - dvb_frontend_stop (fe); + dvb_frontend_stop(fe); } if (signal_pending(current)) return -EINTR; - if (down_interruptible (&fepriv->sem)) + if (down_interruptible(&fepriv->sem)) return -EINTR; fepriv->state = FESTATE_IDLE; @@ -872,12 +880,12 @@ static int dvb_frontend_start(struct dvb_frontend *fe) mb(); fe_thread = kthread_run(dvb_frontend_thread, fe, - "kdvb-ad-%i-fe-%i", fe->dvb->num,fe->id); + "kdvb-ad-%i-fe-%i", fe->dvb->num, fe->id); if (IS_ERR(fe_thread)) { ret = PTR_ERR(fe_thread); dev_warn(fe->dvb->device, - "dvb_frontend_start: failed to start kthread (%d)\n", - ret); + "dvb_frontend_start: failed to start kthread (%d)\n", + ret); up(&fepriv->sem); return ret; } @@ -886,7 +894,7 @@ static int dvb_frontend_start(struct dvb_frontend *fe) } static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe, - u32 *freq_min, u32 *freq_max) + u32 *freq_min, u32 *freq_max) { *freq_min = max(fe->ops.info.frequency_min, fe->ops.tuner_ops.info.frequency_min); @@ -898,8 +906,9 @@ static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe, *freq_max = min(fe->ops.info.frequency_max, fe->ops.tuner_ops.info.frequency_max); if (*freq_min == 0 || *freq_max == 0) - dev_warn(fe->dvb->device, "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n", - fe->dvb->num, fe->id); + dev_warn(fe->dvb->device, + "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n", + fe->dvb->num, fe->id); } static int dvb_frontend_check_parameters(struct dvb_frontend *fe) @@ -913,8 +922,8 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe) if ((freq_min && c->frequency < freq_min) || (freq_max && c->frequency > freq_max)) { dev_warn(fe->dvb->device, "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n", - fe->dvb->num, fe->id, c->frequency, - freq_min, freq_max); + fe->dvb->num, fe->id, c->frequency, + freq_min, freq_max); return -EINVAL; } @@ -930,9 +939,9 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe) (fe->ops.info.symbol_rate_max && c->symbol_rate > fe->ops.info.symbol_rate_max)) { dev_warn(fe->dvb->device, "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n", - fe->dvb->num, fe->id, c->symbol_rate, - fe->ops.info.symbol_rate_min, - fe->ops.info.symbol_rate_max); + fe->dvb->num, fe->id, c->symbol_rate, + fe->ops.info.symbol_rate_min, + fe->ops.info.symbol_rate_max); return -EINVAL; } default: @@ -953,7 +962,7 @@ static int dvb_frontend_clear_cache(struct dvb_frontend *fe) c->delivery_system = delsys; dev_dbg(fe->dvb->device, "%s: Clearing cache for delivery system %d\n", - __func__, c->delivery_system); + __func__, c->delivery_system); c->transmission_mode = TRANSMISSION_MODE_AUTO; c->bandwidth_hz = 0; /* AUTO */ @@ -973,7 +982,7 @@ static int dvb_frontend_clear_cache(struct dvb_frontend *fe) c->isdbt_sb_subchannel = 0; c->isdbt_sb_segment_idx = 0; c->isdbt_sb_segment_count = 0; - c->isdbt_layer_enabled = 0; + c->isdbt_layer_enabled = 7; /* All layers (A,B,C) */ for (i = 0; i < 3; i++) { c->layer[i].fec = FEC_AUTO; c->layer[i].modulation = QAM_AUTO; @@ -1178,8 +1187,8 @@ static int dtv_property_cache_sync(struct dvb_frontend *fe, break; case DVBV3_UNKNOWN: dev_err(fe->dvb->device, - "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n", - __func__, c->delivery_system); + "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n", + __func__, c->delivery_system); return -EINVAL; } @@ -1200,8 +1209,8 @@ dtv_property_legacy_params_sync(struct dvb_frontend *fe, switch (dvbv3_type(c->delivery_system)) { case DVBV3_UNKNOWN: dev_err(fe->dvb->device, - "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n", - __func__, c->delivery_system); + "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n", + __func__, c->delivery_system); return -EINVAL; case DVBV3_QPSK: dev_dbg(fe->dvb->device, "%s: Preparing QPSK req\n", __func__); @@ -1293,7 +1302,7 @@ static int dtv_property_process_get(struct dvb_frontend *fe, { int ncaps; - switch(tvp->cmd) { + switch (tvp->cmd) { case DTV_ENUM_DELSYS: ncaps = 0; while (ncaps < MAX_DELSYS && fe->ops.delsys[ncaps]) { @@ -1622,8 +1631,8 @@ static int dvbv5_set_delivery_system(struct dvb_frontend *fe, if (fe->ops.delsys[ncaps] == desired_system) { c->delivery_system = desired_system; dev_dbg(fe->dvb->device, - "%s: Changing delivery system to %d\n", - __func__, desired_system); + "%s: Changing delivery system to %d\n", + __func__, desired_system); return 0; } ncaps++; @@ -1715,8 +1724,8 @@ static int dvbv3_set_delivery_system(struct dvb_frontend *fe) */ if (is_dvbv3_delsys(c->delivery_system)) { dev_dbg(fe->dvb->device, - "%s: Using delivery system to %d\n", - __func__, c->delivery_system); + "%s: Using delivery system to %d\n", + __func__, c->delivery_system); return 0; } @@ -1756,8 +1765,8 @@ static int dvbv3_set_delivery_system(struct dvb_frontend *fe) * Zero on success, negative errno on failure. */ static int dtv_property_process_set(struct dvb_frontend *fe, - struct file *file, - u32 cmd, u32 data) + struct file *file, + u32 cmd, u32 data) { int r = 0; struct dtv_frontend_properties *c = &fe->dtv_property_cache; @@ -1765,11 +1774,11 @@ static int dtv_property_process_set(struct dvb_frontend *fe, /** Dump DTV command name and value*/ if (!cmd || cmd > DTV_MAX_COMMAND) dev_warn(fe->dvb->device, "%s: SET cmd 0x%08x undefined\n", - __func__, cmd); + __func__, cmd); else dev_dbg(fe->dvb->device, - "%s: SET cmd 0x%08x (%s) to 0x%08x\n", - __func__, cmd, dtv_cmds[cmd].name, data); + "%s: SET cmd 0x%08x (%s) to 0x%08x\n", + __func__, cmd, dtv_cmds[cmd].name, data); switch (cmd) { case DTV_CLEAR: /* @@ -1819,12 +1828,12 @@ static int dtv_property_process_set(struct dvb_frontend *fe, case DTV_VOLTAGE: c->voltage = data; r = dvb_frontend_handle_ioctl(file, FE_SET_VOLTAGE, - (void *)c->voltage); + (void *)c->voltage); break; case DTV_TONE: c->sectone = data; r = dvb_frontend_handle_ioctl(file, FE_SET_TONE, - (void *)c->sectone); + (void *)c->sectone); break; case DTV_CODE_RATE_HP: c->code_rate_HP = data; @@ -2045,8 +2054,8 @@ static int dvb_frontend_handle_compat_ioctl(struct file *file, unsigned int cmd, for (i = 0; i < tvps->num; i++) { err = dtv_property_process_set(fe, file, - (tvp + i)->cmd, - (tvp + i)->u.data); + (tvp + i)->cmd, + (tvp + i)->u.data); if (err < 0) { kfree(tvp); return err; @@ -2265,7 +2274,6 @@ static int dtv_set_frontend(struct dvb_frontend *fe) return 0; } - static int dvb_frontend_handle_ioctl(struct file *file, unsigned int cmd, void *parg) { @@ -2300,8 +2308,8 @@ static int dvb_frontend_handle_ioctl(struct file *file, for (i = 0; i < tvps->num; i++) { err = dtv_property_process_set(fe, file, - (tvp + i)->cmd, - (tvp + i)->u.data); + (tvp + i)->cmd, + (tvp + i)->u.data); if (err < 0) { kfree(tvp); return err; @@ -2365,7 +2373,7 @@ static int dvb_frontend_handle_ioctl(struct file *file, } case FE_GET_INFO: { - struct dvb_frontend_info* info = parg; + struct dvb_frontend_info *info = parg; memcpy(info, &fe->ops.info, sizeof(struct dvb_frontend_info)); dvb_frontend_get_frequency_limits(fe, &info->frequency_min, &info->frequency_max); @@ -2396,12 +2404,12 @@ static int dvb_frontend_handle_ioctl(struct file *file, break; default: dev_err(fe->dvb->device, - "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n", - __func__, c->delivery_system); + "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n", + __func__, c->delivery_system); fe->ops.info.type = FE_OFDM; } dev_dbg(fe->dvb->device, "%s: current delivery system on cache: %d, V3 type: %d\n", - __func__, c->delivery_system, fe->ops.info.type); + __func__, c->delivery_system, fe->ops.info.type); /* Set CAN_INVERSION_AUTO bit on in other than oneshot mode */ if (!(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) @@ -2417,7 +2425,7 @@ static int dvb_frontend_handle_ioctl(struct file *file, * that user get signal state from previous tuning */ if (fepriv->state == FESTATE_RETUNE || fepriv->state == FESTATE_ERROR) { - err=0; + err = 0; *status = 0; break; } @@ -2485,11 +2493,11 @@ static int dvb_frontend_handle_ioctl(struct file *file, case FE_ENABLE_HIGH_LNB_VOLTAGE: if (fe->ops.enable_high_lnb_voltage) - err = fe->ops.enable_high_lnb_voltage(fe, (long) parg); + err = fe->ops.enable_high_lnb_voltage(fe, (long)parg); break; case FE_SET_FRONTEND_TUNE_MODE: - fepriv->tune_mode_flags = (unsigned long) parg; + fepriv->tune_mode_flags = (unsigned long)parg; err = 0; break; @@ -2518,11 +2526,12 @@ static int dvb_frontend_handle_ioctl(struct file *file, * initialization, so parg is 8 bits and does not * include the initialization or start bit */ - unsigned long swcmd = ((unsigned long) parg) << 1; + unsigned long swcmd = ((unsigned long)parg) << 1; ktime_t nexttime; ktime_t tv[10]; int i; u8 last = 1; + if (dvb_frontend_debug) dprintk("%s switch command: 0x%04lx\n", __func__, swcmd); @@ -2537,7 +2546,7 @@ static int dvb_frontend_handle_ioctl(struct file *file, for (i = 0; i < 9; i++) { if (dvb_frontend_debug) - tv[i+1] = ktime_get_boottime(); + tv[i + 1] = ktime_get_boottime(); if ((swcmd & 0x01) != last) { /* set voltage to (last ? 13V : 18V) */ fe->ops.set_voltage(fe, (last) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18); @@ -2552,7 +2561,7 @@ static int dvb_frontend_handle_ioctl(struct file *file, __func__, fe->dvb->num); for (i = 1; i < 10; i++) pr_info("%d: %d\n", i, - (int) ktime_us_delta(tv[i], tv[i-1])); + (int)ktime_us_delta(tv[i], tv[i - 1])); } err = 0; fepriv->state = FESTATE_DISEQC; @@ -2611,7 +2620,7 @@ static int dvb_frontend_handle_ioctl(struct file *file, err = dtv_set_frontend(fe); break; case FE_GET_EVENT: - err = dvb_frontend_get_event (fe, parg, file->f_flags); + err = dvb_frontend_get_event(fe, parg, file->f_flags); break; case FE_GET_FRONTEND: { @@ -2634,7 +2643,6 @@ static int dvb_frontend_handle_ioctl(struct file *file, return err; } - static __poll_t dvb_frontend_poll(struct file *file, struct poll_table_struct *wait) { struct dvb_device *dvbdev = file->private_data; @@ -2643,7 +2651,7 @@ static __poll_t dvb_frontend_poll(struct file *file, struct poll_table_struct *w dev_dbg_ratelimited(fe->dvb->device, "%s:\n", __func__); - poll_wait (file, &fepriv->events.wait_queue, wait); + poll_wait(file, &fepriv->events.wait_queue, wait); if (fepriv->events.eventw != fepriv->events.eventr) return (EPOLLIN | EPOLLRDNORM | EPOLLPRI); @@ -2664,9 +2672,9 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) return -ENODEV; if (adapter->mfe_shared) { - mutex_lock (&adapter->mfe_lock); + mutex_lock(&adapter->mfe_lock); - if (adapter->mfe_dvbdev == NULL) + if (!adapter->mfe_dvbdev) adapter->mfe_dvbdev = dvbdev; else if (adapter->mfe_dvbdev != dvbdev) { @@ -2678,23 +2686,23 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) *mfepriv = mfe->frontend_priv; int mferetry = (dvb_mfe_wait_time << 1); - mutex_unlock (&adapter->mfe_lock); + mutex_unlock(&adapter->mfe_lock); while (mferetry-- && (mfedev->users != -1 || - mfepriv->thread != NULL)) { - if(msleep_interruptible(500)) { - if(signal_pending(current)) + mfepriv->thread)) { + if (msleep_interruptible(500)) { + if (signal_pending(current)) return -EINTR; } } - mutex_lock (&adapter->mfe_lock); - if(adapter->mfe_dvbdev != dvbdev) { + mutex_lock(&adapter->mfe_lock); + if (adapter->mfe_dvbdev != dvbdev) { mfedev = adapter->mfe_dvbdev; mfe = mfedev->priv; mfepriv = mfe->frontend_priv; if (mfedev->users != -1 || - mfepriv->thread != NULL) { - mutex_unlock (&adapter->mfe_lock); + mfepriv->thread) { + mutex_unlock(&adapter->mfe_lock); return -EBUSY; } adapter->mfe_dvbdev = dvbdev; @@ -2715,7 +2723,7 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) fepriv->reinitialise = 1; } - if ((ret = dvb_generic_open (inode, file)) < 0) + if ((ret = dvb_generic_open(inode, file)) < 0) goto err1; if ((file->f_flags & O_ACCMODE) != O_RDONLY) { @@ -2725,6 +2733,7 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) fepriv->voltage = -1; #ifdef CONFIG_MEDIA_CONTROLLER_DVB + mutex_lock(&fe->dvb->mdev_lock); if (fe->dvb->mdev) { mutex_lock(&fe->dvb->mdev->graph_mutex); if (fe->dvb->mdev->enable_source) @@ -2733,13 +2742,15 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) &fepriv->pipe); mutex_unlock(&fe->dvb->mdev->graph_mutex); if (ret) { + mutex_unlock(&fe->dvb->mdev_lock); dev_err(fe->dvb->device, "Tuner is busy. Error %d\n", ret); goto err2; } } + mutex_unlock(&fe->dvb->mdev_lock); #endif - ret = dvb_frontend_start (fe); + ret = dvb_frontend_start(fe); if (ret) goto err3; @@ -2750,17 +2761,19 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) dvb_frontend_get(fe); if (adapter->mfe_shared) - mutex_unlock (&adapter->mfe_lock); + mutex_unlock(&adapter->mfe_lock); return ret; err3: #ifdef CONFIG_MEDIA_CONTROLLER_DVB + mutex_lock(&fe->dvb->mdev_lock); if (fe->dvb->mdev) { mutex_lock(&fe->dvb->mdev->graph_mutex); if (fe->dvb->mdev->disable_source) fe->dvb->mdev->disable_source(dvbdev->entity); mutex_unlock(&fe->dvb->mdev->graph_mutex); } + mutex_unlock(&fe->dvb->mdev_lock); err2: #endif dvb_generic_release(inode, file); @@ -2769,7 +2782,7 @@ err1: fe->ops.ts_bus_ctrl(fe, 0); err0: if (adapter->mfe_shared) - mutex_unlock (&adapter->mfe_lock); + mutex_unlock(&adapter->mfe_lock); return ret; } @@ -2787,17 +2800,19 @@ static int dvb_frontend_release(struct inode *inode, struct file *file) mb(); } - ret = dvb_generic_release (inode, file); + ret = dvb_generic_release(inode, file); if (dvbdev->users == -1) { wake_up(&fepriv->wait_queue); #ifdef CONFIG_MEDIA_CONTROLLER_DVB + mutex_lock(&fe->dvb->mdev_lock); if (fe->dvb->mdev) { mutex_lock(&fe->dvb->mdev->graph_mutex); if (fe->dvb->mdev->disable_source) fe->dvb->mdev->disable_source(dvbdev->entity); mutex_unlock(&fe->dvb->mdev->graph_mutex); } + mutex_unlock(&fe->dvb->mdev_lock); #endif if (fe->exit != DVB_FE_NO_EXIT) wake_up(&dvbdev->wait_queue); @@ -2827,7 +2842,7 @@ int dvb_frontend_suspend(struct dvb_frontend *fe) int ret = 0; dev_dbg(fe->dvb->device, "%s: adap=%d fe=%d\n", __func__, fe->dvb->num, - fe->id); + fe->id); if (fe->ops.tuner_ops.suspend) ret = fe->ops.tuner_ops.suspend(fe); @@ -2847,7 +2862,7 @@ int dvb_frontend_resume(struct dvb_frontend *fe) int ret = 0; dev_dbg(fe->dvb->device, "%s: adap=%d fe=%d\n", __func__, fe->dvb->num, - fe->id); + fe->id); fe->exit = DVB_FE_DEVICE_RESUME; if (fe->ops.init) @@ -2871,14 +2886,14 @@ int dvb_frontend_resume(struct dvb_frontend *fe) } EXPORT_SYMBOL(dvb_frontend_resume); -int dvb_register_frontend(struct dvb_adapter* dvb, - struct dvb_frontend* fe) +int dvb_register_frontend(struct dvb_adapter *dvb, + struct dvb_frontend *fe) { struct dvb_frontend_private *fepriv; const struct dvb_device dvbdev_template = { .users = ~0, .writers = 1, - .readers = (~0)-1, + .readers = (~0) - 1, .fops = &dvb_frontend_fops, #if defined(CONFIG_MEDIA_CONTROLLER_DVB) .name = fe->ops.info.name, @@ -2891,7 +2906,7 @@ int dvb_register_frontend(struct dvb_adapter* dvb, return -ERESTARTSYS; fe->frontend_priv = kzalloc(sizeof(struct dvb_frontend_private), GFP_KERNEL); - if (fe->frontend_priv == NULL) { + if (!fe->frontend_priv) { mutex_unlock(&frontend_mutex); return -ENOMEM; } @@ -2907,18 +2922,18 @@ int dvb_register_frontend(struct dvb_adapter* dvb, dvb_frontend_get(fe); sema_init(&fepriv->sem, 1); - init_waitqueue_head (&fepriv->wait_queue); - init_waitqueue_head (&fepriv->events.wait_queue); + init_waitqueue_head(&fepriv->wait_queue); + init_waitqueue_head(&fepriv->events.wait_queue); mutex_init(&fepriv->events.mtx); fe->dvb = dvb; fepriv->inversion = INVERSION_OFF; dev_info(fe->dvb->device, - "DVB: registering adapter %i frontend %i (%s)...\n", - fe->dvb->num, fe->id, fe->ops.info.name); + "DVB: registering adapter %i frontend %i (%s)...\n", + fe->dvb->num, fe->id, fe->ops.info.name); - dvb_register_device (fe->dvb, &fepriv->dvbdev, &dvbdev_template, - fe, DVB_DEVICE_FRONTEND, 0); + dvb_register_device(fe->dvb, &fepriv->dvbdev, &dvbdev_template, + fe, DVB_DEVICE_FRONTEND, 0); /* * Initialize the cache to the proper values according with the @@ -2933,9 +2948,10 @@ int dvb_register_frontend(struct dvb_adapter* dvb, } EXPORT_SYMBOL(dvb_register_frontend); -int dvb_unregister_frontend(struct dvb_frontend* fe) +int dvb_unregister_frontend(struct dvb_frontend *fe) { struct dvb_frontend_private *fepriv = fe->frontend_priv; + dev_dbg(fe->dvb->device, "%s:\n", __func__); mutex_lock(&frontend_mutex); @@ -2960,7 +2976,7 @@ static void dvb_frontend_invoke_release(struct dvb_frontend *fe, } } -void dvb_frontend_detach(struct dvb_frontend* fe) +void dvb_frontend_detach(struct dvb_frontend *fe) { dvb_frontend_invoke_release(fe, fe->ops.release_sec); dvb_frontend_invoke_release(fe, fe->ops.tuner_ops.release); diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c index ba39f9942e1d..10f78109bb3f 100644 --- a/drivers/media/dvb-core/dvb_net.c +++ b/drivers/media/dvb-core/dvb_net.c @@ -1005,7 +1005,7 @@ static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len, return 0; } -static int dvb_net_tx(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t dvb_net_tx(struct sk_buff *skb, struct net_device *dev) { dev_kfree_skb(skb); return NETDEV_TX_OK; diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c index 787fe06df217..64d6793674b9 100644 --- a/drivers/media/dvb-core/dvbdev.c +++ b/drivers/media/dvb-core/dvbdev.c @@ -859,6 +859,10 @@ int dvb_register_adapter(struct dvb_adapter *adap, const char *name, adap->mfe_dvbdev = NULL; mutex_init (&adap->mfe_lock); +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + mutex_init(&adap->mdev_lock); +#endif + list_add_tail (&adap->list_head, &dvb_adapter_list); mutex_unlock(&dvbdev_register_lock); diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 0712069fd9fe..55e36a4f5215 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -903,7 +903,7 @@ comment "Common Interface (EN50221) controller drivers" depends on DVB_CORE config DVB_CXD2099 - tristate "CXD2099AR Common Interface driver" + tristate "Sony CXD2099AR Common Interface driver" depends on DVB_CORE && I2C select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/dvb-frontends/as102_fe.h b/drivers/media/dvb-frontends/as102_fe.h index a7c91430ca3d..98d33d5ce872 100644 --- a/drivers/media/dvb-frontends/as102_fe.h +++ b/drivers/media/dvb-frontends/as102_fe.h @@ -1,6 +1,6 @@ /* * Abilis Systems Single DVB-T Receiver - * Copyright (C) 2014 Mauro Carvalho Chehab <m.chehab@samsung.com> + * Copyright (C) 2014 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index 343dc92ef54e..f285096a48f0 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -280,14 +280,12 @@ static void setup_decoder_defaults(struct au8522_state *state, bool is_svideo) AU8522_TOREGAAGC_REG0E5H_CVBS); au8522_writereg(state, AU8522_REG016H, AU8522_REG016H_CVBS); - if (is_svideo) { - /* Despite what the table says, for the HVR-950q we still need - to be in CVBS mode for the S-Video input (reason unknown). */ - /* filter_coef_type = 3; */ - filter_coef_type = 5; - } else { - filter_coef_type = 5; - } + /* + * Despite what the table says, for the HVR-950q we still need + * to be in CVBS mode for the S-Video input (reason unknown). + */ + /* filter_coef_type = 3; */ + filter_coef_type = 5; /* Load the Video Decoder Filter Coefficients */ for (i = 0; i < NUM_FILTER_COEF; i++) { diff --git a/drivers/media/dvb-frontends/cx24116.c b/drivers/media/dvb-frontends/cx24116.c index 786c56a4ef76..2dbc7349d870 100644 --- a/drivers/media/dvb-frontends/cx24116.c +++ b/drivers/media/dvb-frontends/cx24116.c @@ -1456,7 +1456,7 @@ static int cx24116_tune(struct dvb_frontend *fe, bool re_tune, return cx24116_read_status(fe, status); } -static int cx24116_get_algo(struct dvb_frontend *fe) +static enum dvbfe_algo cx24116_get_algo(struct dvb_frontend *fe) { return DVBFE_ALGO_HW; } diff --git a/drivers/media/dvb-frontends/cx24117.c b/drivers/media/dvb-frontends/cx24117.c index 8935114b75f3..ba55d75d916c 100644 --- a/drivers/media/dvb-frontends/cx24117.c +++ b/drivers/media/dvb-frontends/cx24117.c @@ -1555,7 +1555,7 @@ static int cx24117_tune(struct dvb_frontend *fe, bool re_tune, return cx24117_read_status(fe, status); } -static int cx24117_get_algo(struct dvb_frontend *fe) +static enum dvbfe_algo cx24117_get_algo(struct dvb_frontend *fe) { return DVBFE_ALGO_HW; } diff --git a/drivers/media/dvb-frontends/cx24120.c b/drivers/media/dvb-frontends/cx24120.c index 810f68acd69b..ccbabdae6a69 100644 --- a/drivers/media/dvb-frontends/cx24120.c +++ b/drivers/media/dvb-frontends/cx24120.c @@ -1491,7 +1491,7 @@ static int cx24120_tune(struct dvb_frontend *fe, bool re_tune, return cx24120_read_status(fe, status); } -static int cx24120_get_algo(struct dvb_frontend *fe) +static enum dvbfe_algo cx24120_get_algo(struct dvb_frontend *fe) { return DVBFE_ALGO_HW; } diff --git a/drivers/media/dvb-frontends/cx24123.c b/drivers/media/dvb-frontends/cx24123.c index 228ba1f4bf63..bf33e7390aaf 100644 --- a/drivers/media/dvb-frontends/cx24123.c +++ b/drivers/media/dvb-frontends/cx24123.c @@ -1005,7 +1005,7 @@ static int cx24123_tune(struct dvb_frontend *fe, return retval; } -static int cx24123_get_algo(struct dvb_frontend *fe) +static enum dvbfe_algo cx24123_get_algo(struct dvb_frontend *fe) { return DVBFE_ALGO_HW; } diff --git a/drivers/media/dvb-frontends/cxd2099.c b/drivers/media/dvb-frontends/cxd2099.c index c0a5849b76bb..4a0ce3037fd6 100644 --- a/drivers/media/dvb-frontends/cxd2099.c +++ b/drivers/media/dvb-frontends/cxd2099.c @@ -1,5 +1,5 @@ /* - * cxd2099.c: Driver for the CXD2099AR Common Interface Controller + * cxd2099.c: Driver for the Sony CXD2099AR Common Interface Controller * * Copyright (C) 2010-2013 Digital Devices GmbH * @@ -699,6 +699,6 @@ static struct i2c_driver cxd2099_driver = { module_i2c_driver(cxd2099_driver); -MODULE_DESCRIPTION("CXD2099AR Common Interface controller driver"); +MODULE_DESCRIPTION("Sony CXD2099AR Common Interface controller driver"); MODULE_AUTHOR("Ralph Metzler"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/cxd2099.h b/drivers/media/dvb-frontends/cxd2099.h index 8fa45a4c615a..ec1910dec3f3 100644 --- a/drivers/media/dvb-frontends/cxd2099.h +++ b/drivers/media/dvb-frontends/cxd2099.h @@ -1,5 +1,5 @@ /* - * cxd2099.h: Driver for the CXD2099AR Common Interface Controller + * cxd2099.h: Driver for the Sony CXD2099AR Common Interface Controller * * Copyright (C) 2010-2011 Digital Devices GmbH * diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c index f6ebbb47b9b2..3e0d8cbd76da 100644 --- a/drivers/media/dvb-frontends/cxd2820r_core.c +++ b/drivers/media/dvb-frontends/cxd2820r_core.c @@ -403,7 +403,7 @@ error: return DVBFE_ALGO_SEARCH_ERROR; } -static int cxd2820r_get_frontend_algo(struct dvb_frontend *fe) +static enum dvbfe_algo cxd2820r_get_frontend_algo(struct dvb_frontend *fe) { return DVBFE_ALGO_CUSTOM; } diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h index fab55038b37b..c6d6c8dd16a1 100644 --- a/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_tnrdmd_driver_version.h @@ -7,6 +7,6 @@ * Copyright (C) 2016, 2017, 2018 Sony Semiconductor Solutions Corporation */ -#define CXD2880_TNRDMD_DRIVER_VERSION "1.4.1 - 1.0.4" +#define CXD2880_TNRDMD_DRIVER_VERSION "1.4.1 - 1.0.5" -#define CXD2880_TNRDMD_DRIVER_RELEASE_DATE "2018-01-17" +#define CXD2880_TNRDMD_DRIVER_RELEASE_DATE "2018-04-25" diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c index d474dc1b05da..bd9101e246d5 100644 --- a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c @@ -520,6 +520,15 @@ static int cxd2880_init(struct dvb_frontend *fe) pr_err("cxd2880 integ init failed %d\n", ret); return ret; } + + ret = cxd2880_tnrdmd_set_cfg(&priv->tnrdmd, + CXD2880_TNRDMD_CFG_TSPIN_CURRENT, + 0x00); + if (ret) { + mutex_unlock(priv->spi_mutex); + pr_err("cxd2880 set config failed %d\n", ret); + return ret; + } mutex_unlock(priv->spi_mutex); pr_debug("OK.\n"); @@ -1126,7 +1135,7 @@ static int cxd2880_get_stats(struct dvb_frontend *fe, priv = fe->demodulator_priv; c = &fe->dtv_property_cache; - if (!(status & FE_HAS_LOCK)) { + if (!(status & FE_HAS_LOCK) || !(status & FE_HAS_CARRIER)) { c->pre_bit_error.len = 1; c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; c->pre_bit_count.len = 1; @@ -1345,7 +1354,8 @@ static int cxd2880_read_status(struct dvb_frontend *fe, pr_debug("status %d\n", *status); - if (priv->s == 0 && (*status & FE_HAS_LOCK)) { + if (priv->s == 0 && (*status & FE_HAS_LOCK) && + (*status & FE_HAS_CARRIER)) { mutex_lock(priv->spi_mutex); if (c->delivery_system == SYS_DVBT) { ret = cxd2880_set_ber_per_period_t(fe); diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c index 5553b89b804e..e3894ff403d7 100644 --- a/drivers/media/dvb-frontends/dvb-pll.c +++ b/drivers/media/dvb-frontends/dvb-pll.c @@ -533,6 +533,45 @@ static const struct dvb_pll_desc dvb_pll_alps_tdee4 = { } }; +/* Infineon TUA6034 ISDB-T, used in Friio */ +/* CP cur. 50uA, AGC takeover: 103dBuV, PORT3 on */ +static const struct dvb_pll_desc dvb_pll_tua6034_friio = { + .name = "Infineon TUA6034 ISDB-T (Friio)", + .min = 90000000, + .max = 770000000, + .iffreq = 57000000, + .initdata = (u8[]){ 4, 0x9a, 0x50, 0xb2, 0x08 }, + .sleepdata = (u8[]){ 4, 0x9a, 0x70, 0xb3, 0x0b }, + .count = 3, + .entries = { + { 170000000, 142857, 0xba, 0x09 }, + { 470000000, 142857, 0xba, 0x0a }, + { 770000000, 142857, 0xb2, 0x08 }, + } +}; + +/* Philips TDA6651 ISDB-T, used in Earthsoft PT1 */ +static const struct dvb_pll_desc dvb_pll_tda665x_earth_pt1 = { + .name = "Philips TDA6651 ISDB-T (EarthSoft PT1)", + .min = 90000000, + .max = 770000000, + .iffreq = 57000000, + .initdata = (u8[]){ 5, 0x0e, 0x7f, 0xc1, 0x80, 0x80 }, + .count = 10, + .entries = { + { 140000000, 142857, 0xc1, 0x81 }, + { 170000000, 142857, 0xc1, 0xa1 }, + { 220000000, 142857, 0xc1, 0x62 }, + { 330000000, 142857, 0xc1, 0xa2 }, + { 402000000, 142857, 0xc1, 0xe2 }, + { 450000000, 142857, 0xc1, 0x64 }, + { 550000000, 142857, 0xc1, 0x84 }, + { 600000000, 142857, 0xc1, 0xa4 }, + { 700000000, 142857, 0xc1, 0xc4 }, + { 770000000, 142857, 0xc1, 0xe4 }, + } +}; + /* ----------------------------------------------------------- */ static const struct dvb_pll_desc *pll_list[] = { @@ -556,6 +595,8 @@ static const struct dvb_pll_desc *pll_list[] = { [DVB_PLL_SAMSUNG_TDTC9251DH0] = &dvb_pll_samsung_tdtc9251dh0, [DVB_PLL_SAMSUNG_TBDU18132] = &dvb_pll_samsung_tbdu18132, [DVB_PLL_SAMSUNG_TBMU24112] = &dvb_pll_samsung_tbmu24112, + [DVB_PLL_TUA6034_FRIIO] = &dvb_pll_tua6034_friio, + [DVB_PLL_TDA665X_EARTH_PT1] = &dvb_pll_tda665x_earth_pt1, }; /* ----------------------------------------------------------- */ @@ -827,6 +868,75 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, } EXPORT_SYMBOL(dvb_pll_attach); + +static int +dvb_pll_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct dvb_pll_config *cfg; + struct dvb_frontend *fe; + unsigned int desc_id; + + cfg = client->dev.platform_data; + fe = cfg->fe; + i2c_set_clientdata(client, fe); + desc_id = (unsigned int) id->driver_data; + + if (!dvb_pll_attach(fe, client->addr, client->adapter, desc_id)) + return -ENOMEM; + + dev_info(&client->dev, "DVB Simple Tuner attached.\n"); + return 0; +} + +static int dvb_pll_remove(struct i2c_client *client) +{ + struct dvb_frontend *fe; + + fe = i2c_get_clientdata(client); + dvb_pll_release(fe); + return 0; +} + + +static const struct i2c_device_id dvb_pll_id[] = { + {"dtt7579", DVB_PLL_THOMSON_DTT7579}, + {"dtt759x", DVB_PLL_THOMSON_DTT759X}, + {"z201", DVB_PLL_LG_Z201}, + {"unknown_1", DVB_PLL_UNKNOWN_1}, + {"tua6010xs", DVB_PLL_TUA6010XS}, + {"env57h1xd5", DVB_PLL_ENV57H1XD5}, + {"tua6034", DVB_PLL_TUA6034}, + {"tda665x", DVB_PLL_TDA665X}, + {"tded4", DVB_PLL_TDED4}, + {"tdhu2", DVB_PLL_TDHU2}, + {"tbmv", DVB_PLL_SAMSUNG_TBMV}, + {"sd1878_tda8261", DVB_PLL_PHILIPS_SD1878_TDA8261}, + {"opera1", DVB_PLL_OPERA1}, + {"dtos403ih102a", DVB_PLL_SAMSUNG_DTOS403IH102A}, + {"tdtc9251dh0", DVB_PLL_SAMSUNG_TDTC9251DH0}, + {"tbdu18132", DVB_PLL_SAMSUNG_TBDU18132}, + {"tbmu24112", DVB_PLL_SAMSUNG_TBMU24112}, + {"tdee4", DVB_PLL_TDEE4}, + {"dtt7520x", DVB_PLL_THOMSON_DTT7520X}, + {"tua6034_friio", DVB_PLL_TUA6034_FRIIO}, + {"tda665x_earthpt1", DVB_PLL_TDA665X_EARTH_PT1}, + {} +}; + + +MODULE_DEVICE_TABLE(i2c, dvb_pll_id); + +static struct i2c_driver dvb_pll_driver = { + .driver = { + .name = "dvb_pll", + }, + .probe = dvb_pll_probe, + .remove = dvb_pll_remove, + .id_table = dvb_pll_id, +}; + +module_i2c_driver(dvb_pll_driver); + MODULE_DESCRIPTION("dvb pll library"); MODULE_AUTHOR("Gerd Knorr"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/dvb-pll.h b/drivers/media/dvb-frontends/dvb-pll.h index ca885e71d2f0..973a66a82e27 100644 --- a/drivers/media/dvb-frontends/dvb-pll.h +++ b/drivers/media/dvb-frontends/dvb-pll.h @@ -29,6 +29,12 @@ #define DVB_PLL_SAMSUNG_TBMU24112 17 #define DVB_PLL_TDEE4 18 #define DVB_PLL_THOMSON_DTT7520X 19 +#define DVB_PLL_TUA6034_FRIIO 20 +#define DVB_PLL_TDA665X_EARTH_PT1 21 + +struct dvb_pll_config { + struct dvb_frontend *fe; +}; #if IS_REACHABLE(CONFIG_DVB_PLL) /** diff --git a/drivers/media/dvb-frontends/l64781.c b/drivers/media/dvb-frontends/l64781.c index e056f36e6f0c..249c18761e6e 100644 --- a/drivers/media/dvb-frontends/l64781.c +++ b/drivers/media/dvb-frontends/l64781.c @@ -546,7 +546,7 @@ struct dvb_frontend* l64781_attach(const struct l64781_config* config, /* Responds to all reads with 0 */ if (l64781_readreg(state, 0x1a) != 0) { - dprintk("Read 1 returned unexpcted value\n"); + dprintk("Read 1 returned unexpected value\n"); goto error; } @@ -555,7 +555,7 @@ struct dvb_frontend* l64781_attach(const struct l64781_config* config, /* Responds with register default value */ if (l64781_readreg(state, 0x1a) != 0xa1) { - dprintk("Read 2 returned unexpcted value\n"); + dprintk("Read 2 returned unexpected value\n"); goto error; } diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c index 7eb4e1469d20..32de824476db 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.c +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -1784,7 +1784,7 @@ static int lgdt3306a_get_tune_settings(struct dvb_frontend *fe, return 0; } -static int lgdt3306a_search(struct dvb_frontend *fe) +static enum dvbfe_search lgdt3306a_search(struct dvb_frontend *fe) { enum fe_status status = 0; int ret; diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c index 8ad03bd81af5..f6731738b073 100644 --- a/drivers/media/dvb-frontends/lgdt330x.c +++ b/drivers/media/dvb-frontends/lgdt330x.c @@ -47,50 +47,50 @@ static int debug; module_param(debug, int, 0644); -MODULE_PARM_DESC(debug,"Turn on/off lgdt330x frontend debugging (default:off)."); -#define dprintk(args...) \ -do { \ -if (debug) printk(KERN_DEBUG "lgdt330x: " args); \ +MODULE_PARM_DESC(debug, "Turn on/off lgdt330x frontend debugging (default:off)."); + +#define dprintk(state, fmt, arg...) do { \ + if (debug) \ + dev_printk(KERN_DEBUG, &state->client->dev, fmt, ##arg);\ } while (0) -struct lgdt330x_state -{ - struct i2c_adapter* i2c; +struct lgdt330x_state { + struct i2c_client *client; /* Configuration settings */ - const struct lgdt330x_config* config; + struct lgdt330x_config config; struct dvb_frontend frontend; /* Demodulator private data */ enum fe_modulation current_modulation; - u32 snr; /* Result of last SNR calculation */ + u32 snr; /* Result of last SNR calculation */ + u16 ucblocks; + unsigned long last_stats_time; /* Tuner private data */ u32 current_frequency; }; -static int i2c_write_demod_bytes (struct lgdt330x_state* state, - u8 *buf, /* data bytes to send */ - int len /* number of bytes to send */ ) +static int i2c_write_demod_bytes(struct lgdt330x_state *state, + const u8 *buf, /* data bytes to send */ + int len /* number of bytes to send */) { - struct i2c_msg msg = - { .addr = state->config->demod_address, - .flags = 0, - .buf = buf, - .len = 2 }; int i; int err; - for (i=0; i<len-1; i+=2){ - if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "lgdt330x: %s error (addr %02x <- %02x, err = %i)\n", __func__, msg.buf[0], msg.buf[1], err); + for (i = 0; i < len - 1; i += 2) { + err = i2c_master_send(state->client, buf, 2); + if (err != 2) { + dev_warn(&state->client->dev, + "%s: error (addr %02x <- %02x, err = %i)\n", + __func__, buf[0], buf[1], err); if (err < 0) return err; else return -EREMOTEIO; } - msg.buf += 2; + buf += 2; } return 0; } @@ -99,21 +99,30 @@ static int i2c_write_demod_bytes (struct lgdt330x_state* state, * This routine writes the register (reg) to the demod bus * then reads the data returned for (len) bytes. */ - static int i2c_read_demod_bytes(struct lgdt330x_state *state, enum I2C_REG reg, u8 *buf, int len) { - u8 wr [] = { reg }; - struct i2c_msg msg [] = { - { .addr = state->config->demod_address, - .flags = 0, .buf = wr, .len = 1 }, - { .addr = state->config->demod_address, - .flags = I2C_M_RD, .buf = buf, .len = len }, + u8 wr[] = { reg }; + struct i2c_msg msg[] = { + { + .addr = state->client->addr, + .flags = 0, + .buf = wr, + .len = 1 + }, { + .addr = state->client->addr, + .flags = I2C_M_RD, + .buf = buf, + .len = len + }, }; int ret; - ret = i2c_transfer(state->i2c, msg, 2); + + ret = i2c_transfer(state->client->adapter, msg, 2); if (ret != 2) { - printk(KERN_WARNING "lgdt330x: %s: addr 0x%02x select 0x%02x error (ret == %i)\n", __func__, state->config->demod_address, reg, ret); + dev_warn(&state->client->dev, + "%s: addr 0x%02x select 0x%02x error (ret == %i)\n", + __func__, state->client->addr, reg, ret); if (ret >= 0) ret = -EIO; } else { @@ -123,19 +132,21 @@ static int i2c_read_demod_bytes(struct lgdt330x_state *state, } /* Software reset */ -static int lgdt3302_SwReset(struct lgdt330x_state* state) +static int lgdt3302_sw_reset(struct lgdt330x_state *state) { u8 ret; u8 reset[] = { IRQ_MASK, - 0x00 /* bit 6 is active low software reset - * bits 5-0 are 1 to mask interrupts */ + /* + * bit 6 is active low software reset + * bits 5-0 are 1 to mask interrupts + */ + 0x00 }; ret = i2c_write_demod_bytes(state, reset, sizeof(reset)); if (ret == 0) { - /* force reset high (inactive) and unmask interrupts */ reset[1] = 0x7f; ret = i2c_write_demod_bytes(state, @@ -144,7 +155,7 @@ static int lgdt3302_SwReset(struct lgdt330x_state* state) return ret; } -static int lgdt3303_SwReset(struct lgdt330x_state* state) +static int lgdt3303_sw_reset(struct lgdt330x_state *state) { u8 ret; u8 reset[] = { @@ -155,7 +166,6 @@ static int lgdt3303_SwReset(struct lgdt330x_state* state) ret = i2c_write_demod_bytes(state, reset, sizeof(reset)); if (ret == 0) { - /* force reset high (inactive) */ reset[1] = 0x01; ret = i2c_write_demod_bytes(state, @@ -164,81 +174,94 @@ static int lgdt3303_SwReset(struct lgdt330x_state* state) return ret; } -static int lgdt330x_SwReset(struct lgdt330x_state* state) +static int lgdt330x_sw_reset(struct lgdt330x_state *state) { - switch (state->config->demod_chip) { + switch (state->config.demod_chip) { case LGDT3302: - return lgdt3302_SwReset(state); + return lgdt3302_sw_reset(state); case LGDT3303: - return lgdt3303_SwReset(state); + return lgdt3303_sw_reset(state); default: return -ENODEV; } } -static int lgdt330x_init(struct dvb_frontend* fe) +static int lgdt330x_init(struct dvb_frontend *fe) { - /* Hardware reset is done using gpio[0] of cx23880x chip. - * I'd like to do it here, but don't know how to find chip address. - * cx88-cards.c arranges for the reset bit to be inactive (high). - * Maybe there needs to be a callable function in cx88-core or - * the caller of this function needs to do it. */ - + struct lgdt330x_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + char *chip_name; + int err; /* * Array of byte pairs <address, value> * to initialize each different chip */ - static u8 lgdt3302_init_data[] = { - /* Use 50MHz parameter values from spec sheet since xtal is 50 */ - /* Change the value of NCOCTFV[25:0] of carrier - recovery center frequency register */ + static const u8 lgdt3302_init_data[] = { + /* Use 50MHz param values from spec sheet since xtal is 50 */ + /* + * Change the value of NCOCTFV[25:0] of carrier + * recovery center frequency register + */ VSB_CARRIER_FREQ0, 0x00, VSB_CARRIER_FREQ1, 0x87, VSB_CARRIER_FREQ2, 0x8e, VSB_CARRIER_FREQ3, 0x01, - /* Change the TPCLK pin polarity - data is valid on falling clock */ + /* + * Change the TPCLK pin polarity + * data is valid on falling clock + */ DEMUX_CONTROL, 0xfb, - /* Change the value of IFBW[11:0] of - AGC IF/RF loop filter bandwidth register */ + /* + * Change the value of IFBW[11:0] of + * AGC IF/RF loop filter bandwidth register + */ AGC_RF_BANDWIDTH0, 0x40, AGC_RF_BANDWIDTH1, 0x93, AGC_RF_BANDWIDTH2, 0x00, - /* Change the value of bit 6, 'nINAGCBY' and - 'NSSEL[1:0] of ACG function control register 2 */ + /* + * Change the value of bit 6, 'nINAGCBY' and + * 'NSSEL[1:0] of ACG function control register 2 + */ AGC_FUNC_CTRL2, 0xc6, - /* Change the value of bit 6 'RFFIX' - of AGC function control register 3 */ + /* + * Change the value of bit 6 'RFFIX' + * of AGC function control register 3 + */ AGC_FUNC_CTRL3, 0x40, - /* Set the value of 'INLVTHD' register 0x2a/0x2c - to 0x7fe */ + /* + * Set the value of 'INLVTHD' register 0x2a/0x2c + * to 0x7fe + */ AGC_DELAY0, 0x07, AGC_DELAY2, 0xfe, - /* Change the value of IAGCBW[15:8] - of inner AGC loop filter bandwidth */ + /* + * Change the value of IAGCBW[15:8] + * of inner AGC loop filter bandwidth + */ AGC_LOOP_BANDWIDTH0, 0x08, AGC_LOOP_BANDWIDTH1, 0x9a }; - - static u8 lgdt3303_init_data[] = { + static const u8 lgdt3303_init_data[] = { 0x4c, 0x14 }; - - static u8 flip_1_lgdt3303_init_data[] = { + static const u8 flip_1_lgdt3303_init_data[] = { 0x4c, 0x14, 0x87, 0xf3 }; - - static u8 flip_2_lgdt3303_init_data[] = { + static const u8 flip_2_lgdt3303_init_data[] = { 0x4c, 0x14, 0x87, 0xda }; - struct lgdt330x_state* state = fe->demodulator_priv; - char *chip_name; - int err; + /* + * Hardware reset is done using gpio[0] of cx23880x chip. + * I'd like to do it here, but don't know how to find chip address. + * cx88-cards.c arranges for the reset bit to be inactive (high). + * Maybe there needs to be a callable function in cx88-core or + * the caller of this function needs to do it. + */ - switch (state->config->demod_chip) { + switch (state->config.demod_chip) { case LGDT3302: chip_name = "LGDT3302"; err = i2c_write_demod_bytes(state, lgdt3302_init_data, @@ -246,16 +269,16 @@ static int lgdt330x_init(struct dvb_frontend* fe) break; case LGDT3303: chip_name = "LGDT3303"; - switch (state->config->clock_polarity_flip) { + switch (state->config.clock_polarity_flip) { case 2: err = i2c_write_demod_bytes(state, - flip_2_lgdt3303_init_data, - sizeof(flip_2_lgdt3303_init_data)); + flip_2_lgdt3303_init_data, + sizeof(flip_2_lgdt3303_init_data)); break; case 1: err = i2c_write_demod_bytes(state, - flip_1_lgdt3303_init_data, - sizeof(flip_1_lgdt3303_init_data)); + flip_1_lgdt3303_init_data, + sizeof(flip_1_lgdt3303_init_data)); break; case 0: default: @@ -265,70 +288,55 @@ static int lgdt330x_init(struct dvb_frontend* fe) break; default: chip_name = "undefined"; - printk (KERN_WARNING "Only LGDT3302 and LGDT3303 are supported chips.\n"); + dev_warn(&state->client->dev, + "Only LGDT3302 and LGDT3303 are supported chips.\n"); err = -ENODEV; } - dprintk("%s entered as %s\n", __func__, chip_name); + dprintk(state, "Initialized the %s chip\n", chip_name); if (err < 0) return err; - return lgdt330x_SwReset(state); -} -static int lgdt330x_read_ber(struct dvb_frontend* fe, u32* ber) -{ - *ber = 0; /* Not supplied by the demod chips */ - return 0; + p->cnr.len = 1; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.len = 1; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_count.len = 1; + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + state->last_stats_time = 0; + + return lgdt330x_sw_reset(state); } -static int lgdt330x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +static int lgdt330x_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { - struct lgdt330x_state* state = fe->demodulator_priv; - int err; - u8 buf[2]; + struct lgdt330x_state *state = fe->demodulator_priv; - *ucblocks = 0; + *ucblocks = state->ucblocks; - switch (state->config->demod_chip) { - case LGDT3302: - err = i2c_read_demod_bytes(state, LGDT3302_PACKET_ERR_COUNTER1, - buf, sizeof(buf)); - break; - case LGDT3303: - err = i2c_read_demod_bytes(state, LGDT3303_PACKET_ERR_COUNTER1, - buf, sizeof(buf)); - break; - default: - printk(KERN_WARNING - "Only LGDT3302 and LGDT3303 are supported chips.\n"); - err = -ENODEV; - } - if (err < 0) - return err; - - *ucblocks = (buf[0] << 8) | buf[1]; return 0; } static int lgdt330x_set_parameters(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct lgdt330x_state *state = fe->demodulator_priv; /* * Array of byte pairs <address, value> * to initialize 8VSB for lgdt3303 chip 50 MHz IF */ - static u8 lgdt3303_8vsb_44_data[] = { + static const u8 lgdt3303_8vsb_44_data[] = { 0x04, 0x00, 0x0d, 0x40, 0x0e, 0x87, 0x0f, 0x8e, 0x10, 0x01, - 0x47, 0x8b }; - + 0x47, 0x8b + }; /* * Array of byte pairs <address, value> * to initialize QAM for lgdt3303 chip */ - static u8 lgdt3303_qam_data[] = { + static const u8 lgdt3303_qam_data[] = { 0x04, 0x00, 0x0d, 0x00, 0x0e, 0x00, @@ -339,98 +347,105 @@ static int lgdt330x_set_parameters(struct dvb_frontend *fe) 0x48, 0x66, 0x4d, 0x1a, 0x49, 0x08, - 0x4a, 0x9b }; - - struct lgdt330x_state* state = fe->demodulator_priv; - - static u8 top_ctrl_cfg[] = { TOP_CONTROL, 0x03 }; + 0x4a, 0x9b + }; + u8 top_ctrl_cfg[] = { TOP_CONTROL, 0x03 }; int err = 0; /* Change only if we are actually changing the modulation */ if (state->current_modulation != p->modulation) { switch (p->modulation) { case VSB_8: - dprintk("%s: VSB_8 MODE\n", __func__); + dprintk(state, "VSB_8 MODE\n"); /* Select VSB mode */ top_ctrl_cfg[1] = 0x03; /* Select ANT connector if supported by card */ - if (state->config->pll_rf_set) - state->config->pll_rf_set(fe, 1); + if (state->config.pll_rf_set) + state->config.pll_rf_set(fe, 1); - if (state->config->demod_chip == LGDT3303) { - err = i2c_write_demod_bytes(state, lgdt3303_8vsb_44_data, + if (state->config.demod_chip == LGDT3303) { + err = i2c_write_demod_bytes(state, + lgdt3303_8vsb_44_data, sizeof(lgdt3303_8vsb_44_data)); } break; case QAM_64: - dprintk("%s: QAM_64 MODE\n", __func__); + dprintk(state, "QAM_64 MODE\n"); /* Select QAM_64 mode */ top_ctrl_cfg[1] = 0x00; /* Select CABLE connector if supported by card */ - if (state->config->pll_rf_set) - state->config->pll_rf_set(fe, 0); + if (state->config.pll_rf_set) + state->config.pll_rf_set(fe, 0); - if (state->config->demod_chip == LGDT3303) { - err = i2c_write_demod_bytes(state, lgdt3303_qam_data, - sizeof(lgdt3303_qam_data)); + if (state->config.demod_chip == LGDT3303) { + err = i2c_write_demod_bytes(state, + lgdt3303_qam_data, + sizeof(lgdt3303_qam_data)); } break; case QAM_256: - dprintk("%s: QAM_256 MODE\n", __func__); + dprintk(state, "QAM_256 MODE\n"); /* Select QAM_256 mode */ top_ctrl_cfg[1] = 0x01; /* Select CABLE connector if supported by card */ - if (state->config->pll_rf_set) - state->config->pll_rf_set(fe, 0); + if (state->config.pll_rf_set) + state->config.pll_rf_set(fe, 0); - if (state->config->demod_chip == LGDT3303) { - err = i2c_write_demod_bytes(state, lgdt3303_qam_data, - sizeof(lgdt3303_qam_data)); + if (state->config.demod_chip == LGDT3303) { + err = i2c_write_demod_bytes(state, + lgdt3303_qam_data, + sizeof(lgdt3303_qam_data)); } break; default: - printk(KERN_WARNING "lgdt330x: %s: Modulation type(%d) UNSUPPORTED\n", __func__, p->modulation); + dev_warn(&state->client->dev, + "%s: Modulation type(%d) UNSUPPORTED\n", + __func__, p->modulation); return -1; } if (err < 0) - printk(KERN_WARNING "lgdt330x: %s: error blasting bytes to lgdt3303 for modulation type(%d)\n", - __func__, p->modulation); + dev_warn(&state->client->dev, + "%s: error blasting bytes to lgdt3303 for modulation type(%d)\n", + __func__, p->modulation); /* - * select serial or parallel MPEG harware interface + * select serial or parallel MPEG hardware interface * Serial: 0x04 for LGDT3302 or 0x40 for LGDT3303 * Parallel: 0x00 */ - top_ctrl_cfg[1] |= state->config->serial_mpeg; + top_ctrl_cfg[1] |= state->config.serial_mpeg; /* Select the requested mode */ i2c_write_demod_bytes(state, top_ctrl_cfg, sizeof(top_ctrl_cfg)); - if (state->config->set_ts_params) - state->config->set_ts_params(fe, 0); + if (state->config.set_ts_params) + state->config.set_ts_params(fe, 0); state->current_modulation = p->modulation; } /* Tune to the specified frequency */ if (fe->ops.tuner_ops.set_params) { fe->ops.tuner_ops.set_params(fe); - if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); } /* Keep track of the new frequency */ - /* FIXME this is the wrong way to do this... */ - /* The tuner is shared with the video4linux analog API */ + /* + * FIXME this is the wrong way to do this... + * The tuner is shared with the video4linux analog API + */ state->current_frequency = p->frequency; - lgdt330x_SwReset(state); + lgdt330x_sw_reset(state); return 0; } @@ -443,20 +458,191 @@ static int lgdt330x_get_frontend(struct dvb_frontend *fe, return 0; } +/* + * Calculate SNR estimation (scaled by 2^24) + * + * 8-VSB SNR equations from LGDT3302 and LGDT3303 datasheets, QAM + * equations from LGDT3303 datasheet. VSB is the same between the '02 + * and '03, so maybe QAM is too? Perhaps someone with a newer datasheet + * that has QAM information could verify? + * + * For 8-VSB: (two ways, take your pick) + * LGDT3302: + * SNR_EQ = 10 * log10(25 * 24^2 / EQ_MSE) + * LGDT3303: + * SNR_EQ = 10 * log10(25 * 32^2 / EQ_MSE) + * LGDT3302 & LGDT3303: + * SNR_PT = 10 * log10(25 * 32^2 / PT_MSE) (we use this one) + * For 64-QAM: + * SNR = 10 * log10( 688128 / MSEQAM) + * For 256-QAM: + * SNR = 10 * log10( 696320 / MSEQAM) + * + * We re-write the snr equation as: + * SNR * 2^24 = 10*(c - intlog10(MSE)) + * Where for 256-QAM, c = log10(696320) * 2^24, and so on. + */ +static u32 calculate_snr(u32 mse, u32 c) +{ + if (mse == 0) /* No signal */ + return 0; + + mse = intlog10(mse); + if (mse > c) { + /* + * Negative SNR, which is possible, but realisticly the + * demod will lose lock before the signal gets this bad. + * The API only allows for unsigned values, so just return 0 + */ + return 0; + } + return 10 * (c - mse); +} + +static int lgdt3302_read_snr(struct dvb_frontend *fe) +{ + struct lgdt330x_state *state = fe->demodulator_priv; + u8 buf[5]; /* read data buffer */ + u32 noise; /* noise value */ + u32 c; /* per-modulation SNR calculation constant */ + + switch (state->current_modulation) { + case VSB_8: + i2c_read_demod_bytes(state, LGDT3302_EQPH_ERR0, buf, 5); +#ifdef USE_EQMSE + /* Use Equalizer Mean-Square Error Register */ + /* SNR for ranges from -15.61 to +41.58 */ + noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2]; + c = 69765745; /* log10(25*24^2)*2^24 */ +#else + /* Use Phase Tracker Mean-Square Error Register */ + /* SNR for ranges from -13.11 to +44.08 */ + noise = ((buf[0] & 7 << 3) << 13) | (buf[3] << 8) | buf[4]; + c = 73957994; /* log10(25*32^2)*2^24 */ +#endif + break; + case QAM_64: + case QAM_256: + i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2); + noise = ((buf[0] & 3) << 8) | buf[1]; + c = state->current_modulation == QAM_64 ? 97939837 : 98026066; + /* log10(688128)*2^24 and log10(696320)*2^24 */ + break; + default: + dev_err(&state->client->dev, + "%s: Modulation set to unsupported value\n", + __func__); + + state->snr = 0; + + return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */ + } + + state->snr = calculate_snr(noise, c); + + dprintk(state, "noise = 0x%08x, snr = %d.%02d dB\n", noise, + state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16); + + return 0; +} + +static int lgdt3303_read_snr(struct dvb_frontend *fe) +{ + struct lgdt330x_state *state = fe->demodulator_priv; + u8 buf[5]; /* read data buffer */ + u32 noise; /* noise value */ + u32 c; /* per-modulation SNR calculation constant */ + + switch (state->current_modulation) { + case VSB_8: + i2c_read_demod_bytes(state, LGDT3303_EQPH_ERR0, buf, 5); +#ifdef USE_EQMSE + /* Use Equalizer Mean-Square Error Register */ + /* SNR for ranges from -16.12 to +44.08 */ + noise = ((buf[0] & 0x78) << 13) | (buf[1] << 8) | buf[2]; + c = 73957994; /* log10(25*32^2)*2^24 */ +#else + /* Use Phase Tracker Mean-Square Error Register */ + /* SNR for ranges from -13.11 to +44.08 */ + noise = ((buf[0] & 7) << 16) | (buf[3] << 8) | buf[4]; + c = 73957994; /* log10(25*32^2)*2^24 */ +#endif + break; + case QAM_64: + case QAM_256: + i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2); + noise = (buf[0] << 8) | buf[1]; + c = state->current_modulation == QAM_64 ? 97939837 : 98026066; + /* log10(688128)*2^24 and log10(696320)*2^24 */ + break; + default: + dev_err(&state->client->dev, + "%s: Modulation set to unsupported value\n", + __func__); + state->snr = 0; + return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */ + } + + state->snr = calculate_snr(noise, c); + + dprintk(state, "noise = 0x%08x, snr = %d.%02d dB\n", noise, + state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16); + + return 0; +} + +static int lgdt330x_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct lgdt330x_state *state = fe->demodulator_priv; + + *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */ + + return 0; +} + +static int lgdt330x_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + /* Calculate Strength from SNR up to 35dB */ + /* + * Even though the SNR can go higher than 35dB, there is some comfort + * factor in having a range of strong signals that can show at 100% + */ + struct lgdt330x_state *state = fe->demodulator_priv; + u16 snr; + int ret; + + ret = fe->ops.read_snr(fe, &snr); + if (ret != 0) + return ret; + /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ + /* scale the range 0 - 35*2^24 into 0 - 65535 */ + if (state->snr >= 8960 * 0x10000) + *strength = 0xffff; + else + *strength = state->snr / 8960; + + return 0; +} + + static int lgdt3302_read_status(struct dvb_frontend *fe, enum fe_status *status) { - struct lgdt330x_state* state = fe->demodulator_priv; + struct lgdt330x_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; u8 buf[3]; + int err; *status = 0; /* Reset status result */ /* AGC status register */ i2c_read_demod_bytes(state, AGC_STATUS, buf, 1); - dprintk("%s: AGC_STATUS = 0x%02x\n", __func__, buf[0]); - if ((buf[0] & 0x0c) == 0x8){ - /* Test signal does not exist flag */ - /* as well as the AGC lock flag. */ + dprintk(state, "AGC_STATUS = 0x%02x\n", buf[0]); + if ((buf[0] & 0x0c) == 0x8) { + /* + * Test signal does not exist flag + * as well as the AGC lock flag. + */ *status |= FE_HAS_SIGNAL; } @@ -465,25 +651,24 @@ static int lgdt3302_read_status(struct dvb_frontend *fe, * to see that status bit in the IRQ_STATUS register. * This is done in SwReset(); */ + /* signal status */ i2c_read_demod_bytes(state, TOP_CONTROL, buf, sizeof(buf)); - dprintk("%s: TOP_CONTROL = 0x%02x, IRO_MASK = 0x%02x, IRQ_STATUS = 0x%02x\n", __func__, buf[0], buf[1], buf[2]); - + dprintk(state, + "TOP_CONTROL = 0x%02x, IRO_MASK = 0x%02x, IRQ_STATUS = 0x%02x\n", + buf[0], buf[1], buf[2]); /* sync status */ - if ((buf[2] & 0x03) == 0x01) { + if ((buf[2] & 0x03) == 0x01) *status |= FE_HAS_SYNC; - } /* FEC error status */ - if ((buf[2] & 0x0c) == 0x08) { - *status |= FE_HAS_LOCK; - *status |= FE_HAS_VITERBI; - } + if ((buf[2] & 0x0c) == 0x08) + *status |= FE_HAS_LOCK | FE_HAS_VITERBI; /* Carrier Recovery Lock Status Register */ i2c_read_demod_bytes(state, CARRIER_LOCK, buf, 1); - dprintk("%s: CARRIER_LOCK = 0x%02x\n", __func__, buf[0]); + dprintk(state, "CARRIER_LOCK = 0x%02x\n", buf[0]); switch (state->current_modulation) { case QAM_256: case QAM_64: @@ -496,7 +681,48 @@ static int lgdt3302_read_status(struct dvb_frontend *fe, *status |= FE_HAS_CARRIER; break; default: - printk(KERN_WARNING "lgdt330x: %s: Modulation set to unsupported value\n", __func__); + dev_warn(&state->client->dev, + "%s: Modulation set to unsupported value\n", + __func__); + } + + if (!(*status & FE_HAS_LOCK)) { + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return 0; + } + + if (state->last_stats_time && + time_is_after_jiffies(state->last_stats_time)) + return 0; + + state->last_stats_time = jiffies + msecs_to_jiffies(1000); + + err = lgdt3302_read_snr(fe); + if (!err) { + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].svalue = (((u64)state->snr) * 1000) >> 24; + } else { + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + err = i2c_read_demod_bytes(state, LGDT3302_PACKET_ERR_COUNTER1, + buf, sizeof(buf)); + if (!err) { + state->ucblocks = (buf[0] << 8) | buf[1]; + + dprintk(state, "UCB = 0x%02x\n", state->ucblocks); + + p->block_error.stat[0].uvalue += state->ucblocks; + /* FIXME: what's the basis for block count */ + p->block_count.stat[0].uvalue += 10000; + + p->block_error.stat[0].scale = FE_SCALE_COUNTER; + p->block_count.stat[0].scale = FE_SCALE_COUNTER; + } else { + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } return 0; @@ -505,9 +731,10 @@ static int lgdt3302_read_status(struct dvb_frontend *fe, static int lgdt3303_read_status(struct dvb_frontend *fe, enum fe_status *status) { - struct lgdt330x_state* state = fe->demodulator_priv; - int err; + struct lgdt330x_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; u8 buf[3]; + int err; *status = 0; /* Reset status result */ @@ -516,16 +743,18 @@ static int lgdt3303_read_status(struct dvb_frontend *fe, if (err < 0) return err; - dprintk("%s: AGC_STATUS = 0x%02x\n", __func__, buf[0]); - if ((buf[0] & 0x21) == 0x01){ - /* Test input signal does not exist flag */ - /* as well as the AGC lock flag. */ + dprintk(state, "AGC_STATUS = 0x%02x\n", buf[0]); + if ((buf[0] & 0x21) == 0x01) { + /* + * Test input signal does not exist flag + * as well as the AGC lock flag. + */ *status |= FE_HAS_SIGNAL; } /* Carrier Recovery Lock Status Register */ i2c_read_demod_bytes(state, CARRIER_LOCK, buf, 1); - dprintk("%s: CARRIER_LOCK = 0x%02x\n", __func__, buf[0]); + dprintk(state, "CARRIER_LOCK = 0x%02x\n", buf[0]); switch (state->current_modulation) { case QAM_256: case QAM_64: @@ -535,6 +764,8 @@ static int lgdt3303_read_status(struct dvb_frontend *fe, else break; i2c_read_demod_bytes(state, 0x8a, buf, 1); + dprintk(state, "QAM LOCK = 0x%02x\n", buf[0]); + if ((buf[0] & 0x04) == 0x04) *status |= FE_HAS_SYNC; if ((buf[0] & 0x01) == 0x01) @@ -548,168 +779,64 @@ static int lgdt3303_read_status(struct dvb_frontend *fe, else break; i2c_read_demod_bytes(state, 0x38, buf, 1); + dprintk(state, "8-VSB LOCK = 0x%02x\n", buf[0]); + if ((buf[0] & 0x02) == 0x00) *status |= FE_HAS_SYNC; - if ((buf[0] & 0x01) == 0x01) { - *status |= FE_HAS_LOCK; - *status |= FE_HAS_VITERBI; - } + if ((buf[0] & 0xfd) == 0x01) + *status |= FE_HAS_VITERBI | FE_HAS_LOCK; break; default: - printk(KERN_WARNING "lgdt330x: %s: Modulation set to unsupported value\n", __func__); + dev_warn(&state->client->dev, + "%s: Modulation set to unsupported value\n", + __func__); } - return 0; -} - -/* Calculate SNR estimation (scaled by 2^24) - - 8-VSB SNR equations from LGDT3302 and LGDT3303 datasheets, QAM - equations from LGDT3303 datasheet. VSB is the same between the '02 - and '03, so maybe QAM is too? Perhaps someone with a newer datasheet - that has QAM information could verify? - - For 8-VSB: (two ways, take your pick) - LGDT3302: - SNR_EQ = 10 * log10(25 * 24^2 / EQ_MSE) - LGDT3303: - SNR_EQ = 10 * log10(25 * 32^2 / EQ_MSE) - LGDT3302 & LGDT3303: - SNR_PT = 10 * log10(25 * 32^2 / PT_MSE) (we use this one) - For 64-QAM: - SNR = 10 * log10( 688128 / MSEQAM) - For 256-QAM: - SNR = 10 * log10( 696320 / MSEQAM) - - We re-write the snr equation as: - SNR * 2^24 = 10*(c - intlog10(MSE)) - Where for 256-QAM, c = log10(696320) * 2^24, and so on. */ -static u32 calculate_snr(u32 mse, u32 c) -{ - if (mse == 0) /* No signal */ + if (!(*status & FE_HAS_LOCK)) { + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; return 0; + } - mse = intlog10(mse); - if (mse > c) { - /* Negative SNR, which is possible, but realisticly the - demod will lose lock before the signal gets this bad. The - API only allows for unsigned values, so just return 0 */ + if (state->last_stats_time && + time_is_after_jiffies(state->last_stats_time)) return 0; - } - return 10*(c - mse); -} -static int lgdt3302_read_snr(struct dvb_frontend* fe, u16* snr) -{ - struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; - u8 buf[5]; /* read data buffer */ - u32 noise; /* noise value */ - u32 c; /* per-modulation SNR calculation constant */ + state->last_stats_time = jiffies + msecs_to_jiffies(1000); - switch(state->current_modulation) { - case VSB_8: - i2c_read_demod_bytes(state, LGDT3302_EQPH_ERR0, buf, 5); -#ifdef USE_EQMSE - /* Use Equalizer Mean-Square Error Register */ - /* SNR for ranges from -15.61 to +41.58 */ - noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2]; - c = 69765745; /* log10(25*24^2)*2^24 */ -#else - /* Use Phase Tracker Mean-Square Error Register */ - /* SNR for ranges from -13.11 to +44.08 */ - noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4]; - c = 73957994; /* log10(25*32^2)*2^24 */ -#endif - break; - case QAM_64: - case QAM_256: - i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2); - noise = ((buf[0] & 3) << 8) | buf[1]; - c = state->current_modulation == QAM_64 ? 97939837 : 98026066; - /* log10(688128)*2^24 and log10(696320)*2^24 */ - break; - default: - printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n", - __func__); - return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */ + err = lgdt3303_read_snr(fe); + if (!err) { + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].svalue = (((u64)state->snr) * 1000) >> 24; + } else { + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } - state->snr = calculate_snr(noise, c); - *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */ - - dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __func__, noise, - state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); + err = i2c_read_demod_bytes(state, LGDT3303_PACKET_ERR_COUNTER1, + buf, sizeof(buf)); + if (!err) { + state->ucblocks = (buf[0] << 8) | buf[1]; - return 0; -} + dprintk(state, "UCB = 0x%02x\n", state->ucblocks); -static int lgdt3303_read_snr(struct dvb_frontend* fe, u16* snr) -{ - struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; - u8 buf[5]; /* read data buffer */ - u32 noise; /* noise value */ - u32 c; /* per-modulation SNR calculation constant */ + p->block_error.stat[0].uvalue += state->ucblocks; + /* FIXME: what's the basis for block count */ + p->block_count.stat[0].uvalue += 10000; - switch(state->current_modulation) { - case VSB_8: - i2c_read_demod_bytes(state, LGDT3303_EQPH_ERR0, buf, 5); -#ifdef USE_EQMSE - /* Use Equalizer Mean-Square Error Register */ - /* SNR for ranges from -16.12 to +44.08 */ - noise = ((buf[0] & 0x78) << 13) | (buf[1] << 8) | buf[2]; - c = 73957994; /* log10(25*32^2)*2^24 */ -#else - /* Use Phase Tracker Mean-Square Error Register */ - /* SNR for ranges from -13.11 to +44.08 */ - noise = ((buf[0] & 7) << 16) | (buf[3] << 8) | buf[4]; - c = 73957994; /* log10(25*32^2)*2^24 */ -#endif - break; - case QAM_64: - case QAM_256: - i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2); - noise = (buf[0] << 8) | buf[1]; - c = state->current_modulation == QAM_64 ? 97939837 : 98026066; - /* log10(688128)*2^24 and log10(696320)*2^24 */ - break; - default: - printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n", - __func__); - return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */ + p->block_error.stat[0].scale = FE_SCALE_COUNTER; + p->block_count.stat[0].scale = FE_SCALE_COUNTER; + } else { + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } - state->snr = calculate_snr(noise, c); - *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */ - - dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __func__, noise, - state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16); - return 0; } -static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength) -{ - /* Calculate Strength from SNR up to 35dB */ - /* Even though the SNR can go higher than 35dB, there is some comfort */ - /* factor in having a range of strong signals that can show at 100% */ - struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; - u16 snr; - int ret; - - ret = fe->ops.read_snr(fe, &snr); - if (ret != 0) - return ret; - /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ - /* scale the range 0 - 35*2^24 into 0 - 65535 */ - if (state->snr >= 8960 * 0x10000) - *strength = 0xffff; - else - *strength = state->snr / 8960; - - return 0; -} - -static int lgdt330x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings) +static int +lgdt330x_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fe_tune_settings) { /* I have no idea about this - it may not be needed */ fe_tune_settings->min_delay_ms = 500; @@ -718,43 +845,63 @@ static int lgdt330x_get_tune_settings(struct dvb_frontend* fe, struct dvb_fronte return 0; } -static void lgdt330x_release(struct dvb_frontend* fe) +static void lgdt330x_release(struct dvb_frontend *fe) { - struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; - kfree(state); + struct lgdt330x_state *state = fe->demodulator_priv; + struct i2c_client *client = state->client; + + dev_dbg(&client->dev, "\n"); + + i2c_unregister_device(client); +} + +static struct dvb_frontend *lgdt330x_get_dvb_frontend(struct i2c_client *client) +{ + struct lgdt330x_state *state = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return &state->frontend; } static const struct dvb_frontend_ops lgdt3302_ops; static const struct dvb_frontend_ops lgdt3303_ops; -struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config, - struct i2c_adapter* i2c) +static int lgdt330x_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct lgdt330x_state* state = NULL; + struct lgdt330x_state *state = NULL; u8 buf[1]; /* Allocate memory for the internal state */ - state = kzalloc(sizeof(struct lgdt330x_state), GFP_KERNEL); - if (state == NULL) + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) goto error; /* Setup the state */ - state->config = config; - state->i2c = i2c; + memcpy(&state->config, client->dev.platform_data, + sizeof(state->config)); + i2c_set_clientdata(client, state); + state->client = client; /* Create dvb_frontend */ - switch (config->demod_chip) { + switch (state->config.demod_chip) { case LGDT3302: - memcpy(&state->frontend.ops, &lgdt3302_ops, sizeof(struct dvb_frontend_ops)); + memcpy(&state->frontend.ops, &lgdt3302_ops, + sizeof(struct dvb_frontend_ops)); break; case LGDT3303: - memcpy(&state->frontend.ops, &lgdt3303_ops, sizeof(struct dvb_frontend_ops)); + memcpy(&state->frontend.ops, &lgdt3303_ops, + sizeof(struct dvb_frontend_ops)); break; default: goto error; } state->frontend.demodulator_priv = state; + /* Setup get frontend callback */ + state->config.get_dvb_frontend = lgdt330x_get_dvb_frontend; + /* Verify communication with demod chip */ if (i2c_read_demod_bytes(state, 2, buf, 1)) goto error; @@ -762,21 +909,44 @@ struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config, state->current_frequency = -1; state->current_modulation = -1; - return &state->frontend; + dev_info(&state->client->dev, + "Demod loaded for LGDT330%s chip\n", + state->config.demod_chip == LGDT3302 ? "2" : "3"); + + return 0; error: kfree(state); - dprintk("%s: ERROR\n",__func__); - return NULL; + if (debug) + dev_printk(KERN_DEBUG, &client->dev, "Error loading lgdt330x driver\n"); + return -ENODEV; +} +struct dvb_frontend *lgdt330x_attach(const struct lgdt330x_config *_config, + u8 demod_address, + struct i2c_adapter *i2c) +{ + struct i2c_client *client; + struct i2c_board_info board_info = {}; + struct lgdt330x_config config = *_config; + + strlcpy(board_info.type, "lgdt330x", sizeof(board_info.type)); + board_info.addr = demod_address; + board_info.platform_data = &config; + client = i2c_new_device(i2c, &board_info); + if (!client || !client->dev.driver) + return NULL; + + return lgdt330x_get_dvb_frontend(client); } +EXPORT_SYMBOL(lgdt330x_attach); static const struct dvb_frontend_ops lgdt3302_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { - .name= "LG Electronics LGDT3302 VSB/QAM Frontend", - .frequency_min= 54000000, - .frequency_max= 858000000, - .frequency_stepsize= 62500, + .name = "LG Electronics LGDT3302 VSB/QAM Frontend", + .frequency_min = 54000000, + .frequency_max = 858000000, + .frequency_stepsize = 62500, .symbol_rate_min = 5056941, /* QAM 64 */ .symbol_rate_max = 10762000, /* VSB 8 */ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB @@ -786,9 +956,8 @@ static const struct dvb_frontend_ops lgdt3302_ops = { .get_frontend = lgdt330x_get_frontend, .get_tune_settings = lgdt330x_get_tune_settings, .read_status = lgdt3302_read_status, - .read_ber = lgdt330x_read_ber, .read_signal_strength = lgdt330x_read_signal_strength, - .read_snr = lgdt3302_read_snr, + .read_snr = lgdt330x_read_snr, .read_ucblocks = lgdt330x_read_ucblocks, .release = lgdt330x_release, }; @@ -796,10 +965,10 @@ static const struct dvb_frontend_ops lgdt3302_ops = { static const struct dvb_frontend_ops lgdt3303_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, .info = { - .name= "LG Electronics LGDT3303 VSB/QAM Frontend", - .frequency_min= 54000000, - .frequency_max= 858000000, - .frequency_stepsize= 62500, + .name = "LG Electronics LGDT3303 VSB/QAM Frontend", + .frequency_min = 54000000, + .frequency_max = 858000000, + .frequency_stepsize = 62500, .symbol_rate_min = 5056941, /* QAM 64 */ .symbol_rate_max = 10762000, /* VSB 8 */ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB @@ -809,15 +978,42 @@ static const struct dvb_frontend_ops lgdt3303_ops = { .get_frontend = lgdt330x_get_frontend, .get_tune_settings = lgdt330x_get_tune_settings, .read_status = lgdt3303_read_status, - .read_ber = lgdt330x_read_ber, .read_signal_strength = lgdt330x_read_signal_strength, - .read_snr = lgdt3303_read_snr, + .read_snr = lgdt330x_read_snr, .read_ucblocks = lgdt330x_read_ucblocks, .release = lgdt330x_release, }; +static int lgdt330x_remove(struct i2c_client *client) +{ + struct lgdt330x_state *state = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + kfree(state); + + return 0; +} + +static const struct i2c_device_id lgdt330x_id_table[] = { + {"lgdt330x", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, lgdt330x_id_table); + +static struct i2c_driver lgdt330x_driver = { + .driver = { + .name = "lgdt330x", + .suppress_bind_attrs = true, + }, + .probe = lgdt330x_probe, + .remove = lgdt330x_remove, + .id_table = lgdt330x_id_table, +}; + +module_i2c_driver(lgdt330x_driver); + + MODULE_DESCRIPTION("LGDT330X (ATSC 8VSB & ITU-T J.83 AnnexB 64/256 QAM) Demodulator Driver"); MODULE_AUTHOR("Wilson Michaels"); MODULE_LICENSE("GPL"); - -EXPORT_SYMBOL(lgdt330x_attach); diff --git a/drivers/media/dvb-frontends/lgdt330x.h b/drivers/media/dvb-frontends/lgdt330x.h index 61434cbecd2c..8ab6b39d02a8 100644 --- a/drivers/media/dvb-frontends/lgdt330x.h +++ b/drivers/media/dvb-frontends/lgdt330x.h @@ -26,34 +26,41 @@ typedef enum lg_chip_t { LGDT3303 }lg_chip_type; +/** + * struct lgdt330x_config - contains lgdt330x configuration + * + * @demod_chip: LG demodulator chip LGDT3302 or LGDT3303 + * @serial_mpeg: MPEG hardware interface - 0:parallel 1:serial + * @pll_rf_set: Callback function to set PLL interface + * @set_ts_params: Callback function to set device param for start_dma + * @clock_polarity_flip: + * Flip the polarity of the mpeg data transfer clock using alternate + * init data. + * This option applies ONLY to LGDT3303 - 0:disabled (default) 1:enabled + * @get_dvb_frontend: + * returns the frontend associated with this I2C client. + * Filled by the driver. + */ struct lgdt330x_config { - /* The demodulator's i2c address */ - u8 demod_address; - - /* LG demodulator chip LGDT3302 or LGDT3303 */ lg_chip_type demod_chip; - - /* MPEG hardware interface - 0:parallel 1:serial */ int serial_mpeg; - - /* PLL interface */ int (*pll_rf_set) (struct dvb_frontend* fe, int index); - - /* Need to set device param for start_dma */ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); - - /* Flip the polarity of the mpeg data transfer clock using alternate init data - * This option applies ONLY to LGDT3303 - 0:disabled (default) 1:enabled */ int clock_polarity_flip; + + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); }; #if IS_REACHABLE(CONFIG_DVB_LGDT330X) -extern struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config, - struct i2c_adapter* i2c); +struct dvb_frontend *lgdt330x_attach(const struct lgdt330x_config *config, + u8 demod_address, + struct i2c_adapter *i2c); #else -static inline struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config, - struct i2c_adapter* i2c) +static inline +struct dvb_frontend *lgdt330x_attach(const struct lgdt330x_config *config, + u8 demod_address, + struct i2c_adapter *i2c) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 36e95196dff4..c3b1b88e2e7a 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -2055,7 +2055,7 @@ static void mb86a20s_release(struct dvb_frontend *fe) kfree(state); } -static int mb86a20s_get_frontend_algo(struct dvb_frontend *fe) +static enum dvbfe_algo mb86a20s_get_frontend_algo(struct dvb_frontend *fe) { return DVBFE_ALGO_HW; } diff --git a/drivers/media/dvb-frontends/mxl5xx.c b/drivers/media/dvb-frontends/mxl5xx.c index 483ee7d6198e..274d8fca0763 100644 --- a/drivers/media/dvb-frontends/mxl5xx.c +++ b/drivers/media/dvb-frontends/mxl5xx.c @@ -375,7 +375,7 @@ static void release(struct dvb_frontend *fe) kfree(state); } -static int get_algo(struct dvb_frontend *fe) +static enum dvbfe_algo get_algo(struct dvb_frontend *fe) { return DVBFE_ALGO_HW; } diff --git a/drivers/media/dvb-frontends/s921.c b/drivers/media/dvb-frontends/s921.c index 2d75ede77aca..6c9015236655 100644 --- a/drivers/media/dvb-frontends/s921.c +++ b/drivers/media/dvb-frontends/s921.c @@ -464,7 +464,7 @@ static int s921_tune(struct dvb_frontend *fe, return rc; } -static int s921_get_algo(struct dvb_frontend *fe) +static enum dvbfe_algo s921_get_algo(struct dvb_frontend *fe) { return DVBFE_ALGO_HW; } diff --git a/drivers/media/dvb-frontends/stv0910.c b/drivers/media/dvb-frontends/stv0910.c index 52355c14fd64..41444fa1c0bb 100644 --- a/drivers/media/dvb-frontends/stv0910.c +++ b/drivers/media/dvb-frontends/stv0910.c @@ -1200,7 +1200,6 @@ static int probe(struct stv *state) write_reg(state, RSTV0910_P1_TSCFGM, 0xC0); /* Manual speed */ write_reg(state, RSTV0910_P1_TSCFGL, 0x20); - /* Speed = 67.5 MHz */ write_reg(state, RSTV0910_P1_TSSPEED, state->tsspeed); write_reg(state, RSTV0910_P2_TSCFGH, state->tscfgh | 0x01); @@ -1208,7 +1207,6 @@ static int probe(struct stv *state) write_reg(state, RSTV0910_P2_TSCFGM, 0xC0); /* Manual speed */ write_reg(state, RSTV0910_P2_TSCFGL, 0x20); - /* Speed = 67.5 MHz */ write_reg(state, RSTV0910_P2_TSSPEED, state->tsspeed); /* Reset stream merger */ @@ -1220,6 +1218,12 @@ static int probe(struct stv *state) write_reg(state, RSTV0910_P1_I2CRPT, state->i2crpt); write_reg(state, RSTV0910_P2_I2CRPT, state->i2crpt); + write_reg(state, RSTV0910_P1_TSINSDELM, 0x17); + write_reg(state, RSTV0910_P1_TSINSDELL, 0xff); + + write_reg(state, RSTV0910_P2_TSINSDELM, 0x17); + write_reg(state, RSTV0910_P2_TSINSDELL, 0xff); + init_diseqc(state); return 0; } @@ -1320,7 +1324,7 @@ static int read_snr(struct dvb_frontend *fe) if (!get_signal_to_noise(state, &snrval)) { p->cnr.stat[0].scale = FE_SCALE_DECIBEL; - p->cnr.stat[0].uvalue = 100 * snrval; /* fix scale */ + p->cnr.stat[0].svalue = 100 * snrval; /* fix scale */ } else { p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } @@ -1633,7 +1637,7 @@ static int tune(struct dvb_frontend *fe, bool re_tune, return 0; } -static int get_algo(struct dvb_frontend *fe) +static enum dvbfe_algo get_algo(struct dvb_frontend *fe) { return DVBFE_ALGO_HW; } @@ -1784,7 +1788,8 @@ struct dvb_frontend *stv0910_attach(struct i2c_adapter *i2c, state->tscfgh = 0x20 | (cfg->parallel ? 0 : 0x40); state->tsgeneral = (cfg->parallel == 2) ? 0x02 : 0x00; state->i2crpt = 0x0A | ((cfg->rptlvl & 0x07) << 4); - state->tsspeed = 0x28; + /* use safe tsspeed value if unspecified through stv0910_cfg */ + state->tsspeed = (cfg->tsspeed ? cfg->tsspeed : 0x28); state->nr = nr; state->regoff = state->nr ? 0 : 0x200; state->search_range = 16000000; diff --git a/drivers/media/dvb-frontends/stv0910.h b/drivers/media/dvb-frontends/stv0910.h index fccd8d9b665f..f37171b7a2de 100644 --- a/drivers/media/dvb-frontends/stv0910.h +++ b/drivers/media/dvb-frontends/stv0910.h @@ -10,6 +10,7 @@ struct stv0910_cfg { u8 parallel; u8 rptlvl; u8 single; + u8 tsspeed; }; #if IS_REACHABLE(CONFIG_DVB_STV0910) diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c index 5572b39614d5..7abf6b0916ed 100644 --- a/drivers/media/dvb-frontends/tc90522.c +++ b/drivers/media/dvb-frontends/tc90522.c @@ -1,17 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Toshiba TC90522 Demodulator * * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ /* @@ -352,7 +343,7 @@ static int tc90522t_get_frontend(struct dvb_frontend *fe, mode = 1; ret = reg_read(state, 0xb0, val, 1); if (ret == 0) { - mode = (val[0] & 0xc0) >> 2; + mode = (val[0] & 0xc0) >> 6; c->transmission_mode = tm_conv[mode]; c->guard_interval = (val[0] & 0x30) >> 4; } @@ -379,7 +370,7 @@ static int tc90522t_get_frontend(struct dvb_frontend *fe, } /* layer B */ - v = (val[3] & 0x03) << 1 | (val[4] & 0xc0) >> 6; + v = (val[3] & 0x03) << 2 | (val[4] & 0xc0) >> 6; if (v == 0x0f) c->layer[1].segment_count = 0; else { diff --git a/drivers/media/dvb-frontends/tc90522.h b/drivers/media/dvb-frontends/tc90522.h index 10e585f32499..ac0e2ab51924 100644 --- a/drivers/media/dvb-frontends/tc90522.h +++ b/drivers/media/dvb-frontends/tc90522.h @@ -1,17 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Toshiba TC90522 Demodulator * * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ /* diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 541f0d28afd8..341452fe98df 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -575,6 +575,17 @@ config VIDEO_APTINA_PLL config VIDEO_SMIAPP_PLL tristate +config VIDEO_IMX258 + tristate "Sony IMX258 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the Sony + IMX258 camera. + + To compile this driver as a module, choose M here: the + module will be called imx258. + config VIDEO_IMX274 tristate "Sony IMX274 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API @@ -688,6 +699,18 @@ config VIDEO_OV5695 To compile this driver as a module, choose M here: the module will be called ov5695. +config VIDEO_OV7251 + tristate "OmniVision OV7251 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + select V4L2_FWNODE + help + This is a Video4Linux2 sensor-level driver for the OmniVision + OV7251 camera. + + To compile this driver as a module, choose M here: the + module will be called ov7251. + config VIDEO_OV772X tristate "OmniVision OV772x sensor support" depends on I2C && VIDEO_V4L2 @@ -974,6 +997,19 @@ config VIDEO_M52790 To compile this driver as a module, choose M here: the module will be called m52790. + +config VIDEO_I2C + tristate "I2C transport video support" + depends on VIDEO_V4L2 && I2C + select VIDEOBUF2_VMALLOC + ---help--- + Enable the I2C transport video support which supports the + following: + * Panasonic AMG88xx Grid-Eye Sensors + + To compile this driver as a module, choose M here: the + module will be called video-i2c + endmenu menu "Sensors used on soc_camera driver" diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index ea34aee1a85a..d679d57cd3b3 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_VIDEO_OV5647) += ov5647.o obj-$(CONFIG_VIDEO_OV5670) += ov5670.o obj-$(CONFIG_VIDEO_OV5695) += ov5695.o obj-$(CONFIG_VIDEO_OV6650) += ov6650.o +obj-$(CONFIG_VIDEO_OV7251) += ov7251.o obj-$(CONFIG_VIDEO_OV7640) += ov7640.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_OV772X) += ov772x.o @@ -96,9 +97,11 @@ obj-$(CONFIG_VIDEO_LM3646) += lm3646.o obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o obj-$(CONFIG_VIDEO_AK881X) += ak881x.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o +obj-$(CONFIG_VIDEO_I2C) += video-i2c.o obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o +obj-$(CONFIG_VIDEO_IMX258) += imx258.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o obj-$(CONFIG_SDR_MAX2175) += max2175.o diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c index 61514bae7e5c..edd25e895e5d 100644 --- a/drivers/media/i2c/adv748x/adv748x-afe.c +++ b/drivers/media/i2c/adv748x/adv748x-afe.c @@ -321,17 +321,17 @@ static const struct v4l2_subdev_video_ops adv748x_afe_video_ops = { static int adv748x_afe_propagate_pixelrate(struct adv748x_afe *afe) { struct v4l2_subdev *tx; - unsigned int width, height, fps; tx = adv748x_get_remote_sd(&afe->pads[ADV748X_AFE_SOURCE]); if (!tx) return -ENOLINK; - width = 720; - height = afe->curr_norm & V4L2_STD_525_60 ? 480 : 576; - fps = afe->curr_norm & V4L2_STD_525_60 ? 30 : 25; - - return adv748x_csi2_set_pixelrate(tx, width * height * fps); + /* + * The ADV748x ADC sampling frequency is twice the externally supplied + * clock whose frequency is required to be 28.63636 MHz. It oversamples + * with a factor of 4 resulting in a pixel rate of 14.3180180 MHz. + */ + return adv748x_csi2_set_pixelrate(tx, 14318180); } static int adv748x_afe_enum_mbus_code(struct v4l2_subdev *sd, diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c index 10d229a4f088..aecc2a84dfec 100644 --- a/drivers/media/i2c/adv748x/adv748x-hdmi.c +++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c @@ -402,8 +402,6 @@ static int adv748x_hdmi_propagate_pixelrate(struct adv748x_hdmi *hdmi) { struct v4l2_subdev *tx; struct v4l2_dv_timings timings; - struct v4l2_bt_timings *bt = &timings.bt; - unsigned int fps; tx = adv748x_get_remote_sd(&hdmi->pads[ADV748X_HDMI_SOURCE]); if (!tx) @@ -411,11 +409,7 @@ static int adv748x_hdmi_propagate_pixelrate(struct adv748x_hdmi *hdmi) adv748x_hdmi_query_dv_timings(&hdmi->sd, &timings); - fps = DIV_ROUND_CLOSEST_ULL(bt->pixelclock, - V4L2_DV_BT_FRAME_WIDTH(bt) * - V4L2_DV_BT_FRAME_HEIGHT(bt)); - - return adv748x_csi2_set_pixelrate(tx, bt->width * bt->height * fps); + return adv748x_csi2_set_pixelrate(tx, timings.bt.pixelclock); } static int adv748x_hdmi_enum_mbus_code(struct v4l2_subdev *sd, diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index d23505a411ee..5731751d3f2a 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -732,8 +732,8 @@ static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable) /* power up cec section */ adv7511_cec_write_and_or(sd, 0x4e, 0xfc, 0x01); /* legacy mode and clear all rx buffers */ + adv7511_cec_write(sd, 0x4a, 0x00); adv7511_cec_write(sd, 0x4a, 0x07); - adv7511_cec_write(sd, 0x4a, 0); adv7511_cec_write_and_or(sd, 0x11, 0xfe, 0); /* initially disable tx */ /* enabled irqs: */ /* tx: ready */ @@ -831,8 +831,8 @@ static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, */ adv7511_cec_write_and_or(sd, 0x12, ~0x70, max(1, attempts - 1) << 4); - /* blocking, clear cec tx irq status */ - adv7511_wr_and_or(sd, 0x97, 0xc7, 0x38); + /* clear cec tx irq status */ + adv7511_wr(sd, 0x97, 0x38); /* write data */ for (i = 0; i < len; i++) @@ -917,9 +917,6 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) else if (adv7511_have_hotplug(sd)) irqs |= MASK_ADV7511_EDID_RDY_INT; - adv7511_wr_and_or(sd, 0x95, 0xc0, - (state->cec_enabled_adap && enable) ? 0x39 : 0x00); - /* * This i2c write can fail (approx. 1 in 1000 writes). But it * is essential that this register is correct, so retry it @@ -933,9 +930,11 @@ static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) irqs_rd = adv7511_rd(sd, 0x94); } while (retries-- && irqs_rd != irqs); - if (irqs_rd == irqs) - return; - v4l2_err(sd, "Could not set interrupts: hw failure?\n"); + if (irqs_rd != irqs) + v4l2_err(sd, "Could not set interrupts: hw failure?\n"); + + adv7511_wr_and_or(sd, 0x95, 0xc0, + (state->cec_enabled_adap && enable) ? 0x39 : 0x00); } /* Interrupt handler */ @@ -982,8 +981,8 @@ static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled) for (i = 0; i < msg.len; i++) msg.msg[i] = adv7511_cec_read(sd, i + 0x15); - adv7511_cec_write(sd, 0x4a, 1); /* toggle to re-enable rx 1 */ - adv7511_cec_write(sd, 0x4a, 0); + adv7511_cec_write(sd, 0x4a, 0); /* toggle to re-enable rx 1 */ + adv7511_cec_write(sd, 0x4a, 1); cec_received_msg(state->cec_adap, &msg); } } @@ -1778,6 +1777,7 @@ static void adv7511_init_setup(struct v4l2_subdev *sd) /* legacy mode */ adv7511_cec_write(sd, 0x4a, 0x00); + adv7511_cec_write(sd, 0x4a, 0x07); if (cec_clk % 750000 != 0) v4l2_err(sd, "%s: cec_clk %d, not multiple of 750 Khz\n", diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c new file mode 100644 index 000000000000..f3b124723aa0 --- /dev/null +++ b/drivers/media/i2c/imx258.c @@ -0,0 +1,1318 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <asm/unaligned.h> + +#define IMX258_REG_VALUE_08BIT 1 +#define IMX258_REG_VALUE_16BIT 2 + +#define IMX258_REG_MODE_SELECT 0x0100 +#define IMX258_MODE_STANDBY 0x00 +#define IMX258_MODE_STREAMING 0x01 + +/* Chip ID */ +#define IMX258_REG_CHIP_ID 0x0016 +#define IMX258_CHIP_ID 0x0258 + +/* V_TIMING internal */ +#define IMX258_VTS_30FPS 0x0c98 +#define IMX258_VTS_30FPS_2K 0x0638 +#define IMX258_VTS_30FPS_VGA 0x034c +#define IMX258_VTS_MAX 0xffff + +/*Frame Length Line*/ +#define IMX258_FLL_MIN 0x08a6 +#define IMX258_FLL_MAX 0xffff +#define IMX258_FLL_STEP 1 +#define IMX258_FLL_DEFAULT 0x0c98 + +/* HBLANK control - read only */ +#define IMX258_PPL_DEFAULT 5352 + +/* Exposure control */ +#define IMX258_REG_EXPOSURE 0x0202 +#define IMX258_EXPOSURE_MIN 4 +#define IMX258_EXPOSURE_STEP 1 +#define IMX258_EXPOSURE_DEFAULT 0x640 +#define IMX258_EXPOSURE_MAX 65535 + +/* Analog gain control */ +#define IMX258_REG_ANALOG_GAIN 0x0204 +#define IMX258_ANA_GAIN_MIN 0 +#define IMX258_ANA_GAIN_MAX 0x1fff +#define IMX258_ANA_GAIN_STEP 1 +#define IMX258_ANA_GAIN_DEFAULT 0x0 + +/* Digital gain control */ +#define IMX258_REG_GR_DIGITAL_GAIN 0x020e +#define IMX258_REG_R_DIGITAL_GAIN 0x0210 +#define IMX258_REG_B_DIGITAL_GAIN 0x0212 +#define IMX258_REG_GB_DIGITAL_GAIN 0x0214 +#define IMX258_DGTL_GAIN_MIN 0 +#define IMX258_DGTL_GAIN_MAX 4096 /* Max = 0xFFF */ +#define IMX258_DGTL_GAIN_DEFAULT 1024 +#define IMX258_DGTL_GAIN_STEP 1 + +/* Test Pattern Control */ +#define IMX258_REG_TEST_PATTERN 0x0600 +#define IMX258_TEST_PATTERN_DISABLE 0 +#define IMX258_TEST_PATTERN_SOLID_COLOR 1 +#define IMX258_TEST_PATTERN_COLOR_BARS 2 +#define IMX258_TEST_PATTERN_GREY_COLOR 3 +#define IMX258_TEST_PATTERN_PN9 4 + +/* Orientation */ +#define REG_MIRROR_FLIP_CONTROL 0x0101 +#define REG_CONFIG_MIRROR_FLIP 0x03 +#define REG_CONFIG_FLIP_TEST_PATTERN 0x02 + +struct imx258_reg { + u16 address; + u8 val; +}; + +struct imx258_reg_list { + u32 num_of_regs; + const struct imx258_reg *regs; +}; + +/* Link frequency config */ +struct imx258_link_freq_config { + u32 pixels_per_line; + + /* PLL registers for this link frequency */ + struct imx258_reg_list reg_list; +}; + +/* Mode : resolution and related config&values */ +struct imx258_mode { + /* Frame width */ + u32 width; + /* Frame height */ + u32 height; + + /* V-timing */ + u32 vts_def; + u32 vts_min; + + /* Index of Link frequency config to be used */ + u32 link_freq_index; + /* Default register values */ + struct imx258_reg_list reg_list; +}; + +/* 4208x3118 needs 1267Mbps/lane, 4 lanes */ +static const struct imx258_reg mipi_data_rate_1267mbps[] = { + { 0x0301, 0x05 }, + { 0x0303, 0x02 }, + { 0x0305, 0x03 }, + { 0x0306, 0x00 }, + { 0x0307, 0xC6 }, + { 0x0309, 0x0A }, + { 0x030B, 0x01 }, + { 0x030D, 0x02 }, + { 0x030E, 0x00 }, + { 0x030F, 0xD8 }, + { 0x0310, 0x00 }, + { 0x0820, 0x13 }, + { 0x0821, 0x4C }, + { 0x0822, 0xCC }, + { 0x0823, 0xCC }, +}; + +static const struct imx258_reg mipi_data_rate_640mbps[] = { + { 0x0301, 0x05 }, + { 0x0303, 0x02 }, + { 0x0305, 0x03 }, + { 0x0306, 0x00 }, + { 0x0307, 0x64 }, + { 0x0309, 0x0A }, + { 0x030B, 0x01 }, + { 0x030D, 0x02 }, + { 0x030E, 0x00 }, + { 0x030F, 0xD8 }, + { 0x0310, 0x00 }, + { 0x0820, 0x0A }, + { 0x0821, 0x00 }, + { 0x0822, 0x00 }, + { 0x0823, 0x00 }, +}; + +static const struct imx258_reg mode_4208x3118_regs[] = { + { 0x0136, 0x13 }, + { 0x0137, 0x33 }, + { 0x3051, 0x00 }, + { 0x3052, 0x00 }, + { 0x4E21, 0x14 }, + { 0x6B11, 0xCF }, + { 0x7FF0, 0x08 }, + { 0x7FF1, 0x0F }, + { 0x7FF2, 0x08 }, + { 0x7FF3, 0x1B }, + { 0x7FF4, 0x23 }, + { 0x7FF5, 0x60 }, + { 0x7FF6, 0x00 }, + { 0x7FF7, 0x01 }, + { 0x7FF8, 0x00 }, + { 0x7FF9, 0x78 }, + { 0x7FFA, 0x00 }, + { 0x7FFB, 0x00 }, + { 0x7FFC, 0x00 }, + { 0x7FFD, 0x00 }, + { 0x7FFE, 0x00 }, + { 0x7FFF, 0x03 }, + { 0x7F76, 0x03 }, + { 0x7F77, 0xFE }, + { 0x7FA8, 0x03 }, + { 0x7FA9, 0xFE }, + { 0x7B24, 0x81 }, + { 0x7B25, 0x00 }, + { 0x6564, 0x07 }, + { 0x6B0D, 0x41 }, + { 0x653D, 0x04 }, + { 0x6B05, 0x8C }, + { 0x6B06, 0xF9 }, + { 0x6B08, 0x65 }, + { 0x6B09, 0xFC }, + { 0x6B0A, 0xCF }, + { 0x6B0B, 0xD2 }, + { 0x6700, 0x0E }, + { 0x6707, 0x0E }, + { 0x9104, 0x00 }, + { 0x4648, 0x7F }, + { 0x7420, 0x00 }, + { 0x7421, 0x1C }, + { 0x7422, 0x00 }, + { 0x7423, 0xD7 }, + { 0x5F04, 0x00 }, + { 0x5F05, 0xED }, + { 0x0112, 0x0A }, + { 0x0113, 0x0A }, + { 0x0114, 0x03 }, + { 0x0342, 0x14 }, + { 0x0343, 0xE8 }, + { 0x0340, 0x0C }, + { 0x0341, 0x50 }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x00 }, + { 0x0347, 0x00 }, + { 0x0348, 0x10 }, + { 0x0349, 0x6F }, + { 0x034A, 0x0C }, + { 0x034B, 0x2E }, + { 0x0381, 0x01 }, + { 0x0383, 0x01 }, + { 0x0385, 0x01 }, + { 0x0387, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0401, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x10 }, + { 0x0408, 0x00 }, + { 0x0409, 0x00 }, + { 0x040A, 0x00 }, + { 0x040B, 0x00 }, + { 0x040C, 0x10 }, + { 0x040D, 0x70 }, + { 0x040E, 0x0C }, + { 0x040F, 0x30 }, + { 0x3038, 0x00 }, + { 0x303A, 0x00 }, + { 0x303B, 0x10 }, + { 0x300D, 0x00 }, + { 0x034C, 0x10 }, + { 0x034D, 0x70 }, + { 0x034E, 0x0C }, + { 0x034F, 0x30 }, + { 0x0350, 0x01 }, + { 0x0202, 0x0C }, + { 0x0203, 0x46 }, + { 0x0204, 0x00 }, + { 0x0205, 0x00 }, + { 0x020E, 0x01 }, + { 0x020F, 0x00 }, + { 0x0210, 0x01 }, + { 0x0211, 0x00 }, + { 0x0212, 0x01 }, + { 0x0213, 0x00 }, + { 0x0214, 0x01 }, + { 0x0215, 0x00 }, + { 0x7BCD, 0x00 }, + { 0x94DC, 0x20 }, + { 0x94DD, 0x20 }, + { 0x94DE, 0x20 }, + { 0x95DC, 0x20 }, + { 0x95DD, 0x20 }, + { 0x95DE, 0x20 }, + { 0x7FB0, 0x00 }, + { 0x9010, 0x3E }, + { 0x9419, 0x50 }, + { 0x941B, 0x50 }, + { 0x9519, 0x50 }, + { 0x951B, 0x50 }, + { 0x3030, 0x00 }, + { 0x3032, 0x00 }, + { 0x0220, 0x00 }, +}; + +static const struct imx258_reg mode_2104_1560_regs[] = { + { 0x0136, 0x13 }, + { 0x0137, 0x33 }, + { 0x3051, 0x00 }, + { 0x3052, 0x00 }, + { 0x4E21, 0x14 }, + { 0x6B11, 0xCF }, + { 0x7FF0, 0x08 }, + { 0x7FF1, 0x0F }, + { 0x7FF2, 0x08 }, + { 0x7FF3, 0x1B }, + { 0x7FF4, 0x23 }, + { 0x7FF5, 0x60 }, + { 0x7FF6, 0x00 }, + { 0x7FF7, 0x01 }, + { 0x7FF8, 0x00 }, + { 0x7FF9, 0x78 }, + { 0x7FFA, 0x00 }, + { 0x7FFB, 0x00 }, + { 0x7FFC, 0x00 }, + { 0x7FFD, 0x00 }, + { 0x7FFE, 0x00 }, + { 0x7FFF, 0x03 }, + { 0x7F76, 0x03 }, + { 0x7F77, 0xFE }, + { 0x7FA8, 0x03 }, + { 0x7FA9, 0xFE }, + { 0x7B24, 0x81 }, + { 0x7B25, 0x00 }, + { 0x6564, 0x07 }, + { 0x6B0D, 0x41 }, + { 0x653D, 0x04 }, + { 0x6B05, 0x8C }, + { 0x6B06, 0xF9 }, + { 0x6B08, 0x65 }, + { 0x6B09, 0xFC }, + { 0x6B0A, 0xCF }, + { 0x6B0B, 0xD2 }, + { 0x6700, 0x0E }, + { 0x6707, 0x0E }, + { 0x9104, 0x00 }, + { 0x4648, 0x7F }, + { 0x7420, 0x00 }, + { 0x7421, 0x1C }, + { 0x7422, 0x00 }, + { 0x7423, 0xD7 }, + { 0x5F04, 0x00 }, + { 0x5F05, 0xED }, + { 0x0112, 0x0A }, + { 0x0113, 0x0A }, + { 0x0114, 0x03 }, + { 0x0342, 0x14 }, + { 0x0343, 0xE8 }, + { 0x0340, 0x06 }, + { 0x0341, 0x38 }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x00 }, + { 0x0347, 0x00 }, + { 0x0348, 0x10 }, + { 0x0349, 0x6F }, + { 0x034A, 0x0C }, + { 0x034B, 0x2E }, + { 0x0381, 0x01 }, + { 0x0383, 0x01 }, + { 0x0385, 0x01 }, + { 0x0387, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x12 }, + { 0x0401, 0x01 }, + { 0x0404, 0x00 }, + { 0x0405, 0x20 }, + { 0x0408, 0x00 }, + { 0x0409, 0x02 }, + { 0x040A, 0x00 }, + { 0x040B, 0x00 }, + { 0x040C, 0x10 }, + { 0x040D, 0x6A }, + { 0x040E, 0x06 }, + { 0x040F, 0x18 }, + { 0x3038, 0x00 }, + { 0x303A, 0x00 }, + { 0x303B, 0x10 }, + { 0x300D, 0x00 }, + { 0x034C, 0x08 }, + { 0x034D, 0x38 }, + { 0x034E, 0x06 }, + { 0x034F, 0x18 }, + { 0x0350, 0x01 }, + { 0x0202, 0x06 }, + { 0x0203, 0x2E }, + { 0x0204, 0x00 }, + { 0x0205, 0x00 }, + { 0x020E, 0x01 }, + { 0x020F, 0x00 }, + { 0x0210, 0x01 }, + { 0x0211, 0x00 }, + { 0x0212, 0x01 }, + { 0x0213, 0x00 }, + { 0x0214, 0x01 }, + { 0x0215, 0x00 }, + { 0x7BCD, 0x01 }, + { 0x94DC, 0x20 }, + { 0x94DD, 0x20 }, + { 0x94DE, 0x20 }, + { 0x95DC, 0x20 }, + { 0x95DD, 0x20 }, + { 0x95DE, 0x20 }, + { 0x7FB0, 0x00 }, + { 0x9010, 0x3E }, + { 0x9419, 0x50 }, + { 0x941B, 0x50 }, + { 0x9519, 0x50 }, + { 0x951B, 0x50 }, + { 0x3030, 0x00 }, + { 0x3032, 0x00 }, + { 0x0220, 0x00 }, +}; + +static const struct imx258_reg mode_1048_780_regs[] = { + { 0x0136, 0x13 }, + { 0x0137, 0x33 }, + { 0x3051, 0x00 }, + { 0x3052, 0x00 }, + { 0x4E21, 0x14 }, + { 0x6B11, 0xCF }, + { 0x7FF0, 0x08 }, + { 0x7FF1, 0x0F }, + { 0x7FF2, 0x08 }, + { 0x7FF3, 0x1B }, + { 0x7FF4, 0x23 }, + { 0x7FF5, 0x60 }, + { 0x7FF6, 0x00 }, + { 0x7FF7, 0x01 }, + { 0x7FF8, 0x00 }, + { 0x7FF9, 0x78 }, + { 0x7FFA, 0x00 }, + { 0x7FFB, 0x00 }, + { 0x7FFC, 0x00 }, + { 0x7FFD, 0x00 }, + { 0x7FFE, 0x00 }, + { 0x7FFF, 0x03 }, + { 0x7F76, 0x03 }, + { 0x7F77, 0xFE }, + { 0x7FA8, 0x03 }, + { 0x7FA9, 0xFE }, + { 0x7B24, 0x81 }, + { 0x7B25, 0x00 }, + { 0x6564, 0x07 }, + { 0x6B0D, 0x41 }, + { 0x653D, 0x04 }, + { 0x6B05, 0x8C }, + { 0x6B06, 0xF9 }, + { 0x6B08, 0x65 }, + { 0x6B09, 0xFC }, + { 0x6B0A, 0xCF }, + { 0x6B0B, 0xD2 }, + { 0x6700, 0x0E }, + { 0x6707, 0x0E }, + { 0x9104, 0x00 }, + { 0x4648, 0x7F }, + { 0x7420, 0x00 }, + { 0x7421, 0x1C }, + { 0x7422, 0x00 }, + { 0x7423, 0xD7 }, + { 0x5F04, 0x00 }, + { 0x5F05, 0xED }, + { 0x0112, 0x0A }, + { 0x0113, 0x0A }, + { 0x0114, 0x03 }, + { 0x0342, 0x14 }, + { 0x0343, 0xE8 }, + { 0x0340, 0x03 }, + { 0x0341, 0x4C }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x00 }, + { 0x0347, 0x00 }, + { 0x0348, 0x10 }, + { 0x0349, 0x6F }, + { 0x034A, 0x0C }, + { 0x034B, 0x2E }, + { 0x0381, 0x01 }, + { 0x0383, 0x01 }, + { 0x0385, 0x01 }, + { 0x0387, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x14 }, + { 0x0401, 0x01 }, + { 0x0404, 0x00 }, + { 0x0405, 0x40 }, + { 0x0408, 0x00 }, + { 0x0409, 0x06 }, + { 0x040A, 0x00 }, + { 0x040B, 0x00 }, + { 0x040C, 0x10 }, + { 0x040D, 0x64 }, + { 0x040E, 0x03 }, + { 0x040F, 0x0C }, + { 0x3038, 0x00 }, + { 0x303A, 0x00 }, + { 0x303B, 0x10 }, + { 0x300D, 0x00 }, + { 0x034C, 0x04 }, + { 0x034D, 0x18 }, + { 0x034E, 0x03 }, + { 0x034F, 0x0C }, + { 0x0350, 0x01 }, + { 0x0202, 0x03 }, + { 0x0203, 0x42 }, + { 0x0204, 0x00 }, + { 0x0205, 0x00 }, + { 0x020E, 0x01 }, + { 0x020F, 0x00 }, + { 0x0210, 0x01 }, + { 0x0211, 0x00 }, + { 0x0212, 0x01 }, + { 0x0213, 0x00 }, + { 0x0214, 0x01 }, + { 0x0215, 0x00 }, + { 0x7BCD, 0x00 }, + { 0x94DC, 0x20 }, + { 0x94DD, 0x20 }, + { 0x94DE, 0x20 }, + { 0x95DC, 0x20 }, + { 0x95DD, 0x20 }, + { 0x95DE, 0x20 }, + { 0x7FB0, 0x00 }, + { 0x9010, 0x3E }, + { 0x9419, 0x50 }, + { 0x941B, 0x50 }, + { 0x9519, 0x50 }, + { 0x951B, 0x50 }, + { 0x3030, 0x00 }, + { 0x3032, 0x00 }, + { 0x0220, 0x00 }, +}; + +static const char * const imx258_test_pattern_menu[] = { + "Disabled", + "Color Bars", + "Solid Color", + "Grey Color Bars", + "PN9" +}; + +static const int imx258_test_pattern_val[] = { + IMX258_TEST_PATTERN_DISABLE, + IMX258_TEST_PATTERN_COLOR_BARS, + IMX258_TEST_PATTERN_SOLID_COLOR, + IMX258_TEST_PATTERN_GREY_COLOR, + IMX258_TEST_PATTERN_PN9, +}; + +/* Configurations for supported link frequencies */ +#define IMX258_LINK_FREQ_634MHZ 633600000ULL +#define IMX258_LINK_FREQ_320MHZ 320000000ULL + +enum { + IMX258_LINK_FREQ_1267MBPS, + IMX258_LINK_FREQ_640MBPS, +}; + +/* + * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample + * data rate => double data rate; number of lanes => 4; bits per pixel => 10 + */ +static u64 link_freq_to_pixel_rate(u64 f) +{ + f *= 2 * 4; + do_div(f, 10); + + return f; +} + +/* Menu items for LINK_FREQ V4L2 control */ +static const s64 link_freq_menu_items[] = { + IMX258_LINK_FREQ_634MHZ, + IMX258_LINK_FREQ_320MHZ, +}; + +/* Link frequency configs */ +static const struct imx258_link_freq_config link_freq_configs[] = { + [IMX258_LINK_FREQ_1267MBPS] = { + .pixels_per_line = IMX258_PPL_DEFAULT, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_1267mbps), + .regs = mipi_data_rate_1267mbps, + } + }, + [IMX258_LINK_FREQ_640MBPS] = { + .pixels_per_line = IMX258_PPL_DEFAULT, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_data_rate_640mbps), + .regs = mipi_data_rate_640mbps, + } + }, +}; + +/* Mode configs */ +static const struct imx258_mode supported_modes[] = { + { + .width = 4208, + .height = 3118, + .vts_def = IMX258_VTS_30FPS, + .vts_min = IMX258_VTS_30FPS, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_4208x3118_regs), + .regs = mode_4208x3118_regs, + }, + .link_freq_index = IMX258_LINK_FREQ_1267MBPS, + }, + { + .width = 2104, + .height = 1560, + .vts_def = IMX258_VTS_30FPS_2K, + .vts_min = IMX258_VTS_30FPS_2K, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_2104_1560_regs), + .regs = mode_2104_1560_regs, + }, + .link_freq_index = IMX258_LINK_FREQ_640MBPS, + }, + { + .width = 1048, + .height = 780, + .vts_def = IMX258_VTS_30FPS_VGA, + .vts_min = IMX258_VTS_30FPS_VGA, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1048_780_regs), + .regs = mode_1048_780_regs, + }, + .link_freq_index = IMX258_LINK_FREQ_640MBPS, + }, +}; + +struct imx258 { + struct v4l2_subdev sd; + struct media_pad pad; + + struct v4l2_ctrl_handler ctrl_handler; + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + + /* Current mode */ + const struct imx258_mode *cur_mode; + + /* + * Mutex for serialized access: + * Protect sensor module set pad format and start/stop streaming safely. + */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static inline struct imx258 *to_imx258(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct imx258, sd); +} + +/* Read registers up to 2 at a time */ +static int imx258_read_reg(struct imx258 *imx258, u16 reg, u32 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2] = { reg >> 8, reg & 0xff }; + u8 data_buf[4] = { 0, }; + int ret; + + if (len > 4) + return -EINVAL; + + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +/* Write registers up to 2 at a time */ +static int imx258_write_reg(struct imx258 *imx258, u16 reg, u32 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + u8 buf[6]; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << (8 * (4 - len)), buf + 2); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +/* Write a list of registers */ +static int imx258_write_regs(struct imx258 *imx258, + const struct imx258_reg *regs, u32 len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + unsigned int i; + int ret; + + for (i = 0; i < len; i++) { + ret = imx258_write_reg(imx258, regs[i].address, 1, + regs[i].val); + if (ret) { + dev_err_ratelimited( + &client->dev, + "Failed to write reg 0x%4.4x. error = %d\n", + regs[i].address, ret); + + return ret; + } + } + + return 0; +} + +/* Open sub-device */ +static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + + /* Initialize try_fmt */ + try_fmt->width = supported_modes[0].width; + try_fmt->height = supported_modes[0].height; + try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + try_fmt->field = V4L2_FIELD_NONE; + + return 0; +} + +static int imx258_update_digital_gain(struct imx258 *imx258, u32 len, u32 val) +{ + int ret; + + ret = imx258_write_reg(imx258, IMX258_REG_GR_DIGITAL_GAIN, + IMX258_REG_VALUE_16BIT, + val); + if (ret) + return ret; + ret = imx258_write_reg(imx258, IMX258_REG_GB_DIGITAL_GAIN, + IMX258_REG_VALUE_16BIT, + val); + if (ret) + return ret; + ret = imx258_write_reg(imx258, IMX258_REG_R_DIGITAL_GAIN, + IMX258_REG_VALUE_16BIT, + val); + if (ret) + return ret; + ret = imx258_write_reg(imx258, IMX258_REG_B_DIGITAL_GAIN, + IMX258_REG_VALUE_16BIT, + val); + if (ret) + return ret; + return 0; +} + +static int imx258_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx258 *imx258 = + container_of(ctrl->handler, struct imx258, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + int ret = 0; + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (pm_runtime_get_if_in_use(&client->dev) == 0) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = imx258_write_reg(imx258, IMX258_REG_ANALOG_GAIN, + IMX258_REG_VALUE_16BIT, + ctrl->val); + break; + case V4L2_CID_EXPOSURE: + ret = imx258_write_reg(imx258, IMX258_REG_EXPOSURE, + IMX258_REG_VALUE_16BIT, + ctrl->val); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = imx258_update_digital_gain(imx258, IMX258_REG_VALUE_16BIT, + ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = imx258_write_reg(imx258, IMX258_REG_TEST_PATTERN, + IMX258_REG_VALUE_16BIT, + imx258_test_pattern_val[ctrl->val]); + + ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL, + IMX258_REG_VALUE_08BIT, + ctrl->val == imx258_test_pattern_val + [IMX258_TEST_PATTERN_DISABLE] ? + REG_CONFIG_MIRROR_FLIP : + REG_CONFIG_FLIP_TEST_PATTERN); + break; + default: + dev_info(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + ret = -EINVAL; + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx258_ctrl_ops = { + .s_ctrl = imx258_set_ctrl, +}; + +static int imx258_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + /* Only one bayer order(GRBG) is supported */ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + + return 0; +} + +static int imx258_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static void imx258_update_pad_format(const struct imx258_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->format.field = V4L2_FIELD_NONE; +} + +static int __imx258_get_pad_format(struct imx258 *imx258, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + fmt->format = *v4l2_subdev_get_try_format(&imx258->sd, cfg, + fmt->pad); + else + imx258_update_pad_format(imx258->cur_mode, fmt); + + return 0; +} + +static int imx258_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx258 *imx258 = to_imx258(sd); + int ret; + + mutex_lock(&imx258->mutex); + ret = __imx258_get_pad_format(imx258, cfg, fmt); + mutex_unlock(&imx258->mutex); + + return ret; +} + +static int imx258_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx258 *imx258 = to_imx258(sd); + const struct imx258_mode *mode; + struct v4l2_mbus_framefmt *framefmt; + s32 vblank_def; + s32 vblank_min; + s64 h_blank; + s64 pixel_rate; + s64 link_freq; + + mutex_lock(&imx258->mutex); + + /* Only one raw bayer(GBRG) order is supported */ + fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), width, height, + fmt->format.width, fmt->format.height); + imx258_update_pad_format(mode, fmt); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + *framefmt = fmt->format; + } else { + imx258->cur_mode = mode; + __v4l2_ctrl_s_ctrl(imx258->link_freq, mode->link_freq_index); + + link_freq = link_freq_menu_items[mode->link_freq_index]; + pixel_rate = link_freq_to_pixel_rate(link_freq); + __v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, pixel_rate); + /* Update limits and set FPS to default */ + vblank_def = imx258->cur_mode->vts_def - + imx258->cur_mode->height; + vblank_min = imx258->cur_mode->vts_min - + imx258->cur_mode->height; + __v4l2_ctrl_modify_range( + imx258->vblank, vblank_min, + IMX258_VTS_MAX - imx258->cur_mode->height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(imx258->vblank, vblank_def); + h_blank = + link_freq_configs[mode->link_freq_index].pixels_per_line + - imx258->cur_mode->width; + __v4l2_ctrl_modify_range(imx258->hblank, h_blank, + h_blank, 1, h_blank); + } + + mutex_unlock(&imx258->mutex); + + return 0; +} + +/* Start streaming */ +static int imx258_start_streaming(struct imx258 *imx258) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + const struct imx258_reg_list *reg_list; + int ret, link_freq_index; + + /* Setup PLL */ + link_freq_index = imx258->cur_mode->link_freq_index; + reg_list = &link_freq_configs[link_freq_index].reg_list; + ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); + if (ret) { + dev_err(&client->dev, "%s failed to set plls\n", __func__); + return ret; + } + + /* Apply default values of current mode */ + reg_list = &imx258->cur_mode->reg_list; + ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); + if (ret) { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + return ret; + } + + /* Set Orientation be 180 degree */ + ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL, + IMX258_REG_VALUE_08BIT, REG_CONFIG_MIRROR_FLIP); + if (ret) { + dev_err(&client->dev, "%s failed to set orientation\n", + __func__); + return ret; + } + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(imx258->sd.ctrl_handler); + if (ret) + return ret; + + /* set stream on register */ + return imx258_write_reg(imx258, IMX258_REG_MODE_SELECT, + IMX258_REG_VALUE_08BIT, + IMX258_MODE_STREAMING); +} + +/* Stop streaming */ +static int imx258_stop_streaming(struct imx258 *imx258) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + int ret; + + /* set stream off register */ + ret = imx258_write_reg(imx258, IMX258_REG_MODE_SELECT, + IMX258_REG_VALUE_08BIT, IMX258_MODE_STANDBY); + if (ret) + dev_err(&client->dev, "%s failed to set stream\n", __func__); + + /* + * Return success even if it was an error, as there is nothing the + * caller can do about it. + */ + return 0; +} + +static int imx258_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct imx258 *imx258 = to_imx258(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + mutex_lock(&imx258->mutex); + if (imx258->streaming == enable) { + mutex_unlock(&imx258->mutex); + return 0; + } + + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto err_unlock; + } + + /* + * Apply default & customized values + * and then start streaming. + */ + ret = imx258_start_streaming(imx258); + if (ret) + goto err_rpm_put; + } else { + imx258_stop_streaming(imx258); + pm_runtime_put(&client->dev); + } + + imx258->streaming = enable; + mutex_unlock(&imx258->mutex); + + return ret; + +err_rpm_put: + pm_runtime_put(&client->dev); +err_unlock: + mutex_unlock(&imx258->mutex); + + return ret; +} + +static int __maybe_unused imx258_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx258 *imx258 = to_imx258(sd); + + if (imx258->streaming) + imx258_stop_streaming(imx258); + + return 0; +} + +static int __maybe_unused imx258_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx258 *imx258 = to_imx258(sd); + int ret; + + if (imx258->streaming) { + ret = imx258_start_streaming(imx258); + if (ret) + goto error; + } + + return 0; + +error: + imx258_stop_streaming(imx258); + imx258->streaming = 0; + return ret; +} + +/* Verify chip ID */ +static int imx258_identify_module(struct imx258 *imx258) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + int ret; + u32 val; + + ret = imx258_read_reg(imx258, IMX258_REG_CHIP_ID, + IMX258_REG_VALUE_16BIT, &val); + if (ret) { + dev_err(&client->dev, "failed to read chip id %x\n", + IMX258_CHIP_ID); + return ret; + } + + if (val != IMX258_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + IMX258_CHIP_ID, val); + return -EIO; + } + + return 0; +} + +static const struct v4l2_subdev_video_ops imx258_video_ops = { + .s_stream = imx258_set_stream, +}; + +static const struct v4l2_subdev_pad_ops imx258_pad_ops = { + .enum_mbus_code = imx258_enum_mbus_code, + .get_fmt = imx258_get_pad_format, + .set_fmt = imx258_set_pad_format, + .enum_frame_size = imx258_enum_frame_size, +}; + +static const struct v4l2_subdev_ops imx258_subdev_ops = { + .video = &imx258_video_ops, + .pad = &imx258_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops imx258_internal_ops = { + .open = imx258_open, +}; + +/* Initialize control handlers */ +static int imx258_init_controls(struct imx258 *imx258) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 vblank_def; + s64 vblank_min; + s64 pixel_rate_min; + s64 pixel_rate_max; + int ret; + + ctrl_hdlr = &imx258->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + if (ret) + return ret; + + mutex_init(&imx258->mutex); + ctrl_hdlr->lock = &imx258->mutex; + imx258->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &imx258_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_menu_items) - 1, + 0, + link_freq_menu_items); + + if (imx258->link_freq) + imx258->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]); + pixel_rate_min = link_freq_to_pixel_rate(link_freq_menu_items[1]); + /* By default, PIXEL_RATE is read only */ + imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, + V4L2_CID_PIXEL_RATE, + pixel_rate_min, pixel_rate_max, + 1, pixel_rate_max); + + + vblank_def = imx258->cur_mode->vts_def - imx258->cur_mode->height; + vblank_min = imx258->cur_mode->vts_min - imx258->cur_mode->height; + imx258->vblank = v4l2_ctrl_new_std( + ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_VBLANK, + vblank_min, + IMX258_VTS_MAX - imx258->cur_mode->height, 1, + vblank_def); + + if (imx258->vblank) + imx258->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + imx258->hblank = v4l2_ctrl_new_std( + ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_HBLANK, + IMX258_PPL_DEFAULT - imx258->cur_mode->width, + IMX258_PPL_DEFAULT - imx258->cur_mode->width, + 1, + IMX258_PPL_DEFAULT - imx258->cur_mode->width); + + if (imx258->hblank) + imx258->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + imx258->exposure = v4l2_ctrl_new_std( + ctrl_hdlr, &imx258_ctrl_ops, + V4L2_CID_EXPOSURE, IMX258_EXPOSURE_MIN, + IMX258_EXPOSURE_MAX, IMX258_EXPOSURE_STEP, + IMX258_EXPOSURE_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX258_ANA_GAIN_MIN, IMX258_ANA_GAIN_MAX, + IMX258_ANA_GAIN_STEP, IMX258_ANA_GAIN_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + IMX258_DGTL_GAIN_MIN, IMX258_DGTL_GAIN_MAX, + IMX258_DGTL_GAIN_STEP, + IMX258_DGTL_GAIN_DEFAULT); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx258_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx258_test_pattern_menu) - 1, + 0, 0, imx258_test_pattern_menu); + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(&client->dev, "%s control init failed (%d)\n", + __func__, ret); + goto error; + } + + imx258->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + mutex_destroy(&imx258->mutex); + + return ret; +} + +static void imx258_free_controls(struct imx258 *imx258) +{ + v4l2_ctrl_handler_free(imx258->sd.ctrl_handler); + mutex_destroy(&imx258->mutex); +} + +static int imx258_probe(struct i2c_client *client) +{ + struct imx258 *imx258; + int ret; + u32 val = 0; + + device_property_read_u32(&client->dev, "clock-frequency", &val); + if (val != 19200000) + return -EINVAL; + + imx258 = devm_kzalloc(&client->dev, sizeof(*imx258), GFP_KERNEL); + if (!imx258) + return -ENOMEM; + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops); + + /* Check module identity */ + ret = imx258_identify_module(imx258); + if (ret) + return ret; + + /* Set default mode to max resolution */ + imx258->cur_mode = &supported_modes[0]; + + ret = imx258_init_controls(imx258); + if (ret) + return ret; + + /* Initialize subdev */ + imx258->sd.internal_ops = &imx258_internal_ops; + imx258->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + imx258->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + imx258->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&imx258->sd.entity, 1, &imx258->pad); + if (ret) + goto error_handler_free; + + ret = v4l2_async_register_subdev_sensor_common(&imx258->sd); + if (ret < 0) + goto error_media_entity; + + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&imx258->sd.entity); + +error_handler_free: + imx258_free_controls(imx258); + + return ret; +} + +static int imx258_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx258 *imx258 = to_imx258(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + imx258_free_controls(imx258); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +static const struct dev_pm_ops imx258_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(imx258_suspend, imx258_resume) +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id imx258_acpi_ids[] = { + { "SONY258A" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(acpi, imx258_acpi_ids); +#endif + +static struct i2c_driver imx258_i2c_driver = { + .driver = { + .name = "imx258", + .pm = &imx258_pm_ops, + .acpi_match_table = ACPI_PTR(imx258_acpi_ids), + }, + .probe_new = imx258_probe, + .remove = imx258_remove, +}; + +module_i2c_driver(imx258_i2c_driver); + +MODULE_AUTHOR("Yeh, Andy <andy.yeh@intel.com>"); +MODULE_AUTHOR("Chiang, Alan <alanx.chiang@intel.com>"); +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 daec33f4196a..63fb94e7da37 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -87,7 +87,7 @@ #define IMX274_SHR_LIMIT_CONST (4) /* - * Constants for sensor reset delay + * Min and max sensor reset delay (microseconds) */ #define IMX274_RESET_DELAY1 (2000) #define IMX274_RESET_DELAY2 (2200) @@ -107,15 +107,15 @@ /* * IMX274 register definitions */ -#define IMX274_FRAME_LENGTH_ADDR_1 0x30FA /* VMAX, MSB */ -#define IMX274_FRAME_LENGTH_ADDR_2 0x30F9 /* VMAX */ -#define IMX274_FRAME_LENGTH_ADDR_3 0x30F8 /* VMAX, LSB */ +#define IMX274_SHR_REG_MSB 0x300D /* SHR */ +#define IMX274_SHR_REG_LSB 0x300C /* SHR */ #define IMX274_SVR_REG_MSB 0x300F /* SVR */ #define IMX274_SVR_REG_LSB 0x300E /* SVR */ +#define IMX274_VMAX_REG_1 0x30FA /* VMAX, MSB */ +#define IMX274_VMAX_REG_2 0x30F9 /* VMAX */ +#define IMX274_VMAX_REG_3 0x30F8 /* VMAX, LSB */ #define IMX274_HMAX_REG_MSB 0x30F7 /* HMAX */ #define IMX274_HMAX_REG_LSB 0x30F6 /* HMAX */ -#define IMX274_COARSE_TIME_ADDR_MSB 0x300D /* SHR */ -#define IMX274_COARSE_TIME_ADDR_LSB 0x300C /* SHR */ #define IMX274_ANALOG_GAIN_ADDR_LSB 0x300A /* ANALOG GAIN LSB */ #define IMX274_ANALOG_GAIN_ADDR_MSB 0x300B /* ANALOG GAIN MSB */ #define IMX274_DIGITAL_GAIN_REG 0x3012 /* Digital Gain */ @@ -144,22 +144,13 @@ enum imx274_mode { IMX274_MODE_3840X2160, IMX274_MODE_1920X1080, IMX274_MODE_1280X720, - - IMX274_MODE_START_STREAM_1, - IMX274_MODE_START_STREAM_2, - IMX274_MODE_START_STREAM_3, - IMX274_MODE_START_STREAM_4, - IMX274_MODE_STOP_STREAM }; /* * imx274 format related structure */ struct imx274_frmfmt { - u32 mbus_code; - enum v4l2_colorspace colorspace; struct v4l2_frmsize_discrete size; - enum imx274_mode mode; }; /* @@ -489,24 +480,15 @@ static const struct reg_8 *mode_table[] = { [IMX274_MODE_3840X2160] = imx274_mode1_3840x2160_raw10, [IMX274_MODE_1920X1080] = imx274_mode3_1920x1080_raw10, [IMX274_MODE_1280X720] = imx274_mode5_1280x720_raw10, - - [IMX274_MODE_START_STREAM_1] = imx274_start_1, - [IMX274_MODE_START_STREAM_2] = imx274_start_2, - [IMX274_MODE_START_STREAM_3] = imx274_start_3, - [IMX274_MODE_START_STREAM_4] = imx274_start_4, - [IMX274_MODE_STOP_STREAM] = imx274_stop, }; /* * imx274 format related structure */ static const struct imx274_frmfmt imx274_formats[] = { - {MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_COLORSPACE_SRGB, {3840, 2160}, - IMX274_MODE_3840X2160}, - {MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_COLORSPACE_SRGB, {1920, 1080}, - IMX274_MODE_1920X1080}, - {MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_COLORSPACE_SRGB, {1280, 720}, - IMX274_MODE_1280X720}, + { {3840, 2160} }, + { {1920, 1080} }, + { {1280, 720} }, }; /* @@ -737,11 +719,11 @@ static int imx274_mode_regs(struct stimx274 *priv, int mode) { int err = 0; - err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM_1]); + err = imx274_write_table(priv, imx274_start_1); if (err) return err; - err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM_2]); + err = imx274_write_table(priv, imx274_start_2); if (err) return err; @@ -766,7 +748,7 @@ static int imx274_start_stream(struct stimx274 *priv) * give it 1 extra ms for margin */ msleep_range(11); - err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM_3]); + err = imx274_write_table(priv, imx274_start_3); if (err) return err; @@ -776,7 +758,7 @@ static int imx274_start_stream(struct stimx274 *priv) * give it 1 extra ms for margin */ msleep_range(8); - err = imx274_write_table(priv, mode_table[IMX274_MODE_START_STREAM_4]); + err = imx274_write_table(priv, imx274_start_4); if (err) return err; @@ -890,9 +872,8 @@ static int imx274_set_fmt(struct v4l2_subdev *sd, int index; dev_dbg(&client->dev, - "%s: width = %d height = %d code = %d mbus_code = %d\n", - __func__, fmt->width, fmt->height, fmt->code, - imx274_formats[imx274->mode_index].mbus_code); + "%s: width = %d height = %d code = %d\n", + __func__, fmt->width, fmt->height, fmt->code); mutex_lock(&imx274->lock); @@ -971,7 +952,7 @@ static int imx274_s_frame_interval(struct v4l2_subdev *sd, if (!ret) { /* * exposure time range is decided by frame interval - * need to update it after frame interal changes + * need to update it after frame interval changes */ min = IMX274_MIN_EXPOSURE_TIME; max = fi->interval.numerator * 1000000 @@ -984,7 +965,7 @@ static int imx274_s_frame_interval(struct v4l2_subdev *sd, } /* update exposure time accordingly */ - imx274_set_exposure(imx274, imx274->ctrls.exposure->val); + imx274_set_exposure(imx274, ctrl->val); dev_dbg(&imx274->client->dev, "set frame interval to %uus\n", fi->interval.numerator * 1000000 @@ -1088,8 +1069,7 @@ static int imx274_s_stream(struct v4l2_subdev *sd, int on) goto fail; } else { /* stop stream */ - ret = imx274_write_table(imx274, - mode_table[IMX274_MODE_STOP_STREAM]); + ret = imx274_write_table(imx274, imx274_stop); if (ret) goto fail; } @@ -1133,15 +1113,15 @@ static int imx274_get_frame_length(struct stimx274 *priv, u32 *val) svr = (reg_val[1] << IMX274_SHIFT_8_BITS) + reg_val[0]; /* vmax */ - err = imx274_read_reg(priv, IMX274_FRAME_LENGTH_ADDR_3, ®_val[0]); + err = imx274_read_reg(priv, IMX274_VMAX_REG_3, ®_val[0]); if (err) goto fail; - err = imx274_read_reg(priv, IMX274_FRAME_LENGTH_ADDR_2, ®_val[1]); + err = imx274_read_reg(priv, IMX274_VMAX_REG_2, ®_val[1]); if (err) goto fail; - err = imx274_read_reg(priv, IMX274_FRAME_LENGTH_ADDR_1, ®_val[2]); + err = imx274_read_reg(priv, IMX274_VMAX_REG_1, ®_val[2]); if (err) goto fail; @@ -1300,10 +1280,10 @@ fail: static inline void imx274_calculate_coarse_time_regs(struct reg_8 regs[2], u32 coarse_time) { - regs->addr = IMX274_COARSE_TIME_ADDR_MSB; + regs->addr = IMX274_SHR_REG_MSB; regs->val = (coarse_time >> IMX274_SHIFT_8_BITS) & IMX274_MASK_LSB_8_BITS; - (regs + 1)->addr = IMX274_COARSE_TIME_ADDR_LSB; + (regs + 1)->addr = IMX274_SHR_REG_LSB; (regs + 1)->val = (coarse_time) & IMX274_MASK_LSB_8_BITS; } @@ -1471,13 +1451,13 @@ static int imx274_set_test_pattern(struct stimx274 *priv, int val) static inline void imx274_calculate_frame_length_regs(struct reg_8 regs[3], u32 frame_length) { - regs->addr = IMX274_FRAME_LENGTH_ADDR_1; + regs->addr = IMX274_VMAX_REG_1; regs->val = (frame_length >> IMX274_SHIFT_16_BITS) & IMX274_MASK_LSB_4_BITS; - (regs + 1)->addr = IMX274_FRAME_LENGTH_ADDR_2; + (regs + 1)->addr = IMX274_VMAX_REG_2; (regs + 1)->val = (frame_length >> IMX274_SHIFT_8_BITS) & IMX274_MASK_LSB_8_BITS; - (regs + 2)->addr = IMX274_FRAME_LENGTH_ADDR_3; + (regs + 2)->addr = IMX274_VMAX_REG_3; (regs + 2)->val = (frame_length) & IMX274_MASK_LSB_8_BITS; } @@ -1786,7 +1766,7 @@ static int imx274_remove(struct i2c_client *client) struct stimx274 *imx274 = to_imx274(sd); /* stop stream */ - imx274_write_table(imx274, mode_table[IMX274_MODE_STOP_STREAM]); + imx274_write_table(imx274, imx274_stop); v4l2_async_unregister_subdev(sd); v4l2_ctrl_handler_free(&imx274->ctrls.handler); diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index a7e23bcf845c..a14a74e6b986 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -739,6 +739,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) struct rc_dev *rc = NULL; struct i2c_adapter *adap = client->adapter; unsigned short addr = client->addr; + bool probe_tx = (id->driver_data & FLAG_TX) != 0; int err; if ((id->driver_data & FLAG_HDPVR) && !enable_hdpvr) { @@ -800,6 +801,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) rc_proto = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_RC6_6A_32; ir_codes = RC_MAP_HAUPPAUGE; + probe_tx = true; break; } @@ -892,7 +894,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) INIT_DELAYED_WORK(&ir->work, ir_work); - if (id->driver_data & FLAG_TX) { + if (probe_tx) { ir->tx_c = i2c_new_dummy(client->adapter, 0x70); if (!ir->tx_c) { dev_err(&client->dev, "failed to setup tx i2c address"); diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index 3dbcae257164..a66f6201f53c 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -1796,7 +1796,6 @@ MODULE_DEVICE_TABLE(acpi, ov13858_acpi_ids); static struct i2c_driver ov13858_i2c_driver = { .driver = { .name = "ov13858", - .owner = THIS_MODULE, .pm = &ov13858_pm_ops, .acpi_match_table = ACPI_PTR(ov13858_acpi_ids), }, diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index 4c3b92763243..beb722065152 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -307,6 +307,10 @@ struct ov2640_priv { struct gpio_desc *resetb_gpio; struct gpio_desc *pwdn_gpio; + + struct mutex lock; /* lock to protect streaming and power_count */ + bool streaming; + int power_count; }; /* @@ -709,9 +713,20 @@ static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl) struct v4l2_subdev *sd = &container_of(ctrl->handler, struct ov2640_priv, hdl)->subdev; struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov2640_priv *priv = to_ov2640(client); u8 val; int ret; + /* v4l2_ctrl_lock() locks our own mutex */ + + /* + * If the device is not powered up by the host driver, do not apply any + * controls to H/W at this time. Instead the controls will be restored + * when the streaming is started. + */ + if (!priv->power_count) + return 0; + ret = i2c_smbus_write_byte_data(client, BANK_SEL, BANK_SEL_SENS); if (ret < 0) return ret; @@ -763,12 +778,9 @@ static int ov2640_s_register(struct v4l2_subdev *sd, } #endif -static int ov2640_s_power(struct v4l2_subdev *sd, int on) +static void ov2640_set_power(struct ov2640_priv *priv, int on) { #ifdef CONFIG_GPIOLIB - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov2640_priv *priv = to_ov2640(client); - if (priv->pwdn_gpio) gpiod_direction_output(priv->pwdn_gpio, !on); if (on && priv->resetb_gpio) { @@ -778,6 +790,25 @@ static int ov2640_s_power(struct v4l2_subdev *sd, int on) gpiod_set_value(priv->resetb_gpio, 0); } #endif +} + +static int ov2640_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov2640_priv *priv = to_ov2640(client); + + mutex_lock(&priv->lock); + + /* + * If the power count is modified from 0 to != 0 or from != 0 to 0, + * update the power state. + */ + if (priv->power_count == !on) + ov2640_set_power(priv, on); + priv->power_count += on ? 1 : -1; + WARN_ON(priv->power_count < 0); + mutex_unlock(&priv->lock); + return 0; } @@ -798,16 +829,13 @@ static const struct ov2640_win_size *ov2640_select_win(u32 width, u32 height) static int ov2640_set_params(struct i2c_client *client, const struct ov2640_win_size *win, u32 code) { - struct ov2640_priv *priv = to_ov2640(client); const struct regval_list *selected_cfmt_regs; u8 val; int ret; - /* select win */ - priv->win = win; + if (!win) + return -EINVAL; - /* select format */ - priv->cfmt_code = 0; switch (code) { case MEDIA_BUS_FMT_RGB565_2X8_BE: dev_dbg(&client->dev, "%s: Selected cfmt RGB565 BE", __func__); @@ -846,13 +874,13 @@ static int ov2640_set_params(struct i2c_client *client, goto err; /* select preamble */ - dev_dbg(&client->dev, "%s: Set size to %s", __func__, priv->win->name); + dev_dbg(&client->dev, "%s: Set size to %s", __func__, win->name); ret = ov2640_write_array(client, ov2640_size_change_preamble_regs); if (ret < 0) goto err; /* set size win */ - ret = ov2640_write_array(client, priv->win->regs); + ret = ov2640_write_array(client, win->regs); if (ret < 0) goto err; @@ -872,14 +900,11 @@ static int ov2640_set_params(struct i2c_client *client, if (ret < 0) goto err; - priv->cfmt_code = code; - return 0; err: dev_err(&client->dev, "%s: Error %d", __func__, ret); ov2640_reset(client); - priv->win = NULL; return ret; } @@ -915,11 +940,15 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd, { struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov2640_priv *priv = to_ov2640(client); const struct ov2640_win_size *win; + int ret = 0; if (format->pad) return -EINVAL; + mutex_lock(&priv->lock); + /* select suitable win */ win = ov2640_select_win(mf->width, mf->height); mf->width = win->width; @@ -941,10 +970,24 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd, break; } - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return ov2640_set_params(client, win, mf->code); - cfg->try_fmt = *mf; - return 0; + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + struct ov2640_priv *priv = to_ov2640(client); + + if (priv->streaming) { + ret = -EBUSY; + goto out; + } + /* select win */ + priv->win = win; + /* select format */ + priv->cfmt_code = mf->code; + } else { + cfg->try_fmt = *mf; + } +out: + mutex_unlock(&priv->lock); + + return ret; } static int ov2640_enum_mbus_code(struct v4l2_subdev *sd, @@ -979,6 +1022,28 @@ static int ov2640_get_selection(struct v4l2_subdev *sd, } } +static int ov2640_s_stream(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov2640_priv *priv = to_ov2640(client); + int ret = 0; + + mutex_lock(&priv->lock); + if (priv->streaming == !on) { + if (on) { + ret = ov2640_set_params(client, priv->win, + priv->cfmt_code); + if (!ret) + ret = __v4l2_ctrl_handler_setup(&priv->hdl); + } + } + if (!ret) + priv->streaming = on; + mutex_unlock(&priv->lock); + + return ret; +} + static int ov2640_video_probe(struct i2c_client *client) { struct ov2640_priv *priv = to_ov2640(client); @@ -1014,8 +1079,6 @@ static int ov2640_video_probe(struct i2c_client *client) "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", devname, pid, ver, midh, midl); - ret = v4l2_ctrl_handler_setup(&priv->hdl); - done: ov2640_s_power(&priv->subdev, 0); return ret; @@ -1040,9 +1103,14 @@ static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = { .set_fmt = ov2640_set_fmt, }; +static const struct v4l2_subdev_video_ops ov2640_subdev_video_ops = { + .s_stream = ov2640_s_stream, +}; + static const struct v4l2_subdev_ops ov2640_subdev_ops = { .core = &ov2640_subdev_core_ops, .pad = &ov2640_subdev_pad_ops, + .video = &ov2640_subdev_video_ops, }; static int ov2640_probe_dt(struct i2c_client *client, @@ -1116,7 +1184,9 @@ static int ov2640_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + mutex_init(&priv->lock); v4l2_ctrl_handler_init(&priv->hdl, 2); + priv->hdl.lock = &priv->lock; v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, @@ -1150,6 +1220,7 @@ err_videoprobe: media_entity_cleanup(&priv->subdev.entity); err_hdl: v4l2_ctrl_handler_free(&priv->hdl); + mutex_destroy(&priv->lock); err_clk: clk_disable_unprepare(priv->clk); return ret; @@ -1161,6 +1232,7 @@ static int ov2640_remove(struct i2c_client *client) v4l2_async_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); + mutex_destroy(&priv->lock); media_entity_cleanup(&priv->subdev.entity); v4l2_device_unregister_subdev(&priv->subdev); clk_disable_unprepare(priv->clk); diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 852026baa2e7..f6e40cc9745c 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -60,6 +60,8 @@ #define OV5640_REG_AEC_PK_MANUAL 0x3503 #define OV5640_REG_AEC_PK_REAL_GAIN 0x350a #define OV5640_REG_AEC_PK_VTS 0x350c +#define OV5640_REG_TIMING_DVPHO 0x3808 +#define OV5640_REG_TIMING_DVPVO 0x380a #define OV5640_REG_TIMING_HTS 0x380c #define OV5640_REG_TIMING_VTS 0x380e #define OV5640_REG_TIMING_TC_REG21 0x3821 @@ -91,6 +93,9 @@ #define OV5640_REG_SDE_CTRL5 0x5585 #define OV5640_REG_AVG_READOUT 0x56a1 +#define OV5640_SCLK2X_ROOT_DIVIDER_DEFAULT 1 +#define OV5640_SCLK_ROOT_DIVIDER_DEFAULT 2 + enum ov5640_mode_id { OV5640_MODE_QCIF_176_144 = 0, OV5640_MODE_QVGA_320_240, @@ -165,8 +170,10 @@ struct reg_value { struct ov5640_mode_info { enum ov5640_mode_id id; enum ov5640_downsize_mode dn_mode; - u32 width; - u32 height; + u32 hact; + u32 htot; + u32 vact; + u32 vtot; const struct reg_value *reg_data; u32 reg_data_size; }; @@ -187,6 +194,7 @@ struct ov5640_ctrls { struct v4l2_ctrl *gain; }; struct v4l2_ctrl *brightness; + struct v4l2_ctrl *light_freq; struct v4l2_ctrl *saturation; struct v4l2_ctrl *contrast; struct v4l2_ctrl *hue; @@ -248,7 +256,7 @@ 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}, {0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, - {0x3037, 0x13, 0, 0}, {0x3108, 0x01, 0, 0}, {0x3630, 0x36, 0, 0}, + {0x3037, 0x13, 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}, @@ -265,9 +273,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -339,9 +345,7 @@ static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -360,9 +364,7 @@ static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -381,9 +383,7 @@ static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x04, 0, 0}, {0x380f, 0x38, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -393,8 +393,7 @@ static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0}, - {0x3808, 0x04, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, - {0x380b, 0x00, 0, 0}, {0x3035, 0x12, 0, 0}, + {0x3035, 0x12, 0, 0}, }; static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { @@ -404,9 +403,7 @@ static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0x80, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -415,8 +412,7 @@ static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3808, 0x04, 0, 0}, - {0x3809, 0x00, 0, 0}, {0x380a, 0x03, 0, 0}, {0x380b, 0x00, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { @@ -426,9 +422,7 @@ static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, - {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -447,9 +441,7 @@ static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x01, 0, 0}, {0x3809, 0x40, 0, 0}, {0x380a, 0x00, 0, 0}, - {0x380b, 0xf0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -468,9 +460,7 @@ static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0}, - {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -489,9 +479,7 @@ static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x00, 0, 0}, {0x3809, 0xb0, 0, 0}, {0x380a, 0x00, 0, 0}, - {0x380b, 0x90, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -510,9 +498,7 @@ static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -531,9 +517,7 @@ static const struct reg_value ov5640_setting_15fps_NTSC_720_480[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x01, 0, 0}, - {0x380b, 0xe0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -552,9 +536,7 @@ static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, - {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -573,9 +555,7 @@ static const struct reg_value ov5640_setting_15fps_PAL_720_576[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3808, 0x02, 0, 0}, {0x3809, 0xd0, 0, 0}, {0x380a, 0x02, 0, 0}, - {0x380b, 0x40, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x68, 0, 0}, - {0x380e, 0x03, 0, 0}, {0x380f, 0xd8, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -595,9 +575,7 @@ static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, - {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, - {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0}, - {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, @@ -617,9 +595,7 @@ static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = { {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, - {0x3808, 0x05, 0, 0}, {0x3809, 0x00, 0, 0}, {0x380a, 0x02, 0, 0}, - {0x380b, 0xd0, 0, 0}, {0x380c, 0x07, 0, 0}, {0x380d, 0x64, 0, 0}, - {0x380e, 0x02, 0, 0}, {0x380f, 0xe4, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, @@ -639,9 +615,7 @@ static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, - {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, - {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0}, - {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -655,10 +629,8 @@ static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, - {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0}, - {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, - {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0}, - {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, + {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, @@ -676,9 +648,7 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, - {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, - {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0}, - {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -692,10 +662,8 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, - {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3808, 0x07, 0, 0}, - {0x3809, 0x80, 0, 0}, {0x380a, 0x04, 0, 0}, {0x380b, 0x38, 0, 0}, - {0x380c, 0x09, 0, 0}, {0x380d, 0xc4, 0, 0}, {0x380e, 0x04, 0, 0}, - {0x380f, 0x60, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, + {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, + {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, @@ -712,9 +680,7 @@ static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, - {0x3808, 0x0a, 0, 0}, {0x3809, 0x20, 0, 0}, {0x380a, 0x07, 0, 0}, - {0x380b, 0x98, 0, 0}, {0x380c, 0x0b, 0, 0}, {0x380d, 0x1c, 0, 0}, - {0x380e, 0x07, 0, 0}, {0x380f, 0xb0, 0, 0}, {0x3810, 0x00, 0, 0}, + {0x3810, 0x00, 0, 0}, {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, @@ -728,66 +694,84 @@ static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { /* power-on sensor init reg table */ static const struct ov5640_mode_info ov5640_mode_init_data = { - 0, SUBSAMPLING, 640, 480, ov5640_init_setting_30fps_VGA, + 0, SUBSAMPLING, 640, 1896, 480, 984, + ov5640_init_setting_30fps_VGA, ARRAY_SIZE(ov5640_init_setting_30fps_VGA), }; static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = { { - {OV5640_MODE_QCIF_176_144, SUBSAMPLING, 176, 144, + {OV5640_MODE_QCIF_176_144, SUBSAMPLING, + 176, 1896, 144, 984, ov5640_setting_15fps_QCIF_176_144, ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)}, - {OV5640_MODE_QVGA_320_240, SUBSAMPLING, 320, 240, + {OV5640_MODE_QVGA_320_240, SUBSAMPLING, + 320, 1896, 240, 984, ov5640_setting_15fps_QVGA_320_240, ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)}, - {OV5640_MODE_VGA_640_480, SUBSAMPLING, 640, 480, + {OV5640_MODE_VGA_640_480, SUBSAMPLING, + 640, 1896, 480, 1080, ov5640_setting_15fps_VGA_640_480, ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)}, - {OV5640_MODE_NTSC_720_480, SUBSAMPLING, 720, 480, + {OV5640_MODE_NTSC_720_480, SUBSAMPLING, + 720, 1896, 480, 984, ov5640_setting_15fps_NTSC_720_480, ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)}, - {OV5640_MODE_PAL_720_576, SUBSAMPLING, 720, 576, + {OV5640_MODE_PAL_720_576, SUBSAMPLING, + 720, 1896, 576, 984, ov5640_setting_15fps_PAL_720_576, ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)}, - {OV5640_MODE_XGA_1024_768, SUBSAMPLING, 1024, 768, + {OV5640_MODE_XGA_1024_768, SUBSAMPLING, + 1024, 1896, 768, 1080, ov5640_setting_15fps_XGA_1024_768, ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)}, - {OV5640_MODE_720P_1280_720, SUBSAMPLING, 1280, 720, + {OV5640_MODE_720P_1280_720, SUBSAMPLING, + 1280, 1892, 720, 740, ov5640_setting_15fps_720P_1280_720, ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)}, - {OV5640_MODE_1080P_1920_1080, SCALING, 1920, 1080, + {OV5640_MODE_1080P_1920_1080, SCALING, + 1920, 2500, 1080, 1120, ov5640_setting_15fps_1080P_1920_1080, ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)}, - {OV5640_MODE_QSXGA_2592_1944, SCALING, 2592, 1944, + {OV5640_MODE_QSXGA_2592_1944, SCALING, + 2592, 2844, 1944, 1968, ov5640_setting_15fps_QSXGA_2592_1944, ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)}, }, { - {OV5640_MODE_QCIF_176_144, SUBSAMPLING, 176, 144, + {OV5640_MODE_QCIF_176_144, SUBSAMPLING, + 176, 1896, 144, 984, ov5640_setting_30fps_QCIF_176_144, ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)}, - {OV5640_MODE_QVGA_320_240, SUBSAMPLING, 320, 240, + {OV5640_MODE_QVGA_320_240, SUBSAMPLING, + 320, 1896, 240, 984, ov5640_setting_30fps_QVGA_320_240, ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)}, - {OV5640_MODE_VGA_640_480, SUBSAMPLING, 640, 480, + {OV5640_MODE_VGA_640_480, SUBSAMPLING, + 640, 1896, 480, 1080, ov5640_setting_30fps_VGA_640_480, ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)}, - {OV5640_MODE_NTSC_720_480, SUBSAMPLING, 720, 480, + {OV5640_MODE_NTSC_720_480, SUBSAMPLING, + 720, 1896, 480, 984, ov5640_setting_30fps_NTSC_720_480, ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)}, - {OV5640_MODE_PAL_720_576, SUBSAMPLING, 720, 576, + {OV5640_MODE_PAL_720_576, SUBSAMPLING, + 720, 1896, 576, 984, ov5640_setting_30fps_PAL_720_576, ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)}, - {OV5640_MODE_XGA_1024_768, SUBSAMPLING, 1024, 768, + {OV5640_MODE_XGA_1024_768, SUBSAMPLING, + 1024, 1896, 768, 1080, ov5640_setting_30fps_XGA_1024_768, ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)}, - {OV5640_MODE_720P_1280_720, SUBSAMPLING, 1280, 720, + {OV5640_MODE_720P_1280_720, SUBSAMPLING, + 1280, 1892, 720, 740, ov5640_setting_30fps_720P_1280_720, ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)}, - {OV5640_MODE_1080P_1920_1080, SCALING, 1920, 1080, + {OV5640_MODE_1080P_1920_1080, SCALING, + 1920, 2500, 1080, 1120, ov5640_setting_30fps_1080P_1920_1080, ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)}, - {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, NULL, 0}, + {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, NULL, 0}, }, }; @@ -1377,6 +1361,30 @@ static int ov5640_set_virtual_channel(struct ov5640_dev *sensor) return ov5640_write_reg(sensor, OV5640_REG_DEBUG_MODE, temp); } +static int ov5640_set_timings(struct ov5640_dev *sensor, + const struct ov5640_mode_info *mode) +{ + int ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->vact); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, mode->vtot); + if (ret < 0) + return ret; + + return 0; +} + static const struct ov5640_mode_info * ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, int width, int height, bool nearest) @@ -1390,10 +1398,10 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, if (!mode->reg_data) continue; - if ((nearest && mode->width <= width && - mode->height <= height) || - (!nearest && mode->width == width && - mode->height == height)) + if ((nearest && mode->hact <= width && + mode->vact <= height) || + (!nearest && mode->hact == width && + mode->vact == height)) break; } @@ -1568,7 +1576,8 @@ static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor, * change mode directly */ static int ov5640_set_mode_direct(struct ov5640_dev *sensor, - const struct ov5640_mode_info *mode) + const struct ov5640_mode_info *mode, + s32 exposure) { int ret; @@ -1584,7 +1593,8 @@ static int ov5640_set_mode_direct(struct ov5640_dev *sensor, ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 1); if (ret) return ret; - return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, V4L2_EXPOSURE_AUTO); + + return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, exposure); } static int ov5640_set_mode(struct ov5640_dev *sensor, @@ -1592,6 +1602,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor, { const struct ov5640_mode_info *mode = sensor->current_mode; enum ov5640_downsize_mode dn_mode, orig_dn_mode; + s32 exposure; int ret; dn_mode = mode->dn_mode; @@ -1601,7 +1612,9 @@ static int ov5640_set_mode(struct ov5640_dev *sensor, ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 0); if (ret) return ret; - ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, V4L2_EXPOSURE_MANUAL); + + exposure = sensor->ctrls.auto_exp->val; + ret = ov5640_set_exposure(sensor, V4L2_EXPOSURE_MANUAL); if (ret) return ret; @@ -1617,12 +1630,16 @@ static int ov5640_set_mode(struct ov5640_dev *sensor, * change inside subsampling or scaling * download firmware directly */ - ret = ov5640_set_mode_direct(sensor, mode); + ret = ov5640_set_mode_direct(sensor, mode, exposure); } if (ret < 0) return ret; + ret = ov5640_set_timings(sensor, mode); + if (ret < 0) + return ret; + ret = ov5640_set_ae_target(sensor, sensor->ae_target); if (ret < 0) return ret; @@ -1654,6 +1671,12 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor) if (ret < 0) return ret; + ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f, + (ilog2(OV5640_SCLK2X_ROOT_DIVIDER_DEFAULT) << 2) | + ilog2(OV5640_SCLK_ROOT_DIVIDER_DEFAULT)); + if (ret) + return ret; + /* now restore the last capture mode */ ret = ov5640_set_mode(sensor, &ov5640_mode_init_data); if (ret < 0) @@ -1871,8 +1894,8 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd, mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true); if (!mode) return -EINVAL; - fmt->width = mode->width; - fmt->height = mode->height; + fmt->width = mode->hact; + fmt->height = mode->vact; if (new_mode) *new_mode = mode; @@ -2155,6 +2178,21 @@ static int ov5640_set_ctrl_test_pattern(struct ov5640_dev *sensor, int value) 0xa4, value ? 0xa4 : 0); } +static int ov5640_set_ctrl_light_freq(struct ov5640_dev *sensor, int value) +{ + int ret; + + ret = ov5640_mod_reg(sensor, OV5640_REG_HZ5060_CTRL01, BIT(7), + (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) ? + 0 : BIT(7)); + if (ret) + return ret; + + return ov5640_mod_reg(sensor, OV5640_REG_HZ5060_CTRL00, BIT(2), + (value == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) ? + BIT(2) : 0); +} + static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); @@ -2223,6 +2261,9 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_TEST_PATTERN: ret = ov5640_set_ctrl_test_pattern(sensor, ctrl->val); break; + case V4L2_CID_POWER_LINE_FREQUENCY: + ret = ov5640_set_ctrl_light_freq(sensor, ctrl->val); + break; default: ret = -EINVAL; break; @@ -2285,6 +2326,12 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) ARRAY_SIZE(test_pattern_menu) - 1, 0, 0, test_pattern_menu); + ctrls->light_freq = + v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ); + if (hdl->error) { ret = hdl->error; goto free_ctrls; @@ -2315,10 +2362,10 @@ static int ov5640_enum_frame_size(struct v4l2_subdev *sd, return -EINVAL; fse->min_width = - ov5640_mode_data[0][fse->index].width; + ov5640_mode_data[0][fse->index].hact; fse->max_width = fse->min_width; fse->min_height = - ov5640_mode_data[0][fse->index].height; + ov5640_mode_data[0][fse->index].vact; fse->max_height = fse->min_height; return 0; @@ -2382,14 +2429,14 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, mode = sensor->current_mode; frame_rate = ov5640_try_frame_interval(sensor, &fi->interval, - mode->width, mode->height); + mode->hact, mode->vact); if (frame_rate < 0) frame_rate = OV5640_15_FPS; sensor->current_fr = frame_rate; sensor->frame_interval = fi->interval; - sensor->current_mode = ov5640_find_mode(sensor, frame_rate, mode->width, - mode->height, true); + sensor->current_mode = ov5640_find_mode(sensor, frame_rate, mode->hact, + mode->vact, true); sensor->pending_mode_change = true; out: mutex_unlock(&sensor->lock); @@ -2536,8 +2583,8 @@ static int ov5640_probe(struct i2c_client *client, sensor->ae_target = 52; - endpoint = fwnode_graph_get_next_endpoint( - of_fwnode_handle(client->dev.of_node), NULL); + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), + NULL); if (!endpoint) { dev_err(dev, "endpoint node not found\n"); return -EINVAL; diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index 4e3142a7e5a7..b3f762578f7f 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -600,11 +600,13 @@ static int ov5645_write_reg(struct ov5645 *ov5645, u16 reg, u8 val) regbuf[2] = val; ret = i2c_master_send(ov5645->i2c_client, regbuf, 3); - if (ret < 0) + if (ret < 0) { dev_err(ov5645->dev, "%s: write reg error %d: reg=%x, val=%x\n", __func__, ret, reg, val); + return ret; + } - return ret; + return 0; } static int ov5645_read_reg(struct ov5645 *ov5645, u16 reg, u8 *val) diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c index 9be38a0a2046..9a80decd93d3 100644 --- a/drivers/media/i2c/ov5695.c +++ b/drivers/media/i2c/ov5695.c @@ -1385,7 +1385,6 @@ MODULE_DEVICE_TABLE(of, ov5695_of_match); static struct i2c_driver ov5695_i2c_driver = { .driver = { .name = "ov5695", - .owner = THIS_MODULE, .pm = &ov5695_pm_ops, .of_match_table = of_match_ptr(ov5695_of_match), }, diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c new file mode 100644 index 000000000000..d3ebb7529fca --- /dev/null +++ b/drivers/media/i2c/ov7251.c @@ -0,0 +1,1503 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the OV7251 camera sensor. + * + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, Linaro Ltd. + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define OV7251_SC_MODE_SELECT 0x0100 +#define OV7251_SC_MODE_SELECT_SW_STANDBY 0x0 +#define OV7251_SC_MODE_SELECT_STREAMING 0x1 + +#define OV7251_CHIP_ID_HIGH 0x300a +#define OV7251_CHIP_ID_HIGH_BYTE 0x77 +#define OV7251_CHIP_ID_LOW 0x300b +#define OV7251_CHIP_ID_LOW_BYTE 0x50 +#define OV7251_SC_GP_IO_IN1 0x3029 +#define OV7251_AEC_EXPO_0 0x3500 +#define OV7251_AEC_EXPO_1 0x3501 +#define OV7251_AEC_EXPO_2 0x3502 +#define OV7251_AEC_AGC_ADJ_0 0x350a +#define OV7251_AEC_AGC_ADJ_1 0x350b +#define OV7251_TIMING_FORMAT1 0x3820 +#define OV7251_TIMING_FORMAT1_VFLIP BIT(2) +#define OV7251_TIMING_FORMAT2 0x3821 +#define OV7251_TIMING_FORMAT2_MIRROR BIT(2) +#define OV7251_PRE_ISP_00 0x5e00 +#define OV7251_PRE_ISP_00_TEST_PATTERN BIT(7) + +struct reg_value { + u16 reg; + u8 val; +}; + +struct ov7251_mode_info { + u32 width; + u32 height; + const struct reg_value *data; + u32 data_size; + u32 pixel_clock; + u32 link_freq; + u16 exposure_max; + u16 exposure_def; + struct v4l2_fract timeperframe; +}; + +struct ov7251 { + struct i2c_client *i2c_client; + struct device *dev; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_fwnode_endpoint ep; + struct v4l2_mbus_framefmt fmt; + struct v4l2_rect crop; + struct clk *xclk; + u32 xclk_freq; + + struct regulator *io_regulator; + struct regulator *core_regulator; + struct regulator *analog_regulator; + + const struct ov7251_mode_info *current_mode; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *pixel_clock; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + + /* Cached register values */ + u8 aec_pk_manual; + u8 pre_isp_00; + u8 timing_format1; + u8 timing_format2; + + struct mutex lock; /* lock to protect power state, ctrls and mode */ + bool power_on; + + struct gpio_desc *enable_gpio; +}; + +static inline struct ov7251 *to_ov7251(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov7251, sd); +} + +static const struct reg_value ov7251_global_init_setting[] = { + { 0x0103, 0x01 }, + { 0x303b, 0x02 }, +}; + +static const struct reg_value ov7251_setting_vga_30fps[] = { + { 0x3005, 0x00 }, + { 0x3012, 0xc0 }, + { 0x3013, 0xd2 }, + { 0x3014, 0x04 }, + { 0x3016, 0xf0 }, + { 0x3017, 0xf0 }, + { 0x3018, 0xf0 }, + { 0x301a, 0xf0 }, + { 0x301b, 0xf0 }, + { 0x301c, 0xf0 }, + { 0x3023, 0x05 }, + { 0x3037, 0xf0 }, + { 0x3098, 0x04 }, /* pll2 pre divider */ + { 0x3099, 0x28 }, /* pll2 multiplier */ + { 0x309a, 0x05 }, /* pll2 sys divider */ + { 0x309b, 0x04 }, /* pll2 adc divider */ + { 0x309d, 0x00 }, /* pll2 divider */ + { 0x30b0, 0x0a }, /* pll1 pix divider */ + { 0x30b1, 0x01 }, /* pll1 divider */ + { 0x30b3, 0x64 }, /* pll1 multiplier */ + { 0x30b4, 0x03 }, /* pll1 pre divider */ + { 0x30b5, 0x05 }, /* pll1 mipi divider */ + { 0x3106, 0xda }, + { 0x3503, 0x07 }, + { 0x3509, 0x10 }, + { 0x3600, 0x1c }, + { 0x3602, 0x62 }, + { 0x3620, 0xb7 }, + { 0x3622, 0x04 }, + { 0x3626, 0x21 }, + { 0x3627, 0x30 }, + { 0x3630, 0x44 }, + { 0x3631, 0x35 }, + { 0x3634, 0x60 }, + { 0x3636, 0x00 }, + { 0x3662, 0x01 }, + { 0x3663, 0x70 }, + { 0x3664, 0x50 }, + { 0x3666, 0x0a }, + { 0x3669, 0x1a }, + { 0x366a, 0x00 }, + { 0x366b, 0x50 }, + { 0x3673, 0x01 }, + { 0x3674, 0xff }, + { 0x3675, 0x03 }, + { 0x3705, 0xc1 }, + { 0x3709, 0x40 }, + { 0x373c, 0x08 }, + { 0x3742, 0x00 }, + { 0x3757, 0xb3 }, + { 0x3788, 0x00 }, + { 0x37a8, 0x01 }, + { 0x37a9, 0xc0 }, + { 0x3800, 0x00 }, + { 0x3801, 0x04 }, + { 0x3802, 0x00 }, + { 0x3803, 0x04 }, + { 0x3804, 0x02 }, + { 0x3805, 0x8b }, + { 0x3806, 0x01 }, + { 0x3807, 0xeb }, + { 0x3808, 0x02 }, /* width high */ + { 0x3809, 0x80 }, /* width low */ + { 0x380a, 0x01 }, /* height high */ + { 0x380b, 0xe0 }, /* height low */ + { 0x380c, 0x03 }, /* total horiz timing high */ + { 0x380d, 0xa0 }, /* total horiz timing low */ + { 0x380e, 0x06 }, /* total vertical timing high */ + { 0x380f, 0xbc }, /* total vertical timing low */ + { 0x3810, 0x00 }, + { 0x3811, 0x04 }, + { 0x3812, 0x00 }, + { 0x3813, 0x05 }, + { 0x3814, 0x11 }, + { 0x3815, 0x11 }, + { 0x3820, 0x40 }, + { 0x3821, 0x00 }, + { 0x382f, 0x0e }, + { 0x3832, 0x00 }, + { 0x3833, 0x05 }, + { 0x3834, 0x00 }, + { 0x3835, 0x0c }, + { 0x3837, 0x00 }, + { 0x3b80, 0x00 }, + { 0x3b81, 0xa5 }, + { 0x3b82, 0x10 }, + { 0x3b83, 0x00 }, + { 0x3b84, 0x08 }, + { 0x3b85, 0x00 }, + { 0x3b86, 0x01 }, + { 0x3b87, 0x00 }, + { 0x3b88, 0x00 }, + { 0x3b89, 0x00 }, + { 0x3b8a, 0x00 }, + { 0x3b8b, 0x05 }, + { 0x3b8c, 0x00 }, + { 0x3b8d, 0x00 }, + { 0x3b8e, 0x00 }, + { 0x3b8f, 0x1a }, + { 0x3b94, 0x05 }, + { 0x3b95, 0xf2 }, + { 0x3b96, 0x40 }, + { 0x3c00, 0x89 }, + { 0x3c01, 0x63 }, + { 0x3c02, 0x01 }, + { 0x3c03, 0x00 }, + { 0x3c04, 0x00 }, + { 0x3c05, 0x03 }, + { 0x3c06, 0x00 }, + { 0x3c07, 0x06 }, + { 0x3c0c, 0x01 }, + { 0x3c0d, 0xd0 }, + { 0x3c0e, 0x02 }, + { 0x3c0f, 0x0a }, + { 0x4001, 0x42 }, + { 0x4004, 0x04 }, + { 0x4005, 0x00 }, + { 0x404e, 0x01 }, + { 0x4300, 0xff }, + { 0x4301, 0x00 }, + { 0x4315, 0x00 }, + { 0x4501, 0x48 }, + { 0x4600, 0x00 }, + { 0x4601, 0x4e }, + { 0x4801, 0x0f }, + { 0x4806, 0x0f }, + { 0x4819, 0xaa }, + { 0x4823, 0x3e }, + { 0x4837, 0x19 }, + { 0x4a0d, 0x00 }, + { 0x4a47, 0x7f }, + { 0x4a49, 0xf0 }, + { 0x4a4b, 0x30 }, + { 0x5000, 0x85 }, + { 0x5001, 0x80 }, +}; + +static const struct reg_value ov7251_setting_vga_60fps[] = { + { 0x3005, 0x00 }, + { 0x3012, 0xc0 }, + { 0x3013, 0xd2 }, + { 0x3014, 0x04 }, + { 0x3016, 0x10 }, + { 0x3017, 0x00 }, + { 0x3018, 0x00 }, + { 0x301a, 0x00 }, + { 0x301b, 0x00 }, + { 0x301c, 0x00 }, + { 0x3023, 0x05 }, + { 0x3037, 0xf0 }, + { 0x3098, 0x04 }, /* pll2 pre divider */ + { 0x3099, 0x28 }, /* pll2 multiplier */ + { 0x309a, 0x05 }, /* pll2 sys divider */ + { 0x309b, 0x04 }, /* pll2 adc divider */ + { 0x309d, 0x00 }, /* pll2 divider */ + { 0x30b0, 0x0a }, /* pll1 pix divider */ + { 0x30b1, 0x01 }, /* pll1 divider */ + { 0x30b3, 0x64 }, /* pll1 multiplier */ + { 0x30b4, 0x03 }, /* pll1 pre divider */ + { 0x30b5, 0x05 }, /* pll1 mipi divider */ + { 0x3106, 0xda }, + { 0x3503, 0x07 }, + { 0x3509, 0x10 }, + { 0x3600, 0x1c }, + { 0x3602, 0x62 }, + { 0x3620, 0xb7 }, + { 0x3622, 0x04 }, + { 0x3626, 0x21 }, + { 0x3627, 0x30 }, + { 0x3630, 0x44 }, + { 0x3631, 0x35 }, + { 0x3634, 0x60 }, + { 0x3636, 0x00 }, + { 0x3662, 0x01 }, + { 0x3663, 0x70 }, + { 0x3664, 0x50 }, + { 0x3666, 0x0a }, + { 0x3669, 0x1a }, + { 0x366a, 0x00 }, + { 0x366b, 0x50 }, + { 0x3673, 0x01 }, + { 0x3674, 0xff }, + { 0x3675, 0x03 }, + { 0x3705, 0xc1 }, + { 0x3709, 0x40 }, + { 0x373c, 0x08 }, + { 0x3742, 0x00 }, + { 0x3757, 0xb3 }, + { 0x3788, 0x00 }, + { 0x37a8, 0x01 }, + { 0x37a9, 0xc0 }, + { 0x3800, 0x00 }, + { 0x3801, 0x04 }, + { 0x3802, 0x00 }, + { 0x3803, 0x04 }, + { 0x3804, 0x02 }, + { 0x3805, 0x8b }, + { 0x3806, 0x01 }, + { 0x3807, 0xeb }, + { 0x3808, 0x02 }, /* width high */ + { 0x3809, 0x80 }, /* width low */ + { 0x380a, 0x01 }, /* height high */ + { 0x380b, 0xe0 }, /* height low */ + { 0x380c, 0x03 }, /* total horiz timing high */ + { 0x380d, 0xa0 }, /* total horiz timing low */ + { 0x380e, 0x03 }, /* total vertical timing high */ + { 0x380f, 0x5c }, /* total vertical timing low */ + { 0x3810, 0x00 }, + { 0x3811, 0x04 }, + { 0x3812, 0x00 }, + { 0x3813, 0x05 }, + { 0x3814, 0x11 }, + { 0x3815, 0x11 }, + { 0x3820, 0x40 }, + { 0x3821, 0x00 }, + { 0x382f, 0x0e }, + { 0x3832, 0x00 }, + { 0x3833, 0x05 }, + { 0x3834, 0x00 }, + { 0x3835, 0x0c }, + { 0x3837, 0x00 }, + { 0x3b80, 0x00 }, + { 0x3b81, 0xa5 }, + { 0x3b82, 0x10 }, + { 0x3b83, 0x00 }, + { 0x3b84, 0x08 }, + { 0x3b85, 0x00 }, + { 0x3b86, 0x01 }, + { 0x3b87, 0x00 }, + { 0x3b88, 0x00 }, + { 0x3b89, 0x00 }, + { 0x3b8a, 0x00 }, + { 0x3b8b, 0x05 }, + { 0x3b8c, 0x00 }, + { 0x3b8d, 0x00 }, + { 0x3b8e, 0x00 }, + { 0x3b8f, 0x1a }, + { 0x3b94, 0x05 }, + { 0x3b95, 0xf2 }, + { 0x3b96, 0x40 }, + { 0x3c00, 0x89 }, + { 0x3c01, 0x63 }, + { 0x3c02, 0x01 }, + { 0x3c03, 0x00 }, + { 0x3c04, 0x00 }, + { 0x3c05, 0x03 }, + { 0x3c06, 0x00 }, + { 0x3c07, 0x06 }, + { 0x3c0c, 0x01 }, + { 0x3c0d, 0xd0 }, + { 0x3c0e, 0x02 }, + { 0x3c0f, 0x0a }, + { 0x4001, 0x42 }, + { 0x4004, 0x04 }, + { 0x4005, 0x00 }, + { 0x404e, 0x01 }, + { 0x4300, 0xff }, + { 0x4301, 0x00 }, + { 0x4315, 0x00 }, + { 0x4501, 0x48 }, + { 0x4600, 0x00 }, + { 0x4601, 0x4e }, + { 0x4801, 0x0f }, + { 0x4806, 0x0f }, + { 0x4819, 0xaa }, + { 0x4823, 0x3e }, + { 0x4837, 0x19 }, + { 0x4a0d, 0x00 }, + { 0x4a47, 0x7f }, + { 0x4a49, 0xf0 }, + { 0x4a4b, 0x30 }, + { 0x5000, 0x85 }, + { 0x5001, 0x80 }, +}; + +static const struct reg_value ov7251_setting_vga_90fps[] = { + { 0x3005, 0x00 }, + { 0x3012, 0xc0 }, + { 0x3013, 0xd2 }, + { 0x3014, 0x04 }, + { 0x3016, 0x10 }, + { 0x3017, 0x00 }, + { 0x3018, 0x00 }, + { 0x301a, 0x00 }, + { 0x301b, 0x00 }, + { 0x301c, 0x00 }, + { 0x3023, 0x05 }, + { 0x3037, 0xf0 }, + { 0x3098, 0x04 }, /* pll2 pre divider */ + { 0x3099, 0x28 }, /* pll2 multiplier */ + { 0x309a, 0x05 }, /* pll2 sys divider */ + { 0x309b, 0x04 }, /* pll2 adc divider */ + { 0x309d, 0x00 }, /* pll2 divider */ + { 0x30b0, 0x0a }, /* pll1 pix divider */ + { 0x30b1, 0x01 }, /* pll1 divider */ + { 0x30b3, 0x64 }, /* pll1 multiplier */ + { 0x30b4, 0x03 }, /* pll1 pre divider */ + { 0x30b5, 0x05 }, /* pll1 mipi divider */ + { 0x3106, 0xda }, + { 0x3503, 0x07 }, + { 0x3509, 0x10 }, + { 0x3600, 0x1c }, + { 0x3602, 0x62 }, + { 0x3620, 0xb7 }, + { 0x3622, 0x04 }, + { 0x3626, 0x21 }, + { 0x3627, 0x30 }, + { 0x3630, 0x44 }, + { 0x3631, 0x35 }, + { 0x3634, 0x60 }, + { 0x3636, 0x00 }, + { 0x3662, 0x01 }, + { 0x3663, 0x70 }, + { 0x3664, 0x50 }, + { 0x3666, 0x0a }, + { 0x3669, 0x1a }, + { 0x366a, 0x00 }, + { 0x366b, 0x50 }, + { 0x3673, 0x01 }, + { 0x3674, 0xff }, + { 0x3675, 0x03 }, + { 0x3705, 0xc1 }, + { 0x3709, 0x40 }, + { 0x373c, 0x08 }, + { 0x3742, 0x00 }, + { 0x3757, 0xb3 }, + { 0x3788, 0x00 }, + { 0x37a8, 0x01 }, + { 0x37a9, 0xc0 }, + { 0x3800, 0x00 }, + { 0x3801, 0x04 }, + { 0x3802, 0x00 }, + { 0x3803, 0x04 }, + { 0x3804, 0x02 }, + { 0x3805, 0x8b }, + { 0x3806, 0x01 }, + { 0x3807, 0xeb }, + { 0x3808, 0x02 }, /* width high */ + { 0x3809, 0x80 }, /* width low */ + { 0x380a, 0x01 }, /* height high */ + { 0x380b, 0xe0 }, /* height low */ + { 0x380c, 0x03 }, /* total horiz timing high */ + { 0x380d, 0xa0 }, /* total horiz timing low */ + { 0x380e, 0x02 }, /* total vertical timing high */ + { 0x380f, 0x3c }, /* total vertical timing low */ + { 0x3810, 0x00 }, + { 0x3811, 0x04 }, + { 0x3812, 0x00 }, + { 0x3813, 0x05 }, + { 0x3814, 0x11 }, + { 0x3815, 0x11 }, + { 0x3820, 0x40 }, + { 0x3821, 0x00 }, + { 0x382f, 0x0e }, + { 0x3832, 0x00 }, + { 0x3833, 0x05 }, + { 0x3834, 0x00 }, + { 0x3835, 0x0c }, + { 0x3837, 0x00 }, + { 0x3b80, 0x00 }, + { 0x3b81, 0xa5 }, + { 0x3b82, 0x10 }, + { 0x3b83, 0x00 }, + { 0x3b84, 0x08 }, + { 0x3b85, 0x00 }, + { 0x3b86, 0x01 }, + { 0x3b87, 0x00 }, + { 0x3b88, 0x00 }, + { 0x3b89, 0x00 }, + { 0x3b8a, 0x00 }, + { 0x3b8b, 0x05 }, + { 0x3b8c, 0x00 }, + { 0x3b8d, 0x00 }, + { 0x3b8e, 0x00 }, + { 0x3b8f, 0x1a }, + { 0x3b94, 0x05 }, + { 0x3b95, 0xf2 }, + { 0x3b96, 0x40 }, + { 0x3c00, 0x89 }, + { 0x3c01, 0x63 }, + { 0x3c02, 0x01 }, + { 0x3c03, 0x00 }, + { 0x3c04, 0x00 }, + { 0x3c05, 0x03 }, + { 0x3c06, 0x00 }, + { 0x3c07, 0x06 }, + { 0x3c0c, 0x01 }, + { 0x3c0d, 0xd0 }, + { 0x3c0e, 0x02 }, + { 0x3c0f, 0x0a }, + { 0x4001, 0x42 }, + { 0x4004, 0x04 }, + { 0x4005, 0x00 }, + { 0x404e, 0x01 }, + { 0x4300, 0xff }, + { 0x4301, 0x00 }, + { 0x4315, 0x00 }, + { 0x4501, 0x48 }, + { 0x4600, 0x00 }, + { 0x4601, 0x4e }, + { 0x4801, 0x0f }, + { 0x4806, 0x0f }, + { 0x4819, 0xaa }, + { 0x4823, 0x3e }, + { 0x4837, 0x19 }, + { 0x4a0d, 0x00 }, + { 0x4a47, 0x7f }, + { 0x4a49, 0xf0 }, + { 0x4a4b, 0x30 }, + { 0x5000, 0x85 }, + { 0x5001, 0x80 }, +}; + +static const s64 link_freq[] = { + 240000000, +}; + +static const struct ov7251_mode_info ov7251_mode_info_data[] = { + { + .width = 640, + .height = 480, + .data = ov7251_setting_vga_30fps, + .data_size = ARRAY_SIZE(ov7251_setting_vga_30fps), + .pixel_clock = 48000000, + .link_freq = 0, /* an index in link_freq[] */ + .exposure_max = 1704, + .exposure_def = 504, + .timeperframe = { + .numerator = 100, + .denominator = 3000 + } + }, + { + .width = 640, + .height = 480, + .data = ov7251_setting_vga_60fps, + .data_size = ARRAY_SIZE(ov7251_setting_vga_60fps), + .pixel_clock = 48000000, + .link_freq = 0, /* an index in link_freq[] */ + .exposure_max = 840, + .exposure_def = 504, + .timeperframe = { + .numerator = 100, + .denominator = 6014 + } + }, + { + .width = 640, + .height = 480, + .data = ov7251_setting_vga_90fps, + .data_size = ARRAY_SIZE(ov7251_setting_vga_90fps), + .pixel_clock = 48000000, + .link_freq = 0, /* an index in link_freq[] */ + .exposure_max = 552, + .exposure_def = 504, + .timeperframe = { + .numerator = 100, + .denominator = 9043 + } + }, +}; + +static int ov7251_regulators_enable(struct ov7251 *ov7251) +{ + int ret; + + /* OV7251 power up sequence requires core regulator + * to be enabled not earlier than io regulator + */ + + ret = regulator_enable(ov7251->io_regulator); + if (ret < 0) { + dev_err(ov7251->dev, "set io voltage failed\n"); + return ret; + } + + ret = regulator_enable(ov7251->analog_regulator); + if (ret) { + dev_err(ov7251->dev, "set analog voltage failed\n"); + goto err_disable_io; + } + + ret = regulator_enable(ov7251->core_regulator); + if (ret) { + dev_err(ov7251->dev, "set core voltage failed\n"); + goto err_disable_analog; + } + + return 0; + +err_disable_analog: + regulator_disable(ov7251->analog_regulator); + +err_disable_io: + regulator_disable(ov7251->io_regulator); + + return ret; +} + +static void ov7251_regulators_disable(struct ov7251 *ov7251) +{ + int ret; + + ret = regulator_disable(ov7251->core_regulator); + if (ret < 0) + dev_err(ov7251->dev, "core regulator disable failed\n"); + + ret = regulator_disable(ov7251->analog_regulator); + if (ret < 0) + dev_err(ov7251->dev, "analog regulator disable failed\n"); + + ret = regulator_disable(ov7251->io_regulator); + if (ret < 0) + dev_err(ov7251->dev, "io regulator disable failed\n"); +} + +static int ov7251_write_reg(struct ov7251 *ov7251, u16 reg, u8 val) +{ + u8 regbuf[3]; + int ret; + + regbuf[0] = reg >> 8; + regbuf[1] = reg & 0xff; + regbuf[2] = val; + + ret = i2c_master_send(ov7251->i2c_client, regbuf, 3); + if (ret < 0) { + dev_err(ov7251->dev, "%s: write reg error %d: reg=%x, val=%x\n", + __func__, ret, reg, val); + return ret; + } + + return 0; +} + +static int ov7251_write_seq_regs(struct ov7251 *ov7251, u16 reg, u8 *val, + u8 num) +{ + u8 regbuf[5]; + u8 nregbuf = sizeof(reg) + num * sizeof(*val); + int ret = 0; + + if (nregbuf > sizeof(regbuf)) + return -EINVAL; + + regbuf[0] = reg >> 8; + regbuf[1] = reg & 0xff; + + memcpy(regbuf + 2, val, num); + + ret = i2c_master_send(ov7251->i2c_client, regbuf, nregbuf); + if (ret < 0) { + dev_err(ov7251->dev, + "%s: write seq regs error %d: first reg=%x\n", + __func__, ret, reg); + return ret; + } + + return 0; +} + +static int ov7251_read_reg(struct ov7251 *ov7251, u16 reg, u8 *val) +{ + u8 regbuf[2]; + int ret; + + regbuf[0] = reg >> 8; + regbuf[1] = reg & 0xff; + + ret = i2c_master_send(ov7251->i2c_client, regbuf, 2); + if (ret < 0) { + dev_err(ov7251->dev, "%s: write reg error %d: reg=%x\n", + __func__, ret, reg); + return ret; + } + + ret = i2c_master_recv(ov7251->i2c_client, val, 1); + if (ret < 0) { + dev_err(ov7251->dev, "%s: read reg error %d: reg=%x\n", + __func__, ret, reg); + return ret; + } + + return 0; +} + +static int ov7251_set_exposure(struct ov7251 *ov7251, s32 exposure) +{ + u16 reg; + u8 val[3]; + + reg = OV7251_AEC_EXPO_0; + val[0] = (exposure & 0xf000) >> 12; /* goes to OV7251_AEC_EXPO_0 */ + val[1] = (exposure & 0x0ff0) >> 4; /* goes to OV7251_AEC_EXPO_1 */ + val[2] = (exposure & 0x000f) << 4; /* goes to OV7251_AEC_EXPO_2 */ + + return ov7251_write_seq_regs(ov7251, reg, val, 3); +} + +static int ov7251_set_gain(struct ov7251 *ov7251, s32 gain) +{ + u16 reg; + u8 val[2]; + + reg = OV7251_AEC_AGC_ADJ_0; + val[0] = (gain & 0x0300) >> 8; /* goes to OV7251_AEC_AGC_ADJ_0 */ + val[1] = gain & 0xff; /* goes to OV7251_AEC_AGC_ADJ_1 */ + + return ov7251_write_seq_regs(ov7251, reg, val, 2); +} + +static int ov7251_set_register_array(struct ov7251 *ov7251, + const struct reg_value *settings, + unsigned int num_settings) +{ + unsigned int i; + int ret; + + for (i = 0; i < num_settings; ++i, ++settings) { + ret = ov7251_write_reg(ov7251, settings->reg, settings->val); + if (ret < 0) + return ret; + } + + return 0; +} + +static int ov7251_set_power_on(struct ov7251 *ov7251) +{ + int ret; + u32 wait_us; + + ret = ov7251_regulators_enable(ov7251); + if (ret < 0) + return ret; + + ret = clk_prepare_enable(ov7251->xclk); + if (ret < 0) { + dev_err(ov7251->dev, "clk prepare enable failed\n"); + ov7251_regulators_disable(ov7251); + return ret; + } + + gpiod_set_value_cansleep(ov7251->enable_gpio, 1); + + /* wait at least 65536 external clock cycles */ + wait_us = DIV_ROUND_UP(65536 * 1000, + DIV_ROUND_UP(ov7251->xclk_freq, 1000)); + usleep_range(wait_us, wait_us + 1000); + + return 0; +} + +static void ov7251_set_power_off(struct ov7251 *ov7251) +{ + clk_disable_unprepare(ov7251->xclk); + gpiod_set_value_cansleep(ov7251->enable_gpio, 0); + ov7251_regulators_disable(ov7251); +} + +static int ov7251_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov7251 *ov7251 = to_ov7251(sd); + int ret = 0; + + mutex_lock(&ov7251->lock); + + /* If the power state is not modified - no work to do. */ + if (ov7251->power_on == !!on) + goto exit; + + if (on) { + ret = ov7251_set_power_on(ov7251); + if (ret < 0) + goto exit; + + ret = ov7251_set_register_array(ov7251, + ov7251_global_init_setting, + ARRAY_SIZE(ov7251_global_init_setting)); + if (ret < 0) { + dev_err(ov7251->dev, "could not set init registers\n"); + ov7251_set_power_off(ov7251); + goto exit; + } + + ov7251->power_on = true; + } else { + ov7251_set_power_off(ov7251); + ov7251->power_on = false; + } + +exit: + mutex_unlock(&ov7251->lock); + + return ret; +} + +static int ov7251_set_hflip(struct ov7251 *ov7251, s32 value) +{ + u8 val = ov7251->timing_format2; + int ret; + + if (value) + val |= OV7251_TIMING_FORMAT2_MIRROR; + else + val &= ~OV7251_TIMING_FORMAT2_MIRROR; + + ret = ov7251_write_reg(ov7251, OV7251_TIMING_FORMAT2, val); + if (!ret) + ov7251->timing_format2 = val; + + return ret; +} + +static int ov7251_set_vflip(struct ov7251 *ov7251, s32 value) +{ + u8 val = ov7251->timing_format1; + int ret; + + if (value) + val |= OV7251_TIMING_FORMAT1_VFLIP; + else + val &= ~OV7251_TIMING_FORMAT1_VFLIP; + + ret = ov7251_write_reg(ov7251, OV7251_TIMING_FORMAT1, val); + if (!ret) + ov7251->timing_format1 = val; + + return ret; +} + +static int ov7251_set_test_pattern(struct ov7251 *ov7251, s32 value) +{ + u8 val = ov7251->pre_isp_00; + int ret; + + if (value) + val |= OV7251_PRE_ISP_00_TEST_PATTERN; + else + val &= ~OV7251_PRE_ISP_00_TEST_PATTERN; + + ret = ov7251_write_reg(ov7251, OV7251_PRE_ISP_00, val); + if (!ret) + ov7251->pre_isp_00 = val; + + return ret; +} + +static const char * const ov7251_test_pattern_menu[] = { + "Disabled", + "Vertical Pattern Bars", +}; + +static int ov7251_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov7251 *ov7251 = container_of(ctrl->handler, + struct ov7251, ctrls); + int ret; + + /* v4l2_ctrl_lock() locks our mutex */ + + if (!ov7251->power_on) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = ov7251_set_exposure(ov7251, ctrl->val); + break; + case V4L2_CID_GAIN: + ret = ov7251_set_gain(ov7251, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov7251_set_test_pattern(ov7251, ctrl->val); + break; + case V4L2_CID_HFLIP: + ret = ov7251_set_hflip(ov7251, ctrl->val); + break; + case V4L2_CID_VFLIP: + ret = ov7251_set_vflip(ov7251, ctrl->val); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct v4l2_ctrl_ops ov7251_ctrl_ops = { + .s_ctrl = ov7251_s_ctrl, +}; + +static int ov7251_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_Y10_1X10; + + return 0; +} + +static int ov7251_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->code != MEDIA_BUS_FMT_Y10_1X10) + return -EINVAL; + + if (fse->index >= ARRAY_SIZE(ov7251_mode_info_data)) + return -EINVAL; + + fse->min_width = ov7251_mode_info_data[fse->index].width; + fse->max_width = ov7251_mode_info_data[fse->index].width; + fse->min_height = ov7251_mode_info_data[fse->index].height; + fse->max_height = ov7251_mode_info_data[fse->index].height; + + return 0; +} + +static int ov7251_enum_frame_ival(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + unsigned int index = fie->index; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ov7251_mode_info_data); i++) { + if (fie->width != ov7251_mode_info_data[i].width || + fie->height != ov7251_mode_info_data[i].height) + continue; + + if (index-- == 0) { + fie->interval = ov7251_mode_info_data[i].timeperframe; + return 0; + } + } + + return -EINVAL; +} + +static struct v4l2_mbus_framefmt * +__ov7251_get_pad_format(struct ov7251 *ov7251, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&ov7251->sd, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &ov7251->fmt; + default: + return NULL; + } +} + +static int ov7251_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov7251 *ov7251 = to_ov7251(sd); + + mutex_lock(&ov7251->lock); + format->format = *__ov7251_get_pad_format(ov7251, cfg, format->pad, + format->which); + mutex_unlock(&ov7251->lock); + + return 0; +} + +static struct v4l2_rect * +__ov7251_get_pad_crop(struct ov7251 *ov7251, struct v4l2_subdev_pad_config *cfg, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&ov7251->sd, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &ov7251->crop; + default: + return NULL; + } +} + +static inline u32 avg_fps(const struct v4l2_fract *t) +{ + return (t->denominator + (t->numerator >> 1)) / t->numerator; +} + +static const struct ov7251_mode_info * +ov7251_find_mode_by_ival(struct ov7251 *ov7251, struct v4l2_fract *timeperframe) +{ + const struct ov7251_mode_info *mode = ov7251->current_mode; + unsigned int fps_req = avg_fps(timeperframe); + unsigned int max_dist_match = (unsigned int) -1; + unsigned int i, n = 0; + + for (i = 0; i < ARRAY_SIZE(ov7251_mode_info_data); i++) { + unsigned int dist; + unsigned int fps_tmp; + + if (mode->width != ov7251_mode_info_data[i].width || + mode->height != ov7251_mode_info_data[i].height) + continue; + + fps_tmp = avg_fps(&ov7251_mode_info_data[i].timeperframe); + + dist = abs(fps_req - fps_tmp); + + if (dist < max_dist_match) { + n = i; + max_dist_match = dist; + } + } + + return &ov7251_mode_info_data[n]; +} + +static int ov7251_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov7251 *ov7251 = to_ov7251(sd); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + const struct ov7251_mode_info *new_mode; + int ret = 0; + + mutex_lock(&ov7251->lock); + + __crop = __ov7251_get_pad_crop(ov7251, cfg, format->pad, format->which); + + new_mode = v4l2_find_nearest_size(ov7251_mode_info_data, + ARRAY_SIZE(ov7251_mode_info_data), + width, height, + format->format.width, format->format.height); + + __crop->width = new_mode->width; + __crop->height = new_mode->height; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ret = __v4l2_ctrl_s_ctrl_int64(ov7251->pixel_clock, + new_mode->pixel_clock); + if (ret < 0) + goto exit; + + ret = __v4l2_ctrl_s_ctrl(ov7251->link_freq, + new_mode->link_freq); + if (ret < 0) + goto exit; + + ret = __v4l2_ctrl_modify_range(ov7251->exposure, + 1, new_mode->exposure_max, + 1, new_mode->exposure_def); + if (ret < 0) + goto exit; + + ret = __v4l2_ctrl_s_ctrl(ov7251->exposure, + new_mode->exposure_def); + if (ret < 0) + goto exit; + + ret = __v4l2_ctrl_s_ctrl(ov7251->gain, 16); + if (ret < 0) + goto exit; + + ov7251->current_mode = new_mode; + } + + __format = __ov7251_get_pad_format(ov7251, cfg, format->pad, + format->which); + __format->width = __crop->width; + __format->height = __crop->height; + __format->code = MEDIA_BUS_FMT_Y10_1X10; + __format->field = V4L2_FIELD_NONE; + __format->colorspace = V4L2_COLORSPACE_SRGB; + __format->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(__format->colorspace); + __format->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + __format->colorspace, __format->ycbcr_enc); + __format->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(__format->colorspace); + + format->format = *__format; + +exit: + mutex_unlock(&ov7251->lock); + + return ret; +} + +static int ov7251_entity_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg) +{ + struct v4l2_subdev_format fmt = { + .which = cfg ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .width = 640, + .height = 480 + } + }; + + ov7251_set_format(subdev, cfg, &fmt); + + return 0; +} + +static int ov7251_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct ov7251 *ov7251 = to_ov7251(sd); + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + mutex_lock(&ov7251->lock); + sel->r = *__ov7251_get_pad_crop(ov7251, cfg, sel->pad, + sel->which); + mutex_unlock(&ov7251->lock); + + return 0; +} + +static int ov7251_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct ov7251 *ov7251 = to_ov7251(subdev); + int ret; + + mutex_lock(&ov7251->lock); + + if (enable) { + ret = ov7251_set_register_array(ov7251, + ov7251->current_mode->data, + ov7251->current_mode->data_size); + if (ret < 0) { + dev_err(ov7251->dev, "could not set mode %dx%d\n", + ov7251->current_mode->width, + ov7251->current_mode->height); + goto exit; + } + ret = __v4l2_ctrl_handler_setup(&ov7251->ctrls); + if (ret < 0) { + dev_err(ov7251->dev, "could not sync v4l2 controls\n"); + goto exit; + } + ret = ov7251_write_reg(ov7251, OV7251_SC_MODE_SELECT, + OV7251_SC_MODE_SELECT_STREAMING); + } else { + ret = ov7251_write_reg(ov7251, OV7251_SC_MODE_SELECT, + OV7251_SC_MODE_SELECT_SW_STANDBY); + } + +exit: + mutex_unlock(&ov7251->lock); + + return ret; +} + +static int ov7251_get_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov7251 *ov7251 = to_ov7251(subdev); + + mutex_lock(&ov7251->lock); + fi->interval = ov7251->current_mode->timeperframe; + mutex_unlock(&ov7251->lock); + + return 0; +} + +static int ov7251_set_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov7251 *ov7251 = to_ov7251(subdev); + const struct ov7251_mode_info *new_mode; + int ret = 0; + + mutex_lock(&ov7251->lock); + new_mode = ov7251_find_mode_by_ival(ov7251, &fi->interval); + + if (new_mode != ov7251->current_mode) { + ret = __v4l2_ctrl_s_ctrl_int64(ov7251->pixel_clock, + new_mode->pixel_clock); + if (ret < 0) + goto exit; + + ret = __v4l2_ctrl_s_ctrl(ov7251->link_freq, + new_mode->link_freq); + if (ret < 0) + goto exit; + + ret = __v4l2_ctrl_modify_range(ov7251->exposure, + 1, new_mode->exposure_max, + 1, new_mode->exposure_def); + if (ret < 0) + goto exit; + + ret = __v4l2_ctrl_s_ctrl(ov7251->exposure, + new_mode->exposure_def); + if (ret < 0) + goto exit; + + ret = __v4l2_ctrl_s_ctrl(ov7251->gain, 16); + if (ret < 0) + goto exit; + + ov7251->current_mode = new_mode; + } + + fi->interval = ov7251->current_mode->timeperframe; + +exit: + mutex_unlock(&ov7251->lock); + + return ret; +} + +static const struct v4l2_subdev_core_ops ov7251_core_ops = { + .s_power = ov7251_s_power, +}; + +static const struct v4l2_subdev_video_ops ov7251_video_ops = { + .s_stream = ov7251_s_stream, + .g_frame_interval = ov7251_get_frame_interval, + .s_frame_interval = ov7251_set_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops ov7251_subdev_pad_ops = { + .init_cfg = ov7251_entity_init_cfg, + .enum_mbus_code = ov7251_enum_mbus_code, + .enum_frame_size = ov7251_enum_frame_size, + .enum_frame_interval = ov7251_enum_frame_ival, + .get_fmt = ov7251_get_format, + .set_fmt = ov7251_set_format, + .get_selection = ov7251_get_selection, +}; + +static const struct v4l2_subdev_ops ov7251_subdev_ops = { + .core = &ov7251_core_ops, + .video = &ov7251_video_ops, + .pad = &ov7251_subdev_pad_ops, +}; + +static int ov7251_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct fwnode_handle *endpoint; + struct ov7251 *ov7251; + u8 chip_id_high, chip_id_low, chip_rev; + int ret; + + ov7251 = devm_kzalloc(dev, sizeof(struct ov7251), GFP_KERNEL); + if (!ov7251) + return -ENOMEM; + + ov7251->i2c_client = client; + ov7251->dev = dev; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_parse(endpoint, &ov7251->ep); + fwnode_handle_put(endpoint); + if (ret < 0) { + dev_err(dev, "parsing endpoint node failed\n"); + return ret; + } + + if (ov7251->ep.bus_type != V4L2_MBUS_CSI2) { + dev_err(dev, "invalid bus type (%u), must be CSI2 (%u)\n", + ov7251->ep.bus_type, V4L2_MBUS_CSI2); + return -EINVAL; + } + + /* get system clock (xclk) */ + ov7251->xclk = devm_clk_get(dev, "xclk"); + if (IS_ERR(ov7251->xclk)) { + dev_err(dev, "could not get xclk"); + return PTR_ERR(ov7251->xclk); + } + + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &ov7251->xclk_freq); + if (ret) { + dev_err(dev, "could not get xclk frequency\n"); + return ret; + } + + /* external clock must be 24MHz, allow 1% tolerance */ + if (ov7251->xclk_freq < 23760000 || ov7251->xclk_freq > 24240000) { + dev_err(dev, "external clock frequency %u is not supported\n", + ov7251->xclk_freq); + return -EINVAL; + } + + ret = clk_set_rate(ov7251->xclk, ov7251->xclk_freq); + if (ret) { + dev_err(dev, "could not set xclk frequency\n"); + return ret; + } + + ov7251->io_regulator = devm_regulator_get(dev, "vdddo"); + if (IS_ERR(ov7251->io_regulator)) { + dev_err(dev, "cannot get io regulator\n"); + return PTR_ERR(ov7251->io_regulator); + } + + ov7251->core_regulator = devm_regulator_get(dev, "vddd"); + if (IS_ERR(ov7251->core_regulator)) { + dev_err(dev, "cannot get core regulator\n"); + return PTR_ERR(ov7251->core_regulator); + } + + ov7251->analog_regulator = devm_regulator_get(dev, "vdda"); + if (IS_ERR(ov7251->analog_regulator)) { + dev_err(dev, "cannot get analog regulator\n"); + return PTR_ERR(ov7251->analog_regulator); + } + + ov7251->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(ov7251->enable_gpio)) { + dev_err(dev, "cannot get enable gpio\n"); + return PTR_ERR(ov7251->enable_gpio); + } + + mutex_init(&ov7251->lock); + + v4l2_ctrl_handler_init(&ov7251->ctrls, 7); + ov7251->ctrls.lock = &ov7251->lock; + + v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + ov7251->exposure = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, + V4L2_CID_EXPOSURE, 1, 32, 1, 32); + ov7251->gain = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, + V4L2_CID_GAIN, 16, 1023, 1, 16); + v4l2_ctrl_new_std_menu_items(&ov7251->ctrls, &ov7251_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov7251_test_pattern_menu) - 1, + 0, 0, ov7251_test_pattern_menu); + ov7251->pixel_clock = v4l2_ctrl_new_std(&ov7251->ctrls, + &ov7251_ctrl_ops, + V4L2_CID_PIXEL_RATE, + 1, INT_MAX, 1, 1); + ov7251->link_freq = v4l2_ctrl_new_int_menu(&ov7251->ctrls, + &ov7251_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq) - 1, + 0, link_freq); + if (ov7251->link_freq) + ov7251->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ov7251->sd.ctrl_handler = &ov7251->ctrls; + + if (ov7251->ctrls.error) { + dev_err(dev, "%s: control initialization error %d\n", + __func__, ov7251->ctrls.error); + ret = ov7251->ctrls.error; + goto free_ctrl; + } + + v4l2_i2c_subdev_init(&ov7251->sd, client, &ov7251_subdev_ops); + ov7251->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov7251->pad.flags = MEDIA_PAD_FL_SOURCE; + ov7251->sd.dev = &client->dev; + ov7251->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&ov7251->sd.entity, 1, &ov7251->pad); + if (ret < 0) { + dev_err(dev, "could not register media entity\n"); + goto free_ctrl; + } + + ret = ov7251_s_power(&ov7251->sd, true); + if (ret < 0) { + dev_err(dev, "could not power up OV7251\n"); + goto free_entity; + } + + ret = ov7251_read_reg(ov7251, OV7251_CHIP_ID_HIGH, &chip_id_high); + if (ret < 0 || chip_id_high != OV7251_CHIP_ID_HIGH_BYTE) { + dev_err(dev, "could not read ID high\n"); + ret = -ENODEV; + goto power_down; + } + ret = ov7251_read_reg(ov7251, OV7251_CHIP_ID_LOW, &chip_id_low); + if (ret < 0 || chip_id_low != OV7251_CHIP_ID_LOW_BYTE) { + dev_err(dev, "could not read ID low\n"); + ret = -ENODEV; + goto power_down; + } + + ret = ov7251_read_reg(ov7251, OV7251_SC_GP_IO_IN1, &chip_rev); + if (ret < 0) { + dev_err(dev, "could not read revision\n"); + ret = -ENODEV; + goto power_down; + } + chip_rev >>= 4; + + dev_info(dev, "OV7251 revision %x (%s) detected at address 0x%02x\n", + chip_rev, + chip_rev == 0x4 ? "1A / 1B" : + chip_rev == 0x5 ? "1C / 1D" : + chip_rev == 0x6 ? "1E" : + chip_rev == 0x7 ? "1F" : "unknown", + client->addr); + + ret = ov7251_read_reg(ov7251, OV7251_PRE_ISP_00, + &ov7251->pre_isp_00); + if (ret < 0) { + dev_err(dev, "could not read test pattern value\n"); + ret = -ENODEV; + goto power_down; + } + + ret = ov7251_read_reg(ov7251, OV7251_TIMING_FORMAT1, + &ov7251->timing_format1); + if (ret < 0) { + dev_err(dev, "could not read vflip value\n"); + ret = -ENODEV; + goto power_down; + } + + ret = ov7251_read_reg(ov7251, OV7251_TIMING_FORMAT2, + &ov7251->timing_format2); + if (ret < 0) { + dev_err(dev, "could not read hflip value\n"); + ret = -ENODEV; + goto power_down; + } + + ov7251_s_power(&ov7251->sd, false); + + ret = v4l2_async_register_subdev(&ov7251->sd); + if (ret < 0) { + dev_err(dev, "could not register v4l2 device\n"); + goto free_entity; + } + + ov7251_entity_init_cfg(&ov7251->sd, NULL); + + return 0; + +power_down: + ov7251_s_power(&ov7251->sd, false); +free_entity: + media_entity_cleanup(&ov7251->sd.entity); +free_ctrl: + v4l2_ctrl_handler_free(&ov7251->ctrls); + mutex_destroy(&ov7251->lock); + + return ret; +} + +static int ov7251_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov7251 *ov7251 = to_ov7251(sd); + + v4l2_async_unregister_subdev(&ov7251->sd); + media_entity_cleanup(&ov7251->sd.entity); + v4l2_ctrl_handler_free(&ov7251->ctrls); + mutex_destroy(&ov7251->lock); + + return 0; +} + +static const struct of_device_id ov7251_of_match[] = { + { .compatible = "ovti,ov7251" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ov7251_of_match); + +static struct i2c_driver ov7251_i2c_driver = { + .driver = { + .of_match_table = ov7251_of_match, + .name = "ov7251", + }, + .probe_new = ov7251_probe, + .remove = ov7251_remove, +}; + +module_i2c_driver(ov7251_i2c_driver); + +MODULE_DESCRIPTION("Omnivision OV7251 Camera Driver"); +MODULE_AUTHOR("Todor Tomov <todor.tomov@linaro.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index b62860c89439..e2550708abc8 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -1035,7 +1035,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, /* Set COM8. */ if (priv->band_filter) { - ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1); + ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF); if (!ret) ret = ov772x_mask_set(client, BDBASE, 0xff, 256 - priv->band_filter); diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 01f578785e79..605f3e25ad82 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -953,7 +953,7 @@ static int ov7740_init_controls(struct ov7740 *ov7740) struct v4l2_ctrl_handler *ctrl_hdlr = &ov7740->ctrl_handler; int ret; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 2); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 12); if (ret < 0) return ret; @@ -980,27 +980,39 @@ static int ov7740_init_controls(struct ov7740 *ov7740) V4L2_CID_HFLIP, 0, 1, 1, 0); ov7740->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + ov7740->gain = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, V4L2_CID_GAIN, 0, 1023, 1, 500); + if (ov7740->gain) + ov7740->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + ov7740->auto_gain = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + ov7740->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, V4L2_CID_EXPOSURE, 0, 65535, 1, 500); + if (ov7740->exposure) + ov7740->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + ov7740->auto_exposure = v4l2_ctrl_new_std_menu(ctrl_hdlr, &ov7740_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); - ov7740->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; - ov7740->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; - v4l2_ctrl_auto_cluster(3, &ov7740->auto_wb, 0, false); v4l2_ctrl_auto_cluster(2, &ov7740->auto_gain, 0, true); v4l2_ctrl_auto_cluster(2, &ov7740->auto_exposure, V4L2_EXPOSURE_MANUAL, false); v4l2_ctrl_cluster(2, &ov7740->hflip); + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(&client->dev, "controls initialisation failed (%d)\n", + ret); + goto error; + } + ret = v4l2_ctrl_handler_setup(ctrl_hdlr); if (ret) { dev_err(&client->dev, "%s control init failed (%d)\n", @@ -1074,7 +1086,7 @@ static int ov7740_probe(struct i2c_client *client, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &ov7740_subdev_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; #endif #if defined(CONFIG_MEDIA_CONTROLLER) diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 3b7ace395ee6..e1f8208581aa 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -1001,7 +1001,7 @@ static int smiapp_read_nvm(struct smiapp_sensor *sensor, if (rval) goto out; - for (i = 0; i < 1000; i++) { + for (i = 1000; i > 0; i--) { rval = smiapp_read( sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s); @@ -1012,11 +1012,10 @@ static int smiapp_read_nvm(struct smiapp_sensor *sensor, if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) break; - if (--i == 0) { - rval = -ETIMEDOUT; - goto out; - } - + } + if (!i) { + rval = -ETIMEDOUT; + goto out; } for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index 33d7fcf541fc..039a92c3294a 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -2569,7 +2569,7 @@ static int tda1997x_probe(struct i2c_client *client, snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", id->name, i2c_adapter_id(client->adapter), client->addr); - sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; sd->entity.function = MEDIA_ENT_F_DTV_DECODER; sd->entity.ops = &tda1997x_media_ops; @@ -2723,7 +2723,7 @@ static int tda1997x_probe(struct i2c_client *client, state->pads); if (ret) { v4l_err(client, "failed entity_init: %d", ret); - goto err_free_mutex; + goto err_free_handler; } ret = v4l2_async_register_subdev(sd); diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 1734ed4ede33..b162c2fe62c3 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -319,136 +319,136 @@ struct i2c_reg_value { /* Default values as sugested at TVP5150AM1 datasheet */ static const struct i2c_reg_value tvp5150_init_default[] = { { /* 0x00 */ - TVP5150_VD_IN_SRC_SEL_1,0x00 + TVP5150_VD_IN_SRC_SEL_1, 0x00 }, { /* 0x01 */ - TVP5150_ANAL_CHL_CTL,0x15 + TVP5150_ANAL_CHL_CTL, 0x15 }, { /* 0x02 */ - TVP5150_OP_MODE_CTL,0x00 + TVP5150_OP_MODE_CTL, 0x00 }, { /* 0x03 */ - TVP5150_MISC_CTL,0x01 + TVP5150_MISC_CTL, 0x01 }, { /* 0x06 */ - TVP5150_COLOR_KIL_THSH_CTL,0x10 + TVP5150_COLOR_KIL_THSH_CTL, 0x10 }, { /* 0x07 */ - TVP5150_LUMA_PROC_CTL_1,0x60 + TVP5150_LUMA_PROC_CTL_1, 0x60 }, { /* 0x08 */ - TVP5150_LUMA_PROC_CTL_2,0x00 + TVP5150_LUMA_PROC_CTL_2, 0x00 }, { /* 0x09 */ - TVP5150_BRIGHT_CTL,0x80 + TVP5150_BRIGHT_CTL, 0x80 }, { /* 0x0a */ - TVP5150_SATURATION_CTL,0x80 + TVP5150_SATURATION_CTL, 0x80 }, { /* 0x0b */ - TVP5150_HUE_CTL,0x00 + TVP5150_HUE_CTL, 0x00 }, { /* 0x0c */ - TVP5150_CONTRAST_CTL,0x80 + TVP5150_CONTRAST_CTL, 0x80 }, { /* 0x0d */ - TVP5150_DATA_RATE_SEL,0x47 + TVP5150_DATA_RATE_SEL, 0x47 }, { /* 0x0e */ - TVP5150_LUMA_PROC_CTL_3,0x00 + TVP5150_LUMA_PROC_CTL_3, 0x00 }, { /* 0x0f */ - TVP5150_CONF_SHARED_PIN,0x08 + TVP5150_CONF_SHARED_PIN, 0x08 }, { /* 0x11 */ - TVP5150_ACT_VD_CROP_ST_MSB,0x00 + TVP5150_ACT_VD_CROP_ST_MSB, 0x00 }, { /* 0x12 */ - TVP5150_ACT_VD_CROP_ST_LSB,0x00 + TVP5150_ACT_VD_CROP_ST_LSB, 0x00 }, { /* 0x13 */ - TVP5150_ACT_VD_CROP_STP_MSB,0x00 + TVP5150_ACT_VD_CROP_STP_MSB, 0x00 }, { /* 0x14 */ - TVP5150_ACT_VD_CROP_STP_LSB,0x00 + TVP5150_ACT_VD_CROP_STP_LSB, 0x00 }, { /* 0x15 */ - TVP5150_GENLOCK,0x01 + TVP5150_GENLOCK, 0x01 }, { /* 0x16 */ - TVP5150_HORIZ_SYNC_START,0x80 + TVP5150_HORIZ_SYNC_START, 0x80 }, { /* 0x18 */ - TVP5150_VERT_BLANKING_START,0x00 + TVP5150_VERT_BLANKING_START, 0x00 }, { /* 0x19 */ - TVP5150_VERT_BLANKING_STOP,0x00 + TVP5150_VERT_BLANKING_STOP, 0x00 }, { /* 0x1a */ - TVP5150_CHROMA_PROC_CTL_1,0x0c + TVP5150_CHROMA_PROC_CTL_1, 0x0c }, { /* 0x1b */ - TVP5150_CHROMA_PROC_CTL_2,0x14 + TVP5150_CHROMA_PROC_CTL_2, 0x14 }, { /* 0x1c */ - TVP5150_INT_RESET_REG_B,0x00 + TVP5150_INT_RESET_REG_B, 0x00 }, { /* 0x1d */ - TVP5150_INT_ENABLE_REG_B,0x00 + TVP5150_INT_ENABLE_REG_B, 0x00 }, { /* 0x1e */ - TVP5150_INTT_CONFIG_REG_B,0x00 + TVP5150_INTT_CONFIG_REG_B, 0x00 }, { /* 0x28 */ - TVP5150_VIDEO_STD,0x00 + TVP5150_VIDEO_STD, 0x00 }, { /* 0x2e */ - TVP5150_MACROVISION_ON_CTR,0x0f + TVP5150_MACROVISION_ON_CTR, 0x0f }, { /* 0x2f */ - TVP5150_MACROVISION_OFF_CTR,0x01 + TVP5150_MACROVISION_OFF_CTR, 0x01 }, { /* 0xbb */ - TVP5150_TELETEXT_FIL_ENA,0x00 + TVP5150_TELETEXT_FIL_ENA, 0x00 }, { /* 0xc0 */ - TVP5150_INT_STATUS_REG_A,0x00 + TVP5150_INT_STATUS_REG_A, 0x00 }, { /* 0xc1 */ - TVP5150_INT_ENABLE_REG_A,0x00 + TVP5150_INT_ENABLE_REG_A, 0x00 }, { /* 0xc2 */ - TVP5150_INT_CONF,0x04 + TVP5150_INT_CONF, 0x04 }, { /* 0xc8 */ - TVP5150_FIFO_INT_THRESHOLD,0x80 + TVP5150_FIFO_INT_THRESHOLD, 0x80 }, { /* 0xc9 */ - TVP5150_FIFO_RESET,0x00 + TVP5150_FIFO_RESET, 0x00 }, { /* 0xca */ - TVP5150_LINE_NUMBER_INT,0x00 + TVP5150_LINE_NUMBER_INT, 0x00 }, { /* 0xcb */ - TVP5150_PIX_ALIGN_REG_LOW,0x4e + TVP5150_PIX_ALIGN_REG_LOW, 0x4e }, { /* 0xcc */ - TVP5150_PIX_ALIGN_REG_HIGH,0x00 + TVP5150_PIX_ALIGN_REG_HIGH, 0x00 }, { /* 0xcd */ - TVP5150_FIFO_OUT_CTRL,0x01 + TVP5150_FIFO_OUT_CTRL, 0x01 }, { /* 0xcf */ - TVP5150_FULL_FIELD_ENA,0x00 + TVP5150_FULL_FIELD_ENA, 0x00 }, { /* 0xd0 */ - TVP5150_LINE_MODE_INI,0x00 + TVP5150_LINE_MODE_INI, 0x00 }, { /* 0xfc */ - TVP5150_FULL_FIELD_MODE_REG,0x7f + TVP5150_FULL_FIELD_MODE_REG, 0x7f }, { /* end of data */ - 0xff,0xff + 0xff, 0xff } }; @@ -456,27 +456,27 @@ static const struct i2c_reg_value tvp5150_init_default[] = { static const struct i2c_reg_value tvp5150_init_enable[] = { { TVP5150_CONF_SHARED_PIN, 2 - },{ /* Automatic offset and AGC enabled */ + }, { /* Automatic offset and AGC enabled */ TVP5150_ANAL_CHL_CTL, 0x15 - },{ /* Activate YCrCb output 0x9 or 0xd ? */ + }, { /* Activate YCrCb output 0x9 or 0xd ? */ TVP5150_MISC_CTL, TVP5150_MISC_CTL_GPCL | TVP5150_MISC_CTL_INTREQ_OE | TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_SYNC_OE | TVP5150_MISC_CTL_VBLANK | TVP5150_MISC_CTL_CLOCK_OE, - },{ /* Activates video std autodetection for all standards */ + }, { /* Activates video std autodetection for all standards */ TVP5150_AUTOSW_MSK, 0x0 - },{ /* Default format: 0x47. For 4:2:2: 0x40 */ + }, { /* Default format: 0x47. For 4:2:2: 0x40 */ TVP5150_DATA_RATE_SEL, 0x47 - },{ + }, { TVP5150_CHROMA_PROC_CTL_1, 0x0c - },{ + }, { TVP5150_CHROMA_PROC_CTL_2, 0x54 - },{ /* Non documented, but initialized on WinTV USB2 */ + }, { /* Non documented, but initialized on WinTV USB2 */ 0x27, 0x20 - },{ - 0xff,0xff + }, { + 0xff, 0xff } }; @@ -500,78 +500,80 @@ struct i2c_vbi_ram_value { * and so on. There are 16 possible locations from 0 to 15. */ -static struct i2c_vbi_ram_value vbi_ram_default[] = -{ - /* FIXME: Current api doesn't handle all VBI types, those not - yet supported are placed under #if 0 */ +static struct i2c_vbi_ram_value vbi_ram_default[] = { + + /* + * FIXME: Current api doesn't handle all VBI types, those not + * yet supported are placed under #if 0 + */ #if 0 [0] = {0x010, /* Teletext, SECAM, WST System A */ - {V4L2_SLICED_TELETEXT_SECAM,6,23,1}, + {V4L2_SLICED_TELETEXT_SECAM, 6, 23, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26, 0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 } }, #endif [1] = {0x030, /* Teletext, PAL, WST System B */ - {V4L2_SLICED_TELETEXT_B,6,22,1}, + {V4L2_SLICED_TELETEXT_B, 6, 22, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b, 0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 } }, #if 0 [2] = {0x050, /* Teletext, PAL, WST System C */ - {V4L2_SLICED_TELETEXT_PAL_C,6,22,1}, + {V4L2_SLICED_TELETEXT_PAL_C, 6, 22, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, 0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, [3] = {0x070, /* Teletext, NTSC, WST System B */ - {V4L2_SLICED_TELETEXT_NTSC_B,10,21,1}, + {V4L2_SLICED_TELETEXT_NTSC_B, 10, 21, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, [4] = {0x090, /* Tetetext, NTSC NABTS System C */ - {V4L2_SLICED_TELETEXT_NTSC_C,10,21,1}, + {V4L2_SLICED_TELETEXT_NTSC_C, 10, 21, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 } }, [5] = {0x0b0, /* Teletext, NTSC-J, NABTS System D */ - {V4L2_SLICED_TELETEXT_NTSC_D,10,21,1}, + {V4L2_SLICED_TELETEXT_NTSC_D, 10, 21, 1}, { 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23, 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } }, [6] = {0x0d0, /* Closed Caption, PAL/SECAM */ - {V4L2_SLICED_CAPTION_625,22,22,1}, + {V4L2_SLICED_CAPTION_625, 22, 22, 1}, { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, 0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } }, #endif [7] = {0x0f0, /* Closed Caption, NTSC */ - {V4L2_SLICED_CAPTION_525,21,21,1}, + {V4L2_SLICED_CAPTION_525, 21, 21, 1}, { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, 0x69, 0x8c, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } }, [8] = {0x110, /* Wide Screen Signal, PAL/SECAM */ - {V4L2_SLICED_WSS_625,23,23,1}, + {V4L2_SLICED_WSS_625, 23, 23, 1}, { 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42, 0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 } }, #if 0 [9] = {0x130, /* Wide Screen Signal, NTSC C */ - {V4L2_SLICED_WSS_525,20,20,1}, + {V4L2_SLICED_WSS_525, 20, 20, 1}, { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43, 0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 } }, [10] = {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */ - {V4l2_SLICED_VITC_625,6,22,0}, + {V4l2_SLICED_VITC_625, 6, 22, 0}, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, 0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } }, [11] = {0x170, /* Vertical Interval Timecode (VITC), NTSC */ - {V4l2_SLICED_VITC_525,10,20,0}, + {V4l2_SLICED_VITC_525, 10, 20, 0}, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, 0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } }, #endif [12] = {0x190, /* Video Program System (VPS), PAL */ - {V4L2_SLICED_VPS,16,16,0}, + {V4L2_SLICED_VPS, 16, 16, 0}, { 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d, 0xa6, 0xda, 0x0b, 0x00, 0x00, 0x00, 0x60, 0x00 } }, @@ -623,7 +625,7 @@ static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, int line, i; dev_dbg_lvl(sd->dev, 1, debug, "g_sliced_vbi_cap\n"); - memset(cap, 0, sizeof *cap); + memset(cap, 0, sizeof(*cap)); for (i = 0; i < ARRAY_SIZE(vbi_ram_default); i++) { const struct i2c_vbi_ram_value *regs = &vbi_ram_default[i]; @@ -655,7 +657,7 @@ static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, * MSB = field2 */ static int tvp5150_set_vbi(struct v4l2_subdev *sd, - unsigned int type,u8 flags, int line, + unsigned int type, u8 flags, int line, const int fields) { struct tvp5150 *decoder = to_tvp5150(sd); @@ -1101,11 +1103,14 @@ static int tvp5150_s_routing(struct v4l2_subdev *sd, static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) { - /* this is for capturing 36 raw vbi lines - if there's a way to cut off the beginning 2 vbi lines - with the tvp5150 then the vbi line count could be lowered - to 17 lines/field again, although I couldn't find a register - which could do that cropping */ + /* + * this is for capturing 36 raw vbi lines + * if there's a way to cut off the beginning 2 vbi lines + * with the tvp5150 then the vbi line count could be lowered + * to 17 lines/field again, although I couldn't find a register + * which could do that cropping + */ + if (fmt->sample_format == V4L2_PIX_FMT_GREY) tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70); if (fmt->count[0] == 18 && fmt->count[1] == 18) { diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c new file mode 100644 index 000000000000..0b347cc19aa5 --- /dev/null +++ b/drivers/media/i2c/video-i2c.c @@ -0,0 +1,564 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * video-i2c.c - Support for I2C transport video devices + * + * Copyright (C) 2018 Matt Ranostay <matt.ranostay@konsulko.com> + * + * Supported: + * - Panasonic AMG88xx Grid-Eye Sensors + */ + +#include <linux/delay.h> +#include <linux/freezer.h> +#include <linux/kthread.h> +#include <linux/i2c.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_device.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-vmalloc.h> + +#define VIDEO_I2C_DRIVER "video-i2c" + +struct video_i2c_chip; + +struct video_i2c_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +struct video_i2c_data { + struct i2c_client *client; + const struct video_i2c_chip *chip; + struct mutex lock; + spinlock_t slock; + unsigned int sequence; + struct mutex queue_lock; + + struct v4l2_device v4l2_dev; + struct video_device vdev; + struct vb2_queue vb_vidq; + + struct task_struct *kthread_vid_cap; + struct list_head vid_cap_active; +}; + +static const struct v4l2_fmtdesc amg88xx_format = { + .pixelformat = V4L2_PIX_FMT_Y12, +}; + +static const struct v4l2_frmsize_discrete amg88xx_size = { + .width = 8, + .height = 8, +}; + +struct video_i2c_chip { + /* video dimensions */ + const struct v4l2_fmtdesc *format; + const struct v4l2_frmsize_discrete *size; + + /* max frames per second */ + unsigned int max_fps; + + /* pixel buffer size */ + unsigned int buffer_size; + + /* pixel size in bits */ + unsigned int bpp; + + /* xfer function */ + int (*xfer)(struct video_i2c_data *data, char *buf); +}; + +static int amg88xx_xfer(struct video_i2c_data *data, char *buf) +{ + struct i2c_client *client = data->client; + struct i2c_msg msg[2]; + u8 reg = 0x80; + int ret; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = (char *)® + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = data->chip->buffer_size; + msg[1].buf = (char *)buf; + + ret = i2c_transfer(client->adapter, msg, 2); + + return (ret == 2) ? 0 : -EIO; +} + +#define AMG88XX 0 + +static const struct video_i2c_chip video_i2c_chip[] = { + [AMG88XX] = { + .size = &amg88xx_size, + .format = &amg88xx_format, + .max_fps = 10, + .buffer_size = 128, + .bpp = 16, + .xfer = &amg88xx_xfer, + }, +}; + +static const struct v4l2_file_operations video_i2c_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static int queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct video_i2c_data *data = vb2_get_drv_priv(vq); + unsigned int size = data->chip->buffer_size; + + if (vq->num_buffers + *nbuffers < 2) + *nbuffers = 2; + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static int buffer_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct video_i2c_data *data = vb2_get_drv_priv(vb->vb2_queue); + unsigned int size = data->chip->buffer_size; + + if (vb2_plane_size(vb, 0) < size) + return -EINVAL; + + vbuf->field = V4L2_FIELD_NONE; + vb2_set_plane_payload(vb, 0, size); + + return 0; +} + +static void buffer_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct video_i2c_data *data = vb2_get_drv_priv(vb->vb2_queue); + struct video_i2c_buffer *buf = + container_of(vbuf, struct video_i2c_buffer, vb); + + spin_lock(&data->slock); + list_add_tail(&buf->list, &data->vid_cap_active); + spin_unlock(&data->slock); +} + +static int video_i2c_thread_vid_cap(void *priv) +{ + struct video_i2c_data *data = priv; + unsigned int delay = msecs_to_jiffies(1000 / data->chip->max_fps); + + set_freezable(); + + do { + unsigned long start_jiffies = jiffies; + struct video_i2c_buffer *vid_cap_buf = NULL; + int schedule_delay; + + try_to_freeze(); + + spin_lock(&data->slock); + + if (!list_empty(&data->vid_cap_active)) { + vid_cap_buf = list_last_entry(&data->vid_cap_active, + struct video_i2c_buffer, list); + list_del(&vid_cap_buf->list); + } + + spin_unlock(&data->slock); + + if (vid_cap_buf) { + struct vb2_buffer *vb2_buf = &vid_cap_buf->vb.vb2_buf; + void *vbuf = vb2_plane_vaddr(vb2_buf, 0); + int ret; + + ret = data->chip->xfer(data, vbuf); + vb2_buf->timestamp = ktime_get_ns(); + vid_cap_buf->vb.sequence = data->sequence++; + vb2_buffer_done(vb2_buf, ret ? + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + } + + schedule_delay = delay - (jiffies - start_jiffies); + + if (time_after(jiffies, start_jiffies + delay)) + schedule_delay = delay; + + schedule_timeout_interruptible(schedule_delay); + } while (!kthread_should_stop()); + + return 0; +} + +static void video_i2c_del_list(struct vb2_queue *vq, enum vb2_buffer_state state) +{ + struct video_i2c_data *data = vb2_get_drv_priv(vq); + struct video_i2c_buffer *buf, *tmp; + + spin_lock(&data->slock); + + list_for_each_entry_safe(buf, tmp, &data->vid_cap_active, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + + spin_unlock(&data->slock); +} + +static int start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct video_i2c_data *data = vb2_get_drv_priv(vq); + + if (data->kthread_vid_cap) + return 0; + + data->sequence = 0; + data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data, + "%s-vid-cap", data->v4l2_dev.name); + if (!IS_ERR(data->kthread_vid_cap)) + return 0; + + video_i2c_del_list(vq, VB2_BUF_STATE_QUEUED); + + return PTR_ERR(data->kthread_vid_cap); +} + +static void stop_streaming(struct vb2_queue *vq) +{ + struct video_i2c_data *data = vb2_get_drv_priv(vq); + + if (data->kthread_vid_cap == NULL) + return; + + kthread_stop(data->kthread_vid_cap); + data->kthread_vid_cap = NULL; + + video_i2c_del_list(vq, VB2_BUF_STATE_ERROR); +} + +static struct vb2_ops video_i2c_video_qops = { + .queue_setup = queue_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int video_i2c_querycap(struct file *file, void *priv, + struct v4l2_capability *vcap) +{ + struct video_i2c_data *data = video_drvdata(file); + struct i2c_client *client = data->client; + + strlcpy(vcap->driver, data->v4l2_dev.name, sizeof(vcap->driver)); + strlcpy(vcap->card, data->vdev.name, sizeof(vcap->card)); + + sprintf(vcap->bus_info, "I2C:%d-%d", client->adapter->nr, client->addr); + + return 0; +} + +static int video_i2c_g_input(struct file *file, void *fh, unsigned int *inp) +{ + *inp = 0; + + return 0; +} + +static int video_i2c_s_input(struct file *file, void *fh, unsigned int inp) +{ + return (inp > 0) ? -EINVAL : 0; +} + +static int video_i2c_enum_input(struct file *file, void *fh, + struct v4l2_input *vin) +{ + if (vin->index > 0) + return -EINVAL; + + strlcpy(vin->name, "Camera", sizeof(vin->name)); + + vin->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; +} + +static int video_i2c_enum_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *fmt) +{ + struct video_i2c_data *data = video_drvdata(file); + enum v4l2_buf_type type = fmt->type; + + if (fmt->index > 0) + return -EINVAL; + + *fmt = *data->chip->format; + fmt->type = type; + + return 0; +} + +static int video_i2c_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + const struct video_i2c_data *data = video_drvdata(file); + const struct v4l2_frmsize_discrete *size = data->chip->size; + + /* currently only one frame size is allowed */ + if (fsize->index > 0) + return -EINVAL; + + if (fsize->pixel_format != data->chip->format->pixelformat) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = size->width; + fsize->discrete.height = size->height; + + return 0; +} + +static int video_i2c_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fe) +{ + const struct video_i2c_data *data = video_drvdata(file); + const struct v4l2_frmsize_discrete *size = data->chip->size; + + if (fe->index > 0) + return -EINVAL; + + if (fe->width != size->width || fe->height != size->height) + return -EINVAL; + + fe->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fe->discrete.numerator = 1; + fe->discrete.denominator = data->chip->max_fps; + + return 0; +} + +static int video_i2c_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *fmt) +{ + const struct video_i2c_data *data = video_drvdata(file); + const struct v4l2_frmsize_discrete *size = data->chip->size; + struct v4l2_pix_format *pix = &fmt->fmt.pix; + unsigned int bpp = data->chip->bpp / 8; + + pix->width = size->width; + pix->height = size->height; + pix->pixelformat = data->chip->format->pixelformat; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = pix->width * bpp; + pix->sizeimage = pix->bytesperline * pix->height; + pix->colorspace = V4L2_COLORSPACE_RAW; + + return 0; +} + +static int video_i2c_s_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *fmt) +{ + struct video_i2c_data *data = video_drvdata(file); + + if (vb2_is_busy(&data->vb_vidq)) + return -EBUSY; + + return video_i2c_try_fmt_vid_cap(file, fh, fmt); +} + +static int video_i2c_g_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parm) +{ + struct video_i2c_data *data = video_drvdata(filp); + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + parm->parm.capture.readbuffers = 1; + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.timeperframe.numerator = 1; + parm->parm.capture.timeperframe.denominator = data->chip->max_fps; + + return 0; +} + +static const struct v4l2_ioctl_ops video_i2c_ioctl_ops = { + .vidioc_querycap = video_i2c_querycap, + .vidioc_g_input = video_i2c_g_input, + .vidioc_s_input = video_i2c_s_input, + .vidioc_enum_input = video_i2c_enum_input, + .vidioc_enum_fmt_vid_cap = video_i2c_enum_fmt_vid_cap, + .vidioc_enum_framesizes = video_i2c_enum_framesizes, + .vidioc_enum_frameintervals = video_i2c_enum_frameintervals, + .vidioc_g_fmt_vid_cap = video_i2c_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = video_i2c_s_fmt_vid_cap, + .vidioc_g_parm = video_i2c_g_parm, + .vidioc_s_parm = video_i2c_g_parm, + .vidioc_try_fmt_vid_cap = video_i2c_try_fmt_vid_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static void video_i2c_release(struct video_device *vdev) +{ + kfree(video_get_drvdata(vdev)); +} + +static int video_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct video_i2c_data *data; + struct v4l2_device *v4l2_dev; + struct vb2_queue *queue; + int ret = -ENODEV; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (dev_fwnode(&client->dev)) + data->chip = device_get_match_data(&client->dev); + else if (id) + data->chip = &video_i2c_chip[id->driver_data]; + else + goto error_free_device; + + data->client = client; + v4l2_dev = &data->v4l2_dev; + strlcpy(v4l2_dev->name, VIDEO_I2C_DRIVER, sizeof(v4l2_dev->name)); + + ret = v4l2_device_register(&client->dev, v4l2_dev); + if (ret < 0) + goto error_free_device; + + mutex_init(&data->lock); + mutex_init(&data->queue_lock); + + queue = &data->vb_vidq; + queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + queue->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR | VB2_READ; + queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + queue->drv_priv = data; + queue->buf_struct_size = sizeof(struct video_i2c_buffer); + queue->min_buffers_needed = 1; + queue->ops = &video_i2c_video_qops; + queue->mem_ops = &vb2_vmalloc_memops; + + ret = vb2_queue_init(queue); + if (ret < 0) + goto error_unregister_device; + + data->vdev.queue = queue; + data->vdev.queue->lock = &data->queue_lock; + + snprintf(data->vdev.name, sizeof(data->vdev.name), + "I2C %d-%d Transport Video", + client->adapter->nr, client->addr); + + data->vdev.v4l2_dev = v4l2_dev; + data->vdev.fops = &video_i2c_fops; + data->vdev.lock = &data->lock; + data->vdev.ioctl_ops = &video_i2c_ioctl_ops; + data->vdev.release = video_i2c_release; + data->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + + spin_lock_init(&data->slock); + INIT_LIST_HEAD(&data->vid_cap_active); + + video_set_drvdata(&data->vdev, data); + i2c_set_clientdata(client, data); + + ret = video_register_device(&data->vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) + goto error_unregister_device; + + return 0; + +error_unregister_device: + v4l2_device_unregister(v4l2_dev); + mutex_destroy(&data->lock); + mutex_destroy(&data->queue_lock); + +error_free_device: + kfree(data); + + return ret; +} + +static int video_i2c_remove(struct i2c_client *client) +{ + struct video_i2c_data *data = i2c_get_clientdata(client); + + video_unregister_device(&data->vdev); + v4l2_device_unregister(&data->v4l2_dev); + + mutex_destroy(&data->lock); + mutex_destroy(&data->queue_lock); + + return 0; +} + +static const struct i2c_device_id video_i2c_id_table[] = { + { "amg88xx", AMG88XX }, + {} +}; +MODULE_DEVICE_TABLE(i2c, video_i2c_id_table); + +static const struct of_device_id video_i2c_of_match[] = { + { .compatible = "panasonic,amg88xx", .data = &video_i2c_chip[AMG88XX] }, + {} +}; +MODULE_DEVICE_TABLE(of, video_i2c_of_match); + +static struct i2c_driver video_i2c_driver = { + .driver = { + .name = VIDEO_I2C_DRIVER, + .of_match_table = video_i2c_of_match, + }, + .probe = video_i2c_probe, + .remove = video_i2c_remove, + .id_table = video_i2c_id_table, +}; + +module_i2c_driver(video_i2c_driver); + +MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>"); +MODULE_DESCRIPTION("I2C transport video support"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c index 35e81f7c0d2f..ae59c3177555 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/media-device.c @@ -54,9 +54,10 @@ static int media_device_close(struct file *filp) return 0; } -static int media_device_get_info(struct media_device *dev, - struct media_device_info *info) +static long media_device_get_info(struct media_device *dev, void *arg) { + struct media_device_info *info = arg; + memset(info, 0, sizeof(*info)); if (dev->driver_name[0]) @@ -93,9 +94,9 @@ static struct media_entity *find_entity(struct media_device *mdev, u32 id) return NULL; } -static long media_device_enum_entities(struct media_device *mdev, - struct media_entity_desc *entd) +static long media_device_enum_entities(struct media_device *mdev, void *arg) { + struct media_entity_desc *entd = arg; struct media_entity *ent; ent = find_entity(mdev, entd->id); @@ -146,9 +147,9 @@ static void media_device_kpad_to_upad(const struct media_pad *kpad, upad->flags = kpad->flags; } -static long media_device_enum_links(struct media_device *mdev, - struct media_links_enum *links) +static long media_device_enum_links(struct media_device *mdev, void *arg) { + struct media_links_enum *links = arg; struct media_entity *entity; entity = find_entity(mdev, links->entity); @@ -195,9 +196,9 @@ static long media_device_enum_links(struct media_device *mdev, return 0; } -static long media_device_setup_link(struct media_device *mdev, - struct media_link_desc *linkd) +static long media_device_setup_link(struct media_device *mdev, void *arg) { + struct media_link_desc *linkd = arg; struct media_link *link = NULL; struct media_entity *source; struct media_entity *sink; @@ -225,9 +226,9 @@ static long media_device_setup_link(struct media_device *mdev, return __media_entity_setup_link(link, linkd->flags); } -static long media_device_get_topology(struct media_device *mdev, - struct media_v2_topology *topo) +static long media_device_get_topology(struct media_device *mdev, void *arg) { + struct media_v2_topology *topo = arg; struct media_entity *entity; struct media_interface *intf; struct media_pad *pad; diff --git a/drivers/media/mmc/siano/smssdio.c b/drivers/media/mmc/siano/smssdio.c index fee2d710bbf8..b9e40d4ca0e8 100644 --- a/drivers/media/mmc/siano/smssdio.c +++ b/drivers/media/mmc/siano/smssdio.c @@ -279,7 +279,7 @@ static int smssdio_probe(struct sdio_func *func, goto free; } - ret = smscore_register_device(¶ms, &smsdev->coredev, NULL); + ret = smscore_register_device(¶ms, &smsdev->coredev, GFP_DMA, NULL); if (ret < 0) goto free; diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 5932e225f9c0..1f09123e2bf9 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -16,7 +16,6 @@ source "drivers/media/pci/sta2x11/Kconfig" source "drivers/media/pci/tw5864/Kconfig" source "drivers/media/pci/tw68/Kconfig" source "drivers/media/pci/tw686x/Kconfig" -source "drivers/media/pci/zoran/Kconfig" endif if MEDIA_ANALOG_TV_SUPPORT diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile index 1c5ab07a8cff..984fa247096d 100644 --- a/drivers/media/pci/Makefile +++ b/drivers/media/pci/Makefile @@ -18,7 +18,6 @@ obj-y += ttpci/ \ intel/ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ -obj-$(CONFIG_VIDEO_ZORAN) += zoran/ obj-$(CONFIG_VIDEO_CX18) += cx18/ obj-$(CONFIG_VIDEO_CX23885) += cx23885/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ diff --git a/drivers/media/pci/bt8xx/bttv-risc.c b/drivers/media/pci/bt8xx/bttv-risc.c index 3859dde98be2..6a6be0b49f70 100644 --- a/drivers/media/pci/bt8xx/bttv-risc.c +++ b/drivers/media/pci/bt8xx/bttv-risc.c @@ -189,20 +189,21 @@ bttv_risc_planar(struct bttv *btv, struct btcx_riscmem *risc, yoffset -= sg_dma_len(ysg); ysg = sg_next(ysg); } - while (uoffset && uoffset >= sg_dma_len(usg)) { - uoffset -= sg_dma_len(usg); - usg = sg_next(usg); - } - while (voffset && voffset >= sg_dma_len(vsg)) { - voffset -= sg_dma_len(vsg); - vsg = sg_next(vsg); - } /* calculate max number of bytes we can write */ ylen = todo; if (yoffset + ylen > sg_dma_len(ysg)) ylen = sg_dma_len(ysg) - yoffset; if (chroma) { + while (uoffset && uoffset >= sg_dma_len(usg)) { + uoffset -= sg_dma_len(usg); + usg = sg_next(usg); + } + while (voffset && voffset >= sg_dma_len(vsg)) { + voffset -= sg_dma_len(vsg); + vsg = sg_next(vsg); + } + if (uoffset + (ylen>>hshift) > sg_dma_len(usg)) ylen = (sg_dma_len(usg) - uoffset) << hshift; if (voffset + (ylen>>hshift) > sg_dma_len(vsg)) diff --git a/drivers/media/pci/bt8xx/dst.c b/drivers/media/pci/bt8xx/dst.c index 4f0bba9e4c48..2e33b7236672 100644 --- a/drivers/media/pci/bt8xx/dst.c +++ b/drivers/media/pci/bt8xx/dst.c @@ -1657,7 +1657,7 @@ static int dst_tune_frontend(struct dvb_frontend* fe, return 0; } -static int dst_get_tuning_algo(struct dvb_frontend *fe) +static enum dvbfe_algo dst_get_tuning_algo(struct dvb_frontend *fe) { return dst_algo ? DVBFE_ALGO_HW : DVBFE_ALGO_SW; } diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.c b/drivers/media/pci/bt8xx/dvb-bt8xx.c index f60d69ac515b..5ef6e2051d45 100644 --- a/drivers/media/pci/bt8xx/dvb-bt8xx.c +++ b/drivers/media/pci/bt8xx/dvb-bt8xx.c @@ -575,7 +575,6 @@ static struct mt352_config digitv_alps_tded4_config = { }; static struct lgdt330x_config tdvs_tua6034_config = { - .demod_address = 0x0e, .demod_chip = LGDT3303, .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ }; @@ -614,7 +613,8 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE: lgdt330x_reset(card); - card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter); + card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, + 0x0e, card->i2c_adapter); if (card->fe != NULL) { dvb_attach(simple_tuner_attach, card->fe, card->i2c_adapter, 0x61, diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 019fac49db5b..94b996ff12a9 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -422,19 +422,30 @@ static void cx23885_wakeup(struct cx23885_tsport *port, struct cx23885_dmaqueue *q, u32 count) { struct cx23885_buffer *buf; - - if (list_empty(&q->active)) - return; - buf = list_entry(q->active.next, - struct cx23885_buffer, queue); - - buf->vb.vb2_buf.timestamp = ktime_get_ns(); - buf->vb.sequence = q->count++; - dprintk(1, "[%p/%d] wakeup reg=%d buf=%d\n", buf, - buf->vb.vb2_buf.index, - count, q->count); - list_del(&buf->queue); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + int count_delta; + int max_buf_done = 5; /* service maximum five buffers */ + + do { + if (list_empty(&q->active)) + return; + buf = list_entry(q->active.next, + struct cx23885_buffer, queue); + + buf->vb.vb2_buf.timestamp = ktime_get_ns(); + buf->vb.sequence = q->count++; + if (count != (q->count % 65536)) { + dprintk(1, "[%p/%d] wakeup reg=%d buf=%d\n", buf, + buf->vb.vb2_buf.index, count, q->count); + } else { + dprintk(7, "[%p/%d] wakeup reg=%d buf=%d\n", buf, + buf->vb.vb2_buf.index, count, q->count); + } + list_del(&buf->queue); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + max_buf_done--; + /* count register is 16 bits so apply modulo appropriately */ + count_delta = ((int)count - (int)(q->count % 65536)); + } while ((count_delta > 0) && (max_buf_done > 0)); } int cx23885_sram_channel_setup(struct cx23885_dev *dev, @@ -590,6 +601,25 @@ static void cx23885_risc_disasm(struct cx23885_tsport *port, } } +static void cx23885_clear_bridge_error(struct cx23885_dev *dev) +{ + uint32_t reg1_val = cx_read(TC_REQ); /* read-only */ + uint32_t reg2_val = cx_read(TC_REQ_SET); + + if (reg1_val && reg2_val) { + cx_write(TC_REQ, reg1_val); + cx_write(TC_REQ_SET, reg2_val); + cx_read(VID_B_DMA); + cx_read(VBI_B_DMA); + cx_read(VID_C_DMA); + cx_read(VBI_C_DMA); + + dev_info(&dev->pci->dev, + "dma in progress detected 0x%08x 0x%08x, clearing\n", + reg1_val, reg2_val); + } +} + static void cx23885_shutdown(struct cx23885_dev *dev) { /* disable RISC controller */ @@ -635,6 +665,8 @@ static void cx23885_reset(struct cx23885_dev *dev) cx_write(CLK_DELAY, cx_read(CLK_DELAY) & 0x80000000); cx_write(PAD_CTRL, 0x00500300); + /* clear dma in progress */ + cx23885_clear_bridge_error(dev); mdelay(100); cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01], @@ -651,6 +683,11 @@ static void cx23885_reset(struct cx23885_dev *dev) cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH09], 128, 0); cx23885_gpio_setup(dev); + + cx23885_irq_get_mask(dev); + + /* clear dma in progress */ + cx23885_clear_bridge_error(dev); } @@ -665,6 +702,8 @@ static int cx23885_pci_quirks(struct cx23885_dev *dev) if (dev->bridge == CX23885_BRIDGE_885) cx_clear(RDR_TLCTL0, 1 << 4); + /* clear dma in progress */ + cx23885_clear_bridge_error(dev); return 0; } @@ -1329,6 +1368,18 @@ static void cx23885_tsport_reg_dump(struct cx23885_tsport *port) port->reg_ts_clk_en, cx_read(port->reg_ts_clk_en)); dprintk(1, "%s() ts_int_msk(0x%08X) 0x%08x\n", __func__, port->reg_ts_int_msk, cx_read(port->reg_ts_int_msk)); + dprintk(1, "%s() ts_int_status(0x%08X) 0x%08x\n", __func__, + port->reg_ts_int_stat, cx_read(port->reg_ts_int_stat)); + dprintk(1, "%s() PCI_INT_STAT 0x%08X\n", __func__, + cx_read(PCI_INT_STAT)); + dprintk(1, "%s() VID_B_INT_MSTAT 0x%08X\n", __func__, + cx_read(VID_B_INT_MSTAT)); + dprintk(1, "%s() VID_B_INT_SSTAT 0x%08X\n", __func__, + cx_read(VID_B_INT_SSTAT)); + dprintk(1, "%s() VID_C_INT_MSTAT 0x%08X\n", __func__, + cx_read(VID_C_INT_MSTAT)); + dprintk(1, "%s() VID_C_INT_SSTAT 0x%08X\n", __func__, + cx_read(VID_C_INT_SSTAT)); } int cx23885_start_dma(struct cx23885_tsport *port, @@ -1341,6 +1392,9 @@ int cx23885_start_dma(struct cx23885_tsport *port, dprintk(1, "%s() w: %d, h: %d, f: %d\n", __func__, dev->width, dev->height, dev->field); + /* clear dma in progress */ + cx23885_clear_bridge_error(dev); + /* Stop the fifo and risc engine for this port */ cx_clear(port->reg_dma_ctl, port->dma_ctl_val); @@ -1410,8 +1464,15 @@ int cx23885_start_dma(struct cx23885_tsport *port, reg = reg | 0xa; cx_write(PAD_CTRL, reg); - /* FIXME and these two registers should be documented. */ + /* Sets MOE_CLK_DIS to disable MoE clock */ + /* sets MCLK_DLY_SEL/BCLK_DLY_SEL to 1 buffer delay each */ cx_write(CLK_DELAY, cx_read(CLK_DELAY) | 0x80000011); + + /* ALT_GPIO_ALT_SET: GPIO[0] + * IR_ALT_TX_SEL: GPIO[1] + * GPIO1_ALT_SEL: VIP_656_DATA[0] + * GPIO0_ALT_SEL: VIP_656_CLK + */ cx_write(ALT_PIN_OUT_SEL, 0x10100045); } @@ -1421,16 +1482,26 @@ int cx23885_start_dma(struct cx23885_tsport *port, case CX23885_BRIDGE_888: /* enable irqs */ dprintk(1, "%s() enabling TS int's and DMA\n", __func__); + /* clear dma in progress */ + cx23885_clear_bridge_error(dev); cx_set(port->reg_ts_int_msk, port->ts_int_msk_val); cx_set(port->reg_dma_ctl, port->dma_ctl_val); + + /* clear dma in progress */ + cx23885_clear_bridge_error(dev); cx23885_irq_add(dev, port->pci_irqmask); cx23885_irq_enable_all(dev); + + /* clear dma in progress */ + cx23885_clear_bridge_error(dev); break; default: BUG(); } cx_set(DEV_CNTRL2, (1<<5)); /* Enable RISC controller */ + /* clear dma in progress */ + cx23885_clear_bridge_error(dev); if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) cx23885_av_clk(dev, 1); @@ -1438,6 +1509,11 @@ int cx23885_start_dma(struct cx23885_tsport *port, if (debug > 4) cx23885_tsport_reg_dump(port); + cx23885_irq_get_mask(dev); + + /* clear dma in progress */ + cx23885_clear_bridge_error(dev); + return 0; } @@ -1445,15 +1521,28 @@ static int cx23885_stop_dma(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; u32 reg; + int delay = 0; + uint32_t reg1_val; + uint32_t reg2_val; dprintk(1, "%s()\n", __func__); /* Stop interrupts and DMA */ cx_clear(port->reg_ts_int_msk, port->ts_int_msk_val); cx_clear(port->reg_dma_ctl, port->dma_ctl_val); + /* just in case wait for any dma to complete before allowing dealloc */ + mdelay(20); + for (delay = 0; delay < 100; delay++) { + reg1_val = cx_read(TC_REQ); + reg2_val = cx_read(TC_REQ_SET); + if (reg1_val == 0 || reg2_val == 0) + break; + mdelay(1); + } + dev_dbg(&dev->pci->dev, "delay=%d reg1=0x%08x reg2=0x%08x\n", + delay, reg1_val, reg2_val); if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) { - reg = cx_read(PAD_CTRL); /* Set TS1_OE */ @@ -1464,7 +1553,6 @@ static int cx23885_stop_dma(struct cx23885_tsport *port) cx_write(PAD_CTRL, reg); cx_write(port->reg_src_sel, 0); cx_write(port->reg_gen_ctrl, 8); - } if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) @@ -1693,6 +1781,12 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) pci_status = cx_read(PCI_INT_STAT); pci_mask = cx23885_irq_get_mask(dev); + if ((pci_status & pci_mask) == 0) { + dprintk(7, "pci_status: 0x%08x pci_mask: 0x%08x\n", + pci_status, pci_mask); + goto out; + } + vida_status = cx_read(VID_A_INT_STAT); vida_mask = cx_read(VID_A_INT_MSK); audint_status = cx_read(AUDIO_INT_INT_STAT); @@ -1702,7 +1796,9 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) ts2_status = cx_read(VID_C_INT_STAT); ts2_mask = cx_read(VID_C_INT_MSK); - if ((pci_status == 0) && (ts2_status == 0) && (ts1_status == 0)) + if (((pci_status & pci_mask) == 0) && + ((ts2_status & ts2_mask) == 0) && + ((ts1_status & ts1_mask) == 0)) goto out; vida_count = cx_read(VID_A_GPCNT); @@ -1829,7 +1925,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) } if (handled) - cx_write(PCI_INT_STAT, pci_status); + cx_write(PCI_INT_STAT, pci_status & pci_mask); out: return IRQ_RETVAL(handled); } diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 114d9bcbe4f4..7d52173073d6 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -252,7 +252,6 @@ static struct mt2131_config hauppauge_generic_tunerconfig = { }; static struct lgdt330x_config fusionhdtv_5_express = { - .demod_address = 0x0e, .demod_chip = LGDT3303, .serial_mpeg = 0x40, }; @@ -1321,8 +1320,9 @@ static int dvb_register(struct cx23885_tsport *port) case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP: i2c_bus = &dev->i2c_bus[0]; fe0->dvb.frontend = dvb_attach(lgdt330x_attach, - &fusionhdtv_5_express, - &i2c_bus->i2c_adap); + &fusionhdtv_5_express, + 0x0e, + &i2c_bus->i2c_adap); if (fe0->dvb.frontend == NULL) break; dvb_attach(simple_tuner_attach, fe0->dvb.frontend, diff --git a/drivers/media/pci/cx23885/cx23885-reg.h b/drivers/media/pci/cx23885/cx23885-reg.h index 2d3cbafe2402..08cec8d91742 100644 --- a/drivers/media/pci/cx23885/cx23885-reg.h +++ b/drivers/media/pci/cx23885/cx23885-reg.h @@ -288,6 +288,18 @@ Channel manager Data Structure entry = 20 DWORD #define AUDIO_EXT_INT_MSTAT 0x00040068 #define AUDIO_EXT_INT_SSTAT 0x0004006C +/* Bits [7:0] set in both TC_REQ and TC_REQ_SET + * indicate a stall in the RISC engine for a + * particular rider traffic class. This causes + * the 885 and 888 bridges (unknown about 887) + * to become inoperable. Setting bits in + * TC_REQ_SET resets the corresponding bits + * in TC_REQ (and TC_REQ_SET) allowing + * operation to continue. + */ +#define TC_REQ 0x00040090 +#define TC_REQ_SET 0x00040094 + #define RDR_CFG0 0x00050000 #define RDR_CFG1 0x00050004 #define RDR_CFG2 0x00050008 @@ -386,6 +398,8 @@ Channel manager Data Structure entry = 20 DWORD #define VID_B_PIXEL_FRMT 0x00130184 /* Video C Interface */ +#define VID_C_DMA 0x00130200 +#define VBI_C_DMA 0x00130208 #define VID_C_GPCNT 0x00130220 #define VID_C_GPCNT_CTL 0x00130230 #define VBI_C_GPCNT_CTL 0x00130234 diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index 2f886140dd2e..ccfde28d4af2 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -411,21 +411,18 @@ static int lgdt330x_set_ts_param(struct dvb_frontend *fe, int is_punctured) } static struct lgdt330x_config fusionhdtv_3_gold = { - .demod_address = 0x0e, .demod_chip = LGDT3302, .serial_mpeg = 0x04, /* TPSERIAL for 3302 in TOP_CONTROL */ .set_ts_params = lgdt330x_set_ts_param, }; static const struct lgdt330x_config fusionhdtv_5_gold = { - .demod_address = 0x0e, .demod_chip = LGDT3303, .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ .set_ts_params = lgdt330x_set_ts_param, }; static const struct lgdt330x_config pchdtv_hd5500 = { - .demod_address = 0x59, .demod_chip = LGDT3303, .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ .set_ts_params = lgdt330x_set_ts_param, @@ -1237,6 +1234,7 @@ static int dvb_register(struct cx8802_dev *dev) fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set; fe0->dvb.frontend = dvb_attach(lgdt330x_attach, &fusionhdtv_3_gold, + 0x0e, &core->i2c_adap); if (fe0->dvb.frontend) { if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, @@ -1255,6 +1253,7 @@ static int dvb_register(struct cx8802_dev *dev) mdelay(200); fe0->dvb.frontend = dvb_attach(lgdt330x_attach, &fusionhdtv_3_gold, + 0x0e, &core->i2c_adap); if (fe0->dvb.frontend) { if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, @@ -1273,6 +1272,7 @@ static int dvb_register(struct cx8802_dev *dev) mdelay(200); fe0->dvb.frontend = dvb_attach(lgdt330x_attach, &fusionhdtv_5_gold, + 0x0e, &core->i2c_adap); if (fe0->dvb.frontend) { if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, @@ -1294,6 +1294,7 @@ static int dvb_register(struct cx8802_dev *dev) mdelay(200); fe0->dvb.frontend = dvb_attach(lgdt330x_attach, &pchdtv_hd5500, + 0x59, &core->i2c_adap); if (fe0->dvb.frontend) { if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c index 6f4e6923a91a..2f5debce4905 100644 --- a/drivers/media/pci/cx88/cx88-input.c +++ b/drivers/media/pci/cx88/cx88-input.c @@ -180,7 +180,8 @@ static enum hrtimer_restart cx88_ir_work(struct hrtimer *timer) struct cx88_IR *ir = container_of(timer, struct cx88_IR, timer); cx88_ir_handle_key(ir); - missed = hrtimer_forward_now(&ir->timer, ir->polling * 1000000LL); + missed = hrtimer_forward_now(&ir->timer, + ktime_set(0, ir->polling * 1000000)); if (missed > 1) ir_dprintk("Missed ticks %ld\n", missed - 1); @@ -200,7 +201,8 @@ static int __cx88_ir_start(void *priv) if (ir->polling) { hrtimer_init(&ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ir->timer.function = cx88_ir_work; - hrtimer_start(&ir->timer, ir->polling * 1000000LL, + hrtimer_start(&ir->timer, + ktime_set(0, ir->polling * 1000000), HRTIMER_MODE_REL); } if (ir->sampling) { @@ -632,8 +634,9 @@ void cx88_i2c_init_ir(struct cx88_core *core) memset(&core->init_data, 0, sizeof(core->init_data)); if (*addrp == 0x71) { - /* Hauppauge XVR */ - core->init_data.name = "cx88 Hauppauge XVR remote"; + /* Hauppauge Z8F0811 */ + strlcpy(info.type, "ir_z8f0811_haup", I2C_NAME_SIZE); + core->init_data.name = core->board.name; core->init_data.ir_codes = RC_MAP_HAUPPAUGE; core->init_data.type = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_RC6_6A_32; diff --git a/drivers/media/pci/cx88/cx88-vbi.c b/drivers/media/pci/cx88/cx88-vbi.c index c637679b01b2..58489ea0c1da 100644 --- a/drivers/media/pci/cx88/cx88-vbi.c +++ b/drivers/media/pci/cx88/cx88-vbi.c @@ -178,7 +178,6 @@ static void buffer_queue(struct vb2_buffer *vb) if (list_empty(&q->active)) { list_add_tail(&buf->list, &q->active); - cx8800_start_vbi_dma(dev, q, buf); dprintk(2, "[%p/%d] vbi_queue - first active\n", buf, buf->vb.vb2_buf.index); diff --git a/drivers/media/pci/ddbridge/Kconfig b/drivers/media/pci/ddbridge/Kconfig index a422dde2f34a..16faef265e97 100644 --- a/drivers/media/pci/ddbridge/Kconfig +++ b/drivers/media/pci/ddbridge/Kconfig @@ -14,6 +14,7 @@ config DVB_DDBRIDGE select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT select DVB_MXL5XX if MEDIA_SUBDRV_AUTOSELECT select DVB_CXD2099 if MEDIA_SUBDRV_AUTOSELECT + select DVB_DUMMY_FE if MEDIA_SUBDRV_AUTOSELECT ---help--- Support for cards with the Digital Devices PCI express bridge: - Octopus PCIe Bridge diff --git a/drivers/media/pci/ddbridge/Makefile b/drivers/media/pci/ddbridge/Makefile index 745b37d07558..9b9e35f171b7 100644 --- a/drivers/media/pci/ddbridge/Makefile +++ b/drivers/media/pci/ddbridge/Makefile @@ -4,7 +4,7 @@ # ddbridge-objs := ddbridge-main.o ddbridge-core.o ddbridge-ci.o \ - ddbridge-hw.o ddbridge-i2c.o ddbridge-max.o + ddbridge-hw.o ddbridge-i2c.o ddbridge-max.o ddbridge-mci.o obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o diff --git a/drivers/media/pci/ddbridge/ddbridge-ci.c b/drivers/media/pci/ddbridge/ddbridge-ci.c index a9dbc4ebf94f..cfe23d02e561 100644 --- a/drivers/media/pci/ddbridge/ddbridge-ci.c +++ b/drivers/media/pci/ddbridge/ddbridge-ci.c @@ -164,7 +164,7 @@ static struct dvb_ca_en50221 en_templ = { static void ci_attach(struct ddb_port *port) { - struct ddb_ci *ci = NULL; + struct ddb_ci *ci; ci = kzalloc(sizeof(*ci), GFP_KERNEL); if (!ci) diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index 90687eff5909..d5b0d1eaf3ad 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -54,6 +54,7 @@ #include "stv6111.h" #include "lnbh25.h" #include "cxd2099.h" +#include "dvb_dummy_fe.h" /****************************************************************************/ @@ -68,11 +69,53 @@ module_param(adapter_alloc, int, 0444); MODULE_PARM_DESC(adapter_alloc, "0-one adapter per io, 1-one per tab with io, 2-one per tab, 3-one for all"); +static int ci_bitrate = 70000; +module_param(ci_bitrate, int, 0444); +MODULE_PARM_DESC(ci_bitrate, " Bitrate in KHz for output to CI."); + +static int ts_loop = -1; +module_param(ts_loop, int, 0444); +MODULE_PARM_DESC(ts_loop, "TS in/out test loop on port ts_loop"); + +static int xo2_speed = 2; +module_param(xo2_speed, int, 0444); +MODULE_PARM_DESC(xo2_speed, "default transfer speed for xo2 based duoflex, 0=55,1=75,2=90,3=104 MBit/s, default=2, use attribute to change for individual cards"); + +#ifdef __arm__ +static int alt_dma = 1; +#else +static int alt_dma; +#endif +module_param(alt_dma, int, 0444); +MODULE_PARM_DESC(alt_dma, "use alternative DMA buffer handling"); + +static int no_init; +module_param(no_init, int, 0444); +MODULE_PARM_DESC(no_init, "do not initialize most devices"); + +static int stv0910_single; +module_param(stv0910_single, int, 0444); +MODULE_PARM_DESC(stv0910_single, "use stv0910 cards as single demods"); + +static int dma_buf_num = 8; +module_param(dma_buf_num, int, 0444); +MODULE_PARM_DESC(dma_buf_num, "Number of DMA buffers, possible values: 8-32"); + +static int dma_buf_size = 21; +module_param(dma_buf_size, int, 0444); +MODULE_PARM_DESC(dma_buf_size, + "DMA buffer size as multiple of 128*47, possible values: 1-43"); + +static int dummy_tuner; +module_param(dummy_tuner, int, 0444); +MODULE_PARM_DESC(dummy_tuner, + "attach dummy tuner to port 0 on Octopus V3 or Octopus Mini cards"); + /****************************************************************************/ static DEFINE_MUTEX(redirect_lock); -struct workqueue_struct *ddb_wq; +static struct workqueue_struct *ddb_wq; static struct ddb *ddbs[DDB_MAX_ADAPTER]; @@ -80,6 +123,16 @@ static struct ddb *ddbs[DDB_MAX_ADAPTER]; /****************************************************************************/ /****************************************************************************/ +struct ddb_irq *ddb_irq_set(struct ddb *dev, u32 link, u32 nr, + void (*handler)(void *), void *data) +{ + struct ddb_irq *irq = &dev->link[link].irq[nr]; + + irq->handler = handler; + irq->data = data; + return irq; +} + static void ddb_set_dma_table(struct ddb_io *io) { struct ddb *dev = io->port->dev; @@ -410,13 +463,11 @@ static void ddb_output_start(struct ddb_output *output) struct ddb *dev = output->port->dev; u32 con = 0x11c, con2 = 0; - if (output->dma) { - spin_lock_irq(&output->dma->lock); - output->dma->cbuf = 0; - output->dma->coff = 0; - output->dma->stat = 0; - ddbwritel(dev, 0, DMA_BUFFER_CONTROL(output->dma)); - } + spin_lock_irq(&output->dma->lock); + output->dma->cbuf = 0; + output->dma->coff = 0; + output->dma->stat = 0; + ddbwritel(dev, 0, DMA_BUFFER_CONTROL(output->dma)); if (output->port->input[0]->port->class == DDB_PORT_LOOP) con = (1UL << 13) | 0x14; @@ -429,36 +480,29 @@ static void ddb_output_start(struct ddb_output *output) ddbwritel(dev, con, TS_CONTROL(output)); ddbwritel(dev, con2, TS_CONTROL2(output)); - if (output->dma) { - ddbwritel(dev, output->dma->bufval, - DMA_BUFFER_SIZE(output->dma)); - ddbwritel(dev, 0, DMA_BUFFER_ACK(output->dma)); - ddbwritel(dev, 1, DMA_BASE_READ); - ddbwritel(dev, 7, DMA_BUFFER_CONTROL(output->dma)); - } + ddbwritel(dev, output->dma->bufval, + DMA_BUFFER_SIZE(output->dma)); + ddbwritel(dev, 0, DMA_BUFFER_ACK(output->dma)); + ddbwritel(dev, 1, DMA_BASE_READ); + ddbwritel(dev, 7, DMA_BUFFER_CONTROL(output->dma)); ddbwritel(dev, con | 1, TS_CONTROL(output)); - if (output->dma) { - output->dma->running = 1; - spin_unlock_irq(&output->dma->lock); - } + output->dma->running = 1; + spin_unlock_irq(&output->dma->lock); } static void ddb_output_stop(struct ddb_output *output) { struct ddb *dev = output->port->dev; - if (output->dma) - spin_lock_irq(&output->dma->lock); + spin_lock_irq(&output->dma->lock); ddbwritel(dev, 0, TS_CONTROL(output)); - if (output->dma) { - ddbwritel(dev, 0, DMA_BUFFER_CONTROL(output->dma)); - output->dma->running = 0; - spin_unlock_irq(&output->dma->lock); - } + ddbwritel(dev, 0, DMA_BUFFER_CONTROL(output->dma)); + output->dma->running = 0; + spin_unlock_irq(&output->dma->lock); } static void ddb_input_stop(struct ddb_input *input) @@ -466,45 +510,42 @@ static void ddb_input_stop(struct ddb_input *input) struct ddb *dev = input->port->dev; u32 tag = DDB_LINK_TAG(input->port->lnr); - if (input->dma) - spin_lock_irq(&input->dma->lock); + spin_lock_irq(&input->dma->lock); + ddbwritel(dev, 0, tag | TS_CONTROL(input)); - if (input->dma) { - ddbwritel(dev, 0, DMA_BUFFER_CONTROL(input->dma)); - input->dma->running = 0; - spin_unlock_irq(&input->dma->lock); - } + + ddbwritel(dev, 0, DMA_BUFFER_CONTROL(input->dma)); + input->dma->running = 0; + spin_unlock_irq(&input->dma->lock); } static void ddb_input_start(struct ddb_input *input) { struct ddb *dev = input->port->dev; - if (input->dma) { - spin_lock_irq(&input->dma->lock); - input->dma->cbuf = 0; - input->dma->coff = 0; - input->dma->stat = 0; - ddbwritel(dev, 0, DMA_BUFFER_CONTROL(input->dma)); - } + spin_lock_irq(&input->dma->lock); + input->dma->cbuf = 0; + input->dma->coff = 0; + input->dma->stat = 0; + ddbwritel(dev, 0, DMA_BUFFER_CONTROL(input->dma)); + ddbwritel(dev, 0, TS_CONTROL(input)); ddbwritel(dev, 2, TS_CONTROL(input)); ddbwritel(dev, 0, TS_CONTROL(input)); - if (input->dma) { - ddbwritel(dev, input->dma->bufval, - DMA_BUFFER_SIZE(input->dma)); - ddbwritel(dev, 0, DMA_BUFFER_ACK(input->dma)); - ddbwritel(dev, 1, DMA_BASE_WRITE); - ddbwritel(dev, 3, DMA_BUFFER_CONTROL(input->dma)); - } + ddbwritel(dev, input->dma->bufval, + DMA_BUFFER_SIZE(input->dma)); + ddbwritel(dev, 0, DMA_BUFFER_ACK(input->dma)); + ddbwritel(dev, 1, DMA_BASE_WRITE); + ddbwritel(dev, 3, DMA_BUFFER_CONTROL(input->dma)); ddbwritel(dev, 0x09, TS_CONTROL(input)); - if (input->dma) { - input->dma->running = 1; - spin_unlock_irq(&input->dma->lock); - } + if (input->port->type == DDB_TUNER_DUMMY) + ddbwritel(dev, 0x000fff01, TS_CONTROL2(input)); + + input->dma->running = 1; + spin_unlock_irq(&input->dma->lock); } static void ddb_input_start_all(struct ddb_input *input) @@ -549,12 +590,12 @@ static u32 ddb_output_free(struct ddb_output *output) if (output->dma->cbuf != idx) { if ((((output->dma->cbuf + 1) % output->dma->num) == idx) && - (output->dma->size - output->dma->coff <= 188)) + (output->dma->size - output->dma->coff <= (2 * 188))) return 0; return 188; } diff = off - output->dma->coff; - if (diff <= 0 || diff > 188) + if (diff <= 0 || diff > (2 * 188)) return 188; return 0; } @@ -1142,6 +1183,7 @@ static const struct stv0910_cfg stv0910_p = { .parallel = 1, .rptlvl = 4, .clk = 30000000, + .tsspeed = 0x28, }; static const struct lnbh25_config lnbh25_cfg = { @@ -1149,7 +1191,7 @@ static const struct lnbh25_config lnbh25_cfg = { .data2_config = LNBH25_TEN }; -static int demod_attach_stv0910(struct ddb_input *input, int type) +static int demod_attach_stv0910(struct ddb_input *input, int type, int tsfast) { struct i2c_adapter *i2c = &input->port->i2c->adap; struct ddb_dvb *dvb = &input->port->dvb[input->nr & 1]; @@ -1162,6 +1204,12 @@ static int demod_attach_stv0910(struct ddb_input *input, int type) if (type) cfg.parallel = 2; + + if (tsfast) { + dev_info(dev, "Enabling stv0910 higher speed TS\n"); + cfg.tsspeed = 0x10; + } + dvb->fe = dvb_attach(stv0910_attach, i2c, &cfg, (input->nr & 1)); if (!dvb->fe) { cfg.adr = 0x6c; @@ -1208,6 +1256,20 @@ static int tuner_attach_stv6111(struct ddb_input *input, int type) return 0; } +static int demod_attach_dummy(struct ddb_input *input) +{ + struct ddb_dvb *dvb = &input->port->dvb[input->nr & 1]; + struct device *dev = input->port->dev->dev; + + dvb->fe = dvb_attach(dvb_dummy_fe_qam_attach); + if (!dvb->fe) { + dev_err(dev, "QAM dummy attach failed!\n"); + return -ENODEV; + } + + return 0; +} + static int start_feed(struct dvb_demux_feed *dvbdmxfeed) { struct dvb_demux *dvbdmx = dvbdmxfeed->demux; @@ -1383,7 +1445,25 @@ static int dvb_input_attach(struct ddb_input *input) struct ddb_port *port = input->port; struct dvb_adapter *adap = dvb->adap; struct dvb_demux *dvbdemux = &dvb->demux; - int par = 0, osc24 = 0; + struct ddb_ids *devids = &input->port->dev->link[input->port->lnr].ids; + int par = 0, osc24 = 0, tsfast = 0; + + /* + * Determine if bridges with stv0910 demods can run with fast TS and + * thus support high bandwidth transponders. + * STV0910_PR and STV0910_P tuner types covers all relevant bridges, + * namely the CineS2 V7(A) and the Octopus CI S2 Pro/Advanced. All + * DuoFlex S2 V4(A) have type=DDB_TUNER_DVBS_STV0910 without any suffix + * and are limited by the serial link to the bridge, thus won't work + * in fast TS mode. + */ + if (port->nr == 0 && + (port->type == DDB_TUNER_DVBS_STV0910_PR || + port->type == DDB_TUNER_DVBS_STV0910_P)) { + /* fast TS on port 0 requires FPGA version >= 1.7 */ + if ((devids->hwid & 0x00ffffff) >= 0x00010007) + tsfast = 1; + } dvb->attached = 0x01; @@ -1440,19 +1520,19 @@ static int dvb_input_attach(struct ddb_input *input) goto err_tuner; break; case DDB_TUNER_DVBS_STV0910: - if (demod_attach_stv0910(input, 0) < 0) + if (demod_attach_stv0910(input, 0, tsfast) < 0) goto err_detach; if (tuner_attach_stv6111(input, 0) < 0) goto err_tuner; break; case DDB_TUNER_DVBS_STV0910_PR: - if (demod_attach_stv0910(input, 1) < 0) + if (demod_attach_stv0910(input, 1, tsfast) < 0) goto err_detach; if (tuner_attach_stv6111(input, 1) < 0) goto err_tuner; break; case DDB_TUNER_DVBS_STV0910_P: - if (demod_attach_stv0910(input, 0) < 0) + if (demod_attach_stv0910(input, 0, tsfast) < 0) goto err_detach; if (tuner_attach_stv6111(input, 1) < 0) goto err_tuner; @@ -1500,6 +1580,14 @@ static int dvb_input_attach(struct ddb_input *input) if (tuner_attach_tda18212(input, port->type) < 0) goto err_tuner; break; + case DDB_TUNER_DUMMY: + if (demod_attach_dummy(input) < 0) + goto err_detach; + break; + case DDB_TUNER_MCI: + if (ddb_fe_attach_mci(input) < 0) + goto err_detach; + break; default: return 0; } @@ -1762,6 +1850,15 @@ static void ddb_port_probe(struct ddb_port *port) /* Handle missing ports and ports without I2C */ + if (dummy_tuner && !port->nr && + dev->link[0].ids.device == 0x0005) { + port->name = "DUMMY"; + port->class = DDB_PORT_TUNER; + port->type = DDB_TUNER_DUMMY; + port->type_name = "DUMMY"; + return; + } + if (port->nr == ts_loop) { port->name = "TS LOOP"; port->class = DDB_PORT_LOOP; @@ -1786,6 +1883,16 @@ static void ddb_port_probe(struct ddb_port *port) return; } + if (dev->link[l].info->type == DDB_OCTOPUS_MCI) { + if (port->nr >= dev->link[l].info->mci) + return; + port->name = "DUAL MCI"; + port->type_name = "MCI"; + port->class = DDB_PORT_TUNER; + port->type = DDB_TUNER_MCI; + return; + } + if (port->nr > 1 && dev->link[l].info->type == DDB_OCTOPUS_CI) { port->name = "CI internal"; port->type_name = "INTERNAL"; @@ -2081,39 +2188,39 @@ static void input_work(struct work_struct *work) spin_unlock_irqrestore(&dma->lock, flags); } -static void input_handler(unsigned long data) +static void input_handler(void *data) { struct ddb_input *input = (struct ddb_input *)data; struct ddb_dma *dma = input->dma; - /* - * If there is no input connected, input_tasklet() will - * just copy pointers and ACK. So, there is no need to go - * through the tasklet scheduler. - */ - if (input->redi) - queue_work(ddb_wq, &dma->work); - else - input_work(&dma->work); + queue_work(ddb_wq, &dma->work); } -static void output_handler(unsigned long data) +static void output_work(struct work_struct *work) { - struct ddb_output *output = (struct ddb_output *)data; - struct ddb_dma *dma = output->dma; + struct ddb_dma *dma = container_of(work, struct ddb_dma, work); + struct ddb_output *output = (struct ddb_output *)dma->io; struct ddb *dev = output->port->dev; + unsigned long flags; - spin_lock(&dma->lock); - if (!dma->running) { - spin_unlock(&dma->lock); - return; - } + spin_lock_irqsave(&dma->lock, flags); + if (!dma->running) + goto unlock_exit; dma->stat = ddbreadl(dev, DMA_BUFFER_CURRENT(dma)); dma->ctrl = ddbreadl(dev, DMA_BUFFER_CONTROL(dma)); if (output->redi) output_ack_input(output, output->redi); wake_up(&dma->wq); - spin_unlock(&dma->lock); +unlock_exit: + spin_unlock_irqrestore(&dma->lock, flags); +} + +static void output_handler(void *data) +{ + struct ddb_output *output = (struct ddb_output *)data; + struct ddb_dma *dma = output->dma; + + queue_work(ddb_wq, &dma->work); } /****************************************************************************/ @@ -2146,18 +2253,19 @@ static void ddb_dma_init(struct ddb_io *io, int nr, int out) spin_lock_init(&dma->lock); init_waitqueue_head(&dma->wq); if (out) { + INIT_WORK(&dma->work, output_work); dma->regs = rm->odma->base + rm->odma->size * nr; dma->bufregs = rm->odma_buf->base + rm->odma_buf->size * nr; - dma->num = OUTPUT_DMA_BUFS; - dma->size = OUTPUT_DMA_SIZE; - dma->div = OUTPUT_DMA_IRQ_DIV; + dma->num = dma_buf_num; + dma->size = dma_buf_size * 128 * 47; + dma->div = 1; } else { INIT_WORK(&dma->work, input_work); dma->regs = rm->idma->base + rm->idma->size * nr; dma->bufregs = rm->idma_buf->base + rm->idma_buf->size * nr; - dma->num = INPUT_DMA_BUFS; - dma->size = INPUT_DMA_SIZE; - dma->div = INPUT_DMA_IRQ_DIV; + dma->num = dma_buf_num; + dma->size = dma_buf_size * 128 * 47; + dma->div = 1; } ddbwritel(io->port->dev, 0, DMA_BUFFER_ACK(dma)); dev_dbg(io->port->dev->dev, "init link %u, io %u, dma %u, dmaregs %08x bufregs %08x\n", @@ -2190,8 +2298,7 @@ static void ddb_input_init(struct ddb_port *port, int nr, int pnr, int anr) dev_dbg(dev->dev, "init link %u, input %u, handler %u\n", port->lnr, nr, dma_nr + base); - dev->handler[0][dma_nr + base] = input_handler; - dev->handler_data[0][dma_nr + base] = (unsigned long)input; + ddb_irq_set(dev, 0, dma_nr + base, &input_handler, input); ddb_dma_init(input, dma_nr, 0); } } @@ -2216,8 +2323,7 @@ static void ddb_output_init(struct ddb_port *port, int nr) const struct ddb_regmap *rm0 = io_regmap(output, 0); u32 base = rm0->irq_base_odma; - dev->handler[0][nr + base] = output_handler; - dev->handler_data[0][nr + base] = (unsigned long)output; + ddb_irq_set(dev, 0, nr + base, &output_handler, output); ddb_dma_init(output, nr, 1); } } @@ -2329,6 +2435,7 @@ void ddb_ports_init(struct ddb *dev) break; case DDB_OCTOPUS_MAX: case DDB_OCTOPUS_MAX_CT: + case DDB_OCTOPUS_MCI: ddb_input_init(port, 2 * i, 0, 2 * p); ddb_input_init(port, 2 * i + 1, 1, 2 * p + 1); break; @@ -2361,73 +2468,62 @@ void ddb_ports_release(struct ddb *dev) /****************************************************************************/ #define IRQ_HANDLE(_nr) \ - do { if ((s & (1UL << ((_nr) & 0x1f))) && dev->handler[0][_nr]) \ - dev->handler[0][_nr](dev->handler_data[0][_nr]); } \ + do { if ((s & (1UL << ((_nr) & 0x1f))) && \ + dev->link[0].irq[_nr].handler) \ + dev->link[0].irq[_nr].handler(dev->link[0].irq[_nr].data); } \ while (0) +#define IRQ_HANDLE_NIBBLE(_shift) { \ + if (s & (0x0000000f << ((_shift) & 0x1f))) { \ + IRQ_HANDLE(0 + (_shift)); \ + IRQ_HANDLE(1 + (_shift)); \ + IRQ_HANDLE(2 + (_shift)); \ + IRQ_HANDLE(3 + (_shift)); \ + } \ +} + +#define IRQ_HANDLE_BYTE(_shift) { \ + if (s & (0x000000ff << ((_shift) & 0x1f))) { \ + IRQ_HANDLE(0 + (_shift)); \ + IRQ_HANDLE(1 + (_shift)); \ + IRQ_HANDLE(2 + (_shift)); \ + IRQ_HANDLE(3 + (_shift)); \ + IRQ_HANDLE(4 + (_shift)); \ + IRQ_HANDLE(5 + (_shift)); \ + IRQ_HANDLE(6 + (_shift)); \ + IRQ_HANDLE(7 + (_shift)); \ + } \ +} + static void irq_handle_msg(struct ddb *dev, u32 s) { dev->i2c_irq++; - IRQ_HANDLE(0); - IRQ_HANDLE(1); - IRQ_HANDLE(2); - IRQ_HANDLE(3); + IRQ_HANDLE_NIBBLE(0); } static void irq_handle_io(struct ddb *dev, u32 s) { dev->ts_irq++; - if ((s & 0x000000f0)) { - IRQ_HANDLE(4); - IRQ_HANDLE(5); - IRQ_HANDLE(6); - IRQ_HANDLE(7); - } - if ((s & 0x0000ff00)) { - IRQ_HANDLE(8); - IRQ_HANDLE(9); - IRQ_HANDLE(10); - IRQ_HANDLE(11); - IRQ_HANDLE(12); - IRQ_HANDLE(13); - IRQ_HANDLE(14); - IRQ_HANDLE(15); - } - if ((s & 0x00ff0000)) { - IRQ_HANDLE(16); - IRQ_HANDLE(17); - IRQ_HANDLE(18); - IRQ_HANDLE(19); - IRQ_HANDLE(20); - IRQ_HANDLE(21); - IRQ_HANDLE(22); - IRQ_HANDLE(23); - } - if ((s & 0xff000000)) { - IRQ_HANDLE(24); - IRQ_HANDLE(25); - IRQ_HANDLE(26); - IRQ_HANDLE(27); - IRQ_HANDLE(28); - IRQ_HANDLE(29); - IRQ_HANDLE(30); - IRQ_HANDLE(31); - } + IRQ_HANDLE_NIBBLE(4); + IRQ_HANDLE_BYTE(8); + IRQ_HANDLE_BYTE(16); + IRQ_HANDLE_BYTE(24); } irqreturn_t ddb_irq_handler0(int irq, void *dev_id) { struct ddb *dev = (struct ddb *)dev_id; - u32 s = ddbreadl(dev, INTERRUPT_STATUS); + u32 mask = 0x8fffff00; + u32 s = mask & ddbreadl(dev, INTERRUPT_STATUS); + if (!s) + return IRQ_NONE; do { if (s & 0x80000000) return IRQ_NONE; - if (!(s & 0xfffff00)) - return IRQ_NONE; - ddbwritel(dev, s & 0xfffff00, INTERRUPT_ACK); + ddbwritel(dev, s, INTERRUPT_ACK); irq_handle_io(dev, s); - } while ((s = ddbreadl(dev, INTERRUPT_STATUS))); + } while ((s = mask & ddbreadl(dev, INTERRUPT_STATUS))); return IRQ_HANDLED; } @@ -2435,16 +2531,17 @@ irqreturn_t ddb_irq_handler0(int irq, void *dev_id) irqreturn_t ddb_irq_handler1(int irq, void *dev_id) { struct ddb *dev = (struct ddb *)dev_id; - u32 s = ddbreadl(dev, INTERRUPT_STATUS); + u32 mask = 0x8000000f; + u32 s = mask & ddbreadl(dev, INTERRUPT_STATUS); + if (!s) + return IRQ_NONE; do { if (s & 0x80000000) return IRQ_NONE; - if (!(s & 0x0000f)) - return IRQ_NONE; - ddbwritel(dev, s & 0x0000f, INTERRUPT_ACK); + ddbwritel(dev, s, INTERRUPT_ACK); irq_handle_msg(dev, s); - } while ((s = ddbreadl(dev, INTERRUPT_STATUS))); + } while ((s = mask & ddbreadl(dev, INTERRUPT_STATUS))); return IRQ_HANDLED; } @@ -3027,7 +3124,7 @@ static struct class ddb_class = { .devnode = ddb_devnode, }; -int ddb_class_create(void) +static int ddb_class_create(void) { ddb_major = register_chrdev(0, DDB_NAME, &ddb_fops); if (ddb_major < 0) @@ -3037,7 +3134,7 @@ int ddb_class_create(void) return 0; } -void ddb_class_destroy(void) +static void ddb_class_destroy(void) { class_unregister(&ddb_class); unregister_chrdev(ddb_major, DDB_NAME); @@ -3173,7 +3270,7 @@ static void tempmon_setfan(struct ddb_link *link) ddblwritel(link, (pwm << 8), TEMPMON_FANCONTROL); } -static void temp_handler(unsigned long data) +static void temp_handler(void *data) { struct ddb_link *link = (struct ddb_link *)data; @@ -3196,8 +3293,7 @@ static int tempmon_init(struct ddb_link *link, int first_time) memcpy(link->temp_tab, temperature_table, sizeof(temperature_table)); } - dev->handler[l][link->info->tempmon_irq] = temp_handler; - dev->handler_data[l][link->info->tempmon_irq] = (unsigned long)link; + ddb_irq_set(dev, l, link->info->tempmon_irq, temp_handler, link); ddblwritel(link, (TEMPMON_CONTROL_OVERTEMP | TEMPMON_CONTROL_AUTOSCAN | TEMPMON_CONTROL_INTENABLE), TEMPMON_CONTROL); @@ -3309,3 +3405,38 @@ void ddb_unmap(struct ddb *dev) iounmap(dev->regs); vfree(dev); } + +int ddb_exit_ddbridge(int stage, int error) +{ + switch (stage) { + default: + case 2: + destroy_workqueue(ddb_wq); + /* fall-through */ + case 1: + ddb_class_destroy(); + break; + } + + return error; +} + +int ddb_init_ddbridge(void) +{ + if (dma_buf_num < 8) + dma_buf_num = 8; + if (dma_buf_num > 32) + dma_buf_num = 32; + if (dma_buf_size < 1) + dma_buf_size = 1; + if (dma_buf_size > 43) + dma_buf_size = 43; + + if (ddb_class_create() < 0) + return -1; + ddb_wq = alloc_workqueue("ddbridge", 0, 0); + if (!ddb_wq) + return ddb_exit_ddbridge(1, -1); + + return 0; +} diff --git a/drivers/media/pci/ddbridge/ddbridge-hw.c b/drivers/media/pci/ddbridge/ddbridge-hw.c index c6d14925e2fc..1d3ee6accdd5 100644 --- a/drivers/media/pci/ddbridge/ddbridge-hw.c +++ b/drivers/media/pci/ddbridge/ddbridge-hw.c @@ -311,6 +311,16 @@ static const struct ddb_info ddb_s2_48 = { .tempmon_irq = 24, }; +static const struct ddb_info ddb_s2x_48 = { + .type = DDB_OCTOPUS_MCI, + .name = "Digital Devices MAX SX8", + .regmap = &octopus_map, + .port_num = 4, + .i2c_mask = 0x00, + .tempmon_irq = 24, + .mci = 4 +}; + /****************************************************************************/ /****************************************************************************/ /****************************************************************************/ @@ -346,6 +356,7 @@ static const struct ddb_device_id ddb_device_ids[] = { DDB_DEVID(0x0008, 0x0036, ddb_isdbt_8), DDB_DEVID(0x0008, 0x0037, ddb_c2t2i_v0_8), DDB_DEVID(0x0008, 0x0038, ddb_c2t2i_8), + DDB_DEVID(0x0009, 0x0025, ddb_s2x_48), DDB_DEVID(0x0006, 0x0039, ddb_ctv7), DDB_DEVID(0x0011, 0x0040, ddb_ci), DDB_DEVID(0x0011, 0x0041, ddb_cis), diff --git a/drivers/media/pci/ddbridge/ddbridge-i2c.c b/drivers/media/pci/ddbridge/ddbridge-i2c.c index 82a9a0e806fc..667340c86ea7 100644 --- a/drivers/media/pci/ddbridge/ddbridge-i2c.c +++ b/drivers/media/pci/ddbridge/ddbridge-i2c.c @@ -147,7 +147,7 @@ void ddb_i2c_release(struct ddb *dev) } } -static void i2c_handler(unsigned long priv) +static void i2c_handler(void *priv) { struct ddb_i2c *i2c = (struct ddb_i2c *)priv; @@ -210,8 +210,7 @@ int ddb_i2c_init(struct ddb *dev) if (!(dev->link[l].info->i2c_mask & (1 << i))) continue; i2c = &dev->i2c[num]; - dev->handler_data[l][i + base] = (unsigned long)i2c; - dev->handler[l][i + base] = i2c_handler; + ddb_irq_set(dev, l, i + base, i2c_handler, i2c); stat = ddb_i2c_add(dev, i2c, regmap, l, i, num); if (stat) break; diff --git a/drivers/media/pci/ddbridge/ddbridge-main.c b/drivers/media/pci/ddbridge/ddbridge-main.c index 26497d6b1395..f4748cfd904b 100644 --- a/drivers/media/pci/ddbridge/ddbridge-main.c +++ b/drivers/media/pci/ddbridge/ddbridge-main.c @@ -55,34 +55,6 @@ MODULE_PARM_DESC(msi, "Control MSI interrupts: 0-disable (default), 1-enable"); #endif #endif -int ci_bitrate = 70000; -module_param(ci_bitrate, int, 0444); -MODULE_PARM_DESC(ci_bitrate, " Bitrate in KHz for output to CI."); - -int ts_loop = -1; -module_param(ts_loop, int, 0444); -MODULE_PARM_DESC(ts_loop, "TS in/out test loop on port ts_loop"); - -int xo2_speed = 2; -module_param(xo2_speed, int, 0444); -MODULE_PARM_DESC(xo2_speed, "default transfer speed for xo2 based duoflex, 0=55,1=75,2=90,3=104 MBit/s, default=2, use attribute to change for individual cards"); - -#ifdef __arm__ -int alt_dma = 1; -#else -int alt_dma; -#endif -module_param(alt_dma, int, 0444); -MODULE_PARM_DESC(alt_dma, "use alternative DMA buffer handling"); - -int no_init; -module_param(no_init, int, 0444); -MODULE_PARM_DESC(no_init, "do not initialize most devices"); - -int stv0910_single; -module_param(stv0910_single, int, 0444); -MODULE_PARM_DESC(stv0910_single, "use stv0910 cards as single demods"); - /****************************************************************************/ /****************************************************************************/ /****************************************************************************/ @@ -93,18 +65,22 @@ static void ddb_irq_disable(struct ddb *dev) ddbwritel(dev, 0, MSI1_ENABLE); } -static void ddb_irq_exit(struct ddb *dev) +static void ddb_msi_exit(struct ddb *dev) { - ddb_irq_disable(dev); - if (dev->msi == 2) - free_irq(dev->pdev->irq + 1, dev); - free_irq(dev->pdev->irq, dev); #ifdef CONFIG_PCI_MSI if (dev->msi) - pci_disable_msi(dev->pdev); + pci_free_irq_vectors(dev->pdev); #endif } +static void ddb_irq_exit(struct ddb *dev) +{ + ddb_irq_disable(dev); + if (dev->msi == 2) + free_irq(pci_irq_vector(dev->pdev, 1), dev); + free_irq(pci_irq_vector(dev->pdev, 0), dev); +} + static void ddb_remove(struct pci_dev *pdev) { struct ddb *dev = (struct ddb *)pci_get_drvdata(pdev); @@ -114,6 +90,7 @@ static void ddb_remove(struct pci_dev *pdev) ddb_i2c_release(dev); ddb_irq_exit(dev); + ddb_msi_exit(dev); ddb_ports_release(dev); ddb_buffers_free(dev); @@ -128,7 +105,8 @@ static void ddb_irq_msi(struct ddb *dev, int nr) int stat; if (msi && pci_msi_enabled()) { - stat = pci_alloc_irq_vectors(dev->pdev, 1, nr, PCI_IRQ_MSI); + stat = pci_alloc_irq_vectors(dev->pdev, 1, nr, + PCI_IRQ_MSI | PCI_IRQ_MSIX); if (stat >= 1) { dev->msi = stat; dev_info(dev->dev, "using %d MSI interrupt(s)\n", @@ -160,21 +138,24 @@ static int ddb_irq_init(struct ddb *dev) if (dev->msi) irq_flag = 0; if (dev->msi == 2) { - stat = request_irq(dev->pdev->irq, ddb_irq_handler0, - irq_flag, "ddbridge", (void *)dev); + stat = request_irq(pci_irq_vector(dev->pdev, 0), + ddb_irq_handler0, irq_flag, "ddbridge", + (void *)dev); if (stat < 0) return stat; - stat = request_irq(dev->pdev->irq + 1, ddb_irq_handler1, - irq_flag, "ddbridge", (void *)dev); + stat = request_irq(pci_irq_vector(dev->pdev, 1), + ddb_irq_handler1, irq_flag, "ddbridge", + (void *)dev); if (stat < 0) { - free_irq(dev->pdev->irq, dev); + free_irq(pci_irq_vector(dev->pdev, 0), dev); return stat; } } else #endif { - stat = request_irq(dev->pdev->irq, ddb_irq_handler, - irq_flag, "ddbridge", (void *)dev); + stat = request_irq(pci_irq_vector(dev->pdev, 0), + ddb_irq_handler, irq_flag, "ddbridge", + (void *)dev); if (stat < 0) return stat; } @@ -217,6 +198,7 @@ static int ddb_probe(struct pci_dev *pdev, dev->link[0].ids.device = id->device; dev->link[0].ids.subvendor = id->subvendor; dev->link[0].ids.subdevice = pdev->subsystem_device; + dev->link[0].ids.devid = (id->device << 16) | id->vendor; dev->link[0].dev = dev; dev->link[0].info = get_ddb_info(id->vendor, id->device, @@ -258,8 +240,7 @@ static int ddb_probe(struct pci_dev *pdev, ddb_irq_exit(dev); fail0: dev_err(&pdev->dev, "fail0\n"); - if (dev->msi) - pci_disable_msi(dev->pdev); + ddb_msi_exit(dev); fail: dev_err(&pdev->dev, "fail\n"); @@ -283,6 +264,7 @@ static const struct pci_device_id ddb_id_table[] = { DDB_DEVICE_ANY(0x0006), DDB_DEVICE_ANY(0x0007), DDB_DEVICE_ANY(0x0008), + DDB_DEVICE_ANY(0x0009), DDB_DEVICE_ANY(0x0011), DDB_DEVICE_ANY(0x0012), DDB_DEVICE_ANY(0x0013), @@ -310,32 +292,25 @@ static struct pci_driver ddb_pci_driver = { static __init int module_init_ddbridge(void) { - int stat = -1; + int stat; pr_info("Digital Devices PCIE bridge driver " DDBRIDGE_VERSION ", Copyright (C) 2010-17 Digital Devices GmbH\n"); - if (ddb_class_create() < 0) - return -1; - ddb_wq = create_workqueue("ddbridge"); - if (!ddb_wq) - goto exit1; + stat = ddb_init_ddbridge(); + if (stat < 0) + return stat; stat = pci_register_driver(&ddb_pci_driver); if (stat < 0) - goto exit2; - return stat; -exit2: - destroy_workqueue(ddb_wq); -exit1: - ddb_class_destroy(); + ddb_exit_ddbridge(0, stat); + return stat; } static __exit void module_exit_ddbridge(void) { pci_unregister_driver(&ddb_pci_driver); - destroy_workqueue(ddb_wq); - ddb_class_destroy(); + ddb_exit_ddbridge(0, 0); } module_init(module_init_ddbridge); diff --git a/drivers/media/pci/ddbridge/ddbridge-max.c b/drivers/media/pci/ddbridge/ddbridge-max.c index dc6b81488746..739e4b444cf4 100644 --- a/drivers/media/pci/ddbridge/ddbridge-max.c +++ b/drivers/media/pci/ddbridge/ddbridge-max.c @@ -33,6 +33,7 @@ #include "ddbridge.h" #include "ddbridge-regs.h" #include "ddbridge-io.h" +#include "ddbridge-mci.h" #include "ddbridge-max.h" #include "mxl5xx.h" @@ -452,3 +453,44 @@ int ddb_fe_attach_mxl5xx(struct ddb_input *input) dvb->input = tuner; return 0; } + +/******************************************************************************/ +/* MAX MCI related functions */ + +int ddb_fe_attach_mci(struct ddb_input *input) +{ + struct ddb *dev = input->port->dev; + struct ddb_dvb *dvb = &input->port->dvb[input->nr & 1]; + struct ddb_port *port = input->port; + struct ddb_link *link = &dev->link[port->lnr]; + int demod, tuner; + + demod = input->nr; + tuner = demod & 3; + if (fmode == 3) + tuner = 0; + dvb->fe = ddb_mci_attach(input, 0, demod, &dvb->set_input); + if (!dvb->fe) { + dev_err(dev->dev, "No MAXSX8 found!\n"); + return -ENODEV; + } + if (!dvb->set_input) { + dev_err(dev->dev, "No MCI set_input function pointer!\n"); + return -ENODEV; + } + if (input->nr < 4) { + lnb_command(dev, port->lnr, input->nr, LNB_CMD_INIT); + lnb_set_voltage(dev, port->lnr, input->nr, SEC_VOLTAGE_OFF); + } + ddb_lnb_init_fmode(dev, link, fmode); + + dvb->fe->ops.set_voltage = max_set_voltage; + dvb->fe->ops.enable_high_lnb_voltage = max_enable_high_lnb_voltage; + dvb->fe->ops.set_tone = max_set_tone; + dvb->diseqc_send_master_cmd = dvb->fe->ops.diseqc_send_master_cmd; + dvb->fe->ops.diseqc_send_master_cmd = max_send_master_cmd; + dvb->fe->ops.diseqc_send_burst = max_send_burst; + dvb->fe->sec_priv = input; + dvb->input = tuner; + return 0; +} diff --git a/drivers/media/pci/ddbridge/ddbridge-max.h b/drivers/media/pci/ddbridge/ddbridge-max.h index bf8bf38739f6..82efc53baa94 100644 --- a/drivers/media/pci/ddbridge/ddbridge-max.h +++ b/drivers/media/pci/ddbridge/ddbridge-max.h @@ -25,5 +25,6 @@ int ddb_lnb_init_fmode(struct ddb *dev, struct ddb_link *link, u32 fm); int ddb_fe_attach_mxl5xx(struct ddb_input *input); +int ddb_fe_attach_mci(struct ddb_input *input); #endif /* _DDBRIDGE_MAX_H */ diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.c b/drivers/media/pci/ddbridge/ddbridge-mci.c new file mode 100644 index 000000000000..4ac634fc96e4 --- /dev/null +++ b/drivers/media/pci/ddbridge/ddbridge-mci.c @@ -0,0 +1,551 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ddbridge-mci.c: Digital Devices microcode interface + * + * Copyright (C) 2017 Digital Devices GmbH + * Ralph Metzler <rjkm@metzlerbros.de> + * Marcus Metzler <mocm@metzlerbros.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "ddbridge.h" +#include "ddbridge-io.h" +#include "ddbridge-mci.h" + +static LIST_HEAD(mci_list); + +static const u32 MCLK = (1550000000 / 12); +static const u32 MAX_DEMOD_LDPC_BITRATE = (1550000000 / 6); +static const u32 MAX_LDPC_BITRATE = (720000000); + +struct mci_base { + struct list_head mci_list; + void *key; + struct ddb_link *link; + struct completion completion; + + struct device *dev; + struct mutex tuner_lock; /* concurrent tuner access lock */ + u8 adr; + struct mutex mci_lock; /* concurrent MCI access lock */ + int count; + + u8 tuner_use_count[MCI_TUNER_MAX]; + u8 assigned_demod[MCI_DEMOD_MAX]; + u32 used_ldpc_bitrate[MCI_DEMOD_MAX]; + u8 demod_in_use[MCI_DEMOD_MAX]; + u32 iq_mode; +}; + +struct mci { + struct mci_base *base; + struct dvb_frontend fe; + int nr; + int demod; + int tuner; + int first_time_lock; + int started; + struct mci_result signal_info; + + u32 bb_mode; +}; + +static int mci_reset(struct mci *state) +{ + struct ddb_link *link = state->base->link; + u32 status = 0; + u32 timeout = 40; + + ddblwritel(link, MCI_CONTROL_RESET, MCI_CONTROL); + ddblwritel(link, 0, MCI_CONTROL + 4); /* 1= no internal init */ + msleep(300); + ddblwritel(link, 0, MCI_CONTROL); + + while (1) { + status = ddblreadl(link, MCI_CONTROL); + if ((status & MCI_CONTROL_READY) == MCI_CONTROL_READY) + break; + if (--timeout == 0) + break; + msleep(50); + } + if ((status & MCI_CONTROL_READY) == 0) + return -1; + if (link->ids.device == 0x0009) + ddblwritel(link, SX8_TSCONFIG_MODE_NORMAL, SX8_TSCONFIG); + return 0; +} + +static int mci_config(struct mci *state, u32 config) +{ + struct ddb_link *link = state->base->link; + + if (link->ids.device != 0x0009) + return -EINVAL; + ddblwritel(link, config, SX8_TSCONFIG); + return 0; +} + +static int _mci_cmd_unlocked(struct mci *state, + u32 *cmd, u32 cmd_len, + u32 *res, u32 res_len) +{ + struct ddb_link *link = state->base->link; + u32 i, val; + unsigned long stat; + + val = ddblreadl(link, MCI_CONTROL); + if (val & (MCI_CONTROL_RESET | MCI_CONTROL_START_COMMAND)) + return -EIO; + if (cmd && cmd_len) + for (i = 0; i < cmd_len; i++) + ddblwritel(link, cmd[i], MCI_COMMAND + i * 4); + val |= (MCI_CONTROL_START_COMMAND | MCI_CONTROL_ENABLE_DONE_INTERRUPT); + ddblwritel(link, val, MCI_CONTROL); + + stat = wait_for_completion_timeout(&state->base->completion, HZ); + if (stat == 0) { + dev_warn(state->base->dev, "MCI-%d: MCI timeout\n", state->nr); + return -EIO; + } + if (res && res_len) + for (i = 0; i < res_len; i++) + res[i] = ddblreadl(link, MCI_RESULT + i * 4); + return 0; +} + +static int mci_cmd(struct mci *state, + struct mci_command *command, + struct mci_result *result) +{ + int stat; + + mutex_lock(&state->base->mci_lock); + stat = _mci_cmd_unlocked(state, + (u32 *)command, sizeof(*command) / sizeof(u32), + (u32 *)result, sizeof(*result) / sizeof(u32)); + mutex_unlock(&state->base->mci_lock); + return stat; +} + +static void mci_handler(void *priv) +{ + struct mci_base *base = (struct mci_base *)priv; + + complete(&base->completion); +} + +static void release(struct dvb_frontend *fe) +{ + struct mci *state = fe->demodulator_priv; + + state->base->count--; + if (state->base->count == 0) { + list_del(&state->base->mci_list); + kfree(state->base); + } + kfree(state); +} + +static int read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + int stat; + struct mci *state = fe->demodulator_priv; + struct mci_command cmd; + struct mci_result res; + + cmd.command = MCI_CMD_GETSTATUS; + cmd.demod = state->demod; + stat = mci_cmd(state, &cmd, &res); + if (stat) + return stat; + *status = 0x00; + if (res.status == SX8_DEMOD_WAIT_MATYPE) + *status = 0x0f; + if (res.status == SX8_DEMOD_LOCKED) + *status = 0x1f; + return stat; +} + +static int mci_set_tuner(struct dvb_frontend *fe, u32 tuner, u32 on) +{ + struct mci *state = fe->demodulator_priv; + struct mci_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.tuner = state->tuner; + cmd.command = on ? SX8_CMD_INPUT_ENABLE : SX8_CMD_INPUT_DISABLE; + return mci_cmd(state, &cmd, NULL); +} + +static int stop(struct dvb_frontend *fe) +{ + struct mci *state = fe->demodulator_priv; + struct mci_command cmd; + u32 input = state->tuner; + + memset(&cmd, 0, sizeof(cmd)); + if (state->demod != DEMOD_UNUSED) { + cmd.command = MCI_CMD_STOP; + cmd.demod = state->demod; + mci_cmd(state, &cmd, NULL); + if (state->base->iq_mode) { + cmd.command = MCI_CMD_STOP; + cmd.demod = state->demod; + cmd.output = 0; + mci_cmd(state, &cmd, NULL); + mci_config(state, SX8_TSCONFIG_MODE_NORMAL); + } + } + mutex_lock(&state->base->tuner_lock); + state->base->tuner_use_count[input]--; + if (!state->base->tuner_use_count[input]) + mci_set_tuner(fe, input, 0); + if (state->demod < MCI_DEMOD_MAX) + state->base->demod_in_use[state->demod] = 0; + state->base->used_ldpc_bitrate[state->nr] = 0; + state->demod = DEMOD_UNUSED; + state->base->assigned_demod[state->nr] = DEMOD_UNUSED; + state->base->iq_mode = 0; + mutex_unlock(&state->base->tuner_lock); + state->started = 0; + return 0; +} + +static int start(struct dvb_frontend *fe, u32 flags, u32 modmask, u32 ts_config) +{ + struct mci *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 used_ldpc_bitrate = 0, free_ldpc_bitrate; + u32 used_demods = 0; + struct mci_command cmd; + u32 input = state->tuner; + u32 bits_per_symbol = 0; + int i, stat = 0; + + if (p->symbol_rate >= (MCLK / 2)) + flags &= ~1; + if ((flags & 3) == 0) + return -EINVAL; + + if (flags & 2) { + u32 tmp = modmask; + + bits_per_symbol = 1; + while (tmp & 1) { + tmp >>= 1; + bits_per_symbol++; + } + } + + mutex_lock(&state->base->tuner_lock); + if (state->base->iq_mode) { + stat = -EBUSY; + goto unlock; + } + for (i = 0; i < MCI_DEMOD_MAX; i++) { + used_ldpc_bitrate += state->base->used_ldpc_bitrate[i]; + if (state->base->demod_in_use[i]) + used_demods++; + } + if (used_ldpc_bitrate >= MAX_LDPC_BITRATE || + ((ts_config & SX8_TSCONFIG_MODE_MASK) > + SX8_TSCONFIG_MODE_NORMAL && used_demods > 0)) { + stat = -EBUSY; + goto unlock; + } + free_ldpc_bitrate = MAX_LDPC_BITRATE - used_ldpc_bitrate; + if (free_ldpc_bitrate > MAX_DEMOD_LDPC_BITRATE) + free_ldpc_bitrate = MAX_DEMOD_LDPC_BITRATE; + + while (p->symbol_rate * bits_per_symbol > free_ldpc_bitrate) + bits_per_symbol--; + + if (bits_per_symbol < 2) { + stat = -EBUSY; + goto unlock; + } + i = (p->symbol_rate > (MCLK / 2)) ? 3 : 7; + while (i >= 0 && state->base->demod_in_use[i]) + i--; + if (i < 0) { + stat = -EBUSY; + goto unlock; + } + state->base->demod_in_use[i] = 1; + state->base->used_ldpc_bitrate[state->nr] = p->symbol_rate + * bits_per_symbol; + state->demod = i; + state->base->assigned_demod[state->nr] = i; + + if (!state->base->tuner_use_count[input]) + mci_set_tuner(fe, input, 1); + state->base->tuner_use_count[input]++; + state->base->iq_mode = (ts_config > 1); +unlock: + mutex_unlock(&state->base->tuner_lock); + if (stat) + return stat; + memset(&cmd, 0, sizeof(cmd)); + + if (state->base->iq_mode) { + cmd.command = SX8_CMD_SELECT_IQOUT; + cmd.demod = state->demod; + cmd.output = 0; + mci_cmd(state, &cmd, NULL); + mci_config(state, ts_config); + } + if (p->stream_id != NO_STREAM_ID_FILTER && p->stream_id != 0x80000000) + flags |= 0x80; + dev_dbg(state->base->dev, "MCI-%d: tuner=%d demod=%d\n", + state->nr, state->tuner, state->demod); + cmd.command = MCI_CMD_SEARCH_DVBS; + cmd.dvbs2_search.flags = flags; + cmd.dvbs2_search.s2_modulation_mask = + modmask & ((1 << (bits_per_symbol - 1)) - 1); + cmd.dvbs2_search.retry = 2; + cmd.dvbs2_search.frequency = p->frequency * 1000; + cmd.dvbs2_search.symbol_rate = p->symbol_rate; + cmd.dvbs2_search.scrambling_sequence_index = + p->scrambling_sequence_index; + cmd.dvbs2_search.input_stream_id = + (p->stream_id != NO_STREAM_ID_FILTER) ? p->stream_id : 0; + cmd.tuner = state->tuner; + cmd.demod = state->demod; + cmd.output = state->nr; + if (p->stream_id == 0x80000000) + cmd.output |= 0x80; + stat = mci_cmd(state, &cmd, NULL); + if (stat) + stop(fe); + return stat; +} + +static int start_iq(struct dvb_frontend *fe, u32 ts_config) +{ + struct mci *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 used_demods = 0; + struct mci_command cmd; + u32 input = state->tuner; + int i, stat = 0; + + mutex_lock(&state->base->tuner_lock); + if (state->base->iq_mode) { + stat = -EBUSY; + goto unlock; + } + for (i = 0; i < MCI_DEMOD_MAX; i++) + if (state->base->demod_in_use[i]) + used_demods++; + if (used_demods > 0) { + stat = -EBUSY; + goto unlock; + } + state->demod = 0; + state->base->assigned_demod[state->nr] = 0; + if (!state->base->tuner_use_count[input]) + mci_set_tuner(fe, input, 1); + state->base->tuner_use_count[input]++; + state->base->iq_mode = (ts_config > 1); +unlock: + mutex_unlock(&state->base->tuner_lock); + if (stat) + return stat; + + memset(&cmd, 0, sizeof(cmd)); + cmd.command = SX8_CMD_START_IQ; + cmd.dvbs2_search.frequency = p->frequency * 1000; + cmd.dvbs2_search.symbol_rate = p->symbol_rate; + cmd.tuner = state->tuner; + cmd.demod = state->demod; + cmd.output = 7; + mci_config(state, ts_config); + stat = mci_cmd(state, &cmd, NULL); + if (stat) + stop(fe); + return stat; +} + +static int set_parameters(struct dvb_frontend *fe) +{ + int stat = 0; + struct mci *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 ts_config, iq_mode = 0, isi; + + if (state->started) + stop(fe); + + isi = p->stream_id; + if (isi != NO_STREAM_ID_FILTER) + iq_mode = (isi & 0x30000000) >> 28; + + switch (iq_mode) { + case 1: + ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ); + break; + case 2: + ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ); + break; + default: + ts_config = SX8_TSCONFIG_MODE_NORMAL; + break; + } + + if (iq_mode != 2) { + u32 flags = 3; + u32 mask = 3; + + if (p->modulation == APSK_16 || + p->modulation == APSK_32) { + flags = 2; + mask = 15; + } + stat = start(fe, flags, mask, ts_config); + } else { + stat = start_iq(fe, ts_config); + } + + if (!stat) { + state->started = 1; + state->first_time_lock = 1; + state->signal_info.status = SX8_DEMOD_WAIT_SIGNAL; + } + + return stat; +} + +static int tune(struct dvb_frontend *fe, bool re_tune, + unsigned int mode_flags, + unsigned int *delay, enum fe_status *status) +{ + int r; + + if (re_tune) { + r = set_parameters(fe); + if (r) + return r; + } + r = read_status(fe, status); + if (r) + return r; + + if (*status & FE_HAS_LOCK) + return 0; + *delay = HZ / 10; + return 0; +} + +static enum dvbfe_algo get_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static int set_input(struct dvb_frontend *fe, int input) +{ + struct mci *state = fe->demodulator_priv; + + state->tuner = input; + dev_dbg(state->base->dev, "MCI-%d: input=%d\n", state->nr, input); + return 0; +} + +static struct dvb_frontend_ops mci_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2 }, + .info = { + .name = "Digital Devices MaxSX8 MCI DVB-S/S2/S2X", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 0, + .frequency_tolerance = 0, + .symbol_rate_min = 100000, + .symbol_rate_max = 100000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_2G_MODULATION | + FE_CAN_MULTISTREAM, + }, + .get_frontend_algo = get_algo, + .tune = tune, + .release = release, + .read_status = read_status, +}; + +static struct mci_base *match_base(void *key) +{ + struct mci_base *p; + + list_for_each_entry(p, &mci_list, mci_list) + if (p->key == key) + return p; + return NULL; +} + +static int probe(struct mci *state) +{ + mci_reset(state); + return 0; +} + +struct dvb_frontend +*ddb_mci_attach(struct ddb_input *input, + int mci_type, int nr, + int (**fn_set_input)(struct dvb_frontend *fe, int input)) +{ + struct ddb_port *port = input->port; + struct ddb *dev = port->dev; + struct ddb_link *link = &dev->link[port->lnr]; + struct mci_base *base; + struct mci *state; + void *key = mci_type ? (void *)port : (void *)link; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + base = match_base(key); + if (base) { + base->count++; + state->base = base; + } else { + base = kzalloc(sizeof(*base), GFP_KERNEL); + if (!base) + goto fail; + base->key = key; + base->count = 1; + base->link = link; + base->dev = dev->dev; + mutex_init(&base->mci_lock); + mutex_init(&base->tuner_lock); + ddb_irq_set(dev, link->nr, 0, mci_handler, base); + init_completion(&base->completion); + state->base = base; + if (probe(state) < 0) { + kfree(base); + goto fail; + } + list_add(&base->mci_list, &mci_list); + } + state->fe.ops = mci_ops; + state->fe.demodulator_priv = state; + state->nr = nr; + *fn_set_input = set_input; + + state->tuner = nr; + state->demod = nr; + + return &state->fe; +fail: + kfree(state); + return NULL; +} diff --git a/drivers/media/pci/ddbridge/ddbridge-mci.h b/drivers/media/pci/ddbridge/ddbridge-mci.h new file mode 100644 index 000000000000..209cc2b92dff --- /dev/null +++ b/drivers/media/pci/ddbridge/ddbridge-mci.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ddbridge-mci.h: Digital Devices micro code interface + * + * Copyright (C) 2017 Digital Devices GmbH + * Marcus Metzler <mocm@metzlerbros.de> + * Ralph Metzler <rjkm@metzlerbros.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DDBRIDGE_MCI_H_ +#define _DDBRIDGE_MCI_H_ + +#define MCI_DEMOD_MAX 8 +#define MCI_TUNER_MAX 4 +#define DEMOD_UNUSED (0xFF) + +#define MCI_CONTROL (0x500) +#define MCI_COMMAND (0x600) +#define MCI_RESULT (0x680) + +#define MCI_COMMAND_SIZE (0x80) +#define MCI_RESULT_SIZE (0x80) + +#define MCI_CONTROL_START_COMMAND (0x00000001) +#define MCI_CONTROL_ENABLE_DONE_INTERRUPT (0x00000002) +#define MCI_CONTROL_RESET (0x00008000) +#define MCI_CONTROL_READY (0x00010000) + +#define SX8_TSCONFIG (0x280) + +#define SX8_TSCONFIG_MODE_MASK (0x00000003) +#define SX8_TSCONFIG_MODE_OFF (0x00000000) +#define SX8_TSCONFIG_MODE_NORMAL (0x00000001) +#define SX8_TSCONFIG_MODE_IQ (0x00000003) + +#define SX8_TSCONFIG_TSHEADER (0x00000004) +#define SX8_TSCONFIG_BURST (0x00000008) + +#define SX8_TSCONFIG_BURSTSIZE_MASK (0x00000030) +#define SX8_TSCONFIG_BURSTSIZE_2K (0x00000000) +#define SX8_TSCONFIG_BURSTSIZE_4K (0x00000010) +#define SX8_TSCONFIG_BURSTSIZE_8K (0x00000020) +#define SX8_TSCONFIG_BURSTSIZE_16K (0x00000030) + +#define SX8_DEMOD_STOPPED (0) +#define SX8_DEMOD_IQ_MODE (1) +#define SX8_DEMOD_WAIT_SIGNAL (2) +#define SX8_DEMOD_WAIT_MATYPE (3) +#define SX8_DEMOD_TIMEOUT (14) +#define SX8_DEMOD_LOCKED (15) + +#define MCI_CMD_STOP (0x01) +#define MCI_CMD_GETSTATUS (0x02) +#define MCI_CMD_GETSIGNALINFO (0x03) +#define MCI_CMD_RFPOWER (0x04) + +#define MCI_CMD_SEARCH_DVBS (0x10) + +#define MCI_CMD_GET_IQSYMBOL (0x30) + +#define SX8_CMD_INPUT_ENABLE (0x40) +#define SX8_CMD_INPUT_DISABLE (0x41) +#define SX8_CMD_START_IQ (0x42) +#define SX8_CMD_STOP_IQ (0x43) +#define SX8_CMD_SELECT_IQOUT (0x44) +#define SX8_CMD_SELECT_TSOUT (0x45) + +#define SX8_ERROR_UNSUPPORTED (0x80) + +#define SX8_SUCCESS(status) (status < SX8_ERROR_UNSUPPORTED) + +#define SX8_CMD_DIAG_READ8 (0xE0) +#define SX8_CMD_DIAG_READ32 (0xE1) +#define SX8_CMD_DIAG_WRITE8 (0xE2) +#define SX8_CMD_DIAG_WRITE32 (0xE3) + +#define SX8_CMD_DIAG_READRF (0xE8) +#define SX8_CMD_DIAG_WRITERF (0xE9) + +struct mci_command { + union { + u32 command_word; + struct { + u8 command; + u8 tuner; + u8 demod; + u8 output; + }; + }; + union { + u32 params[31]; + struct { + u8 flags; + u8 s2_modulation_mask; + u8 rsvd1; + u8 retry; + u32 frequency; + u32 symbol_rate; + u8 input_stream_id; + u8 rsvd2[3]; + u32 scrambling_sequence_index; + } dvbs2_search; + }; +}; + +struct mci_result { + union { + u32 status_word; + struct { + u8 status; + u8 rsvd; + u16 time; + }; + }; + union { + u32 result[27]; + struct { + u8 standard; + /* puncture rate for DVB-S */ + u8 pls_code; + /* 7-6: rolloff, 5-2: rsrvd, 1:short, 0:pilots */ + u8 roll_off; + u8 rsvd; + u32 frequency; + u32 symbol_rate; + s16 channel_power; + s16 band_power; + s16 signal_to_noise; + s16 rsvd2; + u32 packet_errors; + u32 ber_numerator; + u32 ber_denominator; + } dvbs2_signal_info; + struct { + u8 i_symbol; + u8 q_symbol; + } dvbs2_signal_iq; + }; + u32 version[4]; +}; + +struct dvb_frontend +*ddb_mci_attach(struct ddb_input *input, + int mci_type, int nr, + int (**fn_set_input)(struct dvb_frontend *fe, int input)); + +#endif /* _DDBRIDGE_MCI_H_ */ diff --git a/drivers/media/pci/ddbridge/ddbridge-regs.h b/drivers/media/pci/ddbridge/ddbridge-regs.h index 23d74ff83fe4..b978b5991940 100644 --- a/drivers/media/pci/ddbridge/ddbridge-regs.h +++ b/drivers/media/pci/ddbridge/ddbridge-regs.h @@ -17,6 +17,9 @@ * http://www.gnu.org/copyleft/gpl.html */ +#ifndef __DDBRIDGE_REGS_H__ +#define __DDBRIDGE_REGS_H__ + /* ------------------------------------------------------------------------- */ /* SPI Controller */ @@ -154,3 +157,4 @@ #define LNB_BUF_LEVEL(i) (LNB_BASE + (i) * 0x20 + 0x10) #define LNB_BUF_WRITE(i) (LNB_BASE + (i) * 0x20 + 0x14) +#endif /* __DDBRIDGE_REGS_H__ */ diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h index f223dc6c9963..a66b1125cc74 100644 --- a/drivers/media/pci/ddbridge/ddbridge.h +++ b/drivers/media/pci/ddbridge/ddbridge.h @@ -63,7 +63,7 @@ #include <media/dvb_ca_en50221.h> #include <media/dvb_net.h> -#define DDBRIDGE_VERSION "0.9.32-integrated" +#define DDBRIDGE_VERSION "0.9.33-integrated" #define DDB_MAX_I2C 32 #define DDB_MAX_PORT 32 @@ -112,11 +112,12 @@ struct ddb_ids { struct ddb_info { int type; -#define DDB_NONE 0 -#define DDB_OCTOPUS 1 -#define DDB_OCTOPUS_CI 2 -#define DDB_OCTOPUS_MAX 5 +#define DDB_NONE 0 +#define DDB_OCTOPUS 1 +#define DDB_OCTOPUS_CI 2 +#define DDB_OCTOPUS_MAX 5 #define DDB_OCTOPUS_MAX_CT 6 +#define DDB_OCTOPUS_MCI 9 char *name; u32 i2c_mask; u8 port_num; @@ -133,23 +134,12 @@ struct ddb_info { #define TS_QUIRK_REVERSED 2 #define TS_QUIRK_ALT_OSC 8 u32 tempmon_irq; + u8 mci; const struct ddb_regmap *regmap; }; -/* DMA_SIZE MUST be smaller than 256k and - * MUST be divisible by 188 and 128 !!! - */ - #define DMA_MAX_BUFS 32 /* hardware table limit */ -#define INPUT_DMA_BUFS 8 -#define INPUT_DMA_SIZE (128 * 47 * 21) -#define INPUT_DMA_IRQ_DIV 1 - -#define OUTPUT_DMA_BUFS 8 -#define OUTPUT_DMA_SIZE (128 * 47 * 21) -#define OUTPUT_DMA_IRQ_DIV 1 - struct ddb; struct ddb_port; @@ -248,6 +238,7 @@ struct ddb_port { char *name; char *type_name; u32 type; +#define DDB_TUNER_DUMMY 0xffffffff #define DDB_TUNER_NONE 0 #define DDB_TUNER_DVBS_ST 1 #define DDB_TUNER_DVBS_ST_AA 2 @@ -264,6 +255,7 @@ struct ddb_port { #define DDB_CI_EXTERNAL_XO2_B 13 #define DDB_TUNER_DVBS_STV0910_PR 14 #define DDB_TUNER_DVBC2T2I_SONY_P 15 +#define DDB_TUNER_MCI 16 #define DDB_TUNER_XO2 32 #define DDB_TUNER_DVBS_STV0910 (DDB_TUNER_XO2 + 0) @@ -305,6 +297,11 @@ struct ddb_lnb { u32 fmode; }; +struct ddb_irq { + void (*handler)(void *); + void *data; +}; + struct ddb_link { struct ddb *dev; const struct ddb_info *info; @@ -319,6 +316,7 @@ struct ddb_link { spinlock_t temp_lock; /* lock temp chip access */ int overtemperature_error; u8 temp_tab[11]; + struct ddb_irq irq[256]; }; struct ddb { @@ -343,9 +341,6 @@ struct ddb { struct ddb_dma idma[DDB_MAX_INPUT]; struct ddb_dma odma[DDB_MAX_OUTPUT]; - void (*handler[4][256])(unsigned long); - unsigned long handler_data[4][256]; - struct device *ddb_dev; u32 ddb_dev_users; u32 nr; @@ -368,16 +363,9 @@ int ddbridge_flashread(struct ddb *dev, u32 link, u8 *buf, u32 addr, u32 len); /****************************************************************************/ -/* ddbridge-main.c (modparams) */ -extern int ci_bitrate; -extern int ts_loop; -extern int xo2_speed; -extern int alt_dma; -extern int no_init; -extern int stv0910_single; -extern struct workqueue_struct *ddb_wq; - /* ddbridge-core.c */ +struct ddb_irq *ddb_irq_set(struct ddb *dev, u32 link, u32 nr, + void (*handler)(void *), void *data); void ddb_ports_detach(struct ddb *dev); void ddb_ports_release(struct ddb *dev); void ddb_buffers_free(struct ddb *dev); @@ -389,9 +377,9 @@ void ddb_ports_init(struct ddb *dev); int ddb_buffers_alloc(struct ddb *dev); int ddb_ports_attach(struct ddb *dev); int ddb_device_create(struct ddb *dev); -int ddb_class_create(void); -void ddb_class_destroy(void); int ddb_init(struct ddb *dev); void ddb_unmap(struct ddb *dev); +int ddb_exit_ddbridge(int stage, int error); +int ddb_init_ddbridge(void); #endif /* DDBRIDGE_H */ diff --git a/drivers/media/pci/dt3155/Kconfig b/drivers/media/pci/dt3155/Kconfig index 5145e0dfa2aa..858b0f2f15be 100644 --- a/drivers/media/pci/dt3155/Kconfig +++ b/drivers/media/pci/dt3155/Kconfig @@ -1,7 +1,6 @@ config VIDEO_DT3155 tristate "DT3155 frame grabber" depends on PCI && VIDEO_DEV && VIDEO_V4L2 - depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG default n ---help--- diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig index a82d3fe277d2..715f77651482 100644 --- a/drivers/media/pci/intel/ipu3/Kconfig +++ b/drivers/media/pci/intel/ipu3/Kconfig @@ -2,18 +2,16 @@ config VIDEO_IPU3_CIO2 tristate "Intel ipu3-cio2 driver" depends on VIDEO_V4L2 && PCI depends on VIDEO_V4L2_SUBDEV_API - depends on X86 || COMPILE_TEST + depends on (X86 && ACPI) || COMPILE_TEST depends on MEDIA_CONTROLLER - depends on HAS_DMA - depends on ACPI select V4L2_FWNODE select VIDEOBUF2_DMA_SG ---help--- - This is the Intel IPU3 CIO2 CSI-2 receiver unit, found in Intel - Skylake and Kaby Lake SoCs and used for capturing images and - video from a camera sensor. + This is the Intel IPU3 CIO2 CSI-2 receiver unit, found in Intel + Skylake and Kaby Lake SoCs and used for capturing images and + video from a camera sensor. - Say Y or M here if you have a Skylake/Kaby Lake SoC with MIPI CSI-2 - connected camera. - The module will be called ipu3-cio2. + Say Y or M here if you have a Skylake/Kaby Lake SoC with MIPI CSI-2 + connected camera. + The module will be called ipu3-cio2. diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index 7d768ec0f824..29027159eced 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -640,18 +640,10 @@ static const char *const cio2_port_errs[] = { "PKT2LONG", }; -static irqreturn_t cio2_irq(int irq, void *cio2_ptr) +static void cio2_irq_handle_once(struct cio2_device *cio2, u32 int_status) { - struct cio2_device *cio2 = cio2_ptr; void __iomem *const base = cio2->base; struct device *dev = &cio2->pci_dev->dev; - u32 int_status, int_clear; - - int_status = readl(base + CIO2_REG_INT_STS); - int_clear = int_status; - - if (!int_status) - return IRQ_NONE; if (int_status & CIO2_INT_IOOE) { /* @@ -770,9 +762,29 @@ static irqreturn_t cio2_irq(int irq, void *cio2_ptr) int_status &= ~(CIO2_INT_IOIE | CIO2_INT_IOIRQ); } - writel(int_clear, base + CIO2_REG_INT_STS); if (int_status) dev_warn(dev, "unknown interrupt 0x%x on INT\n", int_status); +} + +static irqreturn_t cio2_irq(int irq, void *cio2_ptr) +{ + struct cio2_device *cio2 = cio2_ptr; + void __iomem *const base = cio2->base; + struct device *dev = &cio2->pci_dev->dev; + u32 int_status; + + int_status = readl(base + CIO2_REG_INT_STS); + dev_dbg(dev, "isr enter - interrupt status 0x%x\n", int_status); + if (!int_status) + return IRQ_NONE; + + do { + writel(int_status, base + CIO2_REG_INT_STS); + cio2_irq_handle_once(cio2, int_status); + int_status = readl(base + CIO2_REG_INT_STS); + if (int_status) + dev_dbg(dev, "pending status 0x%x\n", int_status); + } while (int_status); return IRQ_HANDLED; } diff --git a/drivers/media/pci/mantis/mantis_uart.c b/drivers/media/pci/mantis/mantis_uart.c index 18f81c135996..b7765687e0c3 100644 --- a/drivers/media/pci/mantis/mantis_uart.c +++ b/drivers/media/pci/mantis/mantis_uart.c @@ -92,6 +92,7 @@ static void mantis_uart_work(struct work_struct *work) { struct mantis_pci *mantis = container_of(work, struct mantis_pci, uart_work); u32 stat; + unsigned long timeout; stat = mmread(MANTIS_UART_STAT); @@ -102,9 +103,15 @@ static void mantis_uart_work(struct work_struct *work) * MANTIS_UART_RXFIFO_DATA is only set if at least * config->bytes + 1 bytes are in the FIFO. */ + + /* FIXME: is 10ms good enough ? */ + timeout = jiffies + msecs_to_jiffies(10); while (stat & MANTIS_UART_RXFIFO_DATA) { mantis_uart_read(mantis); stat = mmread(MANTIS_UART_STAT); + + if (!time_is_after_jiffies(timeout)) + break; } /* re-enable UART (RX) interrupt */ diff --git a/drivers/media/pci/meye/Kconfig b/drivers/media/pci/meye/Kconfig index b4bf848be5a0..2e60334ffef5 100644 --- a/drivers/media/pci/meye/Kconfig +++ b/drivers/media/pci/meye/Kconfig @@ -1,6 +1,7 @@ config VIDEO_MEYE tristate "Sony Vaio Picturebook Motion Eye Video For Linux" - depends on PCI && SONY_LAPTOP && VIDEO_V4L2 + depends on PCI && VIDEO_V4L2 + depends on SONY_LAPTOP || COMPILE_TEST ---help--- This is the video4linux driver for the Motion Eye camera found in the Vaio Picturebook laptops. Please read the material in diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c index 65fc8f23ad86..7a106bc11a2b 100644 --- a/drivers/media/pci/ngene/ngene-cards.c +++ b/drivers/media/pci/ngene/ngene-cards.c @@ -137,11 +137,6 @@ static int tuner_attach_stv6110(struct ngene_channel *chan) chan->dev->card_info->tuner_config[chan->number]; const struct stv6110x_devctl *ctl; - if (chan->number < 2) - i2c = &chan->dev->channel[0].i2c_adapter; - else - i2c = &chan->dev->channel[1].i2c_adapter; - ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c); if (ctl == NULL) { dev_err(pdev, "No STV6110X found!\n"); @@ -304,14 +299,6 @@ static int demod_attach_stv0900(struct ngene_channel *chan) struct stv090x_config *feconf = (struct stv090x_config *) chan->dev->card_info->fe_config[chan->number]; - /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ - /* Note: Both adapters share the same i2c bus, but the demod */ - /* driver requires that each demod has its own i2c adapter */ - if (chan->number < 2) - i2c = &chan->dev->channel[0].i2c_adapter; - else - i2c = &chan->dev->channel[1].i2c_adapter; - chan->fe = dvb_attach(stv090x_attach, feconf, i2c, (chan->number & 1) == 0 ? STV090x_DEMODULATOR_0 : STV090x_DEMODULATOR_1); @@ -340,6 +327,7 @@ static struct stv0910_cfg stv0910_p = { .parallel = 1, .rptlvl = 4, .clk = 30000000, + .tsspeed = 0x28, }; static struct lnbh25_config lnbh25_cfg = { @@ -721,7 +709,6 @@ static int cineS2_probe(struct ngene_channel *chan) static struct lgdt330x_config aver_m780 = { - .demod_address = 0xb2 >> 1, .demod_chip = LGDT3303, .serial_mpeg = 0x00, /* PARALLEL */ .clock_polarity_flip = 1, @@ -738,7 +725,8 @@ static int demod_attach_lg330x(struct ngene_channel *chan) { struct device *pdev = &chan->dev->pci_dev->dev; - chan->fe = dvb_attach(lgdt330x_attach, &aver_m780, &chan->i2c_adapter); + chan->fe = dvb_attach(lgdt330x_attach, &aver_m780, + 0xb2 >> 1, &chan->i2c_adapter); if (chan->fe == NULL) { dev_err(pdev, "No LGDT330x found!\n"); return -ENODEV; diff --git a/drivers/media/pci/ngene/ngene-dvb.c b/drivers/media/pci/ngene/ngene-dvb.c index fee89b9ed9c1..5147e83397a1 100644 --- a/drivers/media/pci/ngene/ngene-dvb.c +++ b/drivers/media/pci/ngene/ngene-dvb.c @@ -40,7 +40,7 @@ static int ci_tsfix = 1; module_param(ci_tsfix, int, 0444); -MODULE_PARM_DESC(ci_tsfix, "Detect and fix TS buffer offset shifs in conjunction with CI expansions (default: 1/enabled)"); +MODULE_PARM_DESC(ci_tsfix, "Detect and fix TS buffer offset shifts in conjunction with CI expansions (default: 1/enabled)"); /****************************************************************************/ /* COMMAND API interface ****************************************************/ diff --git a/drivers/media/pci/pt1/Kconfig b/drivers/media/pci/pt1/Kconfig index 24501d5bf70d..2718b4c6b7c6 100644 --- a/drivers/media/pci/pt1/Kconfig +++ b/drivers/media/pci/pt1/Kconfig @@ -1,6 +1,9 @@ config DVB_PT1 tristate "PT1 cards" depends on DVB_CORE && PCI && I2C + select DVB_TC90522 if MEDIA_SUBDRV_AUTOSELECT + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QM1D1B0004 if MEDIA_SUBDRV_AUTOSELECT help Support for Earthsoft PT1 PCI cards. diff --git a/drivers/media/pci/pt1/Makefile b/drivers/media/pci/pt1/Makefile index ab873ae088a0..bc491e08dd63 100644 --- a/drivers/media/pci/pt1/Makefile +++ b/drivers/media/pci/pt1/Makefile @@ -1,5 +1,6 @@ -earth-pt1-objs := pt1.o va1j5jf8007s.o va1j5jf8007t.o +earth-pt1-objs := pt1.o obj-$(CONFIG_DVB_PT1) += earth-pt1.o ccflags-y += -Idrivers/media/dvb-frontends +ccflags-y += -Idrivers/media/tuners diff --git a/drivers/media/pci/pt1/pt1.c b/drivers/media/pci/pt1/pt1.c index 4f6867af8311..5708f69622cc 100644 --- a/drivers/media/pci/pt1/pt1.c +++ b/drivers/media/pci/pt1/pt1.c @@ -18,7 +18,10 @@ */ #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/sched/signal.h> +#include <linux/hrtimer.h> +#include <linux/delay.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/vmalloc.h> @@ -26,6 +29,8 @@ #include <linux/kthread.h> #include <linux/freezer.h> #include <linux/ratelimit.h> +#include <linux/string.h> +#include <linux/i2c.h> #include <media/dvbdev.h> #include <media/dvb_demux.h> @@ -33,8 +38,9 @@ #include <media/dvb_net.h> #include <media/dvb_frontend.h> -#include "va1j5jf8007t.h" -#include "va1j5jf8007s.h" +#include "tc90522.h" +#include "qm1d1b0004.h" +#include "dvb-pll.h" #define DRIVER_NAME "earth-pt1" @@ -63,6 +69,11 @@ struct pt1_table { struct pt1_buffer bufs[PT1_NR_BUFS]; }; +enum pt1_fe_clk { + PT1_FE_CLK_20MHZ, /* PT1 */ + PT1_FE_CLK_25MHZ, /* PT2 */ +}; + #define PT1_NR_ADAPS 4 struct pt1_adapter; @@ -81,6 +92,8 @@ struct pt1 { struct mutex lock; int power; int reset; + + enum pt1_fe_clk fe_clk; }; struct pt1_adapter { @@ -97,6 +110,8 @@ struct pt1_adapter { int users; struct dmxdev dmxdev; struct dvb_frontend *fe; + struct i2c_client *demod_i2c_client; + struct i2c_client *tuner_i2c_client; int (*orig_set_voltage)(struct dvb_frontend *fe, enum fe_sec_voltage voltage); int (*orig_sleep)(struct dvb_frontend *fe); @@ -106,6 +121,145 @@ struct pt1_adapter { int sleep; }; +union pt1_tuner_config { + struct qm1d1b0004_config qm1d1b0004; + struct dvb_pll_config tda6651; +}; + +struct pt1_config { + struct i2c_board_info demod_info; + struct tc90522_config demod_cfg; + + struct i2c_board_info tuner_info; + union pt1_tuner_config tuner_cfg; +}; + +static const struct pt1_config pt1_configs[PT1_NR_ADAPS] = { + { + .demod_info = { + I2C_BOARD_INFO(TC90522_I2C_DEV_SAT, 0x1b), + }, + .tuner_info = { + I2C_BOARD_INFO("qm1d1b0004", 0x60), + }, + }, + { + .demod_info = { + I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x1a), + }, + .tuner_info = { + I2C_BOARD_INFO("tda665x_earthpt1", 0x61), + }, + }, + { + .demod_info = { + I2C_BOARD_INFO(TC90522_I2C_DEV_SAT, 0x19), + }, + .tuner_info = { + I2C_BOARD_INFO("qm1d1b0004", 0x60), + }, + }, + { + .demod_info = { + I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x18), + }, + .tuner_info = { + I2C_BOARD_INFO("tda665x_earthpt1", 0x61), + }, + }, +}; + +static const u8 va1j5jf8007s_20mhz_configs[][2] = { + {0x04, 0x02}, {0x0d, 0x55}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, + {0x1c, 0x0a}, {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, + {0x52, 0x89}, {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, + {0x87, 0x04}, {0x8e, 0x02}, {0xa3, 0xf7}, {0xa5, 0xc0}, +}; + +static const u8 va1j5jf8007s_25mhz_configs[][2] = { + {0x04, 0x02}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, {0x1c, 0x0a}, + {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, {0x52, 0x89}, + {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, {0x87, 0x04}, + {0x8e, 0x26}, {0xa3, 0xf7}, {0xa5, 0xc0}, +}; + +static const u8 va1j5jf8007t_20mhz_configs[][2] = { + {0x03, 0x90}, {0x14, 0x8f}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, + {0x22, 0x83}, {0x31, 0x0d}, {0x32, 0xe0}, {0x39, 0xd3}, {0x3a, 0x00}, + {0x3b, 0x11}, {0x3c, 0x3f}, + {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x02}, {0x76, 0x4e}, {0x77, 0x03}, + {0xef, 0x01} +}; + +static const u8 va1j5jf8007t_25mhz_configs[][2] = { + {0x03, 0x90}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, {0x22, 0x83}, + {0x3a, 0x04}, {0x3b, 0x11}, {0x3c, 0x3f}, {0x5c, 0x40}, {0x5f, 0x80}, + {0x75, 0x0a}, {0x76, 0x4c}, {0x77, 0x03}, {0xef, 0x01} +}; + +static int config_demod(struct i2c_client *cl, enum pt1_fe_clk clk) +{ + int ret; + u8 buf[2] = {0x01, 0x80}; + bool is_sat; + const u8 (*cfg_data)[2]; + int i, len; + + ret = i2c_master_send(cl, buf, 2); + if (ret < 0) + return ret; + usleep_range(30000, 50000); + + is_sat = !strncmp(cl->name, TC90522_I2C_DEV_SAT, + strlen(TC90522_I2C_DEV_SAT)); + if (is_sat) { + struct i2c_msg msg[2]; + u8 wbuf, rbuf; + + wbuf = 0x07; + msg[0].addr = cl->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &wbuf; + + msg[1].addr = cl->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = &rbuf; + ret = i2c_transfer(cl->adapter, msg, 2); + if (ret < 0) + return ret; + if (rbuf != 0x41) + return -EIO; + } + + /* frontend init */ + if (clk == PT1_FE_CLK_20MHZ) { + if (is_sat) { + cfg_data = va1j5jf8007s_20mhz_configs; + len = ARRAY_SIZE(va1j5jf8007s_20mhz_configs); + } else { + cfg_data = va1j5jf8007t_20mhz_configs; + len = ARRAY_SIZE(va1j5jf8007t_20mhz_configs); + } + } else { + if (is_sat) { + cfg_data = va1j5jf8007s_25mhz_configs; + len = ARRAY_SIZE(va1j5jf8007s_25mhz_configs); + } else { + cfg_data = va1j5jf8007t_25mhz_configs; + len = ARRAY_SIZE(va1j5jf8007t_25mhz_configs); + } + } + + for (i = 0; i < len; i++) { + ret = i2c_master_send(cl, cfg_data[i], 2); + if (ret < 0) + return ret; + } + return 0; +} + static void pt1_write_reg(struct pt1 *pt1, int reg, u32 data) { writel(data, pt1->regs + reg * 4); @@ -171,7 +325,7 @@ static int pt1_unlock(struct pt1 *pt1) for (i = 0; i < 3; i++) { if (pt1_read_reg(pt1, 0) & 0x80000000) return 0; - schedule_timeout_uninterruptible((HZ + 999) / 1000); + usleep_range(1000, 2000); } dev_err(&pt1->pdev->dev, "could not unlock\n"); return -EIO; @@ -185,7 +339,7 @@ static int pt1_reset_pci(struct pt1 *pt1) for (i = 0; i < 10; i++) { if (pt1_read_reg(pt1, 0) & 0x00000001) return 0; - schedule_timeout_uninterruptible((HZ + 999) / 1000); + usleep_range(1000, 2000); } dev_err(&pt1->pdev->dev, "could not reset PCI\n"); return -EIO; @@ -199,7 +353,7 @@ static int pt1_reset_ram(struct pt1 *pt1) for (i = 0; i < 10; i++) { if (pt1_read_reg(pt1, 0) & 0x00000002) return 0; - schedule_timeout_uninterruptible((HZ + 999) / 1000); + usleep_range(1000, 2000); } dev_err(&pt1->pdev->dev, "could not reset RAM\n"); return -EIO; @@ -216,7 +370,7 @@ static int pt1_do_enable_ram(struct pt1 *pt1) if ((pt1_read_reg(pt1, 0) & 0x00000004) != status) return 0; } - schedule_timeout_uninterruptible((HZ + 999) / 1000); + usleep_range(1000, 2000); } dev_err(&pt1->pdev->dev, "could not enable RAM\n"); return -EIO; @@ -226,7 +380,7 @@ static int pt1_enable_ram(struct pt1 *pt1) { int i, ret; int phase; - schedule_timeout_uninterruptible((HZ + 999) / 1000); + usleep_range(1000, 2000); phase = pt1->pdev->device == 0x211a ? 128 : 166; for (i = 0; i < phase; i++) { ret = pt1_do_enable_ram(pt1); @@ -311,16 +465,31 @@ static int pt1_thread(void *data) { struct pt1 *pt1; struct pt1_buffer_page *page; + bool was_frozen; + +#define PT1_FETCH_DELAY 10 +#define PT1_FETCH_DELAY_DELTA 2 pt1 = data; set_freezable(); - while (!kthread_should_stop()) { - try_to_freeze(); + while (!kthread_freezable_should_stop(&was_frozen)) { + if (was_frozen) { + int i; + + for (i = 0; i < PT1_NR_ADAPS; i++) + pt1_set_stream(pt1, i, !!pt1->adaps[i]->users); + } page = pt1->tables[pt1->table_index].bufs[pt1->buf_index].page; if (!pt1_filter(pt1, page)) { - schedule_timeout_interruptible((HZ + 999) / 1000); + ktime_t delay; + + delay = ktime_set(0, PT1_FETCH_DELAY * NSEC_PER_MSEC); + set_current_state(TASK_INTERRUPTIBLE); + schedule_hrtimeout_range(&delay, + PT1_FETCH_DELAY_DELTA * NSEC_PER_MSEC, + HRTIMER_MODE_REL); continue; } @@ -556,7 +725,7 @@ pt1_update_power(struct pt1 *pt1) adap = pt1->adaps[i]; switch (adap->voltage) { case SEC_VOLTAGE_13: /* actually 11V */ - bits |= 1 << 1; + bits |= 1 << 2; break; case SEC_VOLTAGE_18: /* actually 15V */ bits |= 1 << 1 | 1 << 2; @@ -589,30 +758,33 @@ static int pt1_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage voltage) static int pt1_sleep(struct dvb_frontend *fe) { struct pt1_adapter *adap; + int ret; adap = container_of(fe->dvb, struct pt1_adapter, adap); - adap->sleep = 1; - pt1_update_power(adap->pt1); + ret = 0; if (adap->orig_sleep) - return adap->orig_sleep(fe); - else - return 0; + ret = adap->orig_sleep(fe); + + adap->sleep = 1; + pt1_update_power(adap->pt1); + return ret; } static int pt1_wakeup(struct dvb_frontend *fe) { struct pt1_adapter *adap; + int ret; adap = container_of(fe->dvb, struct pt1_adapter, adap); adap->sleep = 0; pt1_update_power(adap->pt1); - schedule_timeout_uninterruptible((HZ + 999) / 1000); + usleep_range(1000, 2000); - if (adap->orig_init) - return adap->orig_init(fe); - else - return 0; + ret = config_demod(adap->demod_i2c_client, adap->pt1->fe_clk); + if (ret == 0 && adap->orig_init) + ret = adap->orig_init(fe); + return ret; } static void pt1_free_adapter(struct pt1_adapter *adap) @@ -735,6 +907,8 @@ err: static void pt1_cleanup_frontend(struct pt1_adapter *adap) { dvb_unregister_frontend(adap->fe); + dvb_module_release(adap->tuner_i2c_client); + dvb_module_release(adap->demod_i2c_client); } static int pt1_init_frontend(struct pt1_adapter *adap, struct dvb_frontend *fe) @@ -763,112 +937,70 @@ static void pt1_cleanup_frontends(struct pt1 *pt1) pt1_cleanup_frontend(pt1->adaps[i]); } -struct pt1_config { - struct va1j5jf8007s_config va1j5jf8007s_config; - struct va1j5jf8007t_config va1j5jf8007t_config; -}; - -static const struct pt1_config pt1_configs[2] = { - { - { - .demod_address = 0x1b, - .frequency = VA1J5JF8007S_20MHZ, - }, - { - .demod_address = 0x1a, - .frequency = VA1J5JF8007T_20MHZ, - }, - }, { - { - .demod_address = 0x19, - .frequency = VA1J5JF8007S_20MHZ, - }, - { - .demod_address = 0x18, - .frequency = VA1J5JF8007T_20MHZ, - }, - }, -}; - -static const struct pt1_config pt2_configs[2] = { - { - { - .demod_address = 0x1b, - .frequency = VA1J5JF8007S_25MHZ, - }, - { - .demod_address = 0x1a, - .frequency = VA1J5JF8007T_25MHZ, - }, - }, { - { - .demod_address = 0x19, - .frequency = VA1J5JF8007S_25MHZ, - }, - { - .demod_address = 0x18, - .frequency = VA1J5JF8007T_25MHZ, - }, - }, -}; - static int pt1_init_frontends(struct pt1 *pt1) { - int i, j; - struct i2c_adapter *i2c_adap; - const struct pt1_config *configs, *config; - struct dvb_frontend *fe[4]; + int i; int ret; - i = 0; - j = 0; - - i2c_adap = &pt1->i2c_adap; - configs = pt1->pdev->device == 0x211a ? pt1_configs : pt2_configs; - do { - config = &configs[i / 2]; - - fe[i] = va1j5jf8007s_attach(&config->va1j5jf8007s_config, - i2c_adap); - if (!fe[i]) { - ret = -ENODEV; /* This does not sound nice... */ - goto err; - } - i++; - - fe[i] = va1j5jf8007t_attach(&config->va1j5jf8007t_config, - i2c_adap); - if (!fe[i]) { - ret = -ENODEV; - goto err; + for (i = 0; i < ARRAY_SIZE(pt1_configs); i++) { + const struct i2c_board_info *info; + struct tc90522_config dcfg; + struct i2c_client *cl; + + info = &pt1_configs[i].demod_info; + dcfg = pt1_configs[i].demod_cfg; + dcfg.tuner_i2c = NULL; + + ret = -ENODEV; + cl = dvb_module_probe("tc90522", info->type, &pt1->i2c_adap, + info->addr, &dcfg); + if (!cl) + goto fe_unregister; + pt1->adaps[i]->demod_i2c_client = cl; + + if (!strncmp(cl->name, TC90522_I2C_DEV_SAT, + strlen(TC90522_I2C_DEV_SAT))) { + struct qm1d1b0004_config tcfg; + + info = &pt1_configs[i].tuner_info; + tcfg = pt1_configs[i].tuner_cfg.qm1d1b0004; + tcfg.fe = dcfg.fe; + cl = dvb_module_probe("qm1d1b0004", + info->type, dcfg.tuner_i2c, + info->addr, &tcfg); + } else { + struct dvb_pll_config tcfg; + + info = &pt1_configs[i].tuner_info; + tcfg = pt1_configs[i].tuner_cfg.tda6651; + tcfg.fe = dcfg.fe; + cl = dvb_module_probe("dvb_pll", + info->type, dcfg.tuner_i2c, + info->addr, &tcfg); } - i++; - - ret = va1j5jf8007s_prepare(fe[i - 2]); - if (ret < 0) - goto err; + if (!cl) + goto demod_release; + pt1->adaps[i]->tuner_i2c_client = cl; - ret = va1j5jf8007t_prepare(fe[i - 1]); - if (ret < 0) - goto err; - - } while (i < 4); - - do { - ret = pt1_init_frontend(pt1->adaps[j], fe[j]); + ret = pt1_init_frontend(pt1->adaps[i], dcfg.fe); if (ret < 0) - goto err; - } while (++j < 4); + goto tuner_release; + } return 0; -err: - while (i-- > j) - fe[i]->ops.release(fe[i]); - - while (j--) - dvb_unregister_frontend(fe[j]); - +tuner_release: + dvb_module_release(pt1->adaps[i]->tuner_i2c_client); +demod_release: + dvb_module_release(pt1->adaps[i]->demod_i2c_client); +fe_unregister: + dev_warn(&pt1->pdev->dev, "failed to init FE(%d).\n", i); + i--; + for (; i >= 0; i--) { + dvb_unregister_frontend(pt1->adaps[i]->fe); + dvb_module_release(pt1->adaps[i]->tuner_i2c_client); + dvb_module_release(pt1->adaps[i]->demod_i2c_client); + } return ret; } @@ -954,7 +1086,7 @@ static int pt1_i2c_end(struct pt1 *pt1, int addr) do { if (signal_pending(current)) return -EINTR; - schedule_timeout_interruptible((HZ + 999) / 1000); + usleep_range(1000, 2000); } while (pt1_read_reg(pt1, 0) & 0x00000080); return 0; } @@ -1052,6 +1184,98 @@ static void pt1_i2c_init(struct pt1 *pt1) pt1_i2c_emit(pt1, i, 0, 0, 1, 1, 0); } +#ifdef CONFIG_PM_SLEEP + +static int pt1_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct pt1 *pt1 = pci_get_drvdata(pdev); + + pt1_init_streams(pt1); + pt1_disable_ram(pt1); + pt1->power = 0; + pt1->reset = 1; + pt1_update_power(pt1); + return 0; +} + +static int pt1_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct pt1 *pt1 = pci_get_drvdata(pdev); + int ret; + int i; + + pt1->power = 0; + pt1->reset = 1; + pt1_update_power(pt1); + + pt1_i2c_init(pt1); + pt1_i2c_wait(pt1); + + ret = pt1_sync(pt1); + if (ret < 0) + goto resume_err; + + pt1_identify(pt1); + + ret = pt1_unlock(pt1); + if (ret < 0) + goto resume_err; + + ret = pt1_reset_pci(pt1); + if (ret < 0) + goto resume_err; + + ret = pt1_reset_ram(pt1); + if (ret < 0) + goto resume_err; + + ret = pt1_enable_ram(pt1); + if (ret < 0) + goto resume_err; + + pt1_init_streams(pt1); + + pt1->power = 1; + pt1_update_power(pt1); + msleep(20); + + pt1->reset = 0; + pt1_update_power(pt1); + usleep_range(1000, 2000); + + for (i = 0; i < PT1_NR_ADAPS; i++) + dvb_frontend_reinitialise(pt1->adaps[i]->fe); + + pt1_init_table_count(pt1); + for (i = 0; i < pt1_nr_tables; i++) { + int j; + + for (j = 0; j < PT1_NR_BUFS; j++) + pt1->tables[i].bufs[j].page->upackets[PT1_NR_UPACKETS-1] + = 0; + pt1_increment_table_count(pt1); + } + pt1_register_tables(pt1, pt1->tables[0].addr >> PT1_PAGE_SHIFT); + + pt1->table_index = 0; + pt1->buf_index = 0; + for (i = 0; i < PT1_NR_ADAPS; i++) { + pt1->adaps[i]->upacket_count = 0; + pt1->adaps[i]->packet_count = 0; + pt1->adaps[i]->st_count = -1; + } + + return 0; + +resume_err: + dev_info(&pt1->pdev->dev, "failed to resume PT1/PT2."); + return 0; /* resume anyway */ +} + +#endif /* CONFIG_PM_SLEEP */ + static void pt1_remove(struct pci_dev *pdev) { struct pt1 *pt1; @@ -1112,6 +1336,8 @@ static int pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) mutex_init(&pt1->lock); pt1->pdev = pdev; pt1->regs = regs; + pt1->fe_clk = (pdev->device == 0x211a) ? + PT1_FE_CLK_20MHZ : PT1_FE_CLK_25MHZ; pci_set_drvdata(pdev, pt1); ret = pt1_init_adapters(pt1); @@ -1163,11 +1389,11 @@ static int pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pt1->power = 1; pt1_update_power(pt1); - schedule_timeout_uninterruptible((HZ + 49) / 50); + msleep(20); pt1->reset = 0; pt1_update_power(pt1); - schedule_timeout_uninterruptible((HZ + 999) / 1000); + usleep_range(1000, 2000); ret = pt1_init_frontends(pt1); if (ret < 0) @@ -1210,11 +1436,16 @@ static const struct pci_device_id pt1_id_table[] = { }; MODULE_DEVICE_TABLE(pci, pt1_id_table); +static SIMPLE_DEV_PM_OPS(pt1_pm_ops, pt1_suspend, pt1_resume); + static struct pci_driver pt1_driver = { .name = DRIVER_NAME, .probe = pt1_probe, .remove = pt1_remove, .id_table = pt1_id_table, +#ifdef CONFIG_PM_SLEEP + .driver.pm = &pt1_pm_ops, +#endif }; module_pci_driver(pt1_driver); diff --git a/drivers/media/pci/pt1/va1j5jf8007s.c b/drivers/media/pci/pt1/va1j5jf8007s.c deleted file mode 100644 index f49867aef054..000000000000 --- a/drivers/media/pci/pt1/va1j5jf8007s.c +++ /dev/null @@ -1,732 +0,0 @@ -/* - * ISDB-S driver for VA1J5JF8007/VA1J5JF8011 - * - * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info> - * - * based on pt1dvr - http://pt1dvr.sourceforge.jp/ - * by Tomoaki Ishikawa <tomy@users.sourceforge.jp> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/i2c.h> -#include <media/dvb_frontend.h> -#include "va1j5jf8007s.h" - -enum va1j5jf8007s_tune_state { - VA1J5JF8007S_IDLE, - VA1J5JF8007S_SET_FREQUENCY_1, - VA1J5JF8007S_SET_FREQUENCY_2, - VA1J5JF8007S_SET_FREQUENCY_3, - VA1J5JF8007S_CHECK_FREQUENCY, - VA1J5JF8007S_SET_MODULATION, - VA1J5JF8007S_CHECK_MODULATION, - VA1J5JF8007S_SET_TS_ID, - VA1J5JF8007S_CHECK_TS_ID, - VA1J5JF8007S_TRACK, -}; - -struct va1j5jf8007s_state { - const struct va1j5jf8007s_config *config; - struct i2c_adapter *adap; - struct dvb_frontend fe; - enum va1j5jf8007s_tune_state tune_state; -}; - -static int va1j5jf8007s_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - struct va1j5jf8007s_state *state; - u8 addr; - int i; - u8 write_buf[1], read_buf[1]; - struct i2c_msg msgs[2]; - s32 word, x1, x2, x3, x4, x5, y; - - state = fe->demodulator_priv; - addr = state->config->demod_address; - - word = 0; - for (i = 0; i < 2; i++) { - write_buf[0] = 0xbc + i; - - msgs[0].addr = addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(write_buf); - msgs[0].buf = write_buf; - - msgs[1].addr = addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(read_buf); - msgs[1].buf = read_buf; - - if (i2c_transfer(state->adap, msgs, 2) != 2) - return -EREMOTEIO; - - word <<= 8; - word |= read_buf[0]; - } - - word -= 3000; - if (word < 0) - word = 0; - - x1 = int_sqrt(word << 16) * ((15625ll << 21) / 1000000); - x2 = (s64)x1 * x1 >> 31; - x3 = (s64)x2 * x1 >> 31; - x4 = (s64)x2 * x2 >> 31; - x5 = (s64)x4 * x1 >> 31; - - y = (58857ll << 23) / 1000; - y -= (s64)x1 * ((89565ll << 24) / 1000) >> 30; - y += (s64)x2 * ((88977ll << 24) / 1000) >> 28; - y -= (s64)x3 * ((50259ll << 25) / 1000) >> 27; - y += (s64)x4 * ((14341ll << 27) / 1000) >> 27; - y -= (s64)x5 * ((16346ll << 30) / 10000) >> 28; - - *snr = y < 0 ? 0 : y >> 15; - return 0; -} - -static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe) -{ - return DVBFE_ALGO_HW; -} - -static int -va1j5jf8007s_read_status(struct dvb_frontend *fe, enum fe_status *status) -{ - struct va1j5jf8007s_state *state; - - state = fe->demodulator_priv; - - switch (state->tune_state) { - case VA1J5JF8007S_IDLE: - case VA1J5JF8007S_SET_FREQUENCY_1: - case VA1J5JF8007S_SET_FREQUENCY_2: - case VA1J5JF8007S_SET_FREQUENCY_3: - case VA1J5JF8007S_CHECK_FREQUENCY: - *status = 0; - return 0; - - - case VA1J5JF8007S_SET_MODULATION: - case VA1J5JF8007S_CHECK_MODULATION: - *status |= FE_HAS_SIGNAL; - return 0; - - case VA1J5JF8007S_SET_TS_ID: - case VA1J5JF8007S_CHECK_TS_ID: - *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; - return 0; - - case VA1J5JF8007S_TRACK: - *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; - return 0; - } - - BUG(); -} - -struct va1j5jf8007s_cb_map { - u32 frequency; - u8 cb; -}; - -static const struct va1j5jf8007s_cb_map va1j5jf8007s_cb_maps[] = { - { 986000, 0xb2 }, - { 1072000, 0xd2 }, - { 1154000, 0xe2 }, - { 1291000, 0x20 }, - { 1447000, 0x40 }, - { 1615000, 0x60 }, - { 1791000, 0x80 }, - { 1972000, 0xa0 }, -}; - -static u8 va1j5jf8007s_lookup_cb(u32 frequency) -{ - int i; - const struct va1j5jf8007s_cb_map *map; - - for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_cb_maps); i++) { - map = &va1j5jf8007s_cb_maps[i]; - if (frequency < map->frequency) - return map->cb; - } - return 0xc0; -} - -static int va1j5jf8007s_set_frequency_1(struct va1j5jf8007s_state *state) -{ - u32 frequency; - u16 word; - u8 buf[6]; - struct i2c_msg msg; - - frequency = state->fe.dtv_property_cache.frequency; - - word = (frequency + 500) / 1000; - if (frequency < 1072000) - word = (word << 1 & ~0x1f) | (word & 0x0f); - - buf[0] = 0xfe; - buf[1] = 0xc0; - buf[2] = 0x40 | word >> 8; - buf[3] = word; - buf[4] = 0xe0; - buf[5] = va1j5jf8007s_lookup_cb(frequency); - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int va1j5jf8007s_set_frequency_2(struct va1j5jf8007s_state *state) -{ - u8 buf[3]; - struct i2c_msg msg; - - buf[0] = 0xfe; - buf[1] = 0xc0; - buf[2] = 0xe4; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int va1j5jf8007s_set_frequency_3(struct va1j5jf8007s_state *state) -{ - u32 frequency; - u8 buf[4]; - struct i2c_msg msg; - - frequency = state->fe.dtv_property_cache.frequency; - - buf[0] = 0xfe; - buf[1] = 0xc0; - buf[2] = 0xf4; - buf[3] = va1j5jf8007s_lookup_cb(frequency) | 0x4; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int -va1j5jf8007s_check_frequency(struct va1j5jf8007s_state *state, int *lock) -{ - u8 addr; - u8 write_buf[2], read_buf[1]; - struct i2c_msg msgs[2]; - - addr = state->config->demod_address; - - write_buf[0] = 0xfe; - write_buf[1] = 0xc1; - - msgs[0].addr = addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(write_buf); - msgs[0].buf = write_buf; - - msgs[1].addr = addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(read_buf); - msgs[1].buf = read_buf; - - if (i2c_transfer(state->adap, msgs, 2) != 2) - return -EREMOTEIO; - - *lock = read_buf[0] & 0x40; - return 0; -} - -static int va1j5jf8007s_set_modulation(struct va1j5jf8007s_state *state) -{ - u8 buf[2]; - struct i2c_msg msg; - - buf[0] = 0x03; - buf[1] = 0x01; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int -va1j5jf8007s_check_modulation(struct va1j5jf8007s_state *state, int *lock) -{ - u8 addr; - u8 write_buf[1], read_buf[1]; - struct i2c_msg msgs[2]; - - addr = state->config->demod_address; - - write_buf[0] = 0xc3; - - msgs[0].addr = addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(write_buf); - msgs[0].buf = write_buf; - - msgs[1].addr = addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(read_buf); - msgs[1].buf = read_buf; - - if (i2c_transfer(state->adap, msgs, 2) != 2) - return -EREMOTEIO; - - *lock = !(read_buf[0] & 0x10); - return 0; -} - -static int -va1j5jf8007s_set_ts_id(struct va1j5jf8007s_state *state) -{ - u32 ts_id; - u8 buf[3]; - struct i2c_msg msg; - - ts_id = state->fe.dtv_property_cache.stream_id; - if (!ts_id || ts_id == NO_STREAM_ID_FILTER) - return 0; - - buf[0] = 0x8f; - buf[1] = ts_id >> 8; - buf[2] = ts_id; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int -va1j5jf8007s_check_ts_id(struct va1j5jf8007s_state *state, int *lock) -{ - u8 addr; - u8 write_buf[1], read_buf[2]; - struct i2c_msg msgs[2]; - u32 ts_id; - - ts_id = state->fe.dtv_property_cache.stream_id; - if (!ts_id || ts_id == NO_STREAM_ID_FILTER) { - *lock = 1; - return 0; - } - - addr = state->config->demod_address; - - write_buf[0] = 0xe6; - - msgs[0].addr = addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(write_buf); - msgs[0].buf = write_buf; - - msgs[1].addr = addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(read_buf); - msgs[1].buf = read_buf; - - if (i2c_transfer(state->adap, msgs, 2) != 2) - return -EREMOTEIO; - - *lock = (read_buf[0] << 8 | read_buf[1]) == ts_id; - return 0; -} - -static int -va1j5jf8007s_tune(struct dvb_frontend *fe, - bool re_tune, - unsigned int mode_flags, unsigned int *delay, - enum fe_status *status) -{ - struct va1j5jf8007s_state *state; - int ret; - int lock = 0; - - state = fe->demodulator_priv; - - if (re_tune) - state->tune_state = VA1J5JF8007S_SET_FREQUENCY_1; - - switch (state->tune_state) { - case VA1J5JF8007S_IDLE: - *delay = 3 * HZ; - *status = 0; - return 0; - - case VA1J5JF8007S_SET_FREQUENCY_1: - ret = va1j5jf8007s_set_frequency_1(state); - if (ret < 0) - return ret; - - state->tune_state = VA1J5JF8007S_SET_FREQUENCY_2; - *delay = 0; - *status = 0; - return 0; - - case VA1J5JF8007S_SET_FREQUENCY_2: - ret = va1j5jf8007s_set_frequency_2(state); - if (ret < 0) - return ret; - - state->tune_state = VA1J5JF8007S_SET_FREQUENCY_3; - *delay = (HZ + 99) / 100; - *status = 0; - return 0; - - case VA1J5JF8007S_SET_FREQUENCY_3: - ret = va1j5jf8007s_set_frequency_3(state); - if (ret < 0) - return ret; - - state->tune_state = VA1J5JF8007S_CHECK_FREQUENCY; - *delay = 0; - *status = 0; - return 0; - - case VA1J5JF8007S_CHECK_FREQUENCY: - ret = va1j5jf8007s_check_frequency(state, &lock); - if (ret < 0) - return ret; - - if (!lock) { - *delay = (HZ + 999) / 1000; - *status = 0; - return 0; - } - - state->tune_state = VA1J5JF8007S_SET_MODULATION; - *delay = 0; - *status = FE_HAS_SIGNAL; - return 0; - - case VA1J5JF8007S_SET_MODULATION: - ret = va1j5jf8007s_set_modulation(state); - if (ret < 0) - return ret; - - state->tune_state = VA1J5JF8007S_CHECK_MODULATION; - *delay = 0; - *status = FE_HAS_SIGNAL; - return 0; - - case VA1J5JF8007S_CHECK_MODULATION: - ret = va1j5jf8007s_check_modulation(state, &lock); - if (ret < 0) - return ret; - - if (!lock) { - *delay = (HZ + 49) / 50; - *status = FE_HAS_SIGNAL; - return 0; - } - - state->tune_state = VA1J5JF8007S_SET_TS_ID; - *delay = 0; - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; - return 0; - - case VA1J5JF8007S_SET_TS_ID: - ret = va1j5jf8007s_set_ts_id(state); - if (ret < 0) - return ret; - - state->tune_state = VA1J5JF8007S_CHECK_TS_ID; - return 0; - - case VA1J5JF8007S_CHECK_TS_ID: - ret = va1j5jf8007s_check_ts_id(state, &lock); - if (ret < 0) - return ret; - - if (!lock) { - *delay = (HZ + 99) / 100; - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; - return 0; - } - - state->tune_state = VA1J5JF8007S_TRACK; - /* fall through */ - - case VA1J5JF8007S_TRACK: - *delay = 3 * HZ; - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; - return 0; - } - - BUG(); -} - -static int va1j5jf8007s_init_frequency(struct va1j5jf8007s_state *state) -{ - u8 buf[4]; - struct i2c_msg msg; - - buf[0] = 0xfe; - buf[1] = 0xc0; - buf[2] = 0xf0; - buf[3] = 0x04; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int va1j5jf8007s_set_sleep(struct va1j5jf8007s_state *state, int sleep) -{ - u8 buf[2]; - struct i2c_msg msg; - - buf[0] = 0x17; - buf[1] = sleep ? 0x01 : 0x00; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int va1j5jf8007s_sleep(struct dvb_frontend *fe) -{ - struct va1j5jf8007s_state *state; - int ret; - - state = fe->demodulator_priv; - - ret = va1j5jf8007s_init_frequency(state); - if (ret < 0) - return ret; - - return va1j5jf8007s_set_sleep(state, 1); -} - -static int va1j5jf8007s_init(struct dvb_frontend *fe) -{ - struct va1j5jf8007s_state *state; - - state = fe->demodulator_priv; - state->tune_state = VA1J5JF8007S_IDLE; - - return va1j5jf8007s_set_sleep(state, 0); -} - -static void va1j5jf8007s_release(struct dvb_frontend *fe) -{ - struct va1j5jf8007s_state *state; - state = fe->demodulator_priv; - kfree(state); -} - -static const struct dvb_frontend_ops va1j5jf8007s_ops = { - .delsys = { SYS_ISDBS }, - .info = { - .name = "VA1J5JF8007/VA1J5JF8011 ISDB-S", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1000, - .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | - FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | - FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO | - FE_CAN_MULTISTREAM, - }, - - .read_snr = va1j5jf8007s_read_snr, - .get_frontend_algo = va1j5jf8007s_get_frontend_algo, - .read_status = va1j5jf8007s_read_status, - .tune = va1j5jf8007s_tune, - .sleep = va1j5jf8007s_sleep, - .init = va1j5jf8007s_init, - .release = va1j5jf8007s_release, -}; - -static int va1j5jf8007s_prepare_1(struct va1j5jf8007s_state *state) -{ - u8 addr; - u8 write_buf[1], read_buf[1]; - struct i2c_msg msgs[2]; - - addr = state->config->demod_address; - - write_buf[0] = 0x07; - - msgs[0].addr = addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(write_buf); - msgs[0].buf = write_buf; - - msgs[1].addr = addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(read_buf); - msgs[1].buf = read_buf; - - if (i2c_transfer(state->adap, msgs, 2) != 2) - return -EREMOTEIO; - - if (read_buf[0] != 0x41) - return -EIO; - - return 0; -} - -static const u8 va1j5jf8007s_20mhz_prepare_bufs[][2] = { - {0x04, 0x02}, {0x0d, 0x55}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, - {0x1c, 0x0a}, {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, - {0x52, 0x89}, {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, - {0x87, 0x04}, {0x8e, 0x02}, {0xa3, 0xf7}, {0xa5, 0xc0}, -}; - -static const u8 va1j5jf8007s_25mhz_prepare_bufs[][2] = { - {0x04, 0x02}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, {0x1c, 0x0a}, - {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, {0x52, 0x89}, - {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, {0x87, 0x04}, - {0x8e, 0x26}, {0xa3, 0xf7}, {0xa5, 0xc0}, -}; - -static int va1j5jf8007s_prepare_2(struct va1j5jf8007s_state *state) -{ - const u8 (*bufs)[2]; - int size; - u8 addr; - u8 buf[2]; - struct i2c_msg msg; - int i; - - switch (state->config->frequency) { - case VA1J5JF8007S_20MHZ: - bufs = va1j5jf8007s_20mhz_prepare_bufs; - size = ARRAY_SIZE(va1j5jf8007s_20mhz_prepare_bufs); - break; - case VA1J5JF8007S_25MHZ: - bufs = va1j5jf8007s_25mhz_prepare_bufs; - size = ARRAY_SIZE(va1j5jf8007s_25mhz_prepare_bufs); - break; - default: - return -EINVAL; - } - - addr = state->config->demod_address; - - msg.addr = addr; - msg.flags = 0; - msg.len = 2; - msg.buf = buf; - for (i = 0; i < size; i++) { - memcpy(buf, bufs[i], sizeof(buf)); - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - } - - return 0; -} - -/* must be called after va1j5jf8007t_attach */ -int va1j5jf8007s_prepare(struct dvb_frontend *fe) -{ - struct va1j5jf8007s_state *state; - int ret; - - state = fe->demodulator_priv; - - ret = va1j5jf8007s_prepare_1(state); - if (ret < 0) - return ret; - - ret = va1j5jf8007s_prepare_2(state); - if (ret < 0) - return ret; - - return va1j5jf8007s_init_frequency(state); -} - -struct dvb_frontend * -va1j5jf8007s_attach(const struct va1j5jf8007s_config *config, - struct i2c_adapter *adap) -{ - struct va1j5jf8007s_state *state; - struct dvb_frontend *fe; - u8 buf[2]; - struct i2c_msg msg; - - state = kzalloc(sizeof(struct va1j5jf8007s_state), GFP_KERNEL); - if (!state) - return NULL; - - state->config = config; - state->adap = adap; - - fe = &state->fe; - memcpy(&fe->ops, &va1j5jf8007s_ops, sizeof(struct dvb_frontend_ops)); - fe->demodulator_priv = state; - - buf[0] = 0x01; - buf[1] = 0x80; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) { - kfree(state); - return NULL; - } - - return fe; -} diff --git a/drivers/media/pci/pt1/va1j5jf8007s.h b/drivers/media/pci/pt1/va1j5jf8007s.h deleted file mode 100644 index f8ce5609095d..000000000000 --- a/drivers/media/pci/pt1/va1j5jf8007s.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * ISDB-S driver for VA1J5JF8007/VA1J5JF8011 - * - * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info> - * - * based on pt1dvr - http://pt1dvr.sourceforge.jp/ - * by Tomoaki Ishikawa <tomy@users.sourceforge.jp> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef VA1J5JF8007S_H -#define VA1J5JF8007S_H - -enum va1j5jf8007s_frequency { - VA1J5JF8007S_20MHZ, - VA1J5JF8007S_25MHZ, -}; - -struct va1j5jf8007s_config { - u8 demod_address; - enum va1j5jf8007s_frequency frequency; -}; - -struct i2c_adapter; - -struct dvb_frontend * -va1j5jf8007s_attach(const struct va1j5jf8007s_config *config, - struct i2c_adapter *adap); - -/* must be called after va1j5jf8007t_attach */ -int va1j5jf8007s_prepare(struct dvb_frontend *fe); - -#endif diff --git a/drivers/media/pci/pt1/va1j5jf8007t.c b/drivers/media/pci/pt1/va1j5jf8007t.c deleted file mode 100644 index a52984a6f9b3..000000000000 --- a/drivers/media/pci/pt1/va1j5jf8007t.c +++ /dev/null @@ -1,532 +0,0 @@ -/* - * ISDB-T driver for VA1J5JF8007/VA1J5JF8011 - * - * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info> - * - * based on pt1dvr - http://pt1dvr.sourceforge.jp/ - * by Tomoaki Ishikawa <tomy@users.sourceforge.jp> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/i2c.h> -#include <media/dvb_frontend.h> -#include <media/dvb_math.h> -#include "va1j5jf8007t.h" - -enum va1j5jf8007t_tune_state { - VA1J5JF8007T_IDLE, - VA1J5JF8007T_SET_FREQUENCY, - VA1J5JF8007T_CHECK_FREQUENCY, - VA1J5JF8007T_SET_MODULATION, - VA1J5JF8007T_CHECK_MODULATION, - VA1J5JF8007T_TRACK, - VA1J5JF8007T_ABORT, -}; - -struct va1j5jf8007t_state { - const struct va1j5jf8007t_config *config; - struct i2c_adapter *adap; - struct dvb_frontend fe; - enum va1j5jf8007t_tune_state tune_state; -}; - -static int va1j5jf8007t_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - struct va1j5jf8007t_state *state; - u8 addr; - int i; - u8 write_buf[1], read_buf[1]; - struct i2c_msg msgs[2]; - s32 word, x, y; - - state = fe->demodulator_priv; - addr = state->config->demod_address; - - word = 0; - for (i = 0; i < 3; i++) { - write_buf[0] = 0x8b + i; - - msgs[0].addr = addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(write_buf); - msgs[0].buf = write_buf; - - msgs[1].addr = addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(read_buf); - msgs[1].buf = read_buf; - - if (i2c_transfer(state->adap, msgs, 2) != 2) - return -EREMOTEIO; - - word <<= 8; - word |= read_buf[0]; - } - - if (!word) - return -EIO; - - x = 10 * (intlog10(0x540000 * 100 / word) - (2 << 24)); - y = (24ll << 46) / 1000000; - y = ((s64)y * x >> 30) - (16ll << 40) / 10000; - y = ((s64)y * x >> 29) + (398ll << 35) / 10000; - y = ((s64)y * x >> 30) + (5491ll << 29) / 10000; - y = ((s64)y * x >> 30) + (30965ll << 23) / 10000; - *snr = y >> 15; - return 0; -} - -static int va1j5jf8007t_get_frontend_algo(struct dvb_frontend *fe) -{ - return DVBFE_ALGO_HW; -} - -static int -va1j5jf8007t_read_status(struct dvb_frontend *fe, enum fe_status *status) -{ - struct va1j5jf8007t_state *state; - - state = fe->demodulator_priv; - - switch (state->tune_state) { - case VA1J5JF8007T_IDLE: - case VA1J5JF8007T_SET_FREQUENCY: - case VA1J5JF8007T_CHECK_FREQUENCY: - *status = 0; - return 0; - - - case VA1J5JF8007T_SET_MODULATION: - case VA1J5JF8007T_CHECK_MODULATION: - case VA1J5JF8007T_ABORT: - *status |= FE_HAS_SIGNAL; - return 0; - - case VA1J5JF8007T_TRACK: - *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; - return 0; - } - - BUG(); -} - -struct va1j5jf8007t_cb_map { - u32 frequency; - u8 cb; -}; - -static const struct va1j5jf8007t_cb_map va1j5jf8007t_cb_maps[] = { - { 90000000, 0x80 }, - { 140000000, 0x81 }, - { 170000000, 0xa1 }, - { 220000000, 0x62 }, - { 330000000, 0xa2 }, - { 402000000, 0xe2 }, - { 450000000, 0x64 }, - { 550000000, 0x84 }, - { 600000000, 0xa4 }, - { 700000000, 0xc4 }, -}; - -static u8 va1j5jf8007t_lookup_cb(u32 frequency) -{ - int i; - const struct va1j5jf8007t_cb_map *map; - - for (i = 0; i < ARRAY_SIZE(va1j5jf8007t_cb_maps); i++) { - map = &va1j5jf8007t_cb_maps[i]; - if (frequency < map->frequency) - return map->cb; - } - return 0xe4; -} - -static int va1j5jf8007t_set_frequency(struct va1j5jf8007t_state *state) -{ - u32 frequency; - u16 word; - u8 buf[6]; - struct i2c_msg msg; - - frequency = state->fe.dtv_property_cache.frequency; - - word = (frequency + 71428) / 142857 + 399; - buf[0] = 0xfe; - buf[1] = 0xc2; - buf[2] = word >> 8; - buf[3] = word; - buf[4] = 0x80; - buf[5] = va1j5jf8007t_lookup_cb(frequency); - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int -va1j5jf8007t_check_frequency(struct va1j5jf8007t_state *state, int *lock) -{ - u8 addr; - u8 write_buf[2], read_buf[1]; - struct i2c_msg msgs[2]; - - addr = state->config->demod_address; - - write_buf[0] = 0xfe; - write_buf[1] = 0xc3; - - msgs[0].addr = addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(write_buf); - msgs[0].buf = write_buf; - - msgs[1].addr = addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(read_buf); - msgs[1].buf = read_buf; - - if (i2c_transfer(state->adap, msgs, 2) != 2) - return -EREMOTEIO; - - *lock = read_buf[0] & 0x40; - return 0; -} - -static int va1j5jf8007t_set_modulation(struct va1j5jf8007t_state *state) -{ - u8 buf[2]; - struct i2c_msg msg; - - buf[0] = 0x01; - buf[1] = 0x40; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int va1j5jf8007t_check_modulation(struct va1j5jf8007t_state *state, - int *lock, int *retry) -{ - u8 addr; - u8 write_buf[1], read_buf[1]; - struct i2c_msg msgs[2]; - - addr = state->config->demod_address; - - write_buf[0] = 0x80; - - msgs[0].addr = addr; - msgs[0].flags = 0; - msgs[0].len = sizeof(write_buf); - msgs[0].buf = write_buf; - - msgs[1].addr = addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(read_buf); - msgs[1].buf = read_buf; - - if (i2c_transfer(state->adap, msgs, 2) != 2) - return -EREMOTEIO; - - *lock = !(read_buf[0] & 0x10); - *retry = read_buf[0] & 0x80; - return 0; -} - -static int -va1j5jf8007t_tune(struct dvb_frontend *fe, - bool re_tune, - unsigned int mode_flags, unsigned int *delay, - enum fe_status *status) -{ - struct va1j5jf8007t_state *state; - int ret; - int lock = 0, retry = 0; - - state = fe->demodulator_priv; - - if (re_tune) - state->tune_state = VA1J5JF8007T_SET_FREQUENCY; - - switch (state->tune_state) { - case VA1J5JF8007T_IDLE: - *delay = 3 * HZ; - *status = 0; - return 0; - - case VA1J5JF8007T_SET_FREQUENCY: - ret = va1j5jf8007t_set_frequency(state); - if (ret < 0) - return ret; - - state->tune_state = VA1J5JF8007T_CHECK_FREQUENCY; - *delay = 0; - *status = 0; - return 0; - - case VA1J5JF8007T_CHECK_FREQUENCY: - ret = va1j5jf8007t_check_frequency(state, &lock); - if (ret < 0) - return ret; - - if (!lock) { - *delay = (HZ + 999) / 1000; - *status = 0; - return 0; - } - - state->tune_state = VA1J5JF8007T_SET_MODULATION; - *delay = 0; - *status = FE_HAS_SIGNAL; - return 0; - - case VA1J5JF8007T_SET_MODULATION: - ret = va1j5jf8007t_set_modulation(state); - if (ret < 0) - return ret; - - state->tune_state = VA1J5JF8007T_CHECK_MODULATION; - *delay = 0; - *status = FE_HAS_SIGNAL; - return 0; - - case VA1J5JF8007T_CHECK_MODULATION: - ret = va1j5jf8007t_check_modulation(state, &lock, &retry); - if (ret < 0) - return ret; - - if (!lock) { - if (!retry) { - state->tune_state = VA1J5JF8007T_ABORT; - *delay = 3 * HZ; - *status = FE_HAS_SIGNAL; - return 0; - } - *delay = (HZ + 999) / 1000; - *status = FE_HAS_SIGNAL; - return 0; - } - - state->tune_state = VA1J5JF8007T_TRACK; - /* fall through */ - - case VA1J5JF8007T_TRACK: - *delay = 3 * HZ; - *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; - return 0; - - case VA1J5JF8007T_ABORT: - *delay = 3 * HZ; - *status = FE_HAS_SIGNAL; - return 0; - } - - BUG(); -} - -static int va1j5jf8007t_init_frequency(struct va1j5jf8007t_state *state) -{ - u8 buf[7]; - struct i2c_msg msg; - - buf[0] = 0xfe; - buf[1] = 0xc2; - buf[2] = 0x01; - buf[3] = 0x8f; - buf[4] = 0xc1; - buf[5] = 0x80; - buf[6] = 0x80; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int va1j5jf8007t_set_sleep(struct va1j5jf8007t_state *state, int sleep) -{ - u8 buf[2]; - struct i2c_msg msg; - - buf[0] = 0x03; - buf[1] = sleep ? 0x90 : 0x80; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - - return 0; -} - -static int va1j5jf8007t_sleep(struct dvb_frontend *fe) -{ - struct va1j5jf8007t_state *state; - int ret; - - state = fe->demodulator_priv; - - ret = va1j5jf8007t_init_frequency(state); - if (ret < 0) - return ret; - - return va1j5jf8007t_set_sleep(state, 1); -} - -static int va1j5jf8007t_init(struct dvb_frontend *fe) -{ - struct va1j5jf8007t_state *state; - - state = fe->demodulator_priv; - state->tune_state = VA1J5JF8007T_IDLE; - - return va1j5jf8007t_set_sleep(state, 0); -} - -static void va1j5jf8007t_release(struct dvb_frontend *fe) -{ - struct va1j5jf8007t_state *state; - state = fe->demodulator_priv; - kfree(state); -} - -static const struct dvb_frontend_ops va1j5jf8007t_ops = { - .delsys = { SYS_ISDBT }, - .info = { - .name = "VA1J5JF8007/VA1J5JF8011 ISDB-T", - .frequency_min = 90000000, - .frequency_max = 770000000, - .frequency_stepsize = 142857, - .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | - FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | - FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, - }, - - .read_snr = va1j5jf8007t_read_snr, - .get_frontend_algo = va1j5jf8007t_get_frontend_algo, - .read_status = va1j5jf8007t_read_status, - .tune = va1j5jf8007t_tune, - .sleep = va1j5jf8007t_sleep, - .init = va1j5jf8007t_init, - .release = va1j5jf8007t_release, -}; - -static const u8 va1j5jf8007t_20mhz_prepare_bufs[][2] = { - {0x03, 0x90}, {0x14, 0x8f}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, - {0x22, 0x83}, {0x31, 0x0d}, {0x32, 0xe0}, {0x39, 0xd3}, {0x3a, 0x00}, - {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x02}, {0x76, 0x4e}, {0x77, 0x03}, - {0xef, 0x01} -}; - -static const u8 va1j5jf8007t_25mhz_prepare_bufs[][2] = { - {0x03, 0x90}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, {0x22, 0x83}, - {0x3a, 0x00}, {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x0a}, {0x76, 0x4c}, - {0x77, 0x03}, {0xef, 0x01} -}; - -int va1j5jf8007t_prepare(struct dvb_frontend *fe) -{ - struct va1j5jf8007t_state *state; - const u8 (*bufs)[2]; - int size; - u8 buf[2]; - struct i2c_msg msg; - int i; - - state = fe->demodulator_priv; - - switch (state->config->frequency) { - case VA1J5JF8007T_20MHZ: - bufs = va1j5jf8007t_20mhz_prepare_bufs; - size = ARRAY_SIZE(va1j5jf8007t_20mhz_prepare_bufs); - break; - case VA1J5JF8007T_25MHZ: - bufs = va1j5jf8007t_25mhz_prepare_bufs; - size = ARRAY_SIZE(va1j5jf8007t_25mhz_prepare_bufs); - break; - default: - return -EINVAL; - } - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - for (i = 0; i < size; i++) { - memcpy(buf, bufs[i], sizeof(buf)); - if (i2c_transfer(state->adap, &msg, 1) != 1) - return -EREMOTEIO; - } - - return va1j5jf8007t_init_frequency(state); -} - -struct dvb_frontend * -va1j5jf8007t_attach(const struct va1j5jf8007t_config *config, - struct i2c_adapter *adap) -{ - struct va1j5jf8007t_state *state; - struct dvb_frontend *fe; - u8 buf[2]; - struct i2c_msg msg; - - state = kzalloc(sizeof(struct va1j5jf8007t_state), GFP_KERNEL); - if (!state) - return NULL; - - state->config = config; - state->adap = adap; - - fe = &state->fe; - memcpy(&fe->ops, &va1j5jf8007t_ops, sizeof(struct dvb_frontend_ops)); - fe->demodulator_priv = state; - - buf[0] = 0x01; - buf[1] = 0x80; - - msg.addr = state->config->demod_address; - msg.flags = 0; - msg.len = sizeof(buf); - msg.buf = buf; - - if (i2c_transfer(state->adap, &msg, 1) != 1) { - kfree(state); - return NULL; - } - - return fe; -} diff --git a/drivers/media/pci/pt1/va1j5jf8007t.h b/drivers/media/pci/pt1/va1j5jf8007t.h deleted file mode 100644 index 95eb7d294d20..000000000000 --- a/drivers/media/pci/pt1/va1j5jf8007t.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * ISDB-T driver for VA1J5JF8007/VA1J5JF8011 - * - * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info> - * - * based on pt1dvr - http://pt1dvr.sourceforge.jp/ - * by Tomoaki Ishikawa <tomy@users.sourceforge.jp> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef VA1J5JF8007T_H -#define VA1J5JF8007T_H - -enum va1j5jf8007t_frequency { - VA1J5JF8007T_20MHZ, - VA1J5JF8007T_25MHZ, -}; - -struct va1j5jf8007t_config { - u8 demod_address; - enum va1j5jf8007t_frequency frequency; -}; - -struct i2c_adapter; - -struct dvb_frontend * -va1j5jf8007t_attach(const struct va1j5jf8007t_config *config, - struct i2c_adapter *adap); - -/* must be called after va1j5jf8007s_attach */ -int va1j5jf8007t_prepare(struct dvb_frontend *fe); - -#endif diff --git a/drivers/media/pci/pt3/pt3.c b/drivers/media/pci/pt3/pt3.c index da74828805bc..90273b4d772f 100644 --- a/drivers/media/pci/pt3/pt3.c +++ b/drivers/media/pci/pt3/pt3.c @@ -1,17 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Earthsoft PT3 driver * * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/freezer.h> @@ -376,25 +367,22 @@ static int pt3_fe_init(struct pt3_board *pt3) static int pt3_attach_fe(struct pt3_board *pt3, int i) { - struct i2c_board_info info; + const struct i2c_board_info *info; struct tc90522_config cfg; struct i2c_client *cl; struct dvb_adapter *dvb_adap; int ret; - info = adap_conf[i].demod_info; + info = &adap_conf[i].demod_info; cfg = adap_conf[i].demod_cfg; cfg.tuner_i2c = NULL; - info.platform_data = &cfg; ret = -ENODEV; - request_module("tc90522"); - cl = i2c_new_device(&pt3->i2c_adap, &info); - if (!cl || !cl->dev.driver) + cl = dvb_module_probe("tc90522", info->type, &pt3->i2c_adap, + info->addr, &cfg); + if (!cl) return -ENODEV; pt3->adaps[i]->i2c_demod = cl; - if (!try_module_get(cl->dev.driver->owner)) - goto err_demod_i2c_unregister_device; if (!strncmp(cl->name, TC90522_I2C_DEV_SAT, strlen(TC90522_I2C_DEV_SAT))) { @@ -402,41 +390,33 @@ static int pt3_attach_fe(struct pt3_board *pt3, int i) tcfg = adap_conf[i].tuner_cfg.qm1d1c0042; tcfg.fe = cfg.fe; - info = adap_conf[i].tuner_info; - info.platform_data = &tcfg; - request_module("qm1d1c0042"); - cl = i2c_new_device(cfg.tuner_i2c, &info); + info = &adap_conf[i].tuner_info; + cl = dvb_module_probe("qm1d1c0042", info->type, cfg.tuner_i2c, + info->addr, &tcfg); } else { struct mxl301rf_config tcfg; tcfg = adap_conf[i].tuner_cfg.mxl301rf; tcfg.fe = cfg.fe; - info = adap_conf[i].tuner_info; - info.platform_data = &tcfg; - request_module("mxl301rf"); - cl = i2c_new_device(cfg.tuner_i2c, &info); + info = &adap_conf[i].tuner_info; + cl = dvb_module_probe("mxl301rf", info->type, cfg.tuner_i2c, + info->addr, &tcfg); } - if (!cl || !cl->dev.driver) - goto err_demod_module_put; + if (!cl) + goto err_demod_module_release; pt3->adaps[i]->i2c_tuner = cl; - if (!try_module_get(cl->dev.driver->owner)) - goto err_tuner_i2c_unregister_device; dvb_adap = &pt3->adaps[one_adapter ? 0 : i]->dvb_adap; ret = dvb_register_frontend(dvb_adap, cfg.fe); if (ret < 0) - goto err_tuner_module_put; + goto err_tuner_module_release; pt3->adaps[i]->fe = cfg.fe; return 0; -err_tuner_module_put: - module_put(pt3->adaps[i]->i2c_tuner->dev.driver->owner); -err_tuner_i2c_unregister_device: - i2c_unregister_device(pt3->adaps[i]->i2c_tuner); -err_demod_module_put: - module_put(pt3->adaps[i]->i2c_demod->dev.driver->owner); -err_demod_i2c_unregister_device: - i2c_unregister_device(pt3->adaps[i]->i2c_demod); +err_tuner_module_release: + dvb_module_release(pt3->adaps[i]->i2c_tuner); +err_demod_module_release: + dvb_module_release(pt3->adaps[i]->i2c_demod); return ret; } @@ -464,7 +444,7 @@ static int pt3_fetch_thread(void *data) pt3_proc_dma(adap); - delay = PT3_FETCH_DELAY * NSEC_PER_MSEC; + delay = ktime_set(0, PT3_FETCH_DELAY * NSEC_PER_MSEC); set_current_state(TASK_UNINTERRUPTIBLE); freezable_schedule_hrtimeout_range(&delay, PT3_FETCH_DELAY_DELTA * NSEC_PER_MSEC, @@ -630,14 +610,8 @@ static void pt3_cleanup_adapter(struct pt3_board *pt3, int index) adap->fe->callback = NULL; if (adap->fe->frontend_priv) dvb_unregister_frontend(adap->fe); - if (adap->i2c_tuner) { - module_put(adap->i2c_tuner->dev.driver->owner); - i2c_unregister_device(adap->i2c_tuner); - } - if (adap->i2c_demod) { - module_put(adap->i2c_demod->dev.driver->owner); - i2c_unregister_device(adap->i2c_demod); - } + dvb_module_release(adap->i2c_tuner); + dvb_module_release(adap->i2c_demod); } pt3_free_dmabuf(adap); dvb_dmxdev_release(&adap->dmxdev); diff --git a/drivers/media/pci/pt3/pt3.h b/drivers/media/pci/pt3/pt3.h index fbe8d9b847b0..495891a4ee17 100644 --- a/drivers/media/pci/pt3/pt3.h +++ b/drivers/media/pci/pt3/pt3.h @@ -1,17 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Earthsoft PT3 driver * * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef PT3_H diff --git a/drivers/media/pci/pt3/pt3_dma.c b/drivers/media/pci/pt3/pt3_dma.c index f0ce90437fac..de677b90ea5c 100644 --- a/drivers/media/pci/pt3/pt3_dma.c +++ b/drivers/media/pci/pt3/pt3_dma.c @@ -1,17 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Earthsoft PT3 driver * * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/dma-mapping.h> #include <linux/kernel.h> diff --git a/drivers/media/pci/pt3/pt3_i2c.c b/drivers/media/pci/pt3/pt3_i2c.c index b66138c7b364..b02be789a8c5 100644 --- a/drivers/media/pci/pt3/pt3_i2c.c +++ b/drivers/media/pci/pt3/pt3_i2c.c @@ -1,17 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Earthsoft PT3 driver * * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/media/pci/saa7164/saa7164-fw.c b/drivers/media/pci/saa7164/saa7164-fw.c index ef4906406ebf..a50461861133 100644 --- a/drivers/media/pci/saa7164/saa7164-fw.c +++ b/drivers/media/pci/saa7164/saa7164-fw.c @@ -426,7 +426,8 @@ int saa7164_downloadfirmware(struct saa7164_dev *dev) __func__, fw->size); if (fw->size != fwlength) { - printk(KERN_ERR "xc5000: firmware incorrect size\n"); + printk(KERN_ERR "saa7164: firmware incorrect size %zu != %u\n", + fw->size, fwlength); ret = -ENOMEM; goto out; } diff --git a/drivers/media/pci/solo6x10/Kconfig b/drivers/media/pci/solo6x10/Kconfig index 0fb91dc7ca73..d9e06a6bf1eb 100644 --- a/drivers/media/pci/solo6x10/Kconfig +++ b/drivers/media/pci/solo6x10/Kconfig @@ -1,7 +1,6 @@ config VIDEO_SOLO6X10 tristate "Bluecherry / Softlogic 6x10 capture cards (MPEG-4/H.264)" depends on PCI && VIDEO_DEV && SND && I2C - depends on HAS_DMA select BITREVERSE select FONT_SUPPORT select FONT_8x16 diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig index e03587b1af71..4407b9f881e4 100644 --- a/drivers/media/pci/sta2x11/Kconfig +++ b/drivers/media/pci/sta2x11/Kconfig @@ -1,7 +1,6 @@ config STA2X11_VIP tristate "STA2X11 VIP Video For Linux" - depends on STA2X11 - depends on HAS_DMA + depends on STA2X11 || COMPILE_TEST select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT select VIDEOBUF2_DMA_CONTIG depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index dd199bfc1d45..069c4a853346 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -908,10 +908,10 @@ static int sta2x11_vip_init_controls(struct sta2x11_vip *vip) static int vip_gpio_reserve(struct device *dev, int pin, int dir, const char *name) { - int ret; + int ret = -ENODEV; - if (pin == -1) - return 0; + if (!gpio_is_valid(pin)) + return ret; ret = gpio_request(pin, name); if (ret) { @@ -946,7 +946,7 @@ static int vip_gpio_reserve(struct device *dev, int pin, int dir, */ static void vip_gpio_release(struct device *dev, int pin, const char *name) { - if (pin != -1) { + if (gpio_is_valid(pin)) { dev_dbg(dev, "releasing pin %d (%s)\n", pin, name); gpio_unexport(pin); gpio_free(pin); @@ -1003,25 +1003,24 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, if (ret) goto disable; - if (config->reset_pin >= 0) { - ret = vip_gpio_reserve(&pdev->dev, config->reset_pin, 0, - config->reset_name); - if (ret) { - vip_gpio_release(&pdev->dev, config->pwr_pin, - config->pwr_name); - goto disable; - } + ret = vip_gpio_reserve(&pdev->dev, config->reset_pin, 0, + config->reset_name); + if (ret) { + vip_gpio_release(&pdev->dev, config->pwr_pin, + config->pwr_name); + goto disable; } - if (config->pwr_pin != -1) { + + if (gpio_is_valid(config->pwr_pin)) { /* Datasheet says 5ms between PWR and RST */ usleep_range(5000, 25000); - ret = gpio_direction_output(config->pwr_pin, 1); + gpio_direction_output(config->pwr_pin, 1); } - if (config->reset_pin != -1) { + if (gpio_is_valid(config->reset_pin)) { /* Datasheet says 5ms between PWR and RST */ usleep_range(5000, 25000); - ret = gpio_direction_output(config->reset_pin, 1); + gpio_direction_output(config->reset_pin, 1); } usleep_range(5000, 25000); diff --git a/drivers/media/pci/tw5864/Kconfig b/drivers/media/pci/tw5864/Kconfig index 87c8f327e2d4..760fb11dfeae 100644 --- a/drivers/media/pci/tw5864/Kconfig +++ b/drivers/media/pci/tw5864/Kconfig @@ -1,7 +1,6 @@ config VIDEO_TW5864 tristate "Techwell TW5864 video/audio grabber and encoder" depends on VIDEO_DEV && PCI && VIDEO_V4L2 - depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG ---help--- Support for boards based on Techwell TW5864 chip which provides diff --git a/drivers/media/pci/tw686x/Kconfig b/drivers/media/pci/tw686x/Kconfig index 34ff37712313..da8bfee71b44 100644 --- a/drivers/media/pci/tw686x/Kconfig +++ b/drivers/media/pci/tw686x/Kconfig @@ -1,7 +1,6 @@ config VIDEO_TW686X tristate "Intersil/Techwell TW686x video capture cards" depends on PCI && VIDEO_DEV && VIDEO_V4L2 && SND - depends on HAS_DMA select VIDEOBUF2_VMALLOC select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_DMA_SG diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c index c3fafa97b2d0..0ea8dd44026c 100644 --- a/drivers/media/pci/tw686x/tw686x-video.c +++ b/drivers/media/pci/tw686x/tw686x-video.c @@ -1228,7 +1228,8 @@ int tw686x_video_init(struct tw686x_dev *dev) vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vc->vidq.min_buffers_needed = 2; vc->vidq.lock = &vc->vb_mutex; - vc->vidq.gfp_flags = GFP_DMA32; + vc->vidq.gfp_flags = dev->dma_mode != TW686X_DMA_MODE_MEMCPY ? + GFP_DMA32 : 0; vc->vidq.dev = &dev->pci_dev->dev; err = vb2_queue_init(&vc->vidq); diff --git a/drivers/media/pci/zoran/Kconfig b/drivers/media/pci/zoran/Kconfig deleted file mode 100644 index 39ec35bd21a5..000000000000 --- a/drivers/media/pci/zoran/Kconfig +++ /dev/null @@ -1,75 +0,0 @@ -config VIDEO_ZORAN - tristate "Zoran ZR36057/36067 Video For Linux" - depends on PCI && I2C_ALGOBIT && VIDEO_V4L2 && VIRT_TO_BUS - depends on !ALPHA - help - Say Y for support for MJPEG capture cards based on the Zoran - 36057/36067 PCI controller chipset. This includes the Iomega - Buz, Pinnacle DC10+ and the Linux Media Labs LML33. There is - a driver homepage at <http://mjpeg.sf.net/driver-zoran/>. For - more information, check <file:Documentation/video4linux/Zoran>. - - To compile this driver as a module, choose M here: the - module will be called zr36067. - -config VIDEO_ZORAN_DC30 - tristate "Pinnacle/Miro DC30(+) support" - depends on VIDEO_ZORAN - select VIDEO_ADV7175 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_VPX3220 if MEDIA_SUBDRV_AUTOSELECT - help - Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback - card. This also supports really old DC10 cards based on the - zr36050 MJPEG codec and zr36016 VFE. - -config VIDEO_ZORAN_ZR36060 - tristate "Zoran ZR36060" - depends on VIDEO_ZORAN - help - Say Y to support Zoran boards based on 36060 chips. - This includes Iomega Buz, Pinnacle DC10, Linux media Labs 33 - and 33 R10 and AverMedia 6 boards. - -config VIDEO_ZORAN_BUZ - tristate "Iomega Buz support" - depends on VIDEO_ZORAN_ZR36060 - select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_SAA7185 if MEDIA_SUBDRV_AUTOSELECT - help - Support for the Iomega Buz MJPEG capture/playback card. - -config VIDEO_ZORAN_DC10 - tristate "Pinnacle/Miro DC10(+) support" - depends on VIDEO_ZORAN_ZR36060 - select VIDEO_SAA7110 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_ADV7175 if MEDIA_SUBDRV_AUTOSELECT - help - Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback - card. - -config VIDEO_ZORAN_LML33 - tristate "Linux Media Labs LML33 support" - depends on VIDEO_ZORAN_ZR36060 - select VIDEO_BT819 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT - help - Support for the Linux Media Labs LML33 MJPEG capture/playback - card. - -config VIDEO_ZORAN_LML33R10 - tristate "Linux Media Labs LML33R10 support" - depends on VIDEO_ZORAN_ZR36060 - select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_ADV7170 if MEDIA_SUBDRV_AUTOSELECT - help - support for the Linux Media Labs LML33R10 MJPEG capture/playback - card. - -config VIDEO_ZORAN_AVS6EYES - tristate "AverMedia 6 Eyes support" - depends on VIDEO_ZORAN_ZR36060 - select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_BT866 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_KS0127 if MEDIA_SUBDRV_AUTOSELECT - help - Support for the AverMedia 6 Eyes video surveillance card. diff --git a/drivers/media/pci/zoran/Makefile b/drivers/media/pci/zoran/Makefile deleted file mode 100644 index 21ac29a71458..000000000000 --- a/drivers/media/pci/zoran/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -zr36067-objs := zoran_procfs.o zoran_device.o \ - zoran_driver.o zoran_card.o - -obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o videocodec.o -obj-$(CONFIG_VIDEO_ZORAN_DC30) += zr36050.o zr36016.o -obj-$(CONFIG_VIDEO_ZORAN_ZR36060) += zr36060.o diff --git a/drivers/media/pci/zoran/videocodec.c b/drivers/media/pci/zoran/videocodec.c deleted file mode 100644 index 4427ae7126e2..000000000000 --- a/drivers/media/pci/zoran/videocodec.c +++ /dev/null @@ -1,391 +0,0 @@ -/* - * VIDEO MOTION CODECs internal API for video devices - * - * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's - * bound to a master device. - * - * (c) 2002 Wolfgang Scherr <scherr@net4you.at> - * - * $Id: videocodec.c,v 1.1.2.8 2003/03/29 07:16:04 rbultje Exp $ - * - * ------------------------------------------------------------------------ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * ------------------------------------------------------------------------ - */ - -#define VIDEOCODEC_VERSION "v0.2" - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/types.h> -#include <linux/slab.h> - -// kernel config is here (procfs flag) - -#ifdef CONFIG_PROC_FS -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <linux/uaccess.h> -#endif - -#include "videocodec.h" - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-4)"); - -#define dprintk(num, format, args...) \ - do { \ - if (debug >= num) \ - printk(format, ##args); \ - } while (0) - -struct attached_list { - struct videocodec *codec; - struct attached_list *next; -}; - -struct codec_list { - const struct videocodec *codec; - int attached; - struct attached_list *list; - struct codec_list *next; -}; - -static struct codec_list *codeclist_top = NULL; - -/* ================================================= */ -/* function prototypes of the master/slave interface */ -/* ================================================= */ - -struct videocodec * -videocodec_attach (struct videocodec_master *master) -{ - struct codec_list *h = codeclist_top; - struct attached_list *a, *ptr; - struct videocodec *codec; - int res; - - if (!master) { - dprintk(1, KERN_ERR "videocodec_attach: no data\n"); - return NULL; - } - - dprintk(2, - "videocodec_attach: '%s', flags %lx, magic %lx\n", - master->name, master->flags, master->magic); - - if (!h) { - dprintk(1, - KERN_ERR - "videocodec_attach: no device available\n"); - return NULL; - } - - while (h) { - // attach only if the slave has at least the flags - // expected by the master - if ((master->flags & h->codec->flags) == master->flags) { - dprintk(4, "videocodec_attach: try '%s'\n", - h->codec->name); - - if (!try_module_get(h->codec->owner)) - return NULL; - - codec = kmemdup(h->codec, sizeof(struct videocodec), - GFP_KERNEL); - if (!codec) { - dprintk(1, - KERN_ERR - "videocodec_attach: no mem\n"); - goto out_module_put; - } - - res = strlen(codec->name); - snprintf(codec->name + res, sizeof(codec->name) - res, - "[%d]", h->attached); - codec->master_data = master; - res = codec->setup(codec); - if (res == 0) { - dprintk(3, "videocodec_attach '%s'\n", - codec->name); - ptr = kzalloc(sizeof(struct attached_list), GFP_KERNEL); - if (!ptr) { - dprintk(1, - KERN_ERR - "videocodec_attach: no memory\n"); - goto out_kfree; - } - ptr->codec = codec; - - a = h->list; - if (!a) { - h->list = ptr; - dprintk(4, - "videocodec: first element\n"); - } else { - while (a->next) - a = a->next; // find end - a->next = ptr; - dprintk(4, - "videocodec: in after '%s'\n", - h->codec->name); - } - - h->attached += 1; - return codec; - } else { - kfree(codec); - } - } - h = h->next; - } - - dprintk(1, KERN_ERR "videocodec_attach: no codec found!\n"); - return NULL; - - out_module_put: - module_put(h->codec->owner); - out_kfree: - kfree(codec); - return NULL; -} - -int -videocodec_detach (struct videocodec *codec) -{ - struct codec_list *h = codeclist_top; - struct attached_list *a, *prev; - int res; - - if (!codec) { - dprintk(1, KERN_ERR "videocodec_detach: no data\n"); - return -EINVAL; - } - - dprintk(2, - "videocodec_detach: '%s', type: %x, flags %lx, magic %lx\n", - codec->name, codec->type, codec->flags, codec->magic); - - if (!h) { - dprintk(1, - KERN_ERR "videocodec_detach: no device left...\n"); - return -ENXIO; - } - - while (h) { - a = h->list; - prev = NULL; - while (a) { - if (codec == a->codec) { - res = a->codec->unset(a->codec); - if (res >= 0) { - dprintk(3, - "videocodec_detach: '%s'\n", - a->codec->name); - a->codec->master_data = NULL; - } else { - dprintk(1, - KERN_ERR - "videocodec_detach: '%s'\n", - a->codec->name); - a->codec->master_data = NULL; - } - if (prev == NULL) { - h->list = a->next; - dprintk(4, - "videocodec: delete first\n"); - } else { - prev->next = a->next; - dprintk(4, - "videocodec: delete middle\n"); - } - module_put(a->codec->owner); - kfree(a->codec); - kfree(a); - h->attached -= 1; - return 0; - } - prev = a; - a = a->next; - } - h = h->next; - } - - dprintk(1, KERN_ERR "videocodec_detach: given codec not found!\n"); - return -EINVAL; -} - -int -videocodec_register (const struct videocodec *codec) -{ - struct codec_list *ptr, *h = codeclist_top; - - if (!codec) { - dprintk(1, KERN_ERR "videocodec_register: no data!\n"); - return -EINVAL; - } - - dprintk(2, - "videocodec: register '%s', type: %x, flags %lx, magic %lx\n", - codec->name, codec->type, codec->flags, codec->magic); - - ptr = kzalloc(sizeof(struct codec_list), GFP_KERNEL); - if (!ptr) { - dprintk(1, KERN_ERR "videocodec_register: no memory\n"); - return -ENOMEM; - } - ptr->codec = codec; - - if (!h) { - codeclist_top = ptr; - dprintk(4, "videocodec: hooked in as first element\n"); - } else { - while (h->next) - h = h->next; // find the end - h->next = ptr; - dprintk(4, "videocodec: hooked in after '%s'\n", - h->codec->name); - } - - return 0; -} - -int -videocodec_unregister (const struct videocodec *codec) -{ - struct codec_list *prev = NULL, *h = codeclist_top; - - if (!codec) { - dprintk(1, KERN_ERR "videocodec_unregister: no data!\n"); - return -EINVAL; - } - - dprintk(2, - "videocodec: unregister '%s', type: %x, flags %lx, magic %lx\n", - codec->name, codec->type, codec->flags, codec->magic); - - if (!h) { - dprintk(1, - KERN_ERR - "videocodec_unregister: no device left...\n"); - return -ENXIO; - } - - while (h) { - if (codec == h->codec) { - if (h->attached) { - dprintk(1, - KERN_ERR - "videocodec: '%s' is used\n", - h->codec->name); - return -EBUSY; - } - dprintk(3, "videocodec: unregister '%s' is ok.\n", - h->codec->name); - if (prev == NULL) { - codeclist_top = h->next; - dprintk(4, - "videocodec: delete first element\n"); - } else { - prev->next = h->next; - dprintk(4, - "videocodec: delete middle element\n"); - } - kfree(h); - return 0; - } - prev = h; - h = h->next; - } - - dprintk(1, - KERN_ERR - "videocodec_unregister: given codec not found!\n"); - return -EINVAL; -} - -#ifdef CONFIG_PROC_FS -static int proc_videocodecs_show(struct seq_file *m, void *v) -{ - struct codec_list *h = codeclist_top; - struct attached_list *a; - - seq_printf(m, "<S>lave or attached <M>aster name type flags magic "); - seq_printf(m, "(connected as)\n"); - - while (h) { - seq_printf(m, "S %32s %04x %08lx %08lx (TEMPLATE)\n", - h->codec->name, h->codec->type, - h->codec->flags, h->codec->magic); - a = h->list; - while (a) { - seq_printf(m, "M %32s %04x %08lx %08lx (%s)\n", - a->codec->master_data->name, - a->codec->master_data->type, - a->codec->master_data->flags, - a->codec->master_data->magic, - a->codec->name); - a = a->next; - } - h = h->next; - } - - return 0; -} -#endif - -/* ===================== */ -/* hook in driver module */ -/* ===================== */ -static int __init -videocodec_init (void) -{ -#ifdef CONFIG_PROC_FS - static struct proc_dir_entry *videocodec_proc_entry; -#endif - - printk(KERN_INFO "Linux video codec intermediate layer: %s\n", - VIDEOCODEC_VERSION); - -#ifdef CONFIG_PROC_FS - videocodec_proc_entry = proc_create_single("videocodecs", 0, NULL, - proc_videocodecs_show); - if (!videocodec_proc_entry) { - dprintk(1, KERN_ERR "videocodec: can't init procfs.\n"); - } -#endif - return 0; -} - -static void __exit -videocodec_exit (void) -{ -#ifdef CONFIG_PROC_FS - remove_proc_entry("videocodecs", NULL); -#endif -} - -EXPORT_SYMBOL(videocodec_attach); -EXPORT_SYMBOL(videocodec_detach); -EXPORT_SYMBOL(videocodec_register); -EXPORT_SYMBOL(videocodec_unregister); - -module_init(videocodec_init); -module_exit(videocodec_exit); - -MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>"); -MODULE_DESCRIPTION("Intermediate API module for video codecs " - VIDEOCODEC_VERSION); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/zoran/videocodec.h b/drivers/media/pci/zoran/videocodec.h deleted file mode 100644 index 8ed5a0f7ac01..000000000000 --- a/drivers/media/pci/zoran/videocodec.h +++ /dev/null @@ -1,349 +0,0 @@ -/* - * VIDEO MOTION CODECs internal API for video devices - * - * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's - * bound to a master device. - * - * (c) 2002 Wolfgang Scherr <scherr@net4you.at> - * - * $Id: videocodec.h,v 1.1.2.4 2003/01/14 21:15:03 rbultje Exp $ - * - * ------------------------------------------------------------------------ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * ------------------------------------------------------------------------ - */ - -/* =================== */ -/* general description */ -/* =================== */ - -/* Should ease the (re-)usage of drivers supporting cards with (different) - video codecs. The codecs register to this module their functionality, - and the processors (masters) can attach to them if they fit. - - The codecs are typically have a "strong" binding to their master - so I - don't think it makes sense to have a full blown interfacing as with e.g. - i2c. If you have an other opinion, let's discuss & implement it :-))) - - Usage: - - The slave has just to setup the videocodec structure and use two functions: - videocodec_register(codecdata); - videocodec_unregister(codecdata); - The best is just calling them at module (de-)initialisation. - - The master sets up the structure videocodec_master and calls: - codecdata=videocodec_attach(master_codecdata); - videocodec_detach(codecdata); - - The slave is called during attach/detach via functions setup previously - during register. At that time, the master_data pointer is set up - and the slave can access any io registers of the master device (in the case - the slave is bound to it). Otherwise it doesn't need this functions and - therfor they may not be initialized. - - The other functions are just for convenience, as they are for sure used by - most/all of the codecs. The last ones may be omitted, too. - - See the structure declaration below for more information and which data has - to be set up for the master and the slave. - - ---------------------------------------------------------------------------- - The master should have "knowledge" of the slave and vice versa. So the data - structures sent to/from slave via set_data/get_data set_image/get_image are - device dependent and vary between MJPEG/MPEG/WAVELET/... devices. (!!!!) - ---------------------------------------------------------------------------- -*/ - - -/* ========================================== */ -/* description of the videocodec_io structure */ -/* ========================================== */ - -/* - ==== master setup ==== - name -> name of the device structure for reference and debugging - master_data -> data ref. for the master (e.g. the zr36055,57,67) - readreg -> ref. to read-fn from register (setup by master, used by slave) - writereg -> ref. to write-fn to register (setup by master, used by slave) - this two functions do the lowlevel I/O job - - ==== slave functionality setup ==== - slave_data -> data ref. for the slave (e.g. the zr36050,60) - check -> fn-ref. checks availability of an device, returns -EIO on failure or - the type on success - this makes espcecially sense if a driver module supports more than - one codec which may be quite similar to access, nevertheless it - is good for a first functionality check - - -- main functions you always need for compression/decompression -- - - set_mode -> this fn-ref. resets the entire codec, and sets up the mode - with the last defined norm/size (or device default if not - available) - it returns 0 if the mode is possible - set_size -> this fn-ref. sets the norm and image size for - compression/decompression (returns 0 on success) - the norm param is defined in videodev2.h (V4L2_STD_*) - - additional setup may be available, too - but the codec should work with - some default values even without this - - set_data -> sets device-specific data (tables, quality etc.) - get_data -> query device-specific data (tables, quality etc.) - - if the device delivers interrupts, they may be setup/handled here - setup_interrupt -> codec irq setup (not needed for 36050/60) - handle_interrupt -> codec irq handling (not needed for 36050/60) - - if the device delivers pictures, they may be handled here - put_image -> puts image data to the codec (not needed for 36050/60) - get_image -> gets image data from the codec (not needed for 36050/60) - the calls include frame numbers and flags (even/odd/...) - if needed and a flag which allows blocking until its ready -*/ - -/* ============== */ -/* user interface */ -/* ============== */ - -/* - Currently there is only a information display planned, as the layer - is not visible for the user space at all. - - Information is available via procfs. The current entry is "/proc/videocodecs" - but it makes sense to "hide" it in the /proc/video tree of v4l(2) --TODO--. - -A example for such an output is: - -<S>lave or attached <M>aster name type flags magic (connected as) -S zr36050 0002 0000d001 00000000 (TEMPLATE) -M zr36055[0] 0001 0000c001 00000000 (zr36050[0]) -M zr36055[1] 0001 0000c001 00000000 (zr36050[1]) - -*/ - - -/* =============================================== */ -/* special defines for the videocodec_io structure */ -/* =============================================== */ - -#ifndef __LINUX_VIDEOCODEC_H -#define __LINUX_VIDEOCODEC_H - -#include <linux/videodev2.h> - -#define CODEC_DO_COMPRESSION 0 -#define CODEC_DO_EXPANSION 1 - -/* this are the current codec flags I think they are needed */ -/* -> type value in structure */ -#define CODEC_FLAG_JPEG 0x00000001L // JPEG codec -#define CODEC_FLAG_MPEG 0x00000002L // MPEG1/2/4 codec -#define CODEC_FLAG_DIVX 0x00000004L // DIVX codec -#define CODEC_FLAG_WAVELET 0x00000008L // WAVELET codec - // room for other types - -#define CODEC_FLAG_MAGIC 0x00000800L // magic key must match -#define CODEC_FLAG_HARDWARE 0x00001000L // is a hardware codec -#define CODEC_FLAG_VFE 0x00002000L // has direct video frontend -#define CODEC_FLAG_ENCODER 0x00004000L // compression capability -#define CODEC_FLAG_DECODER 0x00008000L // decompression capability -#define CODEC_FLAG_NEEDIRQ 0x00010000L // needs irq handling -#define CODEC_FLAG_RDWRPIC 0x00020000L // handles picture I/O - -/* a list of modes, some are just examples (is there any HW?) */ -#define CODEC_MODE_BJPG 0x0001 // Baseline JPEG -#define CODEC_MODE_LJPG 0x0002 // Lossless JPEG -#define CODEC_MODE_MPEG1 0x0003 // MPEG 1 -#define CODEC_MODE_MPEG2 0x0004 // MPEG 2 -#define CODEC_MODE_MPEG4 0x0005 // MPEG 4 -#define CODEC_MODE_MSDIVX 0x0006 // MS DivX -#define CODEC_MODE_ODIVX 0x0007 // Open DivX -#define CODEC_MODE_WAVELET 0x0008 // Wavelet - -/* this are the current codec types I want to implement */ -/* -> type value in structure */ -#define CODEC_TYPE_NONE 0 -#define CODEC_TYPE_L64702 1 -#define CODEC_TYPE_ZR36050 2 -#define CODEC_TYPE_ZR36016 3 -#define CODEC_TYPE_ZR36060 4 - -/* the type of data may be enhanced by future implementations (data-fn.'s) */ -/* -> used in command */ -#define CODEC_G_STATUS 0x0000 /* codec status (query only) */ -#define CODEC_S_CODEC_MODE 0x0001 /* codec mode (baseline JPEG, MPEG1,... */ -#define CODEC_G_CODEC_MODE 0x8001 -#define CODEC_S_VFE 0x0002 /* additional video frontend setup */ -#define CODEC_G_VFE 0x8002 -#define CODEC_S_MMAP 0x0003 /* MMAP setup (if available) */ - -#define CODEC_S_JPEG_TDS_BYTE 0x0010 /* target data size in bytes */ -#define CODEC_G_JPEG_TDS_BYTE 0x8010 -#define CODEC_S_JPEG_SCALE 0x0011 /* scaling factor for quant. tables */ -#define CODEC_G_JPEG_SCALE 0x8011 -#define CODEC_S_JPEG_HDT_DATA 0x0018 /* huffman-tables */ -#define CODEC_G_JPEG_HDT_DATA 0x8018 -#define CODEC_S_JPEG_QDT_DATA 0x0019 /* quantizing-tables */ -#define CODEC_G_JPEG_QDT_DATA 0x8019 -#define CODEC_S_JPEG_APP_DATA 0x001A /* APP marker */ -#define CODEC_G_JPEG_APP_DATA 0x801A -#define CODEC_S_JPEG_COM_DATA 0x001B /* COM marker */ -#define CODEC_G_JPEG_COM_DATA 0x801B - -#define CODEC_S_PRIVATE 0x1000 /* "private" commands start here */ -#define CODEC_G_PRIVATE 0x9000 - -#define CODEC_G_FLAG 0x8000 /* this is how 'get' is detected */ - -/* types of transfer, directly user space or a kernel buffer (image-fn.'s) */ -/* -> used in get_image, put_image */ -#define CODEC_TRANSFER_KERNEL 0 /* use "memcopy" */ -#define CODEC_TRANSFER_USER 1 /* use "to/from_user" */ - - -/* ========================= */ -/* the structures itself ... */ -/* ========================= */ - -struct vfe_polarity { - unsigned int vsync_pol:1; - unsigned int hsync_pol:1; - unsigned int field_pol:1; - unsigned int blank_pol:1; - unsigned int subimg_pol:1; - unsigned int poe_pol:1; - unsigned int pvalid_pol:1; - unsigned int vclk_pol:1; -}; - -struct vfe_settings { - __u32 x, y; /* Offsets into image */ - __u32 width, height; /* Area to capture */ - __u16 decimation; /* Decimation divider */ - __u16 flags; /* Flags for capture */ - __u16 quality; /* quality of the video */ -}; - -struct tvnorm { - u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart; -}; - -struct jpeg_com_marker { - int len; /* number of usable bytes in data */ - char data[60]; -}; - -struct jpeg_app_marker { - int appn; /* number app segment */ - int len; /* number of usable bytes in data */ - char data[60]; -}; - -struct videocodec { - struct module *owner; - /* -- filled in by slave device during register -- */ - char name[32]; - unsigned long magic; /* may be used for client<->master attaching */ - unsigned long flags; /* functionality flags */ - unsigned int type; /* codec type */ - - /* -- these is filled in later during master device attach -- */ - - struct videocodec_master *master_data; - - /* -- these are filled in by the slave device during register -- */ - - void *data; /* private slave data */ - - /* attach/detach client functions (indirect call) */ - int (*setup) (struct videocodec * codec); - int (*unset) (struct videocodec * codec); - - /* main functions, every client needs them for sure! */ - // set compression or decompression (or freeze, stop, standby, etc) - int (*set_mode) (struct videocodec * codec, - int mode); - // setup picture size and norm (for the codec's video frontend) - int (*set_video) (struct videocodec * codec, - struct tvnorm * norm, - struct vfe_settings * cap, - struct vfe_polarity * pol); - // other control commands, also mmap setup etc. - int (*control) (struct videocodec * codec, - int type, - int size, - void *data); - - /* additional setup/query/processing (may be NULL pointer) */ - // interrupt setup / handling (for irq's delivered by master) - int (*setup_interrupt) (struct videocodec * codec, - long mode); - int (*handle_interrupt) (struct videocodec * codec, - int source, - long flag); - // picture interface (if any) - long (*put_image) (struct videocodec * codec, - int tr_type, - int block, - long *fr_num, - long *flag, - long size, - void *buf); - long (*get_image) (struct videocodec * codec, - int tr_type, - int block, - long *fr_num, - long *flag, - long size, - void *buf); -}; - -struct videocodec_master { - /* -- filled in by master device for registration -- */ - char name[32]; - unsigned long magic; /* may be used for client<->master attaching */ - unsigned long flags; /* functionality flags */ - unsigned int type; /* master type */ - - void *data; /* private master data */ - - __u32(*readreg) (struct videocodec * codec, - __u16 reg); - void (*writereg) (struct videocodec * codec, - __u16 reg, - __u32 value); -}; - - -/* ================================================= */ -/* function prototypes of the master/slave interface */ -/* ================================================= */ - -/* attach and detach commands for the master */ -// * master structure needs to be kmalloc'ed before calling attach -// and free'd after calling detach -// * returns pointer on success, NULL on failure -extern struct videocodec *videocodec_attach(struct videocodec_master *); -// * 0 on success, <0 (errno) on failure -extern int videocodec_detach(struct videocodec *); - -/* register and unregister commands for the slaves */ -// * 0 on success, <0 (errno) on failure -extern int videocodec_register(const struct videocodec *); -// * 0 on success, <0 (errno) on failure -extern int videocodec_unregister(const struct videocodec *); - -/* the other calls are directly done via the videocodec structure! */ - -#endif /*ifndef __LINUX_VIDEOCODEC_H */ diff --git a/drivers/media/pci/zoran/zoran.h b/drivers/media/pci/zoran/zoran.h deleted file mode 100644 index 9bb3c21aa275..000000000000 --- a/drivers/media/pci/zoran/zoran.h +++ /dev/null @@ -1,402 +0,0 @@ -/* - * zoran - Iomega Buz driver - * - * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de> - * - * based on - * - * zoran.0.0.3 Copyright (C) 1998 Dave Perks <dperks@ibm.net> - * - * and - * - * bttv - Bt848 frame grabber driver - * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) - * & Marcus Metzler (mocm@thp.uni-koeln.de) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _BUZ_H_ -#define _BUZ_H_ - -#include <media/v4l2-device.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-fh.h> - -struct zoran_sync { - unsigned long frame; /* number of buffer that has been free'd */ - unsigned long length; /* number of code bytes in buffer (capture only) */ - unsigned long seq; /* frame sequence number */ - struct timeval timestamp; /* timestamp */ -}; - - -#define ZORAN_NAME "ZORAN" /* name of the device */ - -#define ZR_DEVNAME(zr) ((zr)->name) - -#define BUZ_MAX_WIDTH (zr->timing->Wa) -#define BUZ_MAX_HEIGHT (zr->timing->Ha) -#define BUZ_MIN_WIDTH 32 /* never display less than 32 pixels */ -#define BUZ_MIN_HEIGHT 24 /* never display less than 24 rows */ - -#define BUZ_NUM_STAT_COM 4 -#define BUZ_MASK_STAT_COM 3 - -#define BUZ_MAX_FRAME 256 /* Must be a power of 2 */ -#define BUZ_MASK_FRAME 255 /* Must be BUZ_MAX_FRAME-1 */ - -#define BUZ_MAX_INPUT 16 - -#if VIDEO_MAX_FRAME <= 32 -# define V4L_MAX_FRAME 32 -#elif VIDEO_MAX_FRAME <= 64 -# define V4L_MAX_FRAME 64 -#else -# error "Too many video frame buffers to handle" -#endif -#define V4L_MASK_FRAME (V4L_MAX_FRAME - 1) - -#define MAX_FRAME (BUZ_MAX_FRAME > VIDEO_MAX_FRAME ? BUZ_MAX_FRAME : VIDEO_MAX_FRAME) - -#include "zr36057.h" - -enum card_type { - UNKNOWN = -1, - - /* Pinnacle/Miro */ - DC10_old, /* DC30 like */ - DC10_new, /* DC10plus like */ - DC10plus, - DC30, - DC30plus, - - /* Linux Media Labs */ - LML33, - LML33R10, - - /* Iomega */ - BUZ, - - /* AverMedia */ - AVS6EYES, - - /* total number of cards */ - NUM_CARDS -}; - -enum zoran_codec_mode { - BUZ_MODE_IDLE, /* nothing going on */ - BUZ_MODE_MOTION_COMPRESS, /* grabbing frames */ - BUZ_MODE_MOTION_DECOMPRESS, /* playing frames */ - BUZ_MODE_STILL_COMPRESS, /* still frame conversion */ - BUZ_MODE_STILL_DECOMPRESS /* still frame conversion */ -}; - -enum zoran_buffer_state { - BUZ_STATE_USER, /* buffer is owned by application */ - BUZ_STATE_PEND, /* buffer is queued in pend[] ready to feed to I/O */ - BUZ_STATE_DMA, /* buffer is queued in dma[] for I/O */ - BUZ_STATE_DONE /* buffer is ready to return to application */ -}; - -enum zoran_map_mode { - ZORAN_MAP_MODE_RAW, - ZORAN_MAP_MODE_JPG_REC, -#define ZORAN_MAP_MODE_JPG ZORAN_MAP_MODE_JPG_REC - ZORAN_MAP_MODE_JPG_PLAY, -}; - -enum gpio_type { - ZR_GPIO_JPEG_SLEEP = 0, - ZR_GPIO_JPEG_RESET, - ZR_GPIO_JPEG_FRAME, - ZR_GPIO_VID_DIR, - ZR_GPIO_VID_EN, - ZR_GPIO_VID_RESET, - ZR_GPIO_CLK_SEL1, - ZR_GPIO_CLK_SEL2, - ZR_GPIO_MAX, -}; - -enum gpcs_type { - GPCS_JPEG_RESET = 0, - GPCS_JPEG_START, - GPCS_MAX, -}; - -struct zoran_format { - char *name; - __u32 fourcc; - int colorspace; - int depth; - __u32 flags; - __u32 vfespfr; -}; -/* flags */ -#define ZORAN_FORMAT_COMPRESSED 1<<0 -#define ZORAN_FORMAT_OVERLAY 1<<1 -#define ZORAN_FORMAT_CAPTURE 1<<2 -#define ZORAN_FORMAT_PLAYBACK 1<<3 - -/* overlay-settings */ -struct zoran_overlay_settings { - int is_set; - int x, y, width, height; /* position */ - int clipcount; /* position and number of clips */ - const struct zoran_format *format; /* overlay format */ -}; - -/* v4l-capture settings */ -struct zoran_v4l_settings { - int width, height, bytesperline; /* capture size */ - const struct zoran_format *format; /* capture format */ -}; - -/* jpg-capture/-playback settings */ -struct zoran_jpg_settings { - int decimation; /* this bit is used to set everything to default */ - int HorDcm, VerDcm, TmpDcm; /* capture decimation settings (TmpDcm=1 means both fields) */ - int field_per_buff, odd_even; /* field-settings (odd_even=1 (+TmpDcm=1) means top-field-first) */ - int img_x, img_y, img_width, img_height; /* crop settings (subframe capture) */ - struct v4l2_jpegcompression jpg_comp; /* JPEG-specific capture settings */ -}; - -struct zoran_fh; - -struct zoran_mapping { - struct zoran_fh *fh; - atomic_t count; -}; - -struct zoran_buffer { - struct zoran_mapping *map; - enum zoran_buffer_state state; /* state: unused/pending/dma/done */ - struct zoran_sync bs; /* DONE: info to return to application */ - union { - struct { - __le32 *frag_tab; /* addresses of frag table */ - u32 frag_tab_bus; /* same value cached to save time in ISR */ - } jpg; - struct { - char *fbuffer; /* virtual address of frame buffer */ - unsigned long fbuffer_phys;/* physical address of frame buffer */ - unsigned long fbuffer_bus;/* bus address of frame buffer */ - } v4l; - }; -}; - -enum zoran_lock_activity { - ZORAN_FREE, /* free for use */ - ZORAN_ACTIVE, /* active but unlocked */ - ZORAN_LOCKED, /* locked */ -}; - -/* buffer collections */ -struct zoran_buffer_col { - enum zoran_lock_activity active; /* feature currently in use? */ - unsigned int num_buffers, buffer_size; - struct zoran_buffer buffer[MAX_FRAME]; /* buffers */ - u8 allocated; /* Flag if buffers are allocated */ - u8 need_contiguous; /* Flag if contiguous buffers are needed */ - /* only applies to jpg buffers, raw buffers are always contiguous */ -}; - -struct zoran; - -/* zoran_fh contains per-open() settings */ -struct zoran_fh { - struct v4l2_fh fh; - struct zoran *zr; - - enum zoran_map_mode map_mode; /* Flag which bufferset will map by next mmap() */ - - struct zoran_overlay_settings overlay_settings; - u32 *overlay_mask; /* overlay mask */ - enum zoran_lock_activity overlay_active;/* feature currently in use? */ - - struct zoran_buffer_col buffers; /* buffers' info */ - - struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */ - struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */ -}; - -struct card_info { - enum card_type type; - char name[32]; - const char *i2c_decoder; /* i2c decoder device */ - const unsigned short *addrs_decoder; - const char *i2c_encoder; /* i2c encoder device */ - const unsigned short *addrs_encoder; - u16 video_vfe, video_codec; /* videocodec types */ - u16 audio_chip; /* audio type */ - - int inputs; /* number of video inputs */ - struct input { - int muxsel; - char name[32]; - } input[BUZ_MAX_INPUT]; - - v4l2_std_id norms; - struct tvnorm *tvn[3]; /* supported TV norms */ - - u32 jpeg_int; /* JPEG interrupt */ - u32 vsync_int; /* VSYNC interrupt */ - s8 gpio[ZR_GPIO_MAX]; - u8 gpcs[GPCS_MAX]; - - struct vfe_polarity vfe_pol; - u8 gpio_pol[ZR_GPIO_MAX]; - - /* is the /GWS line connected? */ - u8 gws_not_connected; - - /* avs6eyes mux setting */ - u8 input_mux; - - void (*init) (struct zoran * zr); -}; - -struct zoran { - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler hdl; - struct video_device *video_dev; - - struct i2c_adapter i2c_adapter; /* */ - struct i2c_algo_bit_data i2c_algo; /* */ - u32 i2cbr; - - struct v4l2_subdev *decoder; /* video decoder sub-device */ - struct v4l2_subdev *encoder; /* video encoder sub-device */ - - struct videocodec *codec; /* video codec */ - struct videocodec *vfe; /* video front end */ - - struct mutex lock; /* file ops serialize lock */ - - u8 initialized; /* flag if zoran has been correctly initialized */ - int user; /* number of current users */ - struct card_info card; - struct tvnorm *timing; - - unsigned short id; /* number of this device */ - char name[32]; /* name of this device */ - struct pci_dev *pci_dev; /* PCI device */ - unsigned char revision; /* revision of zr36057 */ - unsigned char __iomem *zr36057_mem;/* pointer to mapped IO memory */ - - spinlock_t spinlock; /* Spinlock */ - - /* Video for Linux parameters */ - int input; /* card's norm and input */ - v4l2_std_id norm; - - /* Current buffer params */ - void *vbuf_base; - int vbuf_height, vbuf_width; - int vbuf_depth; - int vbuf_bytesperline; - - struct zoran_overlay_settings overlay_settings; - u32 *overlay_mask; /* overlay mask */ - enum zoran_lock_activity overlay_active; /* feature currently in use? */ - - wait_queue_head_t v4l_capq; - - int v4l_overlay_active; /* Overlay grab is activated */ - int v4l_memgrab_active; /* Memory grab is activated */ - - int v4l_grab_frame; /* Frame number being currently grabbed */ -#define NO_GRAB_ACTIVE (-1) - unsigned long v4l_grab_seq; /* Number of frames grabbed */ - struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */ - - /* V4L grab queue of frames pending */ - unsigned long v4l_pend_head; - unsigned long v4l_pend_tail; - unsigned long v4l_sync_tail; - int v4l_pend[V4L_MAX_FRAME]; - struct zoran_buffer_col v4l_buffers; /* V4L buffers' info */ - - /* Buz MJPEG parameters */ - enum zoran_codec_mode codec_mode; /* status of codec */ - struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */ - - wait_queue_head_t jpg_capq; /* wait here for grab to finish */ - - /* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */ - /* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */ - /* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */ - unsigned long jpg_que_head; /* Index where to put next buffer which is queued */ - unsigned long jpg_dma_head; /* Index of next buffer which goes into stat_com */ - unsigned long jpg_dma_tail; /* Index of last buffer in stat_com */ - unsigned long jpg_que_tail; /* Index of last buffer in queue */ - unsigned long jpg_seq_num; /* count of frames since grab/play started */ - unsigned long jpg_err_seq; /* last seq_num before error */ - unsigned long jpg_err_shift; - unsigned long jpg_queued_num; /* count of frames queued since grab/play started */ - - /* zr36057's code buffer table */ - __le32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */ - - /* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */ - int jpg_pend[BUZ_MAX_FRAME]; - - /* array indexed by frame number */ - struct zoran_buffer_col jpg_buffers; /* MJPEG buffers' info */ - - /* Additional stuff for testing */ -#ifdef CONFIG_PROC_FS - struct proc_dir_entry *zoran_proc; -#else - void *zoran_proc; -#endif - int testing; - int jpeg_error; - int intr_counter_GIRQ1; - int intr_counter_GIRQ0; - int intr_counter_CodRepIRQ; - int intr_counter_JPEGRepIRQ; - int field_counter; - int IRQ1_in; - int IRQ1_out; - int JPEG_in; - int JPEG_out; - int JPEG_0; - int JPEG_1; - int END_event_missed; - int JPEG_missed; - int JPEG_error; - int num_errors; - int JPEG_max_missed; - int JPEG_min_missed; - - u32 last_isr; - unsigned long frame_num; - - wait_queue_head_t test_q; -}; - -static inline struct zoran *to_zoran(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct zoran, v4l2_dev); -} - -/* There was something called _ALPHA_BUZ that used the PCI address instead of - * the kernel iomapped address for btread/btwrite. */ -#define btwrite(dat,adr) writel((dat), zr->zr36057_mem+(adr)) -#define btread(adr) readl(zr->zr36057_mem+(adr)) - -#define btand(dat,adr) btwrite((dat) & btread(adr), adr) -#define btor(dat,adr) btwrite((dat) | btread(adr), adr) -#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) - -#endif diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c deleted file mode 100644 index a6b9ebd20263..000000000000 --- a/drivers/media/pci/zoran/zoran_card.c +++ /dev/null @@ -1,1524 +0,0 @@ -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * This part handles card-specific data and detection - * - * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> - * - * Currently maintained by: - * Ronald Bultje <rbultje@ronald.bitfreak.net> - * Laurent Pinchart <laurent.pinchart@skynet.be> - * Mailinglist <mjpeg-users@lists.sf.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/delay.h> - -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/vmalloc.h> -#include <linux/slab.h> - -#include <linux/proc_fs.h> -#include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> -#include <linux/videodev2.h> -#include <linux/spinlock.h> -#include <linux/sem.h> -#include <linux/kmod.h> -#include <linux/wait.h> - -#include <linux/pci.h> -#include <linux/interrupt.h> -#include <linux/mutex.h> -#include <linux/io.h> -#include <media/v4l2-common.h> -#include <media/i2c/bt819.h> - -#include "videocodec.h" -#include "zoran.h" -#include "zoran_card.h" -#include "zoran_device.h" -#include "zoran_procfs.h" - -extern const struct zoran_format zoran_formats[]; - -static int card[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 }; -module_param_array(card, int, NULL, 0444); -MODULE_PARM_DESC(card, "Card type"); - -/* - The video mem address of the video card. - The driver has a little database for some videocards - to determine it from there. If your video card is not in there - you have either to give it to the driver as a parameter - or set in in a VIDIOCSFBUF ioctl - */ - -static unsigned long vidmem; /* default = 0 - Video memory base address */ -module_param_hw(vidmem, ulong, iomem, 0444); -MODULE_PARM_DESC(vidmem, "Default video memory base address"); - -/* - Default input and video norm at startup of the driver. -*/ - -static unsigned int default_input; /* default 0 = Composite, 1 = S-Video */ -module_param(default_input, uint, 0444); -MODULE_PARM_DESC(default_input, - "Default input (0=Composite, 1=S-Video, 2=Internal)"); - -static int default_mux = 1; /* 6 Eyes input selection */ -module_param(default_mux, int, 0644); -MODULE_PARM_DESC(default_mux, - "Default 6 Eyes mux setting (Input selection)"); - -static int default_norm; /* default 0 = PAL, 1 = NTSC 2 = SECAM */ -module_param(default_norm, int, 0444); -MODULE_PARM_DESC(default_norm, "Default norm (0=PAL, 1=NTSC, 2=SECAM)"); - -/* /dev/videoN, -1 for autodetect */ -static int video_nr[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 }; -module_param_array(video_nr, int, NULL, 0444); -MODULE_PARM_DESC(video_nr, "Video device number (-1=Auto)"); - -int v4l_nbufs = 4; -int v4l_bufsize = 864; /* Everybody should be able to work with this setting */ -module_param(v4l_nbufs, int, 0644); -MODULE_PARM_DESC(v4l_nbufs, "Maximum number of V4L buffers to use"); -module_param(v4l_bufsize, int, 0644); -MODULE_PARM_DESC(v4l_bufsize, "Maximum size per V4L buffer (in kB)"); - -int jpg_nbufs = 32; -int jpg_bufsize = 512; /* max size for 100% quality full-PAL frame */ -module_param(jpg_nbufs, int, 0644); -MODULE_PARM_DESC(jpg_nbufs, "Maximum number of JPG buffers to use"); -module_param(jpg_bufsize, int, 0644); -MODULE_PARM_DESC(jpg_bufsize, "Maximum size per JPG buffer (in kB)"); - -int pass_through = 0; /* 1=Pass through TV signal when device is not used */ - /* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */ -module_param(pass_through, int, 0644); -MODULE_PARM_DESC(pass_through, - "Pass TV signal through to TV-out when idling"); - -int zr36067_debug = 1; -module_param_named(debug, zr36067_debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-5)"); - -#define ZORAN_VERSION "0.10.1" - -MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver"); -MODULE_AUTHOR("Serguei Miridonov"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(ZORAN_VERSION); - -#define ZR_DEVICE(subven, subdev, data) { \ - .vendor = PCI_VENDOR_ID_ZORAN, .device = PCI_DEVICE_ID_ZORAN_36057, \ - .subvendor = (subven), .subdevice = (subdev), .driver_data = (data) } - -static const struct pci_device_id zr36067_pci_tbl[] = { - ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC10PLUS, DC10plus), - ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC30PLUS, DC30plus), - ZR_DEVICE(PCI_VENDOR_ID_ELECTRONICDESIGNGMBH, PCI_DEVICE_ID_LML_33R10, LML33R10), - ZR_DEVICE(PCI_VENDOR_ID_IOMEGA, PCI_DEVICE_ID_IOMEGA_BUZ, BUZ), - ZR_DEVICE(PCI_ANY_ID, PCI_ANY_ID, NUM_CARDS), - {0} -}; -MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl); - -static unsigned int zoran_num; /* number of cards found */ - -/* videocodec bus functions ZR36060 */ -static u32 -zr36060_read (struct videocodec *codec, - u16 reg) -{ - struct zoran *zr = (struct zoran *) codec->master_data->data; - __u32 data; - - if (post_office_wait(zr) - || post_office_write(zr, 0, 1, reg >> 8) - || post_office_write(zr, 0, 2, reg & 0xff)) { - return -1; - } - - data = post_office_read(zr, 0, 3) & 0xff; - return data; -} - -static void -zr36060_write (struct videocodec *codec, - u16 reg, - u32 val) -{ - struct zoran *zr = (struct zoran *) codec->master_data->data; - - if (post_office_wait(zr) - || post_office_write(zr, 0, 1, reg >> 8) - || post_office_write(zr, 0, 2, reg & 0xff)) { - return; - } - - post_office_write(zr, 0, 3, val & 0xff); -} - -/* videocodec bus functions ZR36050 */ -static u32 -zr36050_read (struct videocodec *codec, - u16 reg) -{ - struct zoran *zr = (struct zoran *) codec->master_data->data; - __u32 data; - - if (post_office_wait(zr) - || post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES - return -1; - } - - data = post_office_read(zr, 0, reg & 0x03) & 0xff; // reg. LOWBYTES + read - return data; -} - -static void -zr36050_write (struct videocodec *codec, - u16 reg, - u32 val) -{ - struct zoran *zr = (struct zoran *) codec->master_data->data; - - if (post_office_wait(zr) - || post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES - return; - } - - post_office_write(zr, 0, reg & 0x03, val & 0xff); // reg. LOWBYTES + wr. data -} - -/* videocodec bus functions ZR36016 */ -static u32 -zr36016_read (struct videocodec *codec, - u16 reg) -{ - struct zoran *zr = (struct zoran *) codec->master_data->data; - __u32 data; - - if (post_office_wait(zr)) { - return -1; - } - - data = post_office_read(zr, 2, reg & 0x03) & 0xff; // read - return data; -} - -/* hack for in zoran_device.c */ -void -zr36016_write (struct videocodec *codec, - u16 reg, - u32 val) -{ - struct zoran *zr = (struct zoran *) codec->master_data->data; - - if (post_office_wait(zr)) { - return; - } - - post_office_write(zr, 2, reg & 0x03, val & 0x0ff); // wr. data -} - -/* - * Board specific information - */ - -static void -dc10_init (struct zoran *zr) -{ - dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); - - /* Pixel clock selection */ - GPIO(zr, 4, 0); - GPIO(zr, 5, 1); - /* Enable the video bus sync signals */ - GPIO(zr, 7, 0); -} - -static void -dc10plus_init (struct zoran *zr) -{ - dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); -} - -static void -buz_init (struct zoran *zr) -{ - dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); - - /* some stuff from Iomega */ - pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15); - pci_write_config_dword(zr->pci_dev, 0x0c, 0x00012020); - pci_write_config_dword(zr->pci_dev, 0xe8, 0xc0200000); -} - -static void -lml33_init (struct zoran *zr) -{ - dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); - - GPIO(zr, 2, 1); // Set Composite input/output -} - -static void -avs6eyes_init (struct zoran *zr) -{ - // AverMedia 6-Eyes original driver by Christer Weinigel - - // Lifted straight from Christer's old driver and - // modified slightly by Martin Samuelsson. - - int mux = default_mux; /* 1 = BT866, 7 = VID1 */ - - GPIO(zr, 4, 1); /* Bt866 SLEEP on */ - udelay(2); - - GPIO(zr, 0, 1); /* ZR36060 /RESET on */ - GPIO(zr, 1, 0); /* ZR36060 /SLEEP on */ - GPIO(zr, 2, mux & 1); /* MUX S0 */ - GPIO(zr, 3, 0); /* /FRAME on */ - GPIO(zr, 4, 0); /* Bt866 SLEEP off */ - GPIO(zr, 5, mux & 2); /* MUX S1 */ - GPIO(zr, 6, 0); /* ? */ - GPIO(zr, 7, mux & 4); /* MUX S2 */ - -} - -static char * -codecid_to_modulename (u16 codecid) -{ - char *name = NULL; - - switch (codecid) { - case CODEC_TYPE_ZR36060: - name = "zr36060"; - break; - case CODEC_TYPE_ZR36050: - name = "zr36050"; - break; - case CODEC_TYPE_ZR36016: - name = "zr36016"; - break; - } - - return name; -} - -// struct tvnorm { -// u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart; -// }; - -static struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 }; -static struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 }; -static struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 }; -static struct tvnorm f60ccir601 = { 858, 720, 57, 788, 525, 480, 16 }; - -static struct tvnorm f50ccir601_lml33 = { 864, 720, 75+34, 804, 625, 576, 18 }; -static struct tvnorm f60ccir601_lml33 = { 858, 720, 57+34, 788, 525, 480, 16 }; - -/* The DC10 (57/16/50) uses VActive as HSync, so HStart must be 0 */ -static struct tvnorm f50sqpixel_dc10 = { 944, 768, 0, 880, 625, 576, 0 }; -static struct tvnorm f60sqpixel_dc10 = { 780, 640, 0, 716, 525, 480, 12 }; - -/* FIXME: I cannot swap U and V in saa7114, so i do one - * pixel left shift in zoran (75 -> 74) - * (Maxim Yevtyushkin <max@linuxmedialabs.com>) */ -static struct tvnorm f50ccir601_lm33r10 = { 864, 720, 74+54, 804, 625, 576, 18 }; -static struct tvnorm f60ccir601_lm33r10 = { 858, 720, 56+54, 788, 525, 480, 16 }; - -/* FIXME: The ks0127 seem incapable of swapping U and V, too, which is why I - * copy Maxim's left shift hack for the 6 Eyes. - * - * Christer's driver used the unshifted norms, though... - * /Sam */ -static struct tvnorm f50ccir601_avs6eyes = { 864, 720, 74, 804, 625, 576, 18 }; -static struct tvnorm f60ccir601_avs6eyes = { 858, 720, 56, 788, 525, 480, 16 }; - -static const unsigned short vpx3220_addrs[] = { 0x43, 0x47, I2C_CLIENT_END }; -static const unsigned short saa7110_addrs[] = { 0x4e, 0x4f, I2C_CLIENT_END }; -static const unsigned short saa7111_addrs[] = { 0x25, 0x24, I2C_CLIENT_END }; -static const unsigned short saa7114_addrs[] = { 0x21, 0x20, I2C_CLIENT_END }; -static const unsigned short adv717x_addrs[] = { 0x6a, 0x6b, 0x2a, 0x2b, I2C_CLIENT_END }; -static const unsigned short ks0127_addrs[] = { 0x6c, 0x6d, I2C_CLIENT_END }; -static const unsigned short saa7185_addrs[] = { 0x44, I2C_CLIENT_END }; -static const unsigned short bt819_addrs[] = { 0x45, I2C_CLIENT_END }; -static const unsigned short bt856_addrs[] = { 0x44, I2C_CLIENT_END }; -static const unsigned short bt866_addrs[] = { 0x44, I2C_CLIENT_END }; - -static struct card_info zoran_cards[NUM_CARDS] = { - { - .type = DC10_old, - .name = "DC10(old)", - .i2c_decoder = "vpx3220a", - .addrs_decoder = vpx3220_addrs, - .video_codec = CODEC_TYPE_ZR36050, - .video_vfe = CODEC_TYPE_ZR36016, - - .inputs = 3, - .input = { - { 1, "Composite" }, - { 2, "S-Video" }, - { 0, "Internal/comp" } - }, - .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, - .tvn = { - &f50sqpixel_dc10, - &f60sqpixel_dc10, - &f50sqpixel_dc10 - }, - .jpeg_int = 0, - .vsync_int = ZR36057_ISR_GIRQ1, - .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, - .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, - .gpcs = { -1, 0 }, - .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gws_not_connected = 0, - .input_mux = 0, - .init = &dc10_init, - }, { - .type = DC10_new, - .name = "DC10(new)", - .i2c_decoder = "saa7110", - .addrs_decoder = saa7110_addrs, - .i2c_encoder = "adv7175", - .addrs_encoder = adv717x_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 3, - .input = { - { 0, "Composite" }, - { 7, "S-Video" }, - { 5, "Internal/comp" } - }, - .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, - .tvn = { - &f50sqpixel, - &f60sqpixel, - &f50sqpixel}, - .jpeg_int = ZR36057_ISR_GIRQ0, - .vsync_int = ZR36057_ISR_GIRQ1, - .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 }, - .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gpcs = { -1, 1}, - .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 }, - .gws_not_connected = 0, - .input_mux = 0, - .init = &dc10plus_init, - }, { - .type = DC10plus, - .name = "DC10plus", - .i2c_decoder = "saa7110", - .addrs_decoder = saa7110_addrs, - .i2c_encoder = "adv7175", - .addrs_encoder = adv717x_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 3, - .input = { - { 0, "Composite" }, - { 7, "S-Video" }, - { 5, "Internal/comp" } - }, - .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, - .tvn = { - &f50sqpixel, - &f60sqpixel, - &f50sqpixel - }, - .jpeg_int = ZR36057_ISR_GIRQ0, - .vsync_int = ZR36057_ISR_GIRQ1, - .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 }, - .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gpcs = { -1, 1 }, - .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 }, - .gws_not_connected = 0, - .input_mux = 0, - .init = &dc10plus_init, - }, { - .type = DC30, - .name = "DC30", - .i2c_decoder = "vpx3220a", - .addrs_decoder = vpx3220_addrs, - .i2c_encoder = "adv7175", - .addrs_encoder = adv717x_addrs, - .video_codec = CODEC_TYPE_ZR36050, - .video_vfe = CODEC_TYPE_ZR36016, - - .inputs = 3, - .input = { - { 1, "Composite" }, - { 2, "S-Video" }, - { 0, "Internal/comp" } - }, - .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, - .tvn = { - &f50sqpixel_dc10, - &f60sqpixel_dc10, - &f50sqpixel_dc10 - }, - .jpeg_int = 0, - .vsync_int = ZR36057_ISR_GIRQ1, - .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, - .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, - .gpcs = { -1, 0 }, - .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gws_not_connected = 0, - .input_mux = 0, - .init = &dc10_init, - }, { - .type = DC30plus, - .name = "DC30plus", - .i2c_decoder = "vpx3220a", - .addrs_decoder = vpx3220_addrs, - .i2c_encoder = "adv7175", - .addrs_encoder = adv717x_addrs, - .video_codec = CODEC_TYPE_ZR36050, - .video_vfe = CODEC_TYPE_ZR36016, - - .inputs = 3, - .input = { - { 1, "Composite" }, - { 2, "S-Video" }, - { 0, "Internal/comp" } - }, - .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, - .tvn = { - &f50sqpixel_dc10, - &f60sqpixel_dc10, - &f50sqpixel_dc10 - }, - .jpeg_int = 0, - .vsync_int = ZR36057_ISR_GIRQ1, - .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, - .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, - .gpcs = { -1, 0 }, - .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gws_not_connected = 0, - .input_mux = 0, - .init = &dc10_init, - }, { - .type = LML33, - .name = "LML33", - .i2c_decoder = "bt819a", - .addrs_decoder = bt819_addrs, - .i2c_encoder = "bt856", - .addrs_encoder = bt856_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 2, - .input = { - { 0, "Composite" }, - { 7, "S-Video" } - }, - .norms = V4L2_STD_NTSC|V4L2_STD_PAL, - .tvn = { - &f50ccir601_lml33, - &f60ccir601_lml33, - NULL - }, - .jpeg_int = ZR36057_ISR_GIRQ1, - .vsync_int = ZR36057_ISR_GIRQ0, - .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 }, - .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 }, - .gpcs = { 3, 1 }, - .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, - .gws_not_connected = 1, - .input_mux = 0, - .init = &lml33_init, - }, { - .type = LML33R10, - .name = "LML33R10", - .i2c_decoder = "saa7114", - .addrs_decoder = saa7114_addrs, - .i2c_encoder = "adv7170", - .addrs_encoder = adv717x_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 2, - .input = { - { 0, "Composite" }, - { 7, "S-Video" } - }, - .norms = V4L2_STD_NTSC|V4L2_STD_PAL, - .tvn = { - &f50ccir601_lm33r10, - &f60ccir601_lm33r10, - NULL - }, - .jpeg_int = ZR36057_ISR_GIRQ1, - .vsync_int = ZR36057_ISR_GIRQ0, - .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 }, - .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 }, - .gpcs = { 3, 1 }, - .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, - .gws_not_connected = 1, - .input_mux = 0, - .init = &lml33_init, - }, { - .type = BUZ, - .name = "Buz", - .i2c_decoder = "saa7111", - .addrs_decoder = saa7111_addrs, - .i2c_encoder = "saa7185", - .addrs_encoder = saa7185_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 2, - .input = { - { 3, "Composite" }, - { 7, "S-Video" } - }, - .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, - .tvn = { - &f50ccir601, - &f60ccir601, - &f50ccir601 - }, - .jpeg_int = ZR36057_ISR_GIRQ1, - .vsync_int = ZR36057_ISR_GIRQ0, - .gpio = { 1, -1, 3, -1, -1, -1, -1, -1 }, - .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gpcs = { 3, 1 }, - .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, - .gws_not_connected = 1, - .input_mux = 0, - .init = &buz_init, - }, { - .type = AVS6EYES, - .name = "6-Eyes", - /* AverMedia chose not to brand the 6-Eyes. Thus it - can't be autodetected, and requires card=x. */ - .i2c_decoder = "ks0127", - .addrs_decoder = ks0127_addrs, - .i2c_encoder = "bt866", - .addrs_encoder = bt866_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 10, - .input = { - { 0, "Composite 1" }, - { 1, "Composite 2" }, - { 2, "Composite 3" }, - { 4, "Composite 4" }, - { 5, "Composite 5" }, - { 6, "Composite 6" }, - { 8, "S-Video 1" }, - { 9, "S-Video 2" }, - {10, "S-Video 3" }, - {15, "YCbCr" } - }, - .norms = V4L2_STD_NTSC|V4L2_STD_PAL, - .tvn = { - &f50ccir601_avs6eyes, - &f60ccir601_avs6eyes, - NULL - }, - .jpeg_int = ZR36057_ISR_GIRQ1, - .vsync_int = ZR36057_ISR_GIRQ0, - .gpio = { 1, 0, 3, -1, -1, -1, -1, -1 },// Validity unknown /Sam - .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, // Validity unknown /Sam - .gpcs = { 3, 1 }, // Validity unknown /Sam - .vfe_pol = { 1, 0, 0, 0, 0, 1, 0, 0 }, // Validity unknown /Sam - .gws_not_connected = 1, - .input_mux = 1, - .init = &avs6eyes_init, - } - -}; - -/* - * I2C functions - */ -/* software I2C functions */ -static int -zoran_i2c_getsda (void *data) -{ - struct zoran *zr = (struct zoran *) data; - - return (btread(ZR36057_I2CBR) >> 1) & 1; -} - -static int -zoran_i2c_getscl (void *data) -{ - struct zoran *zr = (struct zoran *) data; - - return btread(ZR36057_I2CBR) & 1; -} - -static void -zoran_i2c_setsda (void *data, - int state) -{ - struct zoran *zr = (struct zoran *) data; - - if (state) - zr->i2cbr |= 2; - else - zr->i2cbr &= ~2; - btwrite(zr->i2cbr, ZR36057_I2CBR); -} - -static void -zoran_i2c_setscl (void *data, - int state) -{ - struct zoran *zr = (struct zoran *) data; - - if (state) - zr->i2cbr |= 1; - else - zr->i2cbr &= ~1; - btwrite(zr->i2cbr, ZR36057_I2CBR); -} - -static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = { - .setsda = zoran_i2c_setsda, - .setscl = zoran_i2c_setscl, - .getsda = zoran_i2c_getsda, - .getscl = zoran_i2c_getscl, - .udelay = 10, - .timeout = 100, -}; - -static int -zoran_register_i2c (struct zoran *zr) -{ - zr->i2c_algo = zoran_i2c_bit_data_template; - zr->i2c_algo.data = zr; - strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr), - sizeof(zr->i2c_adapter.name)); - i2c_set_adapdata(&zr->i2c_adapter, &zr->v4l2_dev); - zr->i2c_adapter.algo_data = &zr->i2c_algo; - zr->i2c_adapter.dev.parent = &zr->pci_dev->dev; - return i2c_bit_add_bus(&zr->i2c_adapter); -} - -static void -zoran_unregister_i2c (struct zoran *zr) -{ - i2c_del_adapter(&zr->i2c_adapter); -} - -/* Check a zoran_params struct for correctness, insert default params */ - -int -zoran_check_jpg_settings (struct zoran *zr, - struct zoran_jpg_settings *settings, - int try) -{ - int err = 0, err0 = 0; - - dprintk(4, - KERN_DEBUG - "%s: %s - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n", - ZR_DEVNAME(zr), __func__, settings->decimation, settings->HorDcm, - settings->VerDcm, settings->TmpDcm); - dprintk(4, - KERN_DEBUG - "%s: %s - x: %d, y: %d, w: %d, y: %d\n", - ZR_DEVNAME(zr), __func__, settings->img_x, settings->img_y, - settings->img_width, settings->img_height); - /* Check decimation, set default values for decimation = 1, 2, 4 */ - switch (settings->decimation) { - case 1: - - settings->HorDcm = 1; - settings->VerDcm = 1; - settings->TmpDcm = 1; - settings->field_per_buff = 2; - settings->img_x = 0; - settings->img_y = 0; - settings->img_width = BUZ_MAX_WIDTH; - settings->img_height = BUZ_MAX_HEIGHT / 2; - break; - case 2: - - settings->HorDcm = 2; - settings->VerDcm = 1; - settings->TmpDcm = 2; - settings->field_per_buff = 1; - settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; - settings->img_y = 0; - settings->img_width = - (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; - settings->img_height = BUZ_MAX_HEIGHT / 2; - break; - case 4: - - if (zr->card.type == DC10_new) { - dprintk(1, - KERN_DEBUG - "%s: %s - HDec by 4 is not supported on the DC10\n", - ZR_DEVNAME(zr), __func__); - err0++; - break; - } - - settings->HorDcm = 4; - settings->VerDcm = 2; - settings->TmpDcm = 2; - settings->field_per_buff = 1; - settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; - settings->img_y = 0; - settings->img_width = - (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; - settings->img_height = BUZ_MAX_HEIGHT / 2; - break; - case 0: - - /* We have to check the data the user has set */ - - if (settings->HorDcm != 1 && settings->HorDcm != 2 && - (zr->card.type == DC10_new || settings->HorDcm != 4)) { - settings->HorDcm = clamp(settings->HorDcm, 1, 2); - err0++; - } - if (settings->VerDcm != 1 && settings->VerDcm != 2) { - settings->VerDcm = clamp(settings->VerDcm, 1, 2); - err0++; - } - if (settings->TmpDcm != 1 && settings->TmpDcm != 2) { - settings->TmpDcm = clamp(settings->TmpDcm, 1, 2); - err0++; - } - if (settings->field_per_buff != 1 && - settings->field_per_buff != 2) { - settings->field_per_buff = clamp(settings->field_per_buff, 1, 2); - err0++; - } - if (settings->img_x < 0) { - settings->img_x = 0; - err0++; - } - if (settings->img_y < 0) { - settings->img_y = 0; - err0++; - } - if (settings->img_width < 0 || settings->img_width > BUZ_MAX_WIDTH) { - settings->img_width = clamp(settings->img_width, 0, (int)BUZ_MAX_WIDTH); - err0++; - } - if (settings->img_height < 0 || settings->img_height > BUZ_MAX_HEIGHT / 2) { - settings->img_height = clamp(settings->img_height, 0, BUZ_MAX_HEIGHT / 2); - err0++; - } - if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH) { - settings->img_x = BUZ_MAX_WIDTH - settings->img_width; - err0++; - } - if (settings->img_y + settings->img_height > BUZ_MAX_HEIGHT / 2) { - settings->img_y = BUZ_MAX_HEIGHT / 2 - settings->img_height; - err0++; - } - if (settings->img_width % (16 * settings->HorDcm) != 0) { - settings->img_width -= settings->img_width % (16 * settings->HorDcm); - if (settings->img_width == 0) - settings->img_width = 16 * settings->HorDcm; - err0++; - } - if (settings->img_height % (8 * settings->VerDcm) != 0) { - settings->img_height -= settings->img_height % (8 * settings->VerDcm); - if (settings->img_height == 0) - settings->img_height = 8 * settings->VerDcm; - err0++; - } - - if (!try && err0) { - dprintk(1, - KERN_ERR - "%s: %s - error in params for decimation = 0\n", - ZR_DEVNAME(zr), __func__); - err++; - } - break; - default: - dprintk(1, - KERN_ERR - "%s: %s - decimation = %d, must be 0, 1, 2 or 4\n", - ZR_DEVNAME(zr), __func__, settings->decimation); - err++; - break; - } - - if (settings->jpg_comp.quality > 100) - settings->jpg_comp.quality = 100; - if (settings->jpg_comp.quality < 5) - settings->jpg_comp.quality = 5; - if (settings->jpg_comp.APPn < 0) - settings->jpg_comp.APPn = 0; - if (settings->jpg_comp.APPn > 15) - settings->jpg_comp.APPn = 15; - if (settings->jpg_comp.APP_len < 0) - settings->jpg_comp.APP_len = 0; - if (settings->jpg_comp.APP_len > 60) - settings->jpg_comp.APP_len = 60; - if (settings->jpg_comp.COM_len < 0) - settings->jpg_comp.COM_len = 0; - if (settings->jpg_comp.COM_len > 60) - settings->jpg_comp.COM_len = 60; - if (err) - return -EINVAL; - return 0; -} - -void -zoran_open_init_params (struct zoran *zr) -{ - int i; - - /* User must explicitly set a window */ - zr->overlay_settings.is_set = 0; - zr->overlay_mask = NULL; - zr->overlay_active = ZORAN_FREE; - - zr->v4l_memgrab_active = 0; - zr->v4l_overlay_active = 0; - zr->v4l_grab_frame = NO_GRAB_ACTIVE; - zr->v4l_grab_seq = 0; - zr->v4l_settings.width = 192; - zr->v4l_settings.height = 144; - zr->v4l_settings.format = &zoran_formats[7]; /* YUY2 - YUV-4:2:2 packed */ - zr->v4l_settings.bytesperline = - zr->v4l_settings.width * - ((zr->v4l_settings.format->depth + 7) / 8); - - /* DMA ring stuff for V4L */ - zr->v4l_pend_tail = 0; - zr->v4l_pend_head = 0; - zr->v4l_sync_tail = 0; - zr->v4l_buffers.active = ZORAN_FREE; - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ - } - zr->v4l_buffers.allocated = 0; - - for (i = 0; i < BUZ_MAX_FRAME; i++) { - zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ - } - zr->jpg_buffers.active = ZORAN_FREE; - zr->jpg_buffers.allocated = 0; - /* Set necessary params and call zoran_check_jpg_settings to set the defaults */ - zr->jpg_settings.decimation = 1; - zr->jpg_settings.jpg_comp.quality = 50; /* default compression factor 8 */ - if (zr->card.type != BUZ) - zr->jpg_settings.odd_even = 1; - else - zr->jpg_settings.odd_even = 0; - zr->jpg_settings.jpg_comp.APPn = 0; - zr->jpg_settings.jpg_comp.APP_len = 0; /* No APPn marker */ - memset(zr->jpg_settings.jpg_comp.APP_data, 0, - sizeof(zr->jpg_settings.jpg_comp.APP_data)); - zr->jpg_settings.jpg_comp.COM_len = 0; /* No COM marker */ - memset(zr->jpg_settings.jpg_comp.COM_data, 0, - sizeof(zr->jpg_settings.jpg_comp.COM_data)); - zr->jpg_settings.jpg_comp.jpeg_markers = - V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT; - i = zoran_check_jpg_settings(zr, &zr->jpg_settings, 0); - if (i) - dprintk(1, KERN_ERR "%s: %s internal error\n", - ZR_DEVNAME(zr), __func__); - - clear_interrupt_counters(zr); - zr->testing = 0; -} - -static void test_interrupts (struct zoran *zr) -{ - DEFINE_WAIT(wait); - int timeout, icr; - - clear_interrupt_counters(zr); - - zr->testing = 1; - icr = btread(ZR36057_ICR); - btwrite(0x78000000 | ZR36057_ICR_IntPinEn, ZR36057_ICR); - prepare_to_wait(&zr->test_q, &wait, TASK_INTERRUPTIBLE); - timeout = schedule_timeout(HZ); - finish_wait(&zr->test_q, &wait); - btwrite(0, ZR36057_ICR); - btwrite(0x78000000, ZR36057_ISR); - zr->testing = 0; - dprintk(5, KERN_INFO "%s: Testing interrupts...\n", ZR_DEVNAME(zr)); - if (timeout) { - dprintk(1, ": time spent: %d\n", 1 * HZ - timeout); - } - if (zr36067_debug > 1) - print_interrupts(zr); - btwrite(icr, ZR36057_ICR); -} - -static int zr36057_init (struct zoran *zr) -{ - int j, err; - - dprintk(1, - KERN_INFO - "%s: %s - initializing card[%d], zr=%p\n", - ZR_DEVNAME(zr), __func__, zr->id, zr); - - /* default setup of all parameters which will persist between opens */ - zr->user = 0; - - init_waitqueue_head(&zr->v4l_capq); - init_waitqueue_head(&zr->jpg_capq); - init_waitqueue_head(&zr->test_q); - zr->jpg_buffers.allocated = 0; - zr->v4l_buffers.allocated = 0; - - zr->vbuf_base = (void *) vidmem; - zr->vbuf_width = 0; - zr->vbuf_height = 0; - zr->vbuf_depth = 0; - zr->vbuf_bytesperline = 0; - - /* Avoid nonsense settings from user for default input/norm */ - if (default_norm < 0 || default_norm > 2) - default_norm = 0; - if (default_norm == 0) { - zr->norm = V4L2_STD_PAL; - zr->timing = zr->card.tvn[0]; - } else if (default_norm == 1) { - zr->norm = V4L2_STD_NTSC; - zr->timing = zr->card.tvn[1]; - } else { - zr->norm = V4L2_STD_SECAM; - zr->timing = zr->card.tvn[2]; - } - if (zr->timing == NULL) { - dprintk(1, - KERN_WARNING - "%s: %s - default TV standard not supported by hardware. PAL will be used.\n", - ZR_DEVNAME(zr), __func__); - zr->norm = V4L2_STD_PAL; - zr->timing = zr->card.tvn[0]; - } - - if (default_input > zr->card.inputs-1) { - dprintk(1, - KERN_WARNING - "%s: default_input value %d out of range (0-%d)\n", - ZR_DEVNAME(zr), default_input, zr->card.inputs-1); - default_input = 0; - } - zr->input = default_input; - - /* default setup (will be repeated at every open) */ - zoran_open_init_params(zr); - - /* allocate memory *before* doing anything to the hardware - * in case allocation fails */ - zr->stat_com = kzalloc(BUZ_NUM_STAT_COM * 4, GFP_KERNEL); - zr->video_dev = video_device_alloc(); - if (!zr->stat_com || !zr->video_dev) { - dprintk(1, - KERN_ERR - "%s: %s - kmalloc (STAT_COM) failed\n", - ZR_DEVNAME(zr), __func__); - err = -ENOMEM; - goto exit_free; - } - for (j = 0; j < BUZ_NUM_STAT_COM; j++) { - zr->stat_com[j] = cpu_to_le32(1); /* mark as unavailable to zr36057 */ - } - - /* - * Now add the template and register the device unit. - */ - *zr->video_dev = zoran_template; - zr->video_dev->v4l2_dev = &zr->v4l2_dev; - zr->video_dev->lock = &zr->lock; - strcpy(zr->video_dev->name, ZR_DEVNAME(zr)); - /* It's not a mem2mem device, but you can both capture and output from - one and the same device. This should really be split up into two - device nodes, but that's a job for another day. */ - zr->video_dev->vfl_dir = VFL_DIR_M2M; - err = video_register_device(zr->video_dev, VFL_TYPE_GRABBER, video_nr[zr->id]); - if (err < 0) - goto exit_free; - video_set_drvdata(zr->video_dev, zr); - - zoran_init_hardware(zr); - if (zr36067_debug > 2) - detect_guest_activity(zr); - test_interrupts(zr); - if (!pass_through) { - decoder_call(zr, video, s_stream, 0); - encoder_call(zr, video, s_routing, 2, 0, 0); - } - - zr->zoran_proc = NULL; - zr->initialized = 1; - return 0; - -exit_free: - kfree(zr->stat_com); - kfree(zr->video_dev); - return err; -} - -static void zoran_remove(struct pci_dev *pdev) -{ - struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); - struct zoran *zr = to_zoran(v4l2_dev); - - if (!zr->initialized) - goto exit_free; - - /* unregister videocodec bus */ - if (zr->codec) { - struct videocodec_master *master = zr->codec->master_data; - - videocodec_detach(zr->codec); - kfree(master); - } - if (zr->vfe) { - struct videocodec_master *master = zr->vfe->master_data; - - videocodec_detach(zr->vfe); - kfree(master); - } - - /* unregister i2c bus */ - zoran_unregister_i2c(zr); - /* disable PCI bus-mastering */ - zoran_set_pci_master(zr, 0); - /* put chip into reset */ - btwrite(0, ZR36057_SPGPPCR); - free_irq(zr->pci_dev->irq, zr); - /* unmap and free memory */ - kfree(zr->stat_com); - zoran_proc_cleanup(zr); - iounmap(zr->zr36057_mem); - pci_disable_device(zr->pci_dev); - video_unregister_device(zr->video_dev); -exit_free: - v4l2_ctrl_handler_free(&zr->hdl); - v4l2_device_unregister(&zr->v4l2_dev); - kfree(zr); -} - -void -zoran_vdev_release (struct video_device *vdev) -{ - kfree(vdev); -} - -static struct videocodec_master *zoran_setup_videocodec(struct zoran *zr, - int type) -{ - struct videocodec_master *m = NULL; - - m = kmalloc(sizeof(struct videocodec_master), GFP_KERNEL); - if (!m) { - dprintk(1, KERN_ERR "%s: %s - no memory\n", - ZR_DEVNAME(zr), __func__); - return m; - } - - /* magic and type are unused for master struct. Makes sense only at - codec structs. - In the past, .type were initialized to the old V4L1 .hardware - value, as VID_HARDWARE_ZR36067 - */ - m->magic = 0L; - m->type = 0; - - m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER; - strlcpy(m->name, ZR_DEVNAME(zr), sizeof(m->name)); - m->data = zr; - - switch (type) - { - case CODEC_TYPE_ZR36060: - m->readreg = zr36060_read; - m->writereg = zr36060_write; - m->flags |= CODEC_FLAG_JPEG | CODEC_FLAG_VFE; - break; - case CODEC_TYPE_ZR36050: - m->readreg = zr36050_read; - m->writereg = zr36050_write; - m->flags |= CODEC_FLAG_JPEG; - break; - case CODEC_TYPE_ZR36016: - m->readreg = zr36016_read; - m->writereg = zr36016_write; - m->flags |= CODEC_FLAG_VFE; - break; - } - - return m; -} - -static void zoran_subdev_notify(struct v4l2_subdev *sd, unsigned int cmd, void *arg) -{ - struct zoran *zr = to_zoran(sd->v4l2_dev); - - /* Bt819 needs to reset its FIFO buffer using #FRST pin and - LML33 card uses GPIO(7) for that. */ - if (cmd == BT819_FIFO_RESET_LOW) - GPIO(zr, 7, 0); - else if (cmd == BT819_FIFO_RESET_HIGH) - GPIO(zr, 7, 1); -} - -/* - * Scan for a Buz card (actually for the PCI controller ZR36057), - * request the irq and map the io memory - */ -static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - unsigned char latency, need_latency; - struct zoran *zr; - int result; - struct videocodec_master *master_vfe = NULL; - struct videocodec_master *master_codec = NULL; - int card_num; - char *codec_name, *vfe_name; - unsigned int nr; - - - nr = zoran_num++; - if (nr >= BUZ_MAX) { - dprintk(1, KERN_ERR "%s: driver limited to %d card(s) maximum\n", - ZORAN_NAME, BUZ_MAX); - return -ENOENT; - } - - zr = kzalloc(sizeof(struct zoran), GFP_KERNEL); - if (!zr) { - dprintk(1, KERN_ERR "%s: %s - kzalloc failed\n", - ZORAN_NAME, __func__); - return -ENOMEM; - } - zr->v4l2_dev.notify = zoran_subdev_notify; - if (v4l2_device_register(&pdev->dev, &zr->v4l2_dev)) - goto zr_free_mem; - zr->pci_dev = pdev; - zr->id = nr; - snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id); - if (v4l2_ctrl_handler_init(&zr->hdl, 10)) - goto zr_unreg; - zr->v4l2_dev.ctrl_handler = &zr->hdl; - spin_lock_init(&zr->spinlock); - mutex_init(&zr->lock); - if (pci_enable_device(pdev)) - goto zr_unreg; - zr->revision = zr->pci_dev->revision; - - dprintk(1, - KERN_INFO - "%s: Zoran ZR360%c7 (rev %d), irq: %d, memory: 0x%08llx\n", - ZR_DEVNAME(zr), zr->revision < 2 ? '5' : '6', zr->revision, - zr->pci_dev->irq, (uint64_t)pci_resource_start(zr->pci_dev, 0)); - if (zr->revision >= 2) { - dprintk(1, - KERN_INFO - "%s: Subsystem vendor=0x%04x id=0x%04x\n", - ZR_DEVNAME(zr), zr->pci_dev->subsystem_vendor, - zr->pci_dev->subsystem_device); - } - - /* Use auto-detected card type? */ - if (card[nr] == -1) { - if (zr->revision < 2) { - dprintk(1, - KERN_ERR - "%s: No card type specified, please use the card=X module parameter\n", - ZR_DEVNAME(zr)); - dprintk(1, - KERN_ERR - "%s: It is not possible to auto-detect ZR36057 based cards\n", - ZR_DEVNAME(zr)); - goto zr_unreg; - } - - card_num = ent->driver_data; - if (card_num >= NUM_CARDS) { - dprintk(1, - KERN_ERR - "%s: Unknown card, try specifying card=X module parameter\n", - ZR_DEVNAME(zr)); - goto zr_unreg; - } - dprintk(3, - KERN_DEBUG - "%s: %s() - card %s detected\n", - ZR_DEVNAME(zr), __func__, zoran_cards[card_num].name); - } else { - card_num = card[nr]; - if (card_num >= NUM_CARDS || card_num < 0) { - dprintk(1, - KERN_ERR - "%s: User specified card type %d out of range (0 .. %d)\n", - ZR_DEVNAME(zr), card_num, NUM_CARDS - 1); - goto zr_unreg; - } - } - - /* even though we make this a non pointer and thus - * theoretically allow for making changes to this struct - * on a per-individual card basis at runtime, this is - * strongly discouraged. This structure is intended to - * keep general card information, no settings or anything */ - zr->card = zoran_cards[card_num]; - snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), - "%s[%u]", zr->card.name, zr->id); - - zr->zr36057_mem = pci_ioremap_bar(zr->pci_dev, 0); - if (!zr->zr36057_mem) { - dprintk(1, KERN_ERR "%s: %s() - ioremap failed\n", - ZR_DEVNAME(zr), __func__); - goto zr_unreg; - } - - result = request_irq(zr->pci_dev->irq, zoran_irq, - IRQF_SHARED, ZR_DEVNAME(zr), zr); - if (result < 0) { - if (result == -EINVAL) { - dprintk(1, - KERN_ERR - "%s: %s - bad irq number or handler\n", - ZR_DEVNAME(zr), __func__); - } else if (result == -EBUSY) { - dprintk(1, - KERN_ERR - "%s: %s - IRQ %d busy, change your PnP config in BIOS\n", - ZR_DEVNAME(zr), __func__, zr->pci_dev->irq); - } else { - dprintk(1, - KERN_ERR - "%s: %s - can't assign irq, error code %d\n", - ZR_DEVNAME(zr), __func__, result); - } - goto zr_unmap; - } - - /* set PCI latency timer */ - pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, - &latency); - need_latency = zr->revision > 1 ? 32 : 48; - if (latency != need_latency) { - dprintk(2, KERN_INFO "%s: Changing PCI latency from %d to %d\n", - ZR_DEVNAME(zr), latency, need_latency); - pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, - need_latency); - } - - zr36057_restart(zr); - /* i2c */ - dprintk(2, KERN_INFO "%s: Initializing i2c bus...\n", - ZR_DEVNAME(zr)); - - if (zoran_register_i2c(zr) < 0) { - dprintk(1, KERN_ERR "%s: %s - can't initialize i2c bus\n", - ZR_DEVNAME(zr), __func__); - goto zr_free_irq; - } - - zr->decoder = v4l2_i2c_new_subdev(&zr->v4l2_dev, - &zr->i2c_adapter, zr->card.i2c_decoder, - 0, zr->card.addrs_decoder); - - if (zr->card.i2c_encoder) - zr->encoder = v4l2_i2c_new_subdev(&zr->v4l2_dev, - &zr->i2c_adapter, zr->card.i2c_encoder, - 0, zr->card.addrs_encoder); - - dprintk(2, - KERN_INFO "%s: Initializing videocodec bus...\n", - ZR_DEVNAME(zr)); - - if (zr->card.video_codec) { - codec_name = codecid_to_modulename(zr->card.video_codec); - if (codec_name) { - result = request_module(codec_name); - if (result) { - dprintk(1, - KERN_ERR - "%s: failed to load modules %s: %d\n", - ZR_DEVNAME(zr), codec_name, result); - } - } - } - if (zr->card.video_vfe) { - vfe_name = codecid_to_modulename(zr->card.video_vfe); - if (vfe_name) { - result = request_module(vfe_name); - if (result < 0) { - dprintk(1, - KERN_ERR - "%s: failed to load modules %s: %d\n", - ZR_DEVNAME(zr), vfe_name, result); - } - } - } - - /* reset JPEG codec */ - jpeg_codec_sleep(zr, 1); - jpeg_codec_reset(zr); - /* video bus enabled */ - /* display codec revision */ - if (zr->card.video_codec != 0) { - master_codec = zoran_setup_videocodec(zr, zr->card.video_codec); - if (!master_codec) - goto zr_unreg_i2c; - zr->codec = videocodec_attach(master_codec); - if (!zr->codec) { - dprintk(1, KERN_ERR "%s: %s - no codec found\n", - ZR_DEVNAME(zr), __func__); - goto zr_free_codec; - } - if (zr->codec->type != zr->card.video_codec) { - dprintk(1, KERN_ERR "%s: %s - wrong codec\n", - ZR_DEVNAME(zr), __func__); - goto zr_detach_codec; - } - } - if (zr->card.video_vfe != 0) { - master_vfe = zoran_setup_videocodec(zr, zr->card.video_vfe); - if (!master_vfe) - goto zr_detach_codec; - zr->vfe = videocodec_attach(master_vfe); - if (!zr->vfe) { - dprintk(1, KERN_ERR "%s: %s - no VFE found\n", - ZR_DEVNAME(zr), __func__); - goto zr_free_vfe; - } - if (zr->vfe->type != zr->card.video_vfe) { - dprintk(1, KERN_ERR "%s: %s = wrong VFE\n", - ZR_DEVNAME(zr), __func__); - goto zr_detach_vfe; - } - } - - /* take care of Natoma chipset and a revision 1 zr36057 */ - if ((pci_pci_problems & PCIPCI_NATOMA) && zr->revision <= 1) { - zr->jpg_buffers.need_contiguous = 1; - dprintk(1, KERN_INFO - "%s: ZR36057/Natoma bug, max. buffer size is 128K\n", - ZR_DEVNAME(zr)); - } - - if (zr36057_init(zr) < 0) - goto zr_detach_vfe; - - zoran_proc_init(zr); - - return 0; - -zr_detach_vfe: - videocodec_detach(zr->vfe); -zr_free_vfe: - kfree(master_vfe); -zr_detach_codec: - videocodec_detach(zr->codec); -zr_free_codec: - kfree(master_codec); -zr_unreg_i2c: - zoran_unregister_i2c(zr); -zr_free_irq: - btwrite(0, ZR36057_SPGPPCR); - free_irq(zr->pci_dev->irq, zr); -zr_unmap: - iounmap(zr->zr36057_mem); -zr_unreg: - v4l2_ctrl_handler_free(&zr->hdl); - v4l2_device_unregister(&zr->v4l2_dev); -zr_free_mem: - kfree(zr); - - return -ENODEV; -} - -static struct pci_driver zoran_driver = { - .name = "zr36067", - .id_table = zr36067_pci_tbl, - .probe = zoran_probe, - .remove = zoran_remove, -}; - -static int __init zoran_init(void) -{ - int res; - - printk(KERN_INFO "Zoran MJPEG board driver version %s\n", - ZORAN_VERSION); - - /* check the parameters we have been given, adjust if necessary */ - if (v4l_nbufs < 2) - v4l_nbufs = 2; - if (v4l_nbufs > VIDEO_MAX_FRAME) - v4l_nbufs = VIDEO_MAX_FRAME; - /* The user specfies the in KB, we want them in byte - * (and page aligned) */ - v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024); - if (v4l_bufsize < 32768) - v4l_bufsize = 32768; - /* 2 MB is arbitrary but sufficient for the maximum possible images */ - if (v4l_bufsize > 2048 * 1024) - v4l_bufsize = 2048 * 1024; - if (jpg_nbufs < 4) - jpg_nbufs = 4; - if (jpg_nbufs > BUZ_MAX_FRAME) - jpg_nbufs = BUZ_MAX_FRAME; - jpg_bufsize = PAGE_ALIGN(jpg_bufsize * 1024); - if (jpg_bufsize < 8192) - jpg_bufsize = 8192; - if (jpg_bufsize > (512 * 1024)) - jpg_bufsize = 512 * 1024; - /* Use parameter for vidmem or try to find a video card */ - if (vidmem) { - dprintk(1, - KERN_INFO - "%s: Using supplied video memory base address @ 0x%lx\n", - ZORAN_NAME, vidmem); - } - - /* some mainboards might not do PCI-PCI data transfer well */ - if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL|PCIPCI_ALIMAGIK)) { - dprintk(1, - KERN_WARNING - "%s: chipset does not support reliable PCI-PCI DMA\n", - ZORAN_NAME); - } - - res = pci_register_driver(&zoran_driver); - if (res) { - dprintk(1, - KERN_ERR - "%s: Unable to register ZR36057 driver\n", - ZORAN_NAME); - return res; - } - - return 0; -} - -static void __exit zoran_exit(void) -{ - pci_unregister_driver(&zoran_driver); -} - -module_init(zoran_init); -module_exit(zoran_exit); diff --git a/drivers/media/pci/zoran/zoran_card.h b/drivers/media/pci/zoran/zoran_card.h deleted file mode 100644 index 0cdb7d34926d..000000000000 --- a/drivers/media/pci/zoran/zoran_card.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * This part handles card-specific data and detection - * - * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> - * - * Currently maintained by: - * Ronald Bultje <rbultje@ronald.bitfreak.net> - * Laurent Pinchart <laurent.pinchart@skynet.be> - * Mailinglist <mjpeg-users@lists.sf.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __ZORAN_CARD_H__ -#define __ZORAN_CARD_H__ - -extern int zr36067_debug; - -#define dprintk(num, format, args...) \ - do { \ - if (zr36067_debug >= num) \ - printk(format, ##args); \ - } while (0) - -/* Anybody who uses more than four? */ -#define BUZ_MAX 4 - -extern const struct video_device zoran_template; - -extern int zoran_check_jpg_settings(struct zoran *zr, - struct zoran_jpg_settings *settings, - int try); -extern void zoran_open_init_params(struct zoran *zr); -extern void zoran_vdev_release(struct video_device *vdev); - -void zr36016_write(struct videocodec *codec, u16 reg, u32 val); - -#endif /* __ZORAN_CARD_H__ */ diff --git a/drivers/media/pci/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c deleted file mode 100644 index 40adceebca7e..000000000000 --- a/drivers/media/pci/zoran/zoran_device.c +++ /dev/null @@ -1,1619 +0,0 @@ -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * This part handles device access (PCI/I2C/codec/...) - * - * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> - * - * Currently maintained by: - * Ronald Bultje <rbultje@ronald.bitfreak.net> - * Laurent Pinchart <laurent.pinchart@skynet.be> - * Mailinglist <mjpeg-users@lists.sf.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/vmalloc.h> -#include <linux/ktime.h> -#include <linux/sched/signal.h> - -#include <linux/interrupt.h> -#include <linux/proc_fs.h> -#include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> -#include <linux/videodev2.h> -#include <media/v4l2-common.h> -#include <linux/spinlock.h> -#include <linux/sem.h> - -#include <linux/pci.h> -#include <linux/delay.h> -#include <linux/wait.h> - -#include <asm/byteorder.h> -#include <asm/io.h> - -#include "videocodec.h" -#include "zoran.h" -#include "zoran_device.h" -#include "zoran_card.h" - -#define IRQ_MASK ( ZR36057_ISR_GIRQ0 | \ - ZR36057_ISR_GIRQ1 | \ - ZR36057_ISR_JPEGRepIRQ ) - -static bool lml33dpath; /* default = 0 - * 1 will use digital path in capture - * mode instead of analog. It can be - * used for picture adjustments using - * tool like xawtv while watching image - * on TV monitor connected to the output. - * However, due to absence of 75 Ohm - * load on Bt819 input, there will be - * some image imperfections */ - -module_param(lml33dpath, bool, 0644); -MODULE_PARM_DESC(lml33dpath, - "Use digital path capture mode (on LML33 cards)"); - -static void -zr36057_init_vfe (struct zoran *zr); - -/* - * General Purpose I/O and Guest bus access - */ - -/* - * This is a bit tricky. When a board lacks a GPIO function, the corresponding - * GPIO bit number in the card_info structure is set to 0. - */ - -void -GPIO (struct zoran *zr, - int bit, - unsigned int value) -{ - u32 reg; - u32 mask; - - /* Make sure the bit number is legal - * A bit number of -1 (lacking) gives a mask of 0, - * making it harmless */ - mask = (1 << (24 + bit)) & 0xff000000; - reg = btread(ZR36057_GPPGCR1) & ~mask; - if (value) { - reg |= mask; - } - btwrite(reg, ZR36057_GPPGCR1); - udelay(1); -} - -/* - * Wait til post office is no longer busy - */ - -int -post_office_wait (struct zoran *zr) -{ - u32 por; - -// while (((por = btread(ZR36057_POR)) & (ZR36057_POR_POPen | ZR36057_POR_POTime)) == ZR36057_POR_POPen) { - while ((por = btread(ZR36057_POR)) & ZR36057_POR_POPen) { - /* wait for something to happen */ - } - if ((por & ZR36057_POR_POTime) && !zr->card.gws_not_connected) { - /* In LML33/BUZ \GWS line is not connected, so it has always timeout set */ - dprintk(1, KERN_INFO "%s: pop timeout %08x\n", ZR_DEVNAME(zr), - por); - return -1; - } - - return 0; -} - -int -post_office_write (struct zoran *zr, - unsigned int guest, - unsigned int reg, - unsigned int value) -{ - u32 por; - - por = - ZR36057_POR_PODir | ZR36057_POR_POTime | ((guest & 7) << 20) | - ((reg & 7) << 16) | (value & 0xFF); - btwrite(por, ZR36057_POR); - - return post_office_wait(zr); -} - -int -post_office_read (struct zoran *zr, - unsigned int guest, - unsigned int reg) -{ - u32 por; - - por = ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16); - btwrite(por, ZR36057_POR); - if (post_office_wait(zr) < 0) { - return -1; - } - - return btread(ZR36057_POR) & 0xFF; -} - -/* - * detect guests - */ - -static void -dump_guests (struct zoran *zr) -{ - if (zr36067_debug > 2) { - int i, guest[8]; - - for (i = 1; i < 8; i++) { // Don't read jpeg codec here - guest[i] = post_office_read(zr, i, 0); - } - - printk(KERN_INFO "%s: Guests: %*ph\n", - ZR_DEVNAME(zr), 8, guest); - } -} - -void -detect_guest_activity (struct zoran *zr) -{ - int timeout, i, j, res, guest[8], guest0[8], change[8][3]; - ktime_t t0, t1; - - dump_guests(zr); - printk(KERN_INFO "%s: Detecting guests activity, please wait...\n", - ZR_DEVNAME(zr)); - for (i = 1; i < 8; i++) { // Don't read jpeg codec here - guest0[i] = guest[i] = post_office_read(zr, i, 0); - } - - timeout = 0; - j = 0; - t0 = ktime_get(); - while (timeout < 10000) { - udelay(10); - timeout++; - for (i = 1; (i < 8) && (j < 8); i++) { - res = post_office_read(zr, i, 0); - if (res != guest[i]) { - t1 = ktime_get(); - change[j][0] = ktime_to_us(ktime_sub(t1, t0)); - t0 = t1; - change[j][1] = i; - change[j][2] = res; - j++; - guest[i] = res; - } - } - if (j >= 8) - break; - } - - printk(KERN_INFO "%s: Guests: %*ph\n", ZR_DEVNAME(zr), 8, guest0); - - if (j == 0) { - printk(KERN_INFO "%s: No activity detected.\n", ZR_DEVNAME(zr)); - return; - } - for (i = 0; i < j; i++) { - printk(KERN_INFO "%s: %6d: %d => 0x%02x\n", ZR_DEVNAME(zr), - change[i][0], change[i][1], change[i][2]); - } -} - -/* - * JPEG Codec access - */ - -void -jpeg_codec_sleep (struct zoran *zr, - int sleep) -{ - GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_SLEEP], !sleep); - if (!sleep) { - dprintk(3, - KERN_DEBUG - "%s: jpeg_codec_sleep() - wake GPIO=0x%08x\n", - ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1)); - udelay(500); - } else { - dprintk(3, - KERN_DEBUG - "%s: jpeg_codec_sleep() - sleep GPIO=0x%08x\n", - ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1)); - udelay(2); - } -} - -int -jpeg_codec_reset (struct zoran *zr) -{ - /* Take the codec out of sleep */ - jpeg_codec_sleep(zr, 0); - - if (zr->card.gpcs[GPCS_JPEG_RESET] != 0xff) { - post_office_write(zr, zr->card.gpcs[GPCS_JPEG_RESET], 0, - 0); - udelay(2); - } else { - GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_RESET], 0); - udelay(2); - GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_RESET], 1); - udelay(2); - } - - return 0; -} - -/* - * Set the registers for the size we have specified. Don't bother - * trying to understand this without the ZR36057 manual in front of - * you [AC]. - * - * PS: The manual is free for download in .pdf format from - * www.zoran.com - nicely done those folks. - */ - -static void -zr36057_adjust_vfe (struct zoran *zr, - enum zoran_codec_mode mode) -{ - u32 reg; - - switch (mode) { - case BUZ_MODE_MOTION_DECOMPRESS: - btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); - reg = btread(ZR36057_VFEHCR); - if ((reg & (1 << 10)) && zr->card.type != LML33R10) { - reg += ((1 << 10) | 1); - } - btwrite(reg, ZR36057_VFEHCR); - break; - case BUZ_MODE_MOTION_COMPRESS: - case BUZ_MODE_IDLE: - default: - if ((zr->norm & V4L2_STD_NTSC) || - (zr->card.type == LML33R10 && - (zr->norm & V4L2_STD_PAL))) - btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); - else - btor(ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); - reg = btread(ZR36057_VFEHCR); - if (!(reg & (1 << 10)) && zr->card.type != LML33R10) { - reg -= ((1 << 10) | 1); - } - btwrite(reg, ZR36057_VFEHCR); - break; - } -} - -/* - * set geometry - */ - -static void -zr36057_set_vfe (struct zoran *zr, - int video_width, - int video_height, - const struct zoran_format *format) -{ - struct tvnorm *tvn; - unsigned HStart, HEnd, VStart, VEnd; - unsigned DispMode; - unsigned VidWinWid, VidWinHt; - unsigned hcrop1, hcrop2, vcrop1, vcrop2; - unsigned Wa, We, Ha, He; - unsigned X, Y, HorDcm, VerDcm; - u32 reg; - unsigned mask_line_size; - - tvn = zr->timing; - - Wa = tvn->Wa; - Ha = tvn->Ha; - - dprintk(2, KERN_INFO "%s: set_vfe() - width = %d, height = %d\n", - ZR_DEVNAME(zr), video_width, video_height); - - if (video_width < BUZ_MIN_WIDTH || - video_height < BUZ_MIN_HEIGHT || - video_width > Wa || video_height > Ha) { - dprintk(1, KERN_ERR "%s: set_vfe: w=%d h=%d not valid\n", - ZR_DEVNAME(zr), video_width, video_height); - return; - } - - /**** zr36057 ****/ - - /* horizontal */ - VidWinWid = video_width; - X = DIV_ROUND_UP(VidWinWid * 64, tvn->Wa); - We = (VidWinWid * 64) / X; - HorDcm = 64 - X; - hcrop1 = 2 * ((tvn->Wa - We) / 4); - hcrop2 = tvn->Wa - We - hcrop1; - HStart = tvn->HStart ? tvn->HStart : 1; - /* (Ronald) Original comment: - * "| 1 Doesn't have any effect, tested on both a DC10 and a DC10+" - * this is false. It inverses chroma values on the LML33R10 (so Cr - * suddenly is shown as Cb and reverse, really cool effect if you - * want to see blue faces, not useful otherwise). So don't use |1. - * However, the DC10 has '0' as HStart, but does need |1, so we - * use a dirty check... - */ - HEnd = HStart + tvn->Wa - 1; - HStart += hcrop1; - HEnd -= hcrop2; - reg = ((HStart & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HStart) - | ((HEnd & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HEnd); - if (zr->card.vfe_pol.hsync_pol) - reg |= ZR36057_VFEHCR_HSPol; - btwrite(reg, ZR36057_VFEHCR); - - /* Vertical */ - DispMode = !(video_height > BUZ_MAX_HEIGHT / 2); - VidWinHt = DispMode ? video_height : video_height / 2; - Y = DIV_ROUND_UP(VidWinHt * 64 * 2, tvn->Ha); - He = (VidWinHt * 64) / Y; - VerDcm = 64 - Y; - vcrop1 = (tvn->Ha / 2 - He) / 2; - vcrop2 = tvn->Ha / 2 - He - vcrop1; - VStart = tvn->VStart; - VEnd = VStart + tvn->Ha / 2; // - 1; FIXME SnapShot times out with -1 in 768*576 on the DC10 - LP - VStart += vcrop1; - VEnd -= vcrop2; - reg = ((VStart & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VStart) - | ((VEnd & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VEnd); - if (zr->card.vfe_pol.vsync_pol) - reg |= ZR36057_VFEVCR_VSPol; - btwrite(reg, ZR36057_VFEVCR); - - /* scaler and pixel format */ - reg = 0; - reg |= (HorDcm << ZR36057_VFESPFR_HorDcm); - reg |= (VerDcm << ZR36057_VFESPFR_VerDcm); - reg |= (DispMode << ZR36057_VFESPFR_DispMode); - /* RJ: I don't know, why the following has to be the opposite - * of the corresponding ZR36060 setting, but only this way - * we get the correct colors when uncompressing to the screen */ - //reg |= ZR36057_VFESPFR_VCLKPol; /**/ - /* RJ: Don't know if that is needed for NTSC also */ - if (!(zr->norm & V4L2_STD_NTSC)) - reg |= ZR36057_VFESPFR_ExtFl; // NEEDED!!!!!!! Wolfgang - reg |= ZR36057_VFESPFR_TopField; - if (HorDcm >= 48) { - reg |= 3 << ZR36057_VFESPFR_HFilter; /* 5 tap filter */ - } else if (HorDcm >= 32) { - reg |= 2 << ZR36057_VFESPFR_HFilter; /* 4 tap filter */ - } else if (HorDcm >= 16) { - reg |= 1 << ZR36057_VFESPFR_HFilter; /* 3 tap filter */ - } - reg |= format->vfespfr; - btwrite(reg, ZR36057_VFESPFR); - - /* display configuration */ - reg = (16 << ZR36057_VDCR_MinPix) - | (VidWinHt << ZR36057_VDCR_VidWinHt) - | (VidWinWid << ZR36057_VDCR_VidWinWid); - if (pci_pci_problems & PCIPCI_TRITON) - // || zr->revision < 1) // Revision 1 has also Triton support - reg &= ~ZR36057_VDCR_Triton; - else - reg |= ZR36057_VDCR_Triton; - btwrite(reg, ZR36057_VDCR); - - /* (Ronald) don't write this if overlay_mask = NULL */ - if (zr->overlay_mask) { - /* Write overlay clipping mask data, but don't enable overlay clipping */ - /* RJ: since this makes only sense on the screen, we use - * zr->overlay_settings.width instead of video_width */ - - mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; - reg = virt_to_bus(zr->overlay_mask); - btwrite(reg, ZR36057_MMTR); - reg = virt_to_bus(zr->overlay_mask + mask_line_size); - btwrite(reg, ZR36057_MMBR); - reg = - mask_line_size - (zr->overlay_settings.width + - 31) / 32; - if (DispMode == 0) - reg += mask_line_size; - reg <<= ZR36057_OCR_MaskStride; - btwrite(reg, ZR36057_OCR); - } - - zr36057_adjust_vfe(zr, zr->codec_mode); -} - -/* - * Switch overlay on or off - */ - -void -zr36057_overlay (struct zoran *zr, - int on) -{ - u32 reg; - - if (on) { - /* do the necessary settings ... */ - btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); /* switch it off first */ - - zr36057_set_vfe(zr, - zr->overlay_settings.width, - zr->overlay_settings.height, - zr->overlay_settings.format); - - /* Start and length of each line MUST be 4-byte aligned. - * This should be already checked before the call to this routine. - * All error messages are internal driver checking only! */ - - /* video display top and bottom registers */ - reg = (long) zr->vbuf_base + - zr->overlay_settings.x * - ((zr->overlay_settings.format->depth + 7) / 8) + - zr->overlay_settings.y * - zr->vbuf_bytesperline; - btwrite(reg, ZR36057_VDTR); - if (reg & 3) - dprintk(1, - KERN_ERR - "%s: zr36057_overlay() - video_address not aligned\n", - ZR_DEVNAME(zr)); - if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2) - reg += zr->vbuf_bytesperline; - btwrite(reg, ZR36057_VDBR); - - /* video stride, status, and frame grab register */ - reg = zr->vbuf_bytesperline - - zr->overlay_settings.width * - ((zr->overlay_settings.format->depth + 7) / 8); - if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2) - reg += zr->vbuf_bytesperline; - if (reg & 3) - dprintk(1, - KERN_ERR - "%s: zr36057_overlay() - video_stride not aligned\n", - ZR_DEVNAME(zr)); - reg = (reg << ZR36057_VSSFGR_DispStride); - reg |= ZR36057_VSSFGR_VidOvf; /* clear overflow status */ - btwrite(reg, ZR36057_VSSFGR); - - /* Set overlay clipping */ - if (zr->overlay_settings.clipcount > 0) - btor(ZR36057_OCR_OvlEnable, ZR36057_OCR); - - /* ... and switch it on */ - btor(ZR36057_VDCR_VidEn, ZR36057_VDCR); - } else { - /* Switch it off */ - btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); - } -} - -/* - * The overlay mask has one bit for each pixel on a scan line, - * and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels. - */ - -void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count) -{ - struct zoran *zr = fh->zr; - unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; - u32 *mask; - int x, y, width, height; - unsigned i, j, k; - - /* fill mask with one bits */ - memset(fh->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT); - - for (i = 0; i < count; ++i) { - /* pick up local copy of clip */ - x = vp[i].c.left; - y = vp[i].c.top; - width = vp[i].c.width; - height = vp[i].c.height; - - /* trim clips that extend beyond the window */ - if (x < 0) { - width += x; - x = 0; - } - if (y < 0) { - height += y; - y = 0; - } - if (x + width > fh->overlay_settings.width) { - width = fh->overlay_settings.width - x; - } - if (y + height > fh->overlay_settings.height) { - height = fh->overlay_settings.height - y; - } - - /* ignore degenerate clips */ - if (height <= 0) { - continue; - } - if (width <= 0) { - continue; - } - - /* apply clip for each scan line */ - for (j = 0; j < height; ++j) { - /* reset bit for each pixel */ - /* this can be optimized later if need be */ - mask = fh->overlay_mask + (y + j) * mask_line_size; - for (k = 0; k < width; ++k) { - mask[(x + k) / 32] &= - ~((u32) 1 << (x + k) % 32); - } - } - } -} - -/* Enable/Disable uncompressed memory grabbing of the 36057 */ - -void -zr36057_set_memgrab (struct zoran *zr, - int mode) -{ - if (mode) { - /* We only check SnapShot and not FrameGrab here. SnapShot==1 - * means a capture is already in progress, but FrameGrab==1 - * doesn't necessary mean that. It's more correct to say a 1 - * to 0 transition indicates a capture completed. If a - * capture is pending when capturing is tuned off, FrameGrab - * will be stuck at 1 until capturing is turned back on. - */ - if (btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SnapShot) - dprintk(1, - KERN_WARNING - "%s: zr36057_set_memgrab(1) with SnapShot on!?\n", - ZR_DEVNAME(zr)); - - /* switch on VSync interrupts */ - btwrite(IRQ_MASK, ZR36057_ISR); // Clear Interrupts - btor(zr->card.vsync_int, ZR36057_ICR); // SW - - /* enable SnapShot */ - btor(ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR); - - /* Set zr36057 video front end and enable video */ - zr36057_set_vfe(zr, zr->v4l_settings.width, - zr->v4l_settings.height, - zr->v4l_settings.format); - - zr->v4l_memgrab_active = 1; - } else { - /* switch off VSync interrupts */ - btand(~zr->card.vsync_int, ZR36057_ICR); // SW - - zr->v4l_memgrab_active = 0; - zr->v4l_grab_frame = NO_GRAB_ACTIVE; - - /* reenable grabbing to screen if it was running */ - if (zr->v4l_overlay_active) { - zr36057_overlay(zr, 1); - } else { - btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); - btand(~ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR); - } - } -} - -int -wait_grab_pending (struct zoran *zr) -{ - unsigned long flags; - - /* wait until all pending grabs are finished */ - - if (!zr->v4l_memgrab_active) - return 0; - - wait_event_interruptible(zr->v4l_capq, - (zr->v4l_pend_tail == zr->v4l_pend_head)); - if (signal_pending(current)) - return -ERESTARTSYS; - - spin_lock_irqsave(&zr->spinlock, flags); - zr36057_set_memgrab(zr, 0); - spin_unlock_irqrestore(&zr->spinlock, flags); - - return 0; -} - -/***************************************************************************** - * * - * Set up the Buz-specific MJPEG part * - * * - *****************************************************************************/ - -static inline void -set_frame (struct zoran *zr, - int val) -{ - GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_FRAME], val); -} - -static void -set_videobus_dir (struct zoran *zr, - int val) -{ - switch (zr->card.type) { - case LML33: - case LML33R10: - if (!lml33dpath) - GPIO(zr, 5, val); - else - GPIO(zr, 5, 1); - break; - default: - GPIO(zr, zr->card.gpio[ZR_GPIO_VID_DIR], - zr->card.gpio_pol[ZR_GPIO_VID_DIR] ? !val : val); - break; - } -} - -static void -init_jpeg_queue (struct zoran *zr) -{ - int i; - - /* re-initialize DMA ring stuff */ - zr->jpg_que_head = 0; - zr->jpg_dma_head = 0; - zr->jpg_dma_tail = 0; - zr->jpg_que_tail = 0; - zr->jpg_seq_num = 0; - zr->JPEG_error = 0; - zr->num_errors = 0; - zr->jpg_err_seq = 0; - zr->jpg_err_shift = 0; - zr->jpg_queued_num = 0; - for (i = 0; i < zr->jpg_buffers.num_buffers; i++) { - zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ - } - for (i = 0; i < BUZ_NUM_STAT_COM; i++) { - zr->stat_com[i] = cpu_to_le32(1); /* mark as unavailable to zr36057 */ - } -} - -static void -zr36057_set_jpg (struct zoran *zr, - enum zoran_codec_mode mode) -{ - struct tvnorm *tvn; - u32 reg; - - tvn = zr->timing; - - /* assert P_Reset, disable code transfer, deassert Active */ - btwrite(0, ZR36057_JPC); - - /* MJPEG compression mode */ - switch (mode) { - - case BUZ_MODE_MOTION_COMPRESS: - default: - reg = ZR36057_JMC_MJPGCmpMode; - break; - - case BUZ_MODE_MOTION_DECOMPRESS: - reg = ZR36057_JMC_MJPGExpMode; - reg |= ZR36057_JMC_SyncMstr; - /* RJ: The following is experimental - improves the output to screen */ - //if(zr->jpg_settings.VFIFO_FB) reg |= ZR36057_JMC_VFIFO_FB; // No, it doesn't. SM - break; - - case BUZ_MODE_STILL_COMPRESS: - reg = ZR36057_JMC_JPGCmpMode; - break; - - case BUZ_MODE_STILL_DECOMPRESS: - reg = ZR36057_JMC_JPGExpMode; - break; - - } - reg |= ZR36057_JMC_JPG; - if (zr->jpg_settings.field_per_buff == 1) - reg |= ZR36057_JMC_Fld_per_buff; - btwrite(reg, ZR36057_JMC); - - /* vertical */ - btor(ZR36057_VFEVCR_VSPol, ZR36057_VFEVCR); - reg = (6 << ZR36057_VSP_VsyncSize) | - (tvn->Ht << ZR36057_VSP_FrmTot); - btwrite(reg, ZR36057_VSP); - reg = ((zr->jpg_settings.img_y + tvn->VStart) << ZR36057_FVAP_NAY) | - (zr->jpg_settings.img_height << ZR36057_FVAP_PAY); - btwrite(reg, ZR36057_FVAP); - - /* horizontal */ - if (zr->card.vfe_pol.hsync_pol) - btor(ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR); - else - btand(~ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR); - reg = ((tvn->HSyncStart) << ZR36057_HSP_HsyncStart) | - (tvn->Wt << ZR36057_HSP_LineTot); - btwrite(reg, ZR36057_HSP); - reg = ((zr->jpg_settings.img_x + - tvn->HStart + 4) << ZR36057_FHAP_NAX) | - (zr->jpg_settings.img_width << ZR36057_FHAP_PAX); - btwrite(reg, ZR36057_FHAP); - - /* field process parameters */ - if (zr->jpg_settings.odd_even) - reg = ZR36057_FPP_Odd_Even; - else - reg = 0; - - btwrite(reg, ZR36057_FPP); - - /* Set proper VCLK Polarity, else colors will be wrong during playback */ - //btor(ZR36057_VFESPFR_VCLKPol, ZR36057_VFESPFR); - - /* code base address */ - reg = virt_to_bus(zr->stat_com); - btwrite(reg, ZR36057_JCBA); - - /* FIFO threshold (FIFO is 160. double words) */ - /* NOTE: decimal values here */ - switch (mode) { - - case BUZ_MODE_STILL_COMPRESS: - case BUZ_MODE_MOTION_COMPRESS: - if (zr->card.type != BUZ) - reg = 140; - else - reg = 60; - break; - - case BUZ_MODE_STILL_DECOMPRESS: - case BUZ_MODE_MOTION_DECOMPRESS: - reg = 20; - break; - - default: - reg = 80; - break; - - } - btwrite(reg, ZR36057_JCFT); - zr36057_adjust_vfe(zr, mode); - -} - -void -print_interrupts (struct zoran *zr) -{ - int res, noerr = 0; - - printk(KERN_INFO "%s: interrupts received:", ZR_DEVNAME(zr)); - if ((res = zr->field_counter) < -1 || res > 1) { - printk(KERN_CONT " FD:%d", res); - } - if ((res = zr->intr_counter_GIRQ1) != 0) { - printk(KERN_CONT " GIRQ1:%d", res); - noerr++; - } - if ((res = zr->intr_counter_GIRQ0) != 0) { - printk(KERN_CONT " GIRQ0:%d", res); - noerr++; - } - if ((res = zr->intr_counter_CodRepIRQ) != 0) { - printk(KERN_CONT " CodRepIRQ:%d", res); - noerr++; - } - if ((res = zr->intr_counter_JPEGRepIRQ) != 0) { - printk(KERN_CONT " JPEGRepIRQ:%d", res); - noerr++; - } - if (zr->JPEG_max_missed) { - printk(KERN_CONT " JPEG delays: max=%d min=%d", zr->JPEG_max_missed, - zr->JPEG_min_missed); - } - if (zr->END_event_missed) { - printk(KERN_CONT " ENDs missed: %d", zr->END_event_missed); - } - //if (zr->jpg_queued_num) { - printk(KERN_CONT " queue_state=%ld/%ld/%ld/%ld", zr->jpg_que_tail, - zr->jpg_dma_tail, zr->jpg_dma_head, zr->jpg_que_head); - //} - if (!noerr) { - printk(KERN_CONT ": no interrupts detected."); - } - printk(KERN_CONT "\n"); -} - -void -clear_interrupt_counters (struct zoran *zr) -{ - zr->intr_counter_GIRQ1 = 0; - zr->intr_counter_GIRQ0 = 0; - zr->intr_counter_CodRepIRQ = 0; - zr->intr_counter_JPEGRepIRQ = 0; - zr->field_counter = 0; - zr->IRQ1_in = 0; - zr->IRQ1_out = 0; - zr->JPEG_in = 0; - zr->JPEG_out = 0; - zr->JPEG_0 = 0; - zr->JPEG_1 = 0; - zr->END_event_missed = 0; - zr->JPEG_missed = 0; - zr->JPEG_max_missed = 0; - zr->JPEG_min_missed = 0x7fffffff; -} - -static u32 -count_reset_interrupt (struct zoran *zr) -{ - u32 isr; - - if ((isr = btread(ZR36057_ISR) & 0x78000000)) { - if (isr & ZR36057_ISR_GIRQ1) { - btwrite(ZR36057_ISR_GIRQ1, ZR36057_ISR); - zr->intr_counter_GIRQ1++; - } - if (isr & ZR36057_ISR_GIRQ0) { - btwrite(ZR36057_ISR_GIRQ0, ZR36057_ISR); - zr->intr_counter_GIRQ0++; - } - if (isr & ZR36057_ISR_CodRepIRQ) { - btwrite(ZR36057_ISR_CodRepIRQ, ZR36057_ISR); - zr->intr_counter_CodRepIRQ++; - } - if (isr & ZR36057_ISR_JPEGRepIRQ) { - btwrite(ZR36057_ISR_JPEGRepIRQ, ZR36057_ISR); - zr->intr_counter_JPEGRepIRQ++; - } - } - return isr; -} - -void -jpeg_start (struct zoran *zr) -{ - int reg; - - zr->frame_num = 0; - - /* deassert P_reset, disable code transfer, deassert Active */ - btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC); - /* stop flushing the internal code buffer */ - btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR); - /* enable code transfer */ - btor(ZR36057_JPC_CodTrnsEn, ZR36057_JPC); - - /* clear IRQs */ - btwrite(IRQ_MASK, ZR36057_ISR); - /* enable the JPEG IRQs */ - btwrite(zr->card.jpeg_int | - ZR36057_ICR_JPEGRepIRQ | - ZR36057_ICR_IntPinEn, - ZR36057_ICR); - - set_frame(zr, 0); // \FRAME - - /* set the JPEG codec guest ID */ - reg = (zr->card.gpcs[1] << ZR36057_JCGI_JPEGuestID) | - (0 << ZR36057_JCGI_JPEGuestReg); - btwrite(reg, ZR36057_JCGI); - - if (zr->card.video_vfe == CODEC_TYPE_ZR36016 && - zr->card.video_codec == CODEC_TYPE_ZR36050) { - /* Enable processing on the ZR36016 */ - if (zr->vfe) - zr36016_write(zr->vfe, 0, 1); - - /* load the address of the GO register in the ZR36050 latch */ - post_office_write(zr, 0, 0, 0); - } - - /* assert Active */ - btor(ZR36057_JPC_Active, ZR36057_JPC); - - /* enable the Go generation */ - btor(ZR36057_JMC_Go_en, ZR36057_JMC); - udelay(30); - - set_frame(zr, 1); // /FRAME - - dprintk(3, KERN_DEBUG "%s: jpeg_start\n", ZR_DEVNAME(zr)); -} - -void -zr36057_enable_jpg (struct zoran *zr, - enum zoran_codec_mode mode) -{ - struct vfe_settings cap; - int field_size = - zr->jpg_buffers.buffer_size / zr->jpg_settings.field_per_buff; - - zr->codec_mode = mode; - - cap.x = zr->jpg_settings.img_x; - cap.y = zr->jpg_settings.img_y; - cap.width = zr->jpg_settings.img_width; - cap.height = zr->jpg_settings.img_height; - cap.decimation = - zr->jpg_settings.HorDcm | (zr->jpg_settings.VerDcm << 8); - cap.quality = zr->jpg_settings.jpg_comp.quality; - - switch (mode) { - - case BUZ_MODE_MOTION_COMPRESS: { - struct jpeg_app_marker app; - struct jpeg_com_marker com; - - /* In motion compress mode, the decoder output must be enabled, and - * the video bus direction set to input. - */ - set_videobus_dir(zr, 0); - decoder_call(zr, video, s_stream, 1); - encoder_call(zr, video, s_routing, 0, 0, 0); - - /* Take the JPEG codec and the VFE out of sleep */ - jpeg_codec_sleep(zr, 0); - - /* set JPEG app/com marker */ - app.appn = zr->jpg_settings.jpg_comp.APPn; - app.len = zr->jpg_settings.jpg_comp.APP_len; - memcpy(app.data, zr->jpg_settings.jpg_comp.APP_data, 60); - zr->codec->control(zr->codec, CODEC_S_JPEG_APP_DATA, - sizeof(struct jpeg_app_marker), &app); - - com.len = zr->jpg_settings.jpg_comp.COM_len; - memcpy(com.data, zr->jpg_settings.jpg_comp.COM_data, 60); - zr->codec->control(zr->codec, CODEC_S_JPEG_COM_DATA, - sizeof(struct jpeg_com_marker), &com); - - /* Setup the JPEG codec */ - zr->codec->control(zr->codec, CODEC_S_JPEG_TDS_BYTE, - sizeof(int), &field_size); - zr->codec->set_video(zr->codec, zr->timing, &cap, - &zr->card.vfe_pol); - zr->codec->set_mode(zr->codec, CODEC_DO_COMPRESSION); - - /* Setup the VFE */ - if (zr->vfe) { - zr->vfe->control(zr->vfe, CODEC_S_JPEG_TDS_BYTE, - sizeof(int), &field_size); - zr->vfe->set_video(zr->vfe, zr->timing, &cap, - &zr->card.vfe_pol); - zr->vfe->set_mode(zr->vfe, CODEC_DO_COMPRESSION); - } - - init_jpeg_queue(zr); - zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO - - clear_interrupt_counters(zr); - dprintk(2, KERN_INFO "%s: enable_jpg(MOTION_COMPRESS)\n", - ZR_DEVNAME(zr)); - break; - } - - case BUZ_MODE_MOTION_DECOMPRESS: - /* In motion decompression mode, the decoder output must be disabled, and - * the video bus direction set to output. - */ - decoder_call(zr, video, s_stream, 0); - set_videobus_dir(zr, 1); - encoder_call(zr, video, s_routing, 1, 0, 0); - - /* Take the JPEG codec and the VFE out of sleep */ - jpeg_codec_sleep(zr, 0); - /* Setup the VFE */ - if (zr->vfe) { - zr->vfe->set_video(zr->vfe, zr->timing, &cap, - &zr->card.vfe_pol); - zr->vfe->set_mode(zr->vfe, CODEC_DO_EXPANSION); - } - /* Setup the JPEG codec */ - zr->codec->set_video(zr->codec, zr->timing, &cap, - &zr->card.vfe_pol); - zr->codec->set_mode(zr->codec, CODEC_DO_EXPANSION); - - init_jpeg_queue(zr); - zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO - - clear_interrupt_counters(zr); - dprintk(2, KERN_INFO "%s: enable_jpg(MOTION_DECOMPRESS)\n", - ZR_DEVNAME(zr)); - break; - - case BUZ_MODE_IDLE: - default: - /* shut down processing */ - btand(~(zr->card.jpeg_int | ZR36057_ICR_JPEGRepIRQ), - ZR36057_ICR); - btwrite(zr->card.jpeg_int | ZR36057_ICR_JPEGRepIRQ, - ZR36057_ISR); - btand(~ZR36057_JMC_Go_en, ZR36057_JMC); // \Go_en - - msleep(50); - - set_videobus_dir(zr, 0); - set_frame(zr, 1); // /FRAME - btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); // /CFlush - btwrite(0, ZR36057_JPC); // \P_Reset,\CodTrnsEn,\Active - btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC); - btand(~ZR36057_JMC_SyncMstr, ZR36057_JMC); - jpeg_codec_reset(zr); - jpeg_codec_sleep(zr, 1); - zr36057_adjust_vfe(zr, mode); - - decoder_call(zr, video, s_stream, 1); - encoder_call(zr, video, s_routing, 0, 0, 0); - - dprintk(2, KERN_INFO "%s: enable_jpg(IDLE)\n", ZR_DEVNAME(zr)); - break; - - } -} - -/* when this is called the spinlock must be held */ -void -zoran_feed_stat_com (struct zoran *zr) -{ - /* move frames from pending queue to DMA */ - - int frame, i, max_stat_com; - - max_stat_com = - (zr->jpg_settings.TmpDcm == - 1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1); - - while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com && - zr->jpg_dma_head < zr->jpg_que_head) { - - frame = zr->jpg_pend[zr->jpg_dma_head & BUZ_MASK_FRAME]; - if (zr->jpg_settings.TmpDcm == 1) { - /* fill 1 stat_com entry */ - i = (zr->jpg_dma_head - - zr->jpg_err_shift) & BUZ_MASK_STAT_COM; - if (!(zr->stat_com[i] & cpu_to_le32(1))) - break; - zr->stat_com[i] = - cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus); - } else { - /* fill 2 stat_com entries */ - i = ((zr->jpg_dma_head - - zr->jpg_err_shift) & 1) * 2; - if (!(zr->stat_com[i] & cpu_to_le32(1))) - break; - zr->stat_com[i] = - cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus); - zr->stat_com[i + 1] = - cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus); - } - zr->jpg_buffers.buffer[frame].state = BUZ_STATE_DMA; - zr->jpg_dma_head++; - - } - if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) - zr->jpg_queued_num++; -} - -/* when this is called the spinlock must be held */ -static void -zoran_reap_stat_com (struct zoran *zr) -{ - /* move frames from DMA queue to done queue */ - - int i; - u32 stat_com; - unsigned int seq; - unsigned int dif; - struct zoran_buffer *buffer; - int frame; - - /* In motion decompress we don't have a hardware frame counter, - * we just count the interrupts here */ - - if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) { - zr->jpg_seq_num++; - } - while (zr->jpg_dma_tail < zr->jpg_dma_head) { - if (zr->jpg_settings.TmpDcm == 1) - i = (zr->jpg_dma_tail - - zr->jpg_err_shift) & BUZ_MASK_STAT_COM; - else - i = ((zr->jpg_dma_tail - - zr->jpg_err_shift) & 1) * 2 + 1; - - stat_com = le32_to_cpu(zr->stat_com[i]); - - if ((stat_com & 1) == 0) { - return; - } - frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; - buffer = &zr->jpg_buffers.buffer[frame]; - v4l2_get_timestamp(&buffer->bs.timestamp); - - if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { - buffer->bs.length = (stat_com & 0x7fffff) >> 1; - - /* update sequence number with the help of the counter in stat_com */ - - seq = ((stat_com >> 24) + zr->jpg_err_seq) & 0xff; - dif = (seq - zr->jpg_seq_num) & 0xff; - zr->jpg_seq_num += dif; - } else { - buffer->bs.length = 0; - } - buffer->bs.seq = - zr->jpg_settings.TmpDcm == - 2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num; - buffer->state = BUZ_STATE_DONE; - - zr->jpg_dma_tail++; - } -} - -static void zoran_restart(struct zoran *zr) -{ - /* Now the stat_comm buffer is ready for restart */ - unsigned int status = 0; - int mode; - - if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { - decoder_call(zr, video, g_input_status, &status); - mode = CODEC_DO_COMPRESSION; - } else { - status = V4L2_IN_ST_NO_SIGNAL; - mode = CODEC_DO_EXPANSION; - } - if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || - !(status & V4L2_IN_ST_NO_SIGNAL)) { - /********** RESTART code *************/ - jpeg_codec_reset(zr); - zr->codec->set_mode(zr->codec, mode); - zr36057_set_jpg(zr, zr->codec_mode); - jpeg_start(zr); - - if (zr->num_errors <= 8) - dprintk(2, KERN_INFO "%s: Restart\n", - ZR_DEVNAME(zr)); - - zr->JPEG_missed = 0; - zr->JPEG_error = 2; - /********** End RESTART code ***********/ - } -} - -static void -error_handler (struct zoran *zr, - u32 astat, - u32 stat) -{ - int i; - - /* This is JPEG error handling part */ - if (zr->codec_mode != BUZ_MODE_MOTION_COMPRESS && - zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS) { - return; - } - - if ((stat & 1) == 0 && - zr->codec_mode == BUZ_MODE_MOTION_COMPRESS && - zr->jpg_dma_tail - zr->jpg_que_tail >= zr->jpg_buffers.num_buffers) { - /* No free buffers... */ - zoran_reap_stat_com(zr); - zoran_feed_stat_com(zr); - wake_up_interruptible(&zr->jpg_capq); - zr->JPEG_missed = 0; - return; - } - - if (zr->JPEG_error == 1) { - zoran_restart(zr); - return; - } - - /* - * First entry: error just happened during normal operation - * - * In BUZ_MODE_MOTION_COMPRESS: - * - * Possible glitch in TV signal. In this case we should - * stop the codec and wait for good quality signal before - * restarting it to avoid further problems - * - * In BUZ_MODE_MOTION_DECOMPRESS: - * - * Bad JPEG frame: we have to mark it as processed (codec crashed - * and was not able to do it itself), and to remove it from queue. - */ - btand(~ZR36057_JMC_Go_en, ZR36057_JMC); - udelay(1); - stat = stat | (post_office_read(zr, 7, 0) & 3) << 8; - btwrite(0, ZR36057_JPC); - btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); - jpeg_codec_reset(zr); - jpeg_codec_sleep(zr, 1); - zr->JPEG_error = 1; - zr->num_errors++; - - /* Report error */ - if (zr36067_debug > 1 && zr->num_errors <= 8) { - long frame; - int j; - - frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; - printk(KERN_ERR - "%s: JPEG error stat=0x%08x(0x%08x) queue_state=%ld/%ld/%ld/%ld seq=%ld frame=%ld. Codec stopped. ", - ZR_DEVNAME(zr), stat, zr->last_isr, - zr->jpg_que_tail, zr->jpg_dma_tail, - zr->jpg_dma_head, zr->jpg_que_head, - zr->jpg_seq_num, frame); - printk(KERN_INFO "stat_com frames:"); - for (j = 0; j < BUZ_NUM_STAT_COM; j++) { - for (i = 0; i < zr->jpg_buffers.num_buffers; i++) { - if (le32_to_cpu(zr->stat_com[j]) == zr->jpg_buffers.buffer[i].jpg.frag_tab_bus) - printk(KERN_CONT "% d->%d", j, i); - } - } - printk(KERN_CONT "\n"); - } - /* Find an entry in stat_com and rotate contents */ - if (zr->jpg_settings.TmpDcm == 1) - i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM; - else - i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2; - if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) { - /* Mimic zr36067 operation */ - zr->stat_com[i] |= cpu_to_le32(1); - if (zr->jpg_settings.TmpDcm != 1) - zr->stat_com[i + 1] |= cpu_to_le32(1); - /* Refill */ - zoran_reap_stat_com(zr); - zoran_feed_stat_com(zr); - wake_up_interruptible(&zr->jpg_capq); - /* Find an entry in stat_com again after refill */ - if (zr->jpg_settings.TmpDcm == 1) - i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM; - else - i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2; - } - if (i) { - /* Rotate stat_comm entries to make current entry first */ - int j; - __le32 bus_addr[BUZ_NUM_STAT_COM]; - - /* Here we are copying the stat_com array, which - * is already in little endian format, so - * no endian conversions here - */ - memcpy(bus_addr, zr->stat_com, sizeof(bus_addr)); - - for (j = 0; j < BUZ_NUM_STAT_COM; j++) - zr->stat_com[j] = bus_addr[(i + j) & BUZ_MASK_STAT_COM]; - - zr->jpg_err_shift += i; - zr->jpg_err_shift &= BUZ_MASK_STAT_COM; - } - if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) - zr->jpg_err_seq = zr->jpg_seq_num; /* + 1; */ - zoran_restart(zr); -} - -irqreturn_t -zoran_irq (int irq, - void *dev_id) -{ - u32 stat, astat; - int count; - struct zoran *zr; - unsigned long flags; - - zr = dev_id; - count = 0; - - if (zr->testing) { - /* Testing interrupts */ - spin_lock_irqsave(&zr->spinlock, flags); - while ((stat = count_reset_interrupt(zr))) { - if (count++ > 100) { - btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR); - dprintk(1, - KERN_ERR - "%s: IRQ lockup while testing, isr=0x%08x, cleared int mask\n", - ZR_DEVNAME(zr), stat); - wake_up_interruptible(&zr->test_q); - } - } - zr->last_isr = stat; - spin_unlock_irqrestore(&zr->spinlock, flags); - return IRQ_HANDLED; - } - - spin_lock_irqsave(&zr->spinlock, flags); - while (1) { - /* get/clear interrupt status bits */ - stat = count_reset_interrupt(zr); - astat = stat & IRQ_MASK; - if (!astat) { - break; - } - dprintk(4, - KERN_DEBUG - "zoran_irq: astat: 0x%08x, mask: 0x%08x\n", - astat, btread(ZR36057_ICR)); - if (astat & zr->card.vsync_int) { // SW - - if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || - zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { - /* count missed interrupts */ - zr->JPEG_missed++; - } - //post_office_read(zr,1,0); - /* Interrupts may still happen when - * zr->v4l_memgrab_active is switched off. - * We simply ignore them */ - - if (zr->v4l_memgrab_active) { - /* A lot more checks should be here ... */ - if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SnapShot) == 0) - dprintk(1, - KERN_WARNING - "%s: BuzIRQ with SnapShot off ???\n", - ZR_DEVNAME(zr)); - - if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) { - /* There is a grab on a frame going on, check if it has finished */ - if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_FrameGrab) == 0) { - /* it is finished, notify the user */ - - zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE; - zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.seq = zr->v4l_grab_seq; - v4l2_get_timestamp(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp); - zr->v4l_grab_frame = NO_GRAB_ACTIVE; - zr->v4l_pend_tail++; - } - } - - if (zr->v4l_grab_frame == NO_GRAB_ACTIVE) - wake_up_interruptible(&zr->v4l_capq); - - /* Check if there is another grab queued */ - - if (zr->v4l_grab_frame == NO_GRAB_ACTIVE && - zr->v4l_pend_tail != zr->v4l_pend_head) { - int frame = zr->v4l_pend[zr->v4l_pend_tail & V4L_MASK_FRAME]; - u32 reg; - - zr->v4l_grab_frame = frame; - - /* Set zr36057 video front end and enable video */ - - /* Buffer address */ - - reg = zr->v4l_buffers.buffer[frame].v4l.fbuffer_bus; - btwrite(reg, ZR36057_VDTR); - if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2) - reg += zr->v4l_settings.bytesperline; - btwrite(reg, ZR36057_VDBR); - - /* video stride, status, and frame grab register */ - reg = 0; - if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2) - reg += zr->v4l_settings.bytesperline; - reg = (reg << ZR36057_VSSFGR_DispStride); - reg |= ZR36057_VSSFGR_VidOvf; - reg |= ZR36057_VSSFGR_SnapShot; - reg |= ZR36057_VSSFGR_FrameGrab; - btwrite(reg, ZR36057_VSSFGR); - - btor(ZR36057_VDCR_VidEn, - ZR36057_VDCR); - } - } - - /* even if we don't grab, we do want to increment - * the sequence counter to see lost frames */ - zr->v4l_grab_seq++; - } -#if (IRQ_MASK & ZR36057_ISR_CodRepIRQ) - if (astat & ZR36057_ISR_CodRepIRQ) { - zr->intr_counter_CodRepIRQ++; - IDEBUG(printk(KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n", - ZR_DEVNAME(zr))); - btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR); - } -#endif /* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */ - -#if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) - if ((astat & ZR36057_ISR_JPEGRepIRQ) && - (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || - zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)) { - if (zr36067_debug > 1 && (!zr->frame_num || zr->JPEG_error)) { - char sv[BUZ_NUM_STAT_COM + 1]; - int i; - - printk(KERN_INFO - "%s: first frame ready: state=0x%08x odd_even=%d field_per_buff=%d delay=%d\n", - ZR_DEVNAME(zr), stat, - zr->jpg_settings.odd_even, - zr->jpg_settings.field_per_buff, - zr->JPEG_missed); - - for (i = 0; i < BUZ_NUM_STAT_COM; i++) - sv[i] = le32_to_cpu(zr->stat_com[i]) & 1 ? '1' : '0'; - sv[BUZ_NUM_STAT_COM] = 0; - printk(KERN_INFO - "%s: stat_com=%s queue_state=%ld/%ld/%ld/%ld\n", - ZR_DEVNAME(zr), sv, - zr->jpg_que_tail, - zr->jpg_dma_tail, - zr->jpg_dma_head, - zr->jpg_que_head); - } else { - /* Get statistics */ - if (zr->JPEG_missed > zr->JPEG_max_missed) - zr->JPEG_max_missed = zr->JPEG_missed; - if (zr->JPEG_missed < zr->JPEG_min_missed) - zr->JPEG_min_missed = zr->JPEG_missed; - } - - if (zr36067_debug > 2 && zr->frame_num < 6) { - int i; - - printk(KERN_INFO "%s: seq=%ld stat_com:", - ZR_DEVNAME(zr), zr->jpg_seq_num); - for (i = 0; i < 4; i++) { - printk(KERN_CONT " %08x", - le32_to_cpu(zr->stat_com[i])); - } - printk(KERN_CONT "\n"); - } - zr->frame_num++; - zr->JPEG_missed = 0; - zr->JPEG_error = 0; - zoran_reap_stat_com(zr); - zoran_feed_stat_com(zr); - wake_up_interruptible(&zr->jpg_capq); - } -#endif /* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */ - - /* DATERR, too many fields missed, error processing */ - if ((astat & zr->card.jpeg_int) || - zr->JPEG_missed > 25 || - zr->JPEG_error == 1 || - ((zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) && - (zr->frame_num && (zr->JPEG_missed > zr->jpg_settings.field_per_buff)))) { - error_handler(zr, astat, stat); - } - - count++; - if (count > 10) { - dprintk(2, KERN_WARNING "%s: irq loop %d\n", - ZR_DEVNAME(zr), count); - if (count > 20) { - btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR); - dprintk(2, - KERN_ERR - "%s: IRQ lockup, cleared int mask\n", - ZR_DEVNAME(zr)); - break; - } - } - zr->last_isr = stat; - } - spin_unlock_irqrestore(&zr->spinlock, flags); - - return IRQ_HANDLED; -} - -void -zoran_set_pci_master (struct zoran *zr, - int set_master) -{ - if (set_master) { - pci_set_master(zr->pci_dev); - } else { - u16 command; - - pci_read_config_word(zr->pci_dev, PCI_COMMAND, &command); - command &= ~PCI_COMMAND_MASTER; - pci_write_config_word(zr->pci_dev, PCI_COMMAND, command); - } -} - -void -zoran_init_hardware (struct zoran *zr) -{ - /* Enable bus-mastering */ - zoran_set_pci_master(zr, 1); - - /* Initialize the board */ - if (zr->card.init) { - zr->card.init(zr); - } - - decoder_call(zr, core, init, 0); - decoder_call(zr, video, s_std, zr->norm); - decoder_call(zr, video, s_routing, - zr->card.input[zr->input].muxsel, 0, 0); - - encoder_call(zr, core, init, 0); - encoder_call(zr, video, s_std_output, zr->norm); - encoder_call(zr, video, s_routing, 0, 0, 0); - - /* toggle JPEG codec sleep to sync PLL */ - jpeg_codec_sleep(zr, 1); - jpeg_codec_sleep(zr, 0); - - /* - * set individual interrupt enables (without GIRQ1) - * but don't global enable until zoran_open() - */ - zr36057_init_vfe(zr); - - zr36057_enable_jpg(zr, BUZ_MODE_IDLE); - - btwrite(IRQ_MASK, ZR36057_ISR); // Clears interrupts -} - -void -zr36057_restart (struct zoran *zr) -{ - btwrite(0, ZR36057_SPGPPCR); - mdelay(1); - btor(ZR36057_SPGPPCR_SoftReset, ZR36057_SPGPPCR); - mdelay(1); - - /* assert P_Reset */ - btwrite(0, ZR36057_JPC); - /* set up GPIO direction - all output */ - btwrite(ZR36057_SPGPPCR_SoftReset | 0, ZR36057_SPGPPCR); - - /* set up GPIO pins and guest bus timing */ - btwrite((0x81 << 24) | 0x8888, ZR36057_GPPGCR1); -} - -/* - * initialize video front end - */ - -static void -zr36057_init_vfe (struct zoran *zr) -{ - u32 reg; - - reg = btread(ZR36057_VFESPFR); - reg |= ZR36057_VFESPFR_LittleEndian; - reg &= ~ZR36057_VFESPFR_VCLKPol; - reg |= ZR36057_VFESPFR_ExtFl; - reg |= ZR36057_VFESPFR_TopField; - btwrite(reg, ZR36057_VFESPFR); - reg = btread(ZR36057_VDCR); - if (pci_pci_problems & PCIPCI_TRITON) - // || zr->revision < 1) // Revision 1 has also Triton support - reg &= ~ZR36057_VDCR_Triton; - else - reg |= ZR36057_VDCR_Triton; - btwrite(reg, ZR36057_VDCR); -} diff --git a/drivers/media/pci/zoran/zoran_device.h b/drivers/media/pci/zoran/zoran_device.h deleted file mode 100644 index a507aaad4ebb..000000000000 --- a/drivers/media/pci/zoran/zoran_device.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * This part handles card-specific data and detection - * - * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> - * - * Currently maintained by: - * Ronald Bultje <rbultje@ronald.bitfreak.net> - * Laurent Pinchart <laurent.pinchart@skynet.be> - * Mailinglist <mjpeg-users@lists.sf.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __ZORAN_DEVICE_H__ -#define __ZORAN_DEVICE_H__ - -/* general purpose I/O */ -extern void GPIO(struct zoran *zr, - int bit, - unsigned int value); - -/* codec (or actually: guest bus) access */ -extern int post_office_wait(struct zoran *zr); -extern int post_office_write(struct zoran *zr, - unsigned guest, - unsigned reg, - unsigned value); -extern int post_office_read(struct zoran *zr, - unsigned guest, - unsigned reg); - -extern void detect_guest_activity(struct zoran *zr); - -extern void jpeg_codec_sleep(struct zoran *zr, - int sleep); -extern int jpeg_codec_reset(struct zoran *zr); - -/* zr360x7 access to raw capture */ -extern void zr36057_overlay(struct zoran *zr, - int on); -extern void write_overlay_mask(struct zoran_fh *fh, - struct v4l2_clip *vp, - int count); -extern void zr36057_set_memgrab(struct zoran *zr, - int mode); -extern int wait_grab_pending(struct zoran *zr); - -/* interrupts */ -extern void print_interrupts(struct zoran *zr); -extern void clear_interrupt_counters(struct zoran *zr); -extern irqreturn_t zoran_irq(int irq, void *dev_id); - -/* JPEG codec access */ -extern void jpeg_start(struct zoran *zr); -extern void zr36057_enable_jpg(struct zoran *zr, - enum zoran_codec_mode mode); -extern void zoran_feed_stat_com(struct zoran *zr); - -/* general */ -extern void zoran_set_pci_master(struct zoran *zr, - int set_master); -extern void zoran_init_hardware(struct zoran *zr); -extern void zr36057_restart(struct zoran *zr); - -extern const struct zoran_format zoran_formats[]; - -extern int v4l_nbufs; -extern int v4l_bufsize; -extern int jpg_nbufs; -extern int jpg_bufsize; -extern int pass_through; - -/* i2c */ -#define decoder_call(zr, o, f, args...) \ - v4l2_subdev_call(zr->decoder, o, f, ##args) -#define encoder_call(zr, o, f, args...) \ - v4l2_subdev_call(zr->encoder, o, f, ##args) - -#endif /* __ZORAN_DEVICE_H__ */ diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c deleted file mode 100644 index 14f9c0e26a1c..000000000000 --- a/drivers/media/pci/zoran/zoran_driver.c +++ /dev/null @@ -1,2849 +0,0 @@ -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> - * - * Changes for BUZ by Wolfgang Scherr <scherr@net4you.net> - * - * Changes for DC10/DC30 by Laurent Pinchart <laurent.pinchart@skynet.be> - * - * Changes for LML33R10 by Maxim Yevtyushkin <max@linuxmedialabs.com> - * - * Changes for videodev2/v4l2 by Ronald Bultje <rbultje@ronald.bitfreak.net> - * - * Based on - * - * Miro DC10 driver - * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net> - * - * Iomega Buz driver version 1.0 - * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de> - * - * buz.0.0.3 - * Copyright (C) 1998 Dave Perks <dperks@ibm.net> - * - * bttv - Bt848 frame grabber driver - * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) - * & Marcus Metzler (mocm@thp.uni-koeln.de) - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <linux/vmalloc.h> -#include <linux/wait.h> - -#include <linux/interrupt.h> -#include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> - -#include <linux/spinlock.h> - -#include <linux/videodev2.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-event.h> -#include "videocodec.h" - -#include <asm/byteorder.h> -#include <asm/io.h> -#include <linux/uaccess.h> -#include <linux/proc_fs.h> - -#include <linux/mutex.h> -#include "zoran.h" -#include "zoran_device.h" -#include "zoran_card.h" - - -const struct zoran_format zoran_formats[] = { - { - .name = "15-bit RGB LE", - .fourcc = V4L2_PIX_FMT_RGB555, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 15, - .flags = ZORAN_FORMAT_CAPTURE | - ZORAN_FORMAT_OVERLAY, - .vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif| - ZR36057_VFESPFR_LittleEndian, - }, { - .name = "15-bit RGB BE", - .fourcc = V4L2_PIX_FMT_RGB555X, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 15, - .flags = ZORAN_FORMAT_CAPTURE | - ZORAN_FORMAT_OVERLAY, - .vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif, - }, { - .name = "16-bit RGB LE", - .fourcc = V4L2_PIX_FMT_RGB565, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 16, - .flags = ZORAN_FORMAT_CAPTURE | - ZORAN_FORMAT_OVERLAY, - .vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif| - ZR36057_VFESPFR_LittleEndian, - }, { - .name = "16-bit RGB BE", - .fourcc = V4L2_PIX_FMT_RGB565X, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 16, - .flags = ZORAN_FORMAT_CAPTURE | - ZORAN_FORMAT_OVERLAY, - .vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif, - }, { - .name = "24-bit RGB", - .fourcc = V4L2_PIX_FMT_BGR24, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 24, - .flags = ZORAN_FORMAT_CAPTURE | - ZORAN_FORMAT_OVERLAY, - .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_Pack24, - }, { - .name = "32-bit RGB LE", - .fourcc = V4L2_PIX_FMT_BGR32, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 32, - .flags = ZORAN_FORMAT_CAPTURE | - ZORAN_FORMAT_OVERLAY, - .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_LittleEndian, - }, { - .name = "32-bit RGB BE", - .fourcc = V4L2_PIX_FMT_RGB32, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 32, - .flags = ZORAN_FORMAT_CAPTURE | - ZORAN_FORMAT_OVERLAY, - .vfespfr = ZR36057_VFESPFR_RGB888, - }, { - .name = "4:2:2, packed, YUYV", - .fourcc = V4L2_PIX_FMT_YUYV, - .colorspace = V4L2_COLORSPACE_SMPTE170M, - .depth = 16, - .flags = ZORAN_FORMAT_CAPTURE | - ZORAN_FORMAT_OVERLAY, - .vfespfr = ZR36057_VFESPFR_YUV422, - }, { - .name = "4:2:2, packed, UYVY", - .fourcc = V4L2_PIX_FMT_UYVY, - .colorspace = V4L2_COLORSPACE_SMPTE170M, - .depth = 16, - .flags = ZORAN_FORMAT_CAPTURE | - ZORAN_FORMAT_OVERLAY, - .vfespfr = ZR36057_VFESPFR_YUV422|ZR36057_VFESPFR_LittleEndian, - }, { - .name = "Hardware-encoded Motion-JPEG", - .fourcc = V4L2_PIX_FMT_MJPEG, - .colorspace = V4L2_COLORSPACE_SMPTE170M, - .depth = 0, - .flags = ZORAN_FORMAT_CAPTURE | - ZORAN_FORMAT_PLAYBACK | - ZORAN_FORMAT_COMPRESSED, - } -}; -#define NUM_FORMATS ARRAY_SIZE(zoran_formats) - - /* small helper function for calculating buffersizes for v4l2 - * we calculate the nearest higher power-of-two, which - * will be the recommended buffersize */ -static __u32 -zoran_v4l2_calc_bufsize (struct zoran_jpg_settings *settings) -{ - __u8 div = settings->VerDcm * settings->HorDcm * settings->TmpDcm; - __u32 num = (1024 * 512) / (div); - __u32 result = 2; - - num--; - while (num) { - num >>= 1; - result <<= 1; - } - - if (result > jpg_bufsize) - return jpg_bufsize; - if (result < 8192) - return 8192; - return result; -} - -/* forward references */ -static void v4l_fbuffer_free(struct zoran_fh *fh); -static void jpg_fbuffer_free(struct zoran_fh *fh); - -/* Set mapping mode */ -static void map_mode_raw(struct zoran_fh *fh) -{ - fh->map_mode = ZORAN_MAP_MODE_RAW; - fh->buffers.buffer_size = v4l_bufsize; - fh->buffers.num_buffers = v4l_nbufs; -} -static void map_mode_jpg(struct zoran_fh *fh, int play) -{ - fh->map_mode = play ? ZORAN_MAP_MODE_JPG_PLAY : ZORAN_MAP_MODE_JPG_REC; - fh->buffers.buffer_size = jpg_bufsize; - fh->buffers.num_buffers = jpg_nbufs; -} -static inline const char *mode_name(enum zoran_map_mode mode) -{ - return mode == ZORAN_MAP_MODE_RAW ? "V4L" : "JPG"; -} - -/* - * Allocate the V4L grab buffers - * - * These have to be pysically contiguous. - */ - -static int v4l_fbuffer_alloc(struct zoran_fh *fh) -{ - struct zoran *zr = fh->zr; - int i, off; - unsigned char *mem; - - for (i = 0; i < fh->buffers.num_buffers; i++) { - if (fh->buffers.buffer[i].v4l.fbuffer) - dprintk(2, - KERN_WARNING - "%s: %s - buffer %d already allocated!?\n", - ZR_DEVNAME(zr), __func__, i); - - //udelay(20); - mem = kmalloc(fh->buffers.buffer_size, - GFP_KERNEL | __GFP_NOWARN); - if (!mem) { - dprintk(1, - KERN_ERR - "%s: %s - kmalloc for V4L buf %d failed\n", - ZR_DEVNAME(zr), __func__, i); - v4l_fbuffer_free(fh); - return -ENOBUFS; - } - fh->buffers.buffer[i].v4l.fbuffer = mem; - fh->buffers.buffer[i].v4l.fbuffer_phys = virt_to_phys(mem); - fh->buffers.buffer[i].v4l.fbuffer_bus = virt_to_bus(mem); - for (off = 0; off < fh->buffers.buffer_size; - off += PAGE_SIZE) - SetPageReserved(virt_to_page(mem + off)); - dprintk(4, - KERN_INFO - "%s: %s - V4L frame %d mem %p (bus: 0x%llx)\n", - ZR_DEVNAME(zr), __func__, i, mem, - (unsigned long long)virt_to_bus(mem)); - } - - fh->buffers.allocated = 1; - - return 0; -} - -/* free the V4L grab buffers */ -static void v4l_fbuffer_free(struct zoran_fh *fh) -{ - struct zoran *zr = fh->zr; - int i, off; - unsigned char *mem; - - dprintk(4, KERN_INFO "%s: %s\n", ZR_DEVNAME(zr), __func__); - - for (i = 0; i < fh->buffers.num_buffers; i++) { - if (!fh->buffers.buffer[i].v4l.fbuffer) - continue; - - mem = fh->buffers.buffer[i].v4l.fbuffer; - for (off = 0; off < fh->buffers.buffer_size; - off += PAGE_SIZE) - ClearPageReserved(virt_to_page(mem + off)); - kfree(fh->buffers.buffer[i].v4l.fbuffer); - fh->buffers.buffer[i].v4l.fbuffer = NULL; - } - - fh->buffers.allocated = 0; -} - -/* - * Allocate the MJPEG grab buffers. - * - * If a Natoma chipset is present and this is a revision 1 zr36057, - * each MJPEG buffer needs to be physically contiguous. - * (RJ: This statement is from Dave Perks' original driver, - * I could never check it because I have a zr36067) - * - * RJ: The contents grab buffers needs never be accessed in the driver. - * Therefore there is no need to allocate them with vmalloc in order - * to get a contiguous virtual memory space. - * I don't understand why many other drivers first allocate them with - * vmalloc (which uses internally also get_zeroed_page, but delivers you - * virtual addresses) and then again have to make a lot of efforts - * to get the physical address. - * - * Ben Capper: - * On big-endian architectures (such as ppc) some extra steps - * are needed. When reading and writing to the stat_com array - * and fragment buffers, the device expects to see little- - * endian values. The use of cpu_to_le32() and le32_to_cpu() - * in this function (and one or two others in zoran_device.c) - * ensure that these values are always stored in little-endian - * form, regardless of architecture. The zr36057 does Very Bad - * Things on big endian architectures if the stat_com array - * and fragment buffers are not little-endian. - */ - -static int jpg_fbuffer_alloc(struct zoran_fh *fh) -{ - struct zoran *zr = fh->zr; - int i, j, off; - u8 *mem; - - for (i = 0; i < fh->buffers.num_buffers; i++) { - if (fh->buffers.buffer[i].jpg.frag_tab) - dprintk(2, - KERN_WARNING - "%s: %s - buffer %d already allocated!?\n", - ZR_DEVNAME(zr), __func__, i); - - /* Allocate fragment table for this buffer */ - - mem = (void *)get_zeroed_page(GFP_KERNEL); - if (!mem) { - dprintk(1, - KERN_ERR - "%s: %s - get_zeroed_page (frag_tab) failed for buffer %d\n", - ZR_DEVNAME(zr), __func__, i); - jpg_fbuffer_free(fh); - return -ENOBUFS; - } - fh->buffers.buffer[i].jpg.frag_tab = (__le32 *)mem; - fh->buffers.buffer[i].jpg.frag_tab_bus = virt_to_bus(mem); - - if (fh->buffers.need_contiguous) { - mem = kmalloc(fh->buffers.buffer_size, GFP_KERNEL); - if (mem == NULL) { - dprintk(1, - KERN_ERR - "%s: %s - kmalloc failed for buffer %d\n", - ZR_DEVNAME(zr), __func__, i); - jpg_fbuffer_free(fh); - return -ENOBUFS; - } - fh->buffers.buffer[i].jpg.frag_tab[0] = - cpu_to_le32(virt_to_bus(mem)); - fh->buffers.buffer[i].jpg.frag_tab[1] = - cpu_to_le32((fh->buffers.buffer_size >> 1) | 1); - for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE) - SetPageReserved(virt_to_page(mem + off)); - } else { - /* jpg_bufsize is already page aligned */ - for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) { - mem = (void *)get_zeroed_page(GFP_KERNEL); - if (mem == NULL) { - dprintk(1, - KERN_ERR - "%s: %s - get_zeroed_page failed for buffer %d\n", - ZR_DEVNAME(zr), __func__, i); - jpg_fbuffer_free(fh); - return -ENOBUFS; - } - - fh->buffers.buffer[i].jpg.frag_tab[2 * j] = - cpu_to_le32(virt_to_bus(mem)); - fh->buffers.buffer[i].jpg.frag_tab[2 * j + 1] = - cpu_to_le32((PAGE_SIZE >> 2) << 1); - SetPageReserved(virt_to_page(mem)); - } - - fh->buffers.buffer[i].jpg.frag_tab[2 * j - 1] |= cpu_to_le32(1); - } - } - - dprintk(4, - KERN_DEBUG "%s: %s - %d KB allocated\n", - ZR_DEVNAME(zr), __func__, - (fh->buffers.num_buffers * fh->buffers.buffer_size) >> 10); - - fh->buffers.allocated = 1; - - return 0; -} - -/* free the MJPEG grab buffers */ -static void jpg_fbuffer_free(struct zoran_fh *fh) -{ - struct zoran *zr = fh->zr; - int i, j, off; - unsigned char *mem; - __le32 frag_tab; - struct zoran_buffer *buffer; - - dprintk(4, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); - - for (i = 0, buffer = &fh->buffers.buffer[0]; - i < fh->buffers.num_buffers; i++, buffer++) { - if (!buffer->jpg.frag_tab) - continue; - - if (fh->buffers.need_contiguous) { - frag_tab = buffer->jpg.frag_tab[0]; - - if (frag_tab) { - mem = bus_to_virt(le32_to_cpu(frag_tab)); - for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE) - ClearPageReserved(virt_to_page(mem + off)); - kfree(mem); - buffer->jpg.frag_tab[0] = 0; - buffer->jpg.frag_tab[1] = 0; - } - } else { - for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) { - frag_tab = buffer->jpg.frag_tab[2 * j]; - - if (!frag_tab) - break; - ClearPageReserved(virt_to_page(bus_to_virt(le32_to_cpu(frag_tab)))); - free_page((unsigned long)bus_to_virt(le32_to_cpu(frag_tab))); - buffer->jpg.frag_tab[2 * j] = 0; - buffer->jpg.frag_tab[2 * j + 1] = 0; - } - } - - free_page((unsigned long)buffer->jpg.frag_tab); - buffer->jpg.frag_tab = NULL; - } - - fh->buffers.allocated = 0; -} - -/* - * V4L Buffer grabbing - */ - -static int -zoran_v4l_set_format (struct zoran_fh *fh, - int width, - int height, - const struct zoran_format *format) -{ - struct zoran *zr = fh->zr; - int bpp; - - /* Check size and format of the grab wanted */ - - if (height < BUZ_MIN_HEIGHT || width < BUZ_MIN_WIDTH || - height > BUZ_MAX_HEIGHT || width > BUZ_MAX_WIDTH) { - dprintk(1, - KERN_ERR - "%s: %s - wrong frame size (%dx%d)\n", - ZR_DEVNAME(zr), __func__, width, height); - return -EINVAL; - } - - bpp = (format->depth + 7) / 8; - - /* Check against available buffer size */ - if (height * width * bpp > fh->buffers.buffer_size) { - dprintk(1, - KERN_ERR - "%s: %s - video buffer size (%d kB) is too small\n", - ZR_DEVNAME(zr), __func__, fh->buffers.buffer_size >> 10); - return -EINVAL; - } - - /* The video front end needs 4-byte alinged line sizes */ - - if ((bpp == 2 && (width & 1)) || (bpp == 3 && (width & 3))) { - dprintk(1, - KERN_ERR - "%s: %s - wrong frame alignment\n", - ZR_DEVNAME(zr), __func__); - return -EINVAL; - } - - fh->v4l_settings.width = width; - fh->v4l_settings.height = height; - fh->v4l_settings.format = format; - fh->v4l_settings.bytesperline = bpp * fh->v4l_settings.width; - - return 0; -} - -static int zoran_v4l_queue_frame(struct zoran_fh *fh, int num) -{ - struct zoran *zr = fh->zr; - unsigned long flags; - int res = 0; - - if (!fh->buffers.allocated) { - dprintk(1, - KERN_ERR - "%s: %s - buffers not yet allocated\n", - ZR_DEVNAME(zr), __func__); - res = -ENOMEM; - } - - /* No grabbing outside the buffer range! */ - if (num >= fh->buffers.num_buffers || num < 0) { - dprintk(1, - KERN_ERR - "%s: %s - buffer %d is out of range\n", - ZR_DEVNAME(zr), __func__, num); - res = -EINVAL; - } - - spin_lock_irqsave(&zr->spinlock, flags); - - if (fh->buffers.active == ZORAN_FREE) { - if (zr->v4l_buffers.active == ZORAN_FREE) { - zr->v4l_buffers = fh->buffers; - fh->buffers.active = ZORAN_ACTIVE; - } else { - dprintk(1, - KERN_ERR - "%s: %s - another session is already capturing\n", - ZR_DEVNAME(zr), __func__); - res = -EBUSY; - } - } - - /* make sure a grab isn't going on currently with this buffer */ - if (!res) { - switch (zr->v4l_buffers.buffer[num].state) { - default: - case BUZ_STATE_PEND: - if (zr->v4l_buffers.active == ZORAN_FREE) { - fh->buffers.active = ZORAN_FREE; - zr->v4l_buffers.allocated = 0; - } - res = -EBUSY; /* what are you doing? */ - break; - case BUZ_STATE_DONE: - dprintk(2, - KERN_WARNING - "%s: %s - queueing buffer %d in state DONE!?\n", - ZR_DEVNAME(zr), __func__, num); - /* fall through */ - case BUZ_STATE_USER: - /* since there is at least one unused buffer there's room for at least - * one more pend[] entry */ - zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] = num; - zr->v4l_buffers.buffer[num].state = BUZ_STATE_PEND; - zr->v4l_buffers.buffer[num].bs.length = - fh->v4l_settings.bytesperline * - zr->v4l_settings.height; - fh->buffers.buffer[num] = zr->v4l_buffers.buffer[num]; - break; - } - } - - spin_unlock_irqrestore(&zr->spinlock, flags); - - if (!res && zr->v4l_buffers.active == ZORAN_FREE) - zr->v4l_buffers.active = fh->buffers.active; - - return res; -} - -/* - * Sync on a V4L buffer - */ - -static int v4l_sync(struct zoran_fh *fh, int frame) -{ - struct zoran *zr = fh->zr; - unsigned long flags; - - if (fh->buffers.active == ZORAN_FREE) { - dprintk(1, - KERN_ERR - "%s: %s - no grab active for this session\n", - ZR_DEVNAME(zr), __func__); - return -EINVAL; - } - - /* check passed-in frame number */ - if (frame >= fh->buffers.num_buffers || frame < 0) { - dprintk(1, - KERN_ERR "%s: %s - frame %d is invalid\n", - ZR_DEVNAME(zr), __func__, frame); - return -EINVAL; - } - - /* Check if is buffer was queued at all */ - if (zr->v4l_buffers.buffer[frame].state == BUZ_STATE_USER) { - dprintk(1, - KERN_ERR - "%s: %s - attempt to sync on a buffer which was not queued?\n", - ZR_DEVNAME(zr), __func__); - return -EPROTO; - } - - mutex_unlock(&zr->lock); - /* wait on this buffer to get ready */ - if (!wait_event_interruptible_timeout(zr->v4l_capq, - (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), 10*HZ)) { - mutex_lock(&zr->lock); - return -ETIME; - } - mutex_lock(&zr->lock); - if (signal_pending(current)) - return -ERESTARTSYS; - - /* buffer should now be in BUZ_STATE_DONE */ - if (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_DONE) - dprintk(2, - KERN_ERR "%s: %s - internal state error\n", - ZR_DEVNAME(zr), __func__); - - zr->v4l_buffers.buffer[frame].state = BUZ_STATE_USER; - fh->buffers.buffer[frame] = zr->v4l_buffers.buffer[frame]; - - spin_lock_irqsave(&zr->spinlock, flags); - - /* Check if streaming capture has finished */ - if (zr->v4l_pend_tail == zr->v4l_pend_head) { - zr36057_set_memgrab(zr, 0); - if (zr->v4l_buffers.active == ZORAN_ACTIVE) { - fh->buffers.active = zr->v4l_buffers.active = ZORAN_FREE; - zr->v4l_buffers.allocated = 0; - } - } - - spin_unlock_irqrestore(&zr->spinlock, flags); - - return 0; -} - -/* - * Queue a MJPEG buffer for capture/playback - */ - -static int zoran_jpg_queue_frame(struct zoran_fh *fh, int num, - enum zoran_codec_mode mode) -{ - struct zoran *zr = fh->zr; - unsigned long flags; - int res = 0; - - /* Check if buffers are allocated */ - if (!fh->buffers.allocated) { - dprintk(1, - KERN_ERR - "%s: %s - buffers not yet allocated\n", - ZR_DEVNAME(zr), __func__); - return -ENOMEM; - } - - /* No grabbing outside the buffer range! */ - if (num >= fh->buffers.num_buffers || num < 0) { - dprintk(1, - KERN_ERR - "%s: %s - buffer %d out of range\n", - ZR_DEVNAME(zr), __func__, num); - return -EINVAL; - } - - /* what is the codec mode right now? */ - if (zr->codec_mode == BUZ_MODE_IDLE) { - zr->jpg_settings = fh->jpg_settings; - } else if (zr->codec_mode != mode) { - /* wrong codec mode active - invalid */ - dprintk(1, - KERN_ERR - "%s: %s - codec in wrong mode\n", - ZR_DEVNAME(zr), __func__); - return -EINVAL; - } - - if (fh->buffers.active == ZORAN_FREE) { - if (zr->jpg_buffers.active == ZORAN_FREE) { - zr->jpg_buffers = fh->buffers; - fh->buffers.active = ZORAN_ACTIVE; - } else { - dprintk(1, - KERN_ERR - "%s: %s - another session is already capturing\n", - ZR_DEVNAME(zr), __func__); - res = -EBUSY; - } - } - - if (!res && zr->codec_mode == BUZ_MODE_IDLE) { - /* Ok load up the jpeg codec */ - zr36057_enable_jpg(zr, mode); - } - - spin_lock_irqsave(&zr->spinlock, flags); - - if (!res) { - switch (zr->jpg_buffers.buffer[num].state) { - case BUZ_STATE_DONE: - dprintk(2, - KERN_WARNING - "%s: %s - queing frame in BUZ_STATE_DONE state!?\n", - ZR_DEVNAME(zr), __func__); - /* fall through */ - case BUZ_STATE_USER: - /* since there is at least one unused buffer there's room for at - *least one more pend[] entry */ - zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = num; - zr->jpg_buffers.buffer[num].state = BUZ_STATE_PEND; - fh->buffers.buffer[num] = zr->jpg_buffers.buffer[num]; - zoran_feed_stat_com(zr); - break; - default: - case BUZ_STATE_DMA: - case BUZ_STATE_PEND: - if (zr->jpg_buffers.active == ZORAN_FREE) { - fh->buffers.active = ZORAN_FREE; - zr->jpg_buffers.allocated = 0; - } - res = -EBUSY; /* what are you doing? */ - break; - } - } - - spin_unlock_irqrestore(&zr->spinlock, flags); - - if (!res && zr->jpg_buffers.active == ZORAN_FREE) - zr->jpg_buffers.active = fh->buffers.active; - - return res; -} - -static int jpg_qbuf(struct zoran_fh *fh, int frame, enum zoran_codec_mode mode) -{ - struct zoran *zr = fh->zr; - int res = 0; - - /* Does the user want to stop streaming? */ - if (frame < 0) { - if (zr->codec_mode == mode) { - if (fh->buffers.active == ZORAN_FREE) { - dprintk(1, - KERN_ERR - "%s: %s(-1) - session not active\n", - ZR_DEVNAME(zr), __func__); - return -EINVAL; - } - fh->buffers.active = zr->jpg_buffers.active = ZORAN_FREE; - zr->jpg_buffers.allocated = 0; - zr36057_enable_jpg(zr, BUZ_MODE_IDLE); - return 0; - } else { - dprintk(1, - KERN_ERR - "%s: %s - stop streaming but not in streaming mode\n", - ZR_DEVNAME(zr), __func__); - return -EINVAL; - } - } - - if ((res = zoran_jpg_queue_frame(fh, frame, mode))) - return res; - - /* Start the jpeg codec when the first frame is queued */ - if (!res && zr->jpg_que_head == 1) - jpeg_start(zr); - - return res; -} - -/* - * Sync on a MJPEG buffer - */ - -static int jpg_sync(struct zoran_fh *fh, struct zoran_sync *bs) -{ - struct zoran *zr = fh->zr; - unsigned long flags; - int frame; - - if (fh->buffers.active == ZORAN_FREE) { - dprintk(1, - KERN_ERR - "%s: %s - capture is not currently active\n", - ZR_DEVNAME(zr), __func__); - return -EINVAL; - } - if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS && - zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) { - dprintk(1, - KERN_ERR - "%s: %s - codec not in streaming mode\n", - ZR_DEVNAME(zr), __func__); - return -EINVAL; - } - mutex_unlock(&zr->lock); - if (!wait_event_interruptible_timeout(zr->jpg_capq, - (zr->jpg_que_tail != zr->jpg_dma_tail || - zr->jpg_dma_tail == zr->jpg_dma_head), - 10*HZ)) { - int isr; - - btand(~ZR36057_JMC_Go_en, ZR36057_JMC); - udelay(1); - zr->codec->control(zr->codec, CODEC_G_STATUS, - sizeof(isr), &isr); - mutex_lock(&zr->lock); - dprintk(1, - KERN_ERR - "%s: %s - timeout: codec isr=0x%02x\n", - ZR_DEVNAME(zr), __func__, isr); - - return -ETIME; - - } - mutex_lock(&zr->lock); - if (signal_pending(current)) - return -ERESTARTSYS; - - spin_lock_irqsave(&zr->spinlock, flags); - - if (zr->jpg_dma_tail != zr->jpg_dma_head) - frame = zr->jpg_pend[zr->jpg_que_tail++ & BUZ_MASK_FRAME]; - else - frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME]; - - /* buffer should now be in BUZ_STATE_DONE */ - if (zr->jpg_buffers.buffer[frame].state != BUZ_STATE_DONE) - dprintk(2, - KERN_ERR "%s: %s - internal state error\n", - ZR_DEVNAME(zr), __func__); - - *bs = zr->jpg_buffers.buffer[frame].bs; - bs->frame = frame; - zr->jpg_buffers.buffer[frame].state = BUZ_STATE_USER; - fh->buffers.buffer[frame] = zr->jpg_buffers.buffer[frame]; - - spin_unlock_irqrestore(&zr->spinlock, flags); - - return 0; -} - -static void zoran_open_init_session(struct zoran_fh *fh) -{ - int i; - struct zoran *zr = fh->zr; - - /* Per default, map the V4L Buffers */ - map_mode_raw(fh); - - /* take over the card's current settings */ - fh->overlay_settings = zr->overlay_settings; - fh->overlay_settings.is_set = 0; - fh->overlay_settings.format = zr->overlay_settings.format; - fh->overlay_active = ZORAN_FREE; - - /* v4l settings */ - fh->v4l_settings = zr->v4l_settings; - /* jpg settings */ - fh->jpg_settings = zr->jpg_settings; - - /* buffers */ - memset(&fh->buffers, 0, sizeof(fh->buffers)); - for (i = 0; i < MAX_FRAME; i++) { - fh->buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ - fh->buffers.buffer[i].bs.frame = i; - } - fh->buffers.allocated = 0; - fh->buffers.active = ZORAN_FREE; -} - -static void zoran_close_end_session(struct zoran_fh *fh) -{ - struct zoran *zr = fh->zr; - - /* overlay */ - if (fh->overlay_active != ZORAN_FREE) { - fh->overlay_active = zr->overlay_active = ZORAN_FREE; - zr->v4l_overlay_active = 0; - if (!zr->v4l_memgrab_active) - zr36057_overlay(zr, 0); - zr->overlay_mask = NULL; - } - - if (fh->map_mode == ZORAN_MAP_MODE_RAW) { - /* v4l capture */ - if (fh->buffers.active != ZORAN_FREE) { - unsigned long flags; - - spin_lock_irqsave(&zr->spinlock, flags); - zr36057_set_memgrab(zr, 0); - zr->v4l_buffers.allocated = 0; - zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE; - spin_unlock_irqrestore(&zr->spinlock, flags); - } - - /* v4l buffers */ - if (fh->buffers.allocated) - v4l_fbuffer_free(fh); - } else { - /* jpg capture */ - if (fh->buffers.active != ZORAN_FREE) { - zr36057_enable_jpg(zr, BUZ_MODE_IDLE); - zr->jpg_buffers.allocated = 0; - zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE; - } - - /* jpg buffers */ - if (fh->buffers.allocated) - jpg_fbuffer_free(fh); - } -} - -/* - * Open a zoran card. Right now the flags stuff is just playing - */ - -static int zoran_open(struct file *file) -{ - struct zoran *zr = video_drvdata(file); - struct zoran_fh *fh; - int res, first_open = 0; - - dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(-)=%d\n", - ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user + 1); - - mutex_lock(&zr->lock); - - if (zr->user >= 2048) { - dprintk(1, KERN_ERR "%s: too many users (%d) on device\n", - ZR_DEVNAME(zr), zr->user); - res = -EBUSY; - goto fail_unlock; - } - - /* now, create the open()-specific file_ops struct */ - fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL); - if (!fh) { - dprintk(1, - KERN_ERR - "%s: %s - allocation of zoran_fh failed\n", - ZR_DEVNAME(zr), __func__); - res = -ENOMEM; - goto fail_unlock; - } - v4l2_fh_init(&fh->fh, video_devdata(file)); - - /* used to be BUZ_MAX_WIDTH/HEIGHT, but that gives overflows - * on norm-change! */ - fh->overlay_mask = - kmalloc(((768 + 31) / 32) * 576 * 4, GFP_KERNEL); - if (!fh->overlay_mask) { - dprintk(1, - KERN_ERR - "%s: %s - allocation of overlay_mask failed\n", - ZR_DEVNAME(zr), __func__); - res = -ENOMEM; - goto fail_fh; - } - - if (zr->user++ == 0) - first_open = 1; - - /* default setup - TODO: look at flags */ - if (first_open) { /* First device open */ - zr36057_restart(zr); - zoran_open_init_params(zr); - zoran_init_hardware(zr); - - btor(ZR36057_ICR_IntPinEn, ZR36057_ICR); - } - - /* set file_ops stuff */ - file->private_data = fh; - fh->zr = zr; - zoran_open_init_session(fh); - v4l2_fh_add(&fh->fh); - mutex_unlock(&zr->lock); - - return 0; - -fail_fh: - v4l2_fh_exit(&fh->fh); - kfree(fh); -fail_unlock: - mutex_unlock(&zr->lock); - - dprintk(2, KERN_INFO "%s: open failed (%d), users(-)=%d\n", - ZR_DEVNAME(zr), res, zr->user); - - return res; -} - -static int -zoran_close(struct file *file) -{ - struct zoran_fh *fh = file->private_data; - struct zoran *zr = fh->zr; - - dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(+)=%d\n", - ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user - 1); - - /* kernel locks (fs/device.c), so don't do that ourselves - * (prevents deadlocks) */ - mutex_lock(&zr->lock); - - zoran_close_end_session(fh); - - if (zr->user-- == 1) { /* Last process */ - /* Clean up JPEG process */ - wake_up_interruptible(&zr->jpg_capq); - zr36057_enable_jpg(zr, BUZ_MODE_IDLE); - zr->jpg_buffers.allocated = 0; - zr->jpg_buffers.active = ZORAN_FREE; - - /* disable interrupts */ - btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR); - - if (zr36067_debug > 1) - print_interrupts(zr); - - /* Overlay off */ - zr->v4l_overlay_active = 0; - zr36057_overlay(zr, 0); - zr->overlay_mask = NULL; - - /* capture off */ - wake_up_interruptible(&zr->v4l_capq); - zr36057_set_memgrab(zr, 0); - zr->v4l_buffers.allocated = 0; - zr->v4l_buffers.active = ZORAN_FREE; - zoran_set_pci_master(zr, 0); - - if (!pass_through) { /* Switch to color bar */ - decoder_call(zr, video, s_stream, 0); - encoder_call(zr, video, s_routing, 2, 0, 0); - } - } - mutex_unlock(&zr->lock); - - v4l2_fh_del(&fh->fh); - v4l2_fh_exit(&fh->fh); - kfree(fh->overlay_mask); - kfree(fh); - - dprintk(4, KERN_INFO "%s: %s done\n", ZR_DEVNAME(zr), __func__); - - return 0; -} - -static int setup_fbuffer(struct zoran_fh *fh, - void *base, - const struct zoran_format *fmt, - int width, - int height, - int bytesperline) -{ - struct zoran *zr = fh->zr; - - /* (Ronald) v4l/v4l2 guidelines */ - if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) - return -EPERM; - - /* Don't allow frame buffer overlay if PCI or AGP is buggy, or on - ALi Magik (that needs very low latency while the card needs a - higher value always) */ - - if (pci_pci_problems & (PCIPCI_FAIL | PCIAGP_FAIL | PCIPCI_ALIMAGIK)) - return -ENXIO; - - /* we need a bytesperline value, even if not given */ - if (!bytesperline) - bytesperline = width * ((fmt->depth + 7) & ~7) / 8; - -#if 0 - if (zr->overlay_active) { - /* dzjee... stupid users... don't even bother to turn off - * overlay before changing the memory location... - * normally, we would return errors here. However, one of - * the tools that does this is... xawtv! and since xawtv - * is used by +/- 99% of the users, we'd rather be user- - * friendly and silently do as if nothing went wrong */ - dprintk(3, - KERN_ERR - "%s: %s - forced overlay turnoff because framebuffer changed\n", - ZR_DEVNAME(zr), __func__); - zr36057_overlay(zr, 0); - } -#endif - - if (!(fmt->flags & ZORAN_FORMAT_OVERLAY)) { - dprintk(1, - KERN_ERR - "%s: %s - no valid overlay format given\n", - ZR_DEVNAME(zr), __func__); - return -EINVAL; - } - if (height <= 0 || width <= 0 || bytesperline <= 0) { - dprintk(1, - KERN_ERR - "%s: %s - invalid height/width/bpl value (%d|%d|%d)\n", - ZR_DEVNAME(zr), __func__, width, height, bytesperline); - return -EINVAL; - } - if (bytesperline & 3) { - dprintk(1, - KERN_ERR - "%s: %s - bytesperline (%d) must be 4-byte aligned\n", - ZR_DEVNAME(zr), __func__, bytesperline); - return -EINVAL; - } - - zr->vbuf_base = (void *) ((unsigned long) base & ~3); - zr->vbuf_height = height; - zr->vbuf_width = width; - zr->vbuf_depth = fmt->depth; - zr->overlay_settings.format = fmt; - zr->vbuf_bytesperline = bytesperline; - - /* The user should set new window parameters */ - zr->overlay_settings.is_set = 0; - - return 0; -} - - -static int setup_window(struct zoran_fh *fh, - int x, - int y, - int width, - int height, - struct v4l2_clip __user *clips, - unsigned int clipcount, - void __user *bitmap) -{ - struct zoran *zr = fh->zr; - struct v4l2_clip *vcp = NULL; - int on, end; - - - if (!zr->vbuf_base) { - dprintk(1, - KERN_ERR - "%s: %s - frame buffer has to be set first\n", - ZR_DEVNAME(zr), __func__); - return -EINVAL; - } - - if (!fh->overlay_settings.format) { - dprintk(1, - KERN_ERR - "%s: %s - no overlay format set\n", - ZR_DEVNAME(zr), __func__); - return -EINVAL; - } - - if (clipcount > 2048) { - dprintk(1, - KERN_ERR - "%s: %s - invalid clipcount\n", - ZR_DEVNAME(zr), __func__); - return -EINVAL; - } - - /* - * The video front end needs 4-byte alinged line sizes, we correct that - * silently here if necessary - */ - if (zr->vbuf_depth == 15 || zr->vbuf_depth == 16) { - end = (x + width) & ~1; /* round down */ - x = (x + 1) & ~1; /* round up */ - width = end - x; - } - - if (zr->vbuf_depth == 24) { - end = (x + width) & ~3; /* round down */ - x = (x + 3) & ~3; /* round up */ - width = end - x; - } - - if (width > BUZ_MAX_WIDTH) - width = BUZ_MAX_WIDTH; - if (height > BUZ_MAX_HEIGHT) - height = BUZ_MAX_HEIGHT; - - /* Check for invalid parameters */ - if (width < BUZ_MIN_WIDTH || height < BUZ_MIN_HEIGHT || - width > BUZ_MAX_WIDTH || height > BUZ_MAX_HEIGHT) { - dprintk(1, - KERN_ERR - "%s: %s - width = %d or height = %d invalid\n", - ZR_DEVNAME(zr), __func__, width, height); - return -EINVAL; - } - - fh->overlay_settings.x = x; - fh->overlay_settings.y = y; - fh->overlay_settings.width = width; - fh->overlay_settings.height = height; - fh->overlay_settings.clipcount = clipcount; - - /* - * If an overlay is running, we have to switch it off - * and switch it on again in order to get the new settings in effect. - * - * We also want to avoid that the overlay mask is written - * when an overlay is running. - */ - - on = zr->v4l_overlay_active && !zr->v4l_memgrab_active && - zr->overlay_active != ZORAN_FREE && - fh->overlay_active != ZORAN_FREE; - if (on) - zr36057_overlay(zr, 0); - - /* - * Write the overlay mask if clips are wanted. - * We prefer a bitmap. - */ - if (bitmap) { - /* fake value - it just means we want clips */ - fh->overlay_settings.clipcount = 1; - - if (copy_from_user(fh->overlay_mask, bitmap, - (width * height + 7) / 8)) { - return -EFAULT; - } - } else if (clipcount) { - /* write our own bitmap from the clips */ - vcp = vmalloc(sizeof(struct v4l2_clip) * (clipcount + 4)); - if (vcp == NULL) { - dprintk(1, - KERN_ERR - "%s: %s - Alloc of clip mask failed\n", - ZR_DEVNAME(zr), __func__); - return -ENOMEM; - } - if (copy_from_user - (vcp, clips, sizeof(struct v4l2_clip) * clipcount)) { - vfree(vcp); - return -EFAULT; - } - write_overlay_mask(fh, vcp, clipcount); - vfree(vcp); - } - - fh->overlay_settings.is_set = 1; - if (fh->overlay_active != ZORAN_FREE && - zr->overlay_active != ZORAN_FREE) - zr->overlay_settings = fh->overlay_settings; - - if (on) - zr36057_overlay(zr, 1); - - /* Make sure the changes come into effect */ - return wait_grab_pending(zr); -} - -static int setup_overlay(struct zoran_fh *fh, int on) -{ - struct zoran *zr = fh->zr; - - /* If there is nothing to do, return immediately */ - if ((on && fh->overlay_active != ZORAN_FREE) || - (!on && fh->overlay_active == ZORAN_FREE)) - return 0; - - /* check whether we're touching someone else's overlay */ - if (on && zr->overlay_active != ZORAN_FREE && - fh->overlay_active == ZORAN_FREE) { - dprintk(1, - KERN_ERR - "%s: %s - overlay is already active for another session\n", - ZR_DEVNAME(zr), __func__); - return -EBUSY; - } - if (!on && zr->overlay_active != ZORAN_FREE && - fh->overlay_active == ZORAN_FREE) { - dprintk(1, - KERN_ERR - "%s: %s - you cannot cancel someone else's session\n", - ZR_DEVNAME(zr), __func__); - return -EPERM; - } - - if (on == 0) { - zr->overlay_active = fh->overlay_active = ZORAN_FREE; - zr->v4l_overlay_active = 0; - /* When a grab is running, the video simply - * won't be switched on any more */ - if (!zr->v4l_memgrab_active) - zr36057_overlay(zr, 0); - zr->overlay_mask = NULL; - } else { - if (!zr->vbuf_base || !fh->overlay_settings.is_set) { - dprintk(1, - KERN_ERR - "%s: %s - buffer or window not set\n", - ZR_DEVNAME(zr), __func__); - return -EINVAL; - } - if (!fh->overlay_settings.format) { - dprintk(1, - KERN_ERR - "%s: %s - no overlay format set\n", - ZR_DEVNAME(zr), __func__); - return -EINVAL; - } - zr->overlay_active = fh->overlay_active = ZORAN_LOCKED; - zr->v4l_overlay_active = 1; - zr->overlay_mask = fh->overlay_mask; - zr->overlay_settings = fh->overlay_settings; - if (!zr->v4l_memgrab_active) - zr36057_overlay(zr, 1); - /* When a grab is running, the video will be - * switched on when grab is finished */ - } - - /* Make sure the changes come into effect */ - return wait_grab_pending(zr); -} - -/* get the status of a buffer in the clients buffer queue */ -static int zoran_v4l2_buffer_status(struct zoran_fh *fh, - struct v4l2_buffer *buf, int num) -{ - struct zoran *zr = fh->zr; - unsigned long flags; - - buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - - switch (fh->map_mode) { - case ZORAN_MAP_MODE_RAW: - /* check range */ - if (num < 0 || num >= fh->buffers.num_buffers || - !fh->buffers.allocated) { - dprintk(1, - KERN_ERR - "%s: %s - wrong number or buffers not allocated\n", - ZR_DEVNAME(zr), __func__); - return -EINVAL; - } - - spin_lock_irqsave(&zr->spinlock, flags); - dprintk(3, - KERN_DEBUG - "%s: %s() - raw active=%c, buffer %d: state=%c, map=%c\n", - ZR_DEVNAME(zr), __func__, - "FAL"[fh->buffers.active], num, - "UPMD"[zr->v4l_buffers.buffer[num].state], - fh->buffers.buffer[num].map ? 'Y' : 'N'); - spin_unlock_irqrestore(&zr->spinlock, flags); - - buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf->length = fh->buffers.buffer_size; - - /* get buffer */ - buf->bytesused = fh->buffers.buffer[num].bs.length; - if (fh->buffers.buffer[num].state == BUZ_STATE_DONE || - fh->buffers.buffer[num].state == BUZ_STATE_USER) { - buf->sequence = fh->buffers.buffer[num].bs.seq; - buf->flags |= V4L2_BUF_FLAG_DONE; - buf->timestamp = fh->buffers.buffer[num].bs.timestamp; - } else { - buf->flags |= V4L2_BUF_FLAG_QUEUED; - } - - if (fh->v4l_settings.height <= BUZ_MAX_HEIGHT / 2) - buf->field = V4L2_FIELD_TOP; - else - buf->field = V4L2_FIELD_INTERLACED; - - break; - - case ZORAN_MAP_MODE_JPG_REC: - case ZORAN_MAP_MODE_JPG_PLAY: - - /* check range */ - if (num < 0 || num >= fh->buffers.num_buffers || - !fh->buffers.allocated) { - dprintk(1, - KERN_ERR - "%s: %s - wrong number or buffers not allocated\n", - ZR_DEVNAME(zr), __func__); - return -EINVAL; - } - - buf->type = (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ? - V4L2_BUF_TYPE_VIDEO_CAPTURE : - V4L2_BUF_TYPE_VIDEO_OUTPUT; - buf->length = fh->buffers.buffer_size; - - /* these variables are only written after frame has been captured */ - if (fh->buffers.buffer[num].state == BUZ_STATE_DONE || - fh->buffers.buffer[num].state == BUZ_STATE_USER) { - buf->sequence = fh->buffers.buffer[num].bs.seq; - buf->timestamp = fh->buffers.buffer[num].bs.timestamp; - buf->bytesused = fh->buffers.buffer[num].bs.length; - buf->flags |= V4L2_BUF_FLAG_DONE; - } else { - buf->flags |= V4L2_BUF_FLAG_QUEUED; - } - - /* which fields are these? */ - if (fh->jpg_settings.TmpDcm != 1) - buf->field = fh->jpg_settings.odd_even ? - V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; - else - buf->field = fh->jpg_settings.odd_even ? - V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT; - - break; - - default: - - dprintk(5, - KERN_ERR - "%s: %s - invalid buffer type|map_mode (%d|%d)\n", - ZR_DEVNAME(zr), __func__, buf->type, fh->map_mode); - return -EINVAL; - } - - buf->memory = V4L2_MEMORY_MMAP; - buf->index = num; - buf->m.offset = buf->length * num; - - return 0; -} - -static int -zoran_set_norm (struct zoran *zr, - v4l2_std_id norm) -{ - int on; - - if (zr->v4l_buffers.active != ZORAN_FREE || - zr->jpg_buffers.active != ZORAN_FREE) { - dprintk(1, - KERN_WARNING - "%s: %s called while in playback/capture mode\n", - ZR_DEVNAME(zr), __func__); - return -EBUSY; - } - - if (!(norm & zr->card.norms)) { - dprintk(1, - KERN_ERR "%s: %s - unsupported norm %llx\n", - ZR_DEVNAME(zr), __func__, norm); - return -EINVAL; - } - - if (norm & V4L2_STD_SECAM) - zr->timing = zr->card.tvn[2]; - else if (norm & V4L2_STD_NTSC) - zr->timing = zr->card.tvn[1]; - else - zr->timing = zr->card.tvn[0]; - - /* We switch overlay off and on since a change in the - * norm needs different VFE settings */ - on = zr->overlay_active && !zr->v4l_memgrab_active; - if (on) - zr36057_overlay(zr, 0); - - decoder_call(zr, video, s_std, norm); - encoder_call(zr, video, s_std_output, norm); - - if (on) - zr36057_overlay(zr, 1); - - /* Make sure the changes come into effect */ - zr->norm = norm; - - return 0; -} - -static int -zoran_set_input (struct zoran *zr, - int input) -{ - if (input == zr->input) { - return 0; - } - - if (zr->v4l_buffers.active != ZORAN_FREE || - zr->jpg_buffers.active != ZORAN_FREE) { - dprintk(1, - KERN_WARNING - "%s: %s called while in playback/capture mode\n", - ZR_DEVNAME(zr), __func__); - return -EBUSY; - } - - if (input < 0 || input >= zr->card.inputs) { - dprintk(1, - KERN_ERR - "%s: %s - unsupported input %d\n", - ZR_DEVNAME(zr), __func__, input); - return -EINVAL; - } - - zr->input = input; - - decoder_call(zr, video, s_routing, - zr->card.input[input].muxsel, 0, 0); - - return 0; -} - -/* - * ioctl routine - */ - -static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability *cap) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - strncpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)-1); - strncpy(cap->driver, "zoran", sizeof(cap->driver)-1); - snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", - pci_name(zr->pci_dev)); - cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int zoran_enum_fmt(struct zoran *zr, struct v4l2_fmtdesc *fmt, int flag) -{ - unsigned int num, i; - - for (num = i = 0; i < NUM_FORMATS; i++) { - if (zoran_formats[i].flags & flag && num++ == fmt->index) { - strncpy(fmt->description, zoran_formats[i].name, - sizeof(fmt->description) - 1); - /* fmt struct pre-zeroed, so adding '\0' not needed */ - fmt->pixelformat = zoran_formats[i].fourcc; - if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED) - fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; - return 0; - } - } - return -EINVAL; -} - -static int zoran_enum_fmt_vid_cap(struct file *file, void *__fh, - struct v4l2_fmtdesc *f) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - return zoran_enum_fmt(zr, f, ZORAN_FORMAT_CAPTURE); -} - -static int zoran_enum_fmt_vid_out(struct file *file, void *__fh, - struct v4l2_fmtdesc *f) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - return zoran_enum_fmt(zr, f, ZORAN_FORMAT_PLAYBACK); -} - -static int zoran_enum_fmt_vid_overlay(struct file *file, void *__fh, - struct v4l2_fmtdesc *f) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - return zoran_enum_fmt(zr, f, ZORAN_FORMAT_OVERLAY); -} - -static int zoran_g_fmt_vid_out(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran_fh *fh = __fh; - - fmt->fmt.pix.width = fh->jpg_settings.img_width / fh->jpg_settings.HorDcm; - fmt->fmt.pix.height = fh->jpg_settings.img_height * 2 / - (fh->jpg_settings.VerDcm * fh->jpg_settings.TmpDcm); - fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&fh->jpg_settings); - fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; - if (fh->jpg_settings.TmpDcm == 1) - fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? - V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); - else - fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? - V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); - fmt->fmt.pix.bytesperline = 0; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - - return 0; -} - -static int zoran_g_fmt_vid_cap(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - if (fh->map_mode != ZORAN_MAP_MODE_RAW) - return zoran_g_fmt_vid_out(file, fh, fmt); - - fmt->fmt.pix.width = fh->v4l_settings.width; - fmt->fmt.pix.height = fh->v4l_settings.height; - fmt->fmt.pix.sizeimage = fh->v4l_settings.bytesperline * - fh->v4l_settings.height; - fmt->fmt.pix.pixelformat = fh->v4l_settings.format->fourcc; - fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace; - fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline; - if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2)) - fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; - else - fmt->fmt.pix.field = V4L2_FIELD_TOP; - return 0; -} - -static int zoran_g_fmt_vid_overlay(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - fmt->fmt.win.w.left = fh->overlay_settings.x; - fmt->fmt.win.w.top = fh->overlay_settings.y; - fmt->fmt.win.w.width = fh->overlay_settings.width; - fmt->fmt.win.w.height = fh->overlay_settings.height; - if (fh->overlay_settings.width * 2 > BUZ_MAX_HEIGHT) - fmt->fmt.win.field = V4L2_FIELD_INTERLACED; - else - fmt->fmt.win.field = V4L2_FIELD_TOP; - - return 0; -} - -static int zoran_try_fmt_vid_overlay(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - if (fmt->fmt.win.w.width > BUZ_MAX_WIDTH) - fmt->fmt.win.w.width = BUZ_MAX_WIDTH; - if (fmt->fmt.win.w.width < BUZ_MIN_WIDTH) - fmt->fmt.win.w.width = BUZ_MIN_WIDTH; - if (fmt->fmt.win.w.height > BUZ_MAX_HEIGHT) - fmt->fmt.win.w.height = BUZ_MAX_HEIGHT; - if (fmt->fmt.win.w.height < BUZ_MIN_HEIGHT) - fmt->fmt.win.w.height = BUZ_MIN_HEIGHT; - - return 0; -} - -static int zoran_try_fmt_vid_out(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - struct zoran_jpg_settings settings; - int res = 0; - - if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - - settings = fh->jpg_settings; - - /* we actually need to set 'real' parameters now */ - if ((fmt->fmt.pix.height * 2) > BUZ_MAX_HEIGHT) - settings.TmpDcm = 1; - else - settings.TmpDcm = 2; - settings.decimation = 0; - if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2) - settings.VerDcm = 2; - else - settings.VerDcm = 1; - if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4) - settings.HorDcm = 4; - else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2) - settings.HorDcm = 2; - else - settings.HorDcm = 1; - if (settings.TmpDcm == 1) - settings.field_per_buff = 2; - else - settings.field_per_buff = 1; - - if (settings.HorDcm > 1) { - settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; - settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; - } else { - settings.img_x = 0; - settings.img_width = BUZ_MAX_WIDTH; - } - - /* check */ - res = zoran_check_jpg_settings(zr, &settings, 1); - if (res) - return res; - - /* tell the user what we actually did */ - fmt->fmt.pix.width = settings.img_width / settings.HorDcm; - fmt->fmt.pix.height = settings.img_height * 2 / - (settings.TmpDcm * settings.VerDcm); - if (settings.TmpDcm == 1) - fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? - V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); - else - fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? - V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); - - fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&settings); - fmt->fmt.pix.bytesperline = 0; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - return res; -} - -static int zoran_try_fmt_vid_cap(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - int bpp; - int i; - - if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) - return zoran_try_fmt_vid_out(file, fh, fmt); - - for (i = 0; i < NUM_FORMATS; i++) - if (zoran_formats[i].fourcc == fmt->fmt.pix.pixelformat) - break; - - if (i == NUM_FORMATS) - return -EINVAL; - - bpp = DIV_ROUND_UP(zoran_formats[i].depth, 8); - v4l_bound_align_image( - &fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, bpp == 2 ? 1 : 2, - &fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, 0, 0); - return 0; -} - -static int zoran_s_fmt_vid_overlay(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran_fh *fh = __fh; - int res; - - dprintk(3, "x=%d, y=%d, w=%d, h=%d, cnt=%d, map=0x%p\n", - fmt->fmt.win.w.left, fmt->fmt.win.w.top, - fmt->fmt.win.w.width, - fmt->fmt.win.w.height, - fmt->fmt.win.clipcount, - fmt->fmt.win.bitmap); - res = setup_window(fh, fmt->fmt.win.w.left, fmt->fmt.win.w.top, - fmt->fmt.win.w.width, fmt->fmt.win.w.height, - (struct v4l2_clip __user *)fmt->fmt.win.clips, - fmt->fmt.win.clipcount, fmt->fmt.win.bitmap); - return res; -} - -static int zoran_s_fmt_vid_out(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - __le32 printformat = __cpu_to_le32(fmt->fmt.pix.pixelformat); - struct zoran_jpg_settings settings; - int res = 0; - - dprintk(3, "size=%dx%d, fmt=0x%x (%4.4s)\n", - fmt->fmt.pix.width, fmt->fmt.pix.height, - fmt->fmt.pix.pixelformat, - (char *) &printformat); - if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - - if (fh->buffers.allocated) { - dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n", - ZR_DEVNAME(zr)); - res = -EBUSY; - return res; - } - - settings = fh->jpg_settings; - - /* we actually need to set 'real' parameters now */ - if (fmt->fmt.pix.height * 2 > BUZ_MAX_HEIGHT) - settings.TmpDcm = 1; - else - settings.TmpDcm = 2; - settings.decimation = 0; - if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2) - settings.VerDcm = 2; - else - settings.VerDcm = 1; - if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4) - settings.HorDcm = 4; - else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2) - settings.HorDcm = 2; - else - settings.HorDcm = 1; - if (settings.TmpDcm == 1) - settings.field_per_buff = 2; - else - settings.field_per_buff = 1; - - if (settings.HorDcm > 1) { - settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; - settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; - } else { - settings.img_x = 0; - settings.img_width = BUZ_MAX_WIDTH; - } - - /* check */ - res = zoran_check_jpg_settings(zr, &settings, 0); - if (res) - return res; - - /* it's ok, so set them */ - fh->jpg_settings = settings; - - map_mode_jpg(fh, fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT); - fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings); - - /* tell the user what we actually did */ - fmt->fmt.pix.width = settings.img_width / settings.HorDcm; - fmt->fmt.pix.height = settings.img_height * 2 / - (settings.TmpDcm * settings.VerDcm); - if (settings.TmpDcm == 1) - fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? - V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); - else - fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? - V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); - fmt->fmt.pix.bytesperline = 0; - fmt->fmt.pix.sizeimage = fh->buffers.buffer_size; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - return res; -} - -static int zoran_s_fmt_vid_cap(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - int i; - int res = 0; - - if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) - return zoran_s_fmt_vid_out(file, fh, fmt); - - for (i = 0; i < NUM_FORMATS; i++) - if (fmt->fmt.pix.pixelformat == zoran_formats[i].fourcc) - break; - if (i == NUM_FORMATS) { - dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - unknown/unsupported format 0x%x\n", - ZR_DEVNAME(zr), fmt->fmt.pix.pixelformat); - return -EINVAL; - } - - if ((fh->map_mode != ZORAN_MAP_MODE_RAW && fh->buffers.allocated) || - fh->buffers.active != ZORAN_FREE) { - dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n", - ZR_DEVNAME(zr)); - res = -EBUSY; - return res; - } - if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT) - fmt->fmt.pix.height = BUZ_MAX_HEIGHT; - if (fmt->fmt.pix.width > BUZ_MAX_WIDTH) - fmt->fmt.pix.width = BUZ_MAX_WIDTH; - - map_mode_raw(fh); - - res = zoran_v4l_set_format(fh, fmt->fmt.pix.width, fmt->fmt.pix.height, - &zoran_formats[i]); - if (res) - return res; - - /* tell the user the results/missing stuff */ - fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline; - fmt->fmt.pix.sizeimage = fh->v4l_settings.height * fh->v4l_settings.bytesperline; - fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace; - if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2)) - fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; - else - fmt->fmt.pix.field = V4L2_FIELD_TOP; - return res; -} - -static int zoran_g_fbuf(struct file *file, void *__fh, - struct v4l2_framebuffer *fb) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - memset(fb, 0, sizeof(*fb)); - fb->base = zr->vbuf_base; - fb->fmt.width = zr->vbuf_width; - fb->fmt.height = zr->vbuf_height; - if (zr->overlay_settings.format) - fb->fmt.pixelformat = fh->overlay_settings.format->fourcc; - fb->fmt.bytesperline = zr->vbuf_bytesperline; - fb->fmt.colorspace = V4L2_COLORSPACE_SRGB; - fb->fmt.field = V4L2_FIELD_INTERLACED; - fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; - - return 0; -} - -static int zoran_s_fbuf(struct file *file, void *__fh, - const struct v4l2_framebuffer *fb) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - int i, res = 0; - __le32 printformat = __cpu_to_le32(fb->fmt.pixelformat); - - for (i = 0; i < NUM_FORMATS; i++) - if (zoran_formats[i].fourcc == fb->fmt.pixelformat) - break; - if (i == NUM_FORMATS) { - dprintk(1, KERN_ERR "%s: VIDIOC_S_FBUF - format=0x%x (%4.4s) not allowed\n", - ZR_DEVNAME(zr), fb->fmt.pixelformat, - (char *)&printformat); - return -EINVAL; - } - - res = setup_fbuffer(fh, fb->base, &zoran_formats[i], fb->fmt.width, - fb->fmt.height, fb->fmt.bytesperline); - - return res; -} - -static int zoran_overlay(struct file *file, void *__fh, unsigned int on) -{ - struct zoran_fh *fh = __fh; - int res; - - res = setup_overlay(fh, on); - - return res; -} - -static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type); - -static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *req) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - int res = 0; - - if (req->memory != V4L2_MEMORY_MMAP) { - dprintk(2, - KERN_ERR - "%s: only MEMORY_MMAP capture is supported, not %d\n", - ZR_DEVNAME(zr), req->memory); - return -EINVAL; - } - - if (req->count == 0) - return zoran_streamoff(file, fh, req->type); - - if (fh->buffers.allocated) { - dprintk(2, - KERN_ERR - "%s: VIDIOC_REQBUFS - buffers already allocated\n", - ZR_DEVNAME(zr)); - res = -EBUSY; - return res; - } - - if (fh->map_mode == ZORAN_MAP_MODE_RAW && - req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - /* control user input */ - if (req->count < 2) - req->count = 2; - if (req->count > v4l_nbufs) - req->count = v4l_nbufs; - - /* The next mmap will map the V4L buffers */ - map_mode_raw(fh); - fh->buffers.num_buffers = req->count; - - if (v4l_fbuffer_alloc(fh)) { - res = -ENOMEM; - return res; - } - } else if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC || - fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) { - /* we need to calculate size ourselves now */ - if (req->count < 4) - req->count = 4; - if (req->count > jpg_nbufs) - req->count = jpg_nbufs; - - /* The next mmap will map the MJPEG buffers */ - map_mode_jpg(fh, req->type == V4L2_BUF_TYPE_VIDEO_OUTPUT); - fh->buffers.num_buffers = req->count; - fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings); - - if (jpg_fbuffer_alloc(fh)) { - res = -ENOMEM; - return res; - } - } else { - dprintk(1, - KERN_ERR - "%s: VIDIOC_REQBUFS - unknown type %d\n", - ZR_DEVNAME(zr), req->type); - res = -EINVAL; - return res; - } - return res; -} - -static int zoran_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf) -{ - struct zoran_fh *fh = __fh; - int res; - - res = zoran_v4l2_buffer_status(fh, buf, buf->index); - - return res; -} - -static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - int res = 0, codec_mode, buf_type; - - switch (fh->map_mode) { - case ZORAN_MAP_MODE_RAW: - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dprintk(1, KERN_ERR - "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", - ZR_DEVNAME(zr), buf->type, fh->map_mode); - res = -EINVAL; - return res; - } - - res = zoran_v4l_queue_frame(fh, buf->index); - if (res) - return res; - if (!zr->v4l_memgrab_active && fh->buffers.active == ZORAN_LOCKED) - zr36057_set_memgrab(zr, 1); - break; - - case ZORAN_MAP_MODE_JPG_REC: - case ZORAN_MAP_MODE_JPG_PLAY: - if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) { - buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - codec_mode = BUZ_MODE_MOTION_DECOMPRESS; - } else { - buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - codec_mode = BUZ_MODE_MOTION_COMPRESS; - } - - if (buf->type != buf_type) { - dprintk(1, KERN_ERR - "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", - ZR_DEVNAME(zr), buf->type, fh->map_mode); - res = -EINVAL; - return res; - } - - res = zoran_jpg_queue_frame(fh, buf->index, codec_mode); - if (res != 0) - return res; - if (zr->codec_mode == BUZ_MODE_IDLE && - fh->buffers.active == ZORAN_LOCKED) - zr36057_enable_jpg(zr, codec_mode); - - break; - - default: - dprintk(1, KERN_ERR - "%s: VIDIOC_QBUF - unsupported type %d\n", - ZR_DEVNAME(zr), buf->type); - res = -EINVAL; - break; - } - return res; -} - -static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - int res = 0, buf_type, num = -1; /* compiler borks here (?) */ - - switch (fh->map_mode) { - case ZORAN_MAP_MODE_RAW: - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dprintk(1, KERN_ERR - "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", - ZR_DEVNAME(zr), buf->type, fh->map_mode); - res = -EINVAL; - return res; - } - - num = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME]; - if (file->f_flags & O_NONBLOCK && - zr->v4l_buffers.buffer[num].state != BUZ_STATE_DONE) { - res = -EAGAIN; - return res; - } - res = v4l_sync(fh, num); - if (res) - return res; - zr->v4l_sync_tail++; - res = zoran_v4l2_buffer_status(fh, buf, num); - break; - - case ZORAN_MAP_MODE_JPG_REC: - case ZORAN_MAP_MODE_JPG_PLAY: - { - struct zoran_sync bs; - - if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) - buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - else - buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - if (buf->type != buf_type) { - dprintk(1, KERN_ERR - "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", - ZR_DEVNAME(zr), buf->type, fh->map_mode); - res = -EINVAL; - return res; - } - - num = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME]; - - if (file->f_flags & O_NONBLOCK && - zr->jpg_buffers.buffer[num].state != BUZ_STATE_DONE) { - res = -EAGAIN; - return res; - } - bs.frame = 0; /* suppress compiler warning */ - res = jpg_sync(fh, &bs); - if (res) - return res; - res = zoran_v4l2_buffer_status(fh, buf, bs.frame); - break; - } - - default: - dprintk(1, KERN_ERR - "%s: VIDIOC_DQBUF - unsupported type %d\n", - ZR_DEVNAME(zr), buf->type); - res = -EINVAL; - break; - } - return res; -} - -static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - int res = 0; - - switch (fh->map_mode) { - case ZORAN_MAP_MODE_RAW: /* raw capture */ - if (zr->v4l_buffers.active != ZORAN_ACTIVE || - fh->buffers.active != ZORAN_ACTIVE) { - res = -EBUSY; - return res; - } - - zr->v4l_buffers.active = fh->buffers.active = ZORAN_LOCKED; - zr->v4l_settings = fh->v4l_settings; - - zr->v4l_sync_tail = zr->v4l_pend_tail; - if (!zr->v4l_memgrab_active && - zr->v4l_pend_head != zr->v4l_pend_tail) { - zr36057_set_memgrab(zr, 1); - } - break; - - case ZORAN_MAP_MODE_JPG_REC: - case ZORAN_MAP_MODE_JPG_PLAY: - /* what is the codec mode right now? */ - if (zr->jpg_buffers.active != ZORAN_ACTIVE || - fh->buffers.active != ZORAN_ACTIVE) { - res = -EBUSY; - return res; - } - - zr->jpg_buffers.active = fh->buffers.active = ZORAN_LOCKED; - - if (zr->jpg_que_head != zr->jpg_que_tail) { - /* Start the jpeg codec when the first frame is queued */ - jpeg_start(zr); - } - break; - - default: - dprintk(1, - KERN_ERR - "%s: VIDIOC_STREAMON - invalid map mode %d\n", - ZR_DEVNAME(zr), fh->map_mode); - res = -EINVAL; - break; - } - return res; -} - -static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - int i, res = 0; - unsigned long flags; - - switch (fh->map_mode) { - case ZORAN_MAP_MODE_RAW: /* raw capture */ - if (fh->buffers.active == ZORAN_FREE && - zr->v4l_buffers.active != ZORAN_FREE) { - res = -EPERM; /* stay off other's settings! */ - return res; - } - if (zr->v4l_buffers.active == ZORAN_FREE) - return res; - - spin_lock_irqsave(&zr->spinlock, flags); - /* unload capture */ - if (zr->v4l_memgrab_active) { - - zr36057_set_memgrab(zr, 0); - } - - for (i = 0; i < fh->buffers.num_buffers; i++) - zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER; - fh->buffers = zr->v4l_buffers; - - zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE; - - zr->v4l_grab_seq = 0; - zr->v4l_pend_head = zr->v4l_pend_tail = 0; - zr->v4l_sync_tail = 0; - - spin_unlock_irqrestore(&zr->spinlock, flags); - - break; - - case ZORAN_MAP_MODE_JPG_REC: - case ZORAN_MAP_MODE_JPG_PLAY: - if (fh->buffers.active == ZORAN_FREE && - zr->jpg_buffers.active != ZORAN_FREE) { - res = -EPERM; /* stay off other's settings! */ - return res; - } - if (zr->jpg_buffers.active == ZORAN_FREE) - return res; - - res = jpg_qbuf(fh, -1, - (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ? - BUZ_MODE_MOTION_COMPRESS : - BUZ_MODE_MOTION_DECOMPRESS); - if (res) - return res; - break; - default: - dprintk(1, KERN_ERR - "%s: VIDIOC_STREAMOFF - invalid map mode %d\n", - ZR_DEVNAME(zr), fh->map_mode); - res = -EINVAL; - break; - } - return res; -} -static int zoran_g_std(struct file *file, void *__fh, v4l2_std_id *std) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - *std = zr->norm; - return 0; -} - -static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id std) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - int res = 0; - - res = zoran_set_norm(zr, std); - if (res) - return res; - - res = wait_grab_pending(zr); - return res; -} - -static int zoran_enum_input(struct file *file, void *__fh, - struct v4l2_input *inp) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - if (inp->index >= zr->card.inputs) - return -EINVAL; - - strncpy(inp->name, zr->card.input[inp->index].name, - sizeof(inp->name) - 1); - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = V4L2_STD_ALL; - - /* Get status of video decoder */ - decoder_call(zr, video, g_input_status, &inp->status); - return 0; -} - -static int zoran_g_input(struct file *file, void *__fh, unsigned int *input) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - *input = zr->input; - - return 0; -} - -static int zoran_s_input(struct file *file, void *__fh, unsigned int input) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - int res; - - res = zoran_set_input(zr, input); - if (res) - return res; - - /* Make sure the changes come into effect */ - res = wait_grab_pending(zr); - return res; -} - -static int zoran_enum_output(struct file *file, void *__fh, - struct v4l2_output *outp) -{ - if (outp->index != 0) - return -EINVAL; - - outp->index = 0; - outp->type = V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY; - strncpy(outp->name, "Autodetect", sizeof(outp->name)-1); - - return 0; -} - -static int zoran_g_output(struct file *file, void *__fh, unsigned int *output) -{ - *output = 0; - - return 0; -} - -static int zoran_s_output(struct file *file, void *__fh, unsigned int output) -{ - if (output != 0) - return -EINVAL; - - return 0; -} - -/* cropping (sub-frame capture) */ -static int zoran_g_selection(struct file *file, void *__fh, struct v4l2_selection *sel) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - - if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (fh->map_mode == ZORAN_MAP_MODE_RAW) { - dprintk(1, KERN_ERR - "%s: VIDIOC_G_SELECTION - subcapture only supported for compressed capture\n", - ZR_DEVNAME(zr)); - return -EINVAL; - } - - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - sel->r.top = fh->jpg_settings.img_y; - sel->r.left = fh->jpg_settings.img_x; - sel->r.width = fh->jpg_settings.img_width; - sel->r.height = fh->jpg_settings.img_height; - break; - case V4L2_SEL_TGT_CROP_DEFAULT: - sel->r.top = sel->r.left = 0; - sel->r.width = BUZ_MIN_WIDTH; - sel->r.height = BUZ_MIN_HEIGHT; - break; - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.top = sel->r.left = 0; - sel->r.width = BUZ_MAX_WIDTH; - sel->r.height = BUZ_MAX_HEIGHT; - break; - default: - return -EINVAL; - } - return 0; -} - -static int zoran_s_selection(struct file *file, void *__fh, struct v4l2_selection *sel) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - struct zoran_jpg_settings settings; - int res; - - if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - if (fh->map_mode == ZORAN_MAP_MODE_RAW) { - dprintk(1, KERN_ERR - "%s: VIDIOC_S_SELECTION - subcapture only supported for compressed capture\n", - ZR_DEVNAME(zr)); - return -EINVAL; - } - - settings = fh->jpg_settings; - - if (fh->buffers.allocated) { - dprintk(1, KERN_ERR - "%s: VIDIOC_S_SELECTION - cannot change settings while active\n", - ZR_DEVNAME(zr)); - return -EBUSY; - } - - /* move into a form that we understand */ - settings.img_x = sel->r.left; - settings.img_y = sel->r.top; - settings.img_width = sel->r.width; - settings.img_height = sel->r.height; - - /* check validity */ - res = zoran_check_jpg_settings(zr, &settings, 0); - if (res) - return res; - - /* accept */ - fh->jpg_settings = settings; - return res; -} - -static int zoran_g_jpegcomp(struct file *file, void *__fh, - struct v4l2_jpegcompression *params) -{ - struct zoran_fh *fh = __fh; - memset(params, 0, sizeof(*params)); - - params->quality = fh->jpg_settings.jpg_comp.quality; - params->APPn = fh->jpg_settings.jpg_comp.APPn; - memcpy(params->APP_data, - fh->jpg_settings.jpg_comp.APP_data, - fh->jpg_settings.jpg_comp.APP_len); - params->APP_len = fh->jpg_settings.jpg_comp.APP_len; - memcpy(params->COM_data, - fh->jpg_settings.jpg_comp.COM_data, - fh->jpg_settings.jpg_comp.COM_len); - params->COM_len = fh->jpg_settings.jpg_comp.COM_len; - params->jpeg_markers = - fh->jpg_settings.jpg_comp.jpeg_markers; - - return 0; -} - -static int zoran_s_jpegcomp(struct file *file, void *__fh, - const struct v4l2_jpegcompression *params) -{ - struct zoran_fh *fh = __fh; - struct zoran *zr = fh->zr; - int res = 0; - struct zoran_jpg_settings settings; - - settings = fh->jpg_settings; - - settings.jpg_comp = *params; - - if (fh->buffers.active != ZORAN_FREE) { - dprintk(1, KERN_WARNING - "%s: VIDIOC_S_JPEGCOMP called while in playback/capture mode\n", - ZR_DEVNAME(zr)); - res = -EBUSY; - return res; - } - - res = zoran_check_jpg_settings(zr, &settings, 0); - if (res) - return res; - if (!fh->buffers.allocated) - fh->buffers.buffer_size = - zoran_v4l2_calc_bufsize(&fh->jpg_settings); - fh->jpg_settings.jpg_comp = settings.jpg_comp; - return res; -} - -static __poll_t -zoran_poll (struct file *file, - poll_table *wait) -{ - struct zoran_fh *fh = file->private_data; - struct zoran *zr = fh->zr; - __poll_t res = v4l2_ctrl_poll(file, wait); - int frame; - unsigned long flags; - - /* we should check whether buffers are ready to be synced on - * (w/o waits - O_NONBLOCK) here - * if ready for read (sync), return EPOLLIN|EPOLLRDNORM, - * if ready for write (sync), return EPOLLOUT|EPOLLWRNORM, - * if error, return EPOLLERR, - * if no buffers queued or so, return EPOLLNVAL - */ - - switch (fh->map_mode) { - case ZORAN_MAP_MODE_RAW: - poll_wait(file, &zr->v4l_capq, wait); - frame = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME]; - - spin_lock_irqsave(&zr->spinlock, flags); - dprintk(3, - KERN_DEBUG - "%s: %s() raw - active=%c, sync_tail=%lu/%c, pend_tail=%lu, pend_head=%lu\n", - ZR_DEVNAME(zr), __func__, - "FAL"[fh->buffers.active], zr->v4l_sync_tail, - "UPMD"[zr->v4l_buffers.buffer[frame].state], - zr->v4l_pend_tail, zr->v4l_pend_head); - /* Process is the one capturing? */ - if (fh->buffers.active != ZORAN_FREE && - /* Buffer ready to DQBUF? */ - zr->v4l_buffers.buffer[frame].state == BUZ_STATE_DONE) - res |= EPOLLIN | EPOLLRDNORM; - spin_unlock_irqrestore(&zr->spinlock, flags); - - break; - - case ZORAN_MAP_MODE_JPG_REC: - case ZORAN_MAP_MODE_JPG_PLAY: - poll_wait(file, &zr->jpg_capq, wait); - frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME]; - - spin_lock_irqsave(&zr->spinlock, flags); - dprintk(3, - KERN_DEBUG - "%s: %s() jpg - active=%c, que_tail=%lu/%c, que_head=%lu, dma=%lu/%lu\n", - ZR_DEVNAME(zr), __func__, - "FAL"[fh->buffers.active], zr->jpg_que_tail, - "UPMD"[zr->jpg_buffers.buffer[frame].state], - zr->jpg_que_head, zr->jpg_dma_tail, zr->jpg_dma_head); - if (fh->buffers.active != ZORAN_FREE && - zr->jpg_buffers.buffer[frame].state == BUZ_STATE_DONE) { - if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) - res |= EPOLLIN | EPOLLRDNORM; - else - res |= EPOLLOUT | EPOLLWRNORM; - } - spin_unlock_irqrestore(&zr->spinlock, flags); - - break; - - default: - dprintk(1, - KERN_ERR - "%s: %s - internal error, unknown map_mode=%d\n", - ZR_DEVNAME(zr), __func__, fh->map_mode); - res |= EPOLLERR; - } - - return res; -} - - -/* - * This maps the buffers to user space. - * - * Depending on the state of fh->map_mode - * the V4L or the MJPEG buffers are mapped - * per buffer or all together - * - * Note that we need to connect to some - * unmap signal event to unmap the de-allocate - * the buffer accordingly (zoran_vm_close()) - */ - -static void -zoran_vm_open (struct vm_area_struct *vma) -{ - struct zoran_mapping *map = vma->vm_private_data; - atomic_inc(&map->count); -} - -static void -zoran_vm_close (struct vm_area_struct *vma) -{ - struct zoran_mapping *map = vma->vm_private_data; - struct zoran_fh *fh = map->fh; - struct zoran *zr = fh->zr; - int i; - - dprintk(3, KERN_INFO "%s: %s - munmap(%s)\n", ZR_DEVNAME(zr), - __func__, mode_name(fh->map_mode)); - - for (i = 0; i < fh->buffers.num_buffers; i++) { - if (fh->buffers.buffer[i].map == map) - fh->buffers.buffer[i].map = NULL; - } - kfree(map); - - /* Any buffers still mapped? */ - for (i = 0; i < fh->buffers.num_buffers; i++) { - if (fh->buffers.buffer[i].map) { - return; - } - } - - dprintk(3, KERN_INFO "%s: %s - free %s buffers\n", ZR_DEVNAME(zr), - __func__, mode_name(fh->map_mode)); - - if (fh->map_mode == ZORAN_MAP_MODE_RAW) { - if (fh->buffers.active != ZORAN_FREE) { - unsigned long flags; - - spin_lock_irqsave(&zr->spinlock, flags); - zr36057_set_memgrab(zr, 0); - zr->v4l_buffers.allocated = 0; - zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE; - spin_unlock_irqrestore(&zr->spinlock, flags); - } - v4l_fbuffer_free(fh); - } else { - if (fh->buffers.active != ZORAN_FREE) { - jpg_qbuf(fh, -1, zr->codec_mode); - zr->jpg_buffers.allocated = 0; - zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE; - } - jpg_fbuffer_free(fh); - } -} - -static const struct vm_operations_struct zoran_vm_ops = { - .open = zoran_vm_open, - .close = zoran_vm_close, -}; - -static int -zoran_mmap (struct file *file, - struct vm_area_struct *vma) -{ - struct zoran_fh *fh = file->private_data; - struct zoran *zr = fh->zr; - unsigned long size = (vma->vm_end - vma->vm_start); - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - int i, j; - unsigned long page, start = vma->vm_start, todo, pos, fraglen; - int first, last; - struct zoran_mapping *map; - int res = 0; - - dprintk(3, - KERN_INFO "%s: %s(%s) of 0x%08lx-0x%08lx (size=%lu)\n", - ZR_DEVNAME(zr), __func__, - mode_name(fh->map_mode), vma->vm_start, vma->vm_end, size); - - if (!(vma->vm_flags & VM_SHARED) || !(vma->vm_flags & VM_READ) || - !(vma->vm_flags & VM_WRITE)) { - dprintk(1, - KERN_ERR - "%s: %s - no MAP_SHARED/PROT_{READ,WRITE} given\n", - ZR_DEVNAME(zr), __func__); - return -EINVAL; - } - - if (!fh->buffers.allocated) { - dprintk(1, - KERN_ERR - "%s: %s(%s) - buffers not yet allocated\n", - ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode)); - res = -ENOMEM; - return res; - } - - first = offset / fh->buffers.buffer_size; - last = first - 1 + size / fh->buffers.buffer_size; - if (offset % fh->buffers.buffer_size != 0 || - size % fh->buffers.buffer_size != 0 || first < 0 || - last < 0 || first >= fh->buffers.num_buffers || - last >= fh->buffers.buffer_size) { - dprintk(1, - KERN_ERR - "%s: %s(%s) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n", - ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), offset, size, - fh->buffers.buffer_size, - fh->buffers.num_buffers); - res = -EINVAL; - return res; - } - - /* Check if any buffers are already mapped */ - for (i = first; i <= last; i++) { - if (fh->buffers.buffer[i].map) { - dprintk(1, - KERN_ERR - "%s: %s(%s) - buffer %d already mapped\n", - ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), i); - res = -EBUSY; - return res; - } - } - - /* map these buffers */ - map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL); - if (!map) { - res = -ENOMEM; - return res; - } - map->fh = fh; - atomic_set(&map->count, 1); - - vma->vm_ops = &zoran_vm_ops; - vma->vm_flags |= VM_DONTEXPAND; - vma->vm_private_data = map; - - if (fh->map_mode == ZORAN_MAP_MODE_RAW) { - for (i = first; i <= last; i++) { - todo = size; - if (todo > fh->buffers.buffer_size) - todo = fh->buffers.buffer_size; - page = fh->buffers.buffer[i].v4l.fbuffer_phys; - if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, - todo, PAGE_SHARED)) { - dprintk(1, - KERN_ERR - "%s: %s(V4L) - remap_pfn_range failed\n", - ZR_DEVNAME(zr), __func__); - res = -EAGAIN; - return res; - } - size -= todo; - start += todo; - fh->buffers.buffer[i].map = map; - if (size == 0) - break; - } - } else { - for (i = first; i <= last; i++) { - for (j = 0; - j < fh->buffers.buffer_size / PAGE_SIZE; - j++) { - fraglen = - (le32_to_cpu(fh->buffers.buffer[i].jpg. - frag_tab[2 * j + 1]) & ~1) << 1; - todo = size; - if (todo > fraglen) - todo = fraglen; - pos = - le32_to_cpu(fh->buffers. - buffer[i].jpg.frag_tab[2 * j]); - /* should just be pos on i386 */ - page = virt_to_phys(bus_to_virt(pos)) - >> PAGE_SHIFT; - if (remap_pfn_range(vma, start, page, - todo, PAGE_SHARED)) { - dprintk(1, - KERN_ERR - "%s: %s(V4L) - remap_pfn_range failed\n", - ZR_DEVNAME(zr), __func__); - res = -EAGAIN; - return res; - } - size -= todo; - start += todo; - if (size == 0) - break; - if (le32_to_cpu(fh->buffers.buffer[i].jpg. - frag_tab[2 * j + 1]) & 1) - break; /* was last fragment */ - } - fh->buffers.buffer[i].map = map; - if (size == 0) - break; - - } - } - return res; -} - -static const struct v4l2_ioctl_ops zoran_ioctl_ops = { - .vidioc_querycap = zoran_querycap, - .vidioc_s_selection = zoran_s_selection, - .vidioc_g_selection = zoran_g_selection, - .vidioc_enum_input = zoran_enum_input, - .vidioc_g_input = zoran_g_input, - .vidioc_s_input = zoran_s_input, - .vidioc_enum_output = zoran_enum_output, - .vidioc_g_output = zoran_g_output, - .vidioc_s_output = zoran_s_output, - .vidioc_g_fbuf = zoran_g_fbuf, - .vidioc_s_fbuf = zoran_s_fbuf, - .vidioc_g_std = zoran_g_std, - .vidioc_s_std = zoran_s_std, - .vidioc_g_jpegcomp = zoran_g_jpegcomp, - .vidioc_s_jpegcomp = zoran_s_jpegcomp, - .vidioc_overlay = zoran_overlay, - .vidioc_reqbufs = zoran_reqbufs, - .vidioc_querybuf = zoran_querybuf, - .vidioc_qbuf = zoran_qbuf, - .vidioc_dqbuf = zoran_dqbuf, - .vidioc_streamon = zoran_streamon, - .vidioc_streamoff = zoran_streamoff, - .vidioc_enum_fmt_vid_cap = zoran_enum_fmt_vid_cap, - .vidioc_enum_fmt_vid_out = zoran_enum_fmt_vid_out, - .vidioc_enum_fmt_vid_overlay = zoran_enum_fmt_vid_overlay, - .vidioc_g_fmt_vid_cap = zoran_g_fmt_vid_cap, - .vidioc_g_fmt_vid_out = zoran_g_fmt_vid_out, - .vidioc_g_fmt_vid_overlay = zoran_g_fmt_vid_overlay, - .vidioc_s_fmt_vid_cap = zoran_s_fmt_vid_cap, - .vidioc_s_fmt_vid_out = zoran_s_fmt_vid_out, - .vidioc_s_fmt_vid_overlay = zoran_s_fmt_vid_overlay, - .vidioc_try_fmt_vid_cap = zoran_try_fmt_vid_cap, - .vidioc_try_fmt_vid_out = zoran_try_fmt_vid_out, - .vidioc_try_fmt_vid_overlay = zoran_try_fmt_vid_overlay, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct v4l2_file_operations zoran_fops = { - .owner = THIS_MODULE, - .open = zoran_open, - .release = zoran_close, - .unlocked_ioctl = video_ioctl2, - .mmap = zoran_mmap, - .poll = zoran_poll, -}; - -const struct video_device zoran_template = { - .name = ZORAN_NAME, - .fops = &zoran_fops, - .ioctl_ops = &zoran_ioctl_ops, - .release = &zoran_vdev_release, - .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, -}; - diff --git a/drivers/media/pci/zoran/zoran_procfs.c b/drivers/media/pci/zoran/zoran_procfs.c deleted file mode 100644 index 78ac8f853748..000000000000 --- a/drivers/media/pci/zoran/zoran_procfs.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * This part handles the procFS entries (/proc/ZORAN[%d]) - * - * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> - * - * Currently maintained by: - * Ronald Bultje <rbultje@ronald.bitfreak.net> - * Laurent Pinchart <laurent.pinchart@skynet.be> - * Mailinglist <mjpeg-users@lists.sf.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/vmalloc.h> - -#include <linux/proc_fs.h> -#include <linux/pci.h> -#include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> -#include <linux/videodev2.h> -#include <linux/spinlock.h> -#include <linux/sem.h> -#include <linux/seq_file.h> - -#include <linux/ctype.h> -#include <linux/poll.h> -#include <asm/io.h> - -#include "videocodec.h" -#include "zoran.h" -#include "zoran_procfs.h" -#include "zoran_card.h" - -#ifdef CONFIG_PROC_FS -struct procfs_params_zr36067 { - char *name; - short reg; - u32 mask; - short bit; -}; - -static const struct procfs_params_zr36067 zr67[] = { - {"HSPol", 0x000, 1, 30}, - {"HStart", 0x000, 0x3ff, 10}, - {"HEnd", 0x000, 0x3ff, 0}, - - {"VSPol", 0x004, 1, 30}, - {"VStart", 0x004, 0x3ff, 10}, - {"VEnd", 0x004, 0x3ff, 0}, - - {"ExtFl", 0x008, 1, 26}, - {"TopField", 0x008, 1, 25}, - {"VCLKPol", 0x008, 1, 24}, - {"DupFld", 0x008, 1, 20}, - {"LittleEndian", 0x008, 1, 0}, - - {"HsyncStart", 0x10c, 0xffff, 16}, - {"LineTot", 0x10c, 0xffff, 0}, - - {"NAX", 0x110, 0xffff, 16}, - {"PAX", 0x110, 0xffff, 0}, - - {"NAY", 0x114, 0xffff, 16}, - {"PAY", 0x114, 0xffff, 0}, - - /* {"",,,}, */ - - {NULL, 0, 0, 0}, -}; - -static void -setparam (struct zoran *zr, - char *name, - char *sval) -{ - int i = 0, reg0, reg, val; - - while (zr67[i].name != NULL) { - if (!strncmp(name, zr67[i].name, strlen(zr67[i].name))) { - reg = reg0 = btread(zr67[i].reg); - reg &= ~(zr67[i].mask << zr67[i].bit); - if (!isdigit(sval[0])) - break; - val = simple_strtoul(sval, NULL, 0); - if ((val & ~zr67[i].mask)) - break; - reg |= (val & zr67[i].mask) << zr67[i].bit; - dprintk(4, - KERN_INFO - "%s: setparam: setting ZR36067 register 0x%03x: 0x%08x=>0x%08x %s=%d\n", - ZR_DEVNAME(zr), zr67[i].reg, reg0, reg, - zr67[i].name, val); - btwrite(reg, zr67[i].reg); - break; - } - i++; - } -} - -static int zoran_show(struct seq_file *p, void *v) -{ - struct zoran *zr = p->private; - int i; - - seq_printf(p, "ZR36067 registers:\n"); - for (i = 0; i < 0x130; i += 16) - seq_printf(p, "%03X %08X %08X %08X %08X \n", i, - btread(i), btread(i+4), btread(i+8), btread(i+12)); - return 0; -} - -static int zoran_open(struct inode *inode, struct file *file) -{ - struct zoran *data = PDE_DATA(inode); - return single_open(file, zoran_show, data); -} - -static ssize_t zoran_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) -{ - struct zoran *zr = PDE_DATA(file_inode(file)); - char *string, *sp; - char *line, *ldelim, *varname, *svar, *tdelim; - - if (count > 32768) /* Stupidity filter */ - return -EINVAL; - - string = sp = vmalloc(count + 1); - if (!string) { - dprintk(1, - KERN_ERR - "%s: write_proc: can not allocate memory\n", - ZR_DEVNAME(zr)); - return -ENOMEM; - } - if (copy_from_user(string, buffer, count)) { - vfree (string); - return -EFAULT; - } - string[count] = 0; - dprintk(4, KERN_INFO "%s: write_proc: name=%pD count=%zu zr=%p\n", - ZR_DEVNAME(zr), file, count, zr); - ldelim = " \t\n"; - tdelim = "="; - line = strpbrk(sp, ldelim); - while (line) { - *line = 0; - svar = strpbrk(sp, tdelim); - if (svar) { - *svar = 0; - varname = sp; - svar++; - setparam(zr, varname, svar); - } - sp = line + 1; - line = strpbrk(sp, ldelim); - } - vfree(string); - - return count; -} - -static const struct file_operations zoran_operations = { - .owner = THIS_MODULE, - .open = zoran_open, - .read = seq_read, - .write = zoran_write, - .llseek = seq_lseek, - .release = single_release, -}; -#endif - -int -zoran_proc_init (struct zoran *zr) -{ -#ifdef CONFIG_PROC_FS - char name[8]; - - snprintf(name, 7, "zoran%d", zr->id); - zr->zoran_proc = proc_create_data(name, 0, NULL, &zoran_operations, zr); - if (zr->zoran_proc != NULL) { - dprintk(2, - KERN_INFO - "%s: procfs entry /proc/%s allocated. data=%p\n", - ZR_DEVNAME(zr), name, zr); - } else { - dprintk(1, KERN_ERR "%s: Unable to initialise /proc/%s\n", - ZR_DEVNAME(zr), name); - return 1; - } -#endif - return 0; -} - -void -zoran_proc_cleanup (struct zoran *zr) -{ -#ifdef CONFIG_PROC_FS - char name[8]; - - snprintf(name, 7, "zoran%d", zr->id); - if (zr->zoran_proc) - remove_proc_entry(name, NULL); - zr->zoran_proc = NULL; -#endif -} diff --git a/drivers/media/pci/zoran/zoran_procfs.h b/drivers/media/pci/zoran/zoran_procfs.h deleted file mode 100644 index 0ac7cb0011f2..000000000000 --- a/drivers/media/pci/zoran/zoran_procfs.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * This part handles card-specific data and detection - * - * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> - * - * Currently maintained by: - * Ronald Bultje <rbultje@ronald.bitfreak.net> - * Laurent Pinchart <laurent.pinchart@skynet.be> - * Mailinglist <mjpeg-users@lists.sf.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __ZORAN_PROCFS_H__ -#define __ZORAN_PROCFS_H__ - -extern int zoran_proc_init(struct zoran *zr); -extern void zoran_proc_cleanup(struct zoran *zr); - -#endif /* __ZORAN_PROCFS_H__ */ diff --git a/drivers/media/pci/zoran/zr36016.c b/drivers/media/pci/zoran/zr36016.c deleted file mode 100644 index 8736b9d8d97e..000000000000 --- a/drivers/media/pci/zoran/zr36016.c +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Zoran ZR36016 basic configuration functions - * - * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at> - * - * $Id: zr36016.c,v 1.1.2.14 2003/08/20 19:46:55 rbultje Exp $ - * - * ------------------------------------------------------------------------ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * ------------------------------------------------------------------------ - */ - -#define ZR016_VERSION "v0.7" - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/delay.h> - -#include <linux/types.h> -#include <linux/wait.h> - -/* I/O commands, error codes */ -#include <asm/io.h> - -/* v4l API */ - -/* headerfile of this module */ -#include "zr36016.h" - -/* codec io API */ -#include "videocodec.h" - -/* it doesn't make sense to have more than 20 or so, - just to prevent some unwanted loops */ -#define MAX_CODECS 20 - -/* amount of chips attached via this driver */ -static int zr36016_codecs; - -/* debugging is available via module parameter */ -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-4)"); - -#define dprintk(num, format, args...) \ - do { \ - if (debug >= num) \ - printk(format, ##args); \ - } while (0) - -/* ========================================================================= - Local hardware I/O functions: - - read/write via codec layer (registers are located in the master device) - ========================================================================= */ - -/* read and write functions */ -static u8 -zr36016_read (struct zr36016 *ptr, - u16 reg) -{ - u8 value = 0; - - // just in case something is wrong... - if (ptr->codec->master_data->readreg) - value = - (ptr->codec->master_data-> - readreg(ptr->codec, reg)) & 0xFF; - else - dprintk(1, - KERN_ERR "%s: invalid I/O setup, nothing read!\n", - ptr->name); - - dprintk(4, "%s: reading from 0x%04x: %02x\n", ptr->name, reg, - value); - - return value; -} - -static void -zr36016_write (struct zr36016 *ptr, - u16 reg, - u8 value) -{ - dprintk(4, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value, - reg); - - // just in case something is wrong... - if (ptr->codec->master_data->writereg) { - ptr->codec->master_data->writereg(ptr->codec, reg, value); - } else - dprintk(1, - KERN_ERR - "%s: invalid I/O setup, nothing written!\n", - ptr->name); -} - -/* indirect read and write functions */ -/* the 016 supports auto-addr-increment, but - * writing it all time cost not much and is safer... */ -static u8 -zr36016_readi (struct zr36016 *ptr, - u16 reg) -{ - u8 value = 0; - - // just in case something is wrong... - if ((ptr->codec->master_data->writereg) && - (ptr->codec->master_data->readreg)) { - ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR - value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF; // DATA - } else - dprintk(1, - KERN_ERR - "%s: invalid I/O setup, nothing read (i)!\n", - ptr->name); - - dprintk(4, "%s: reading indirect from 0x%04x: %02x\n", ptr->name, - reg, value); - return value; -} - -static void -zr36016_writei (struct zr36016 *ptr, - u16 reg, - u8 value) -{ - dprintk(4, "%s: writing indirect 0x%02x to 0x%04x\n", ptr->name, - value, reg); - - // just in case something is wrong... - if (ptr->codec->master_data->writereg) { - ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR - ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF); // DATA - } else - dprintk(1, - KERN_ERR - "%s: invalid I/O setup, nothing written (i)!\n", - ptr->name); -} - -/* ========================================================================= - Local helper function: - - version read - ========================================================================= */ - -/* version kept in datastructure */ -static u8 -zr36016_read_version (struct zr36016 *ptr) -{ - ptr->version = zr36016_read(ptr, 0) >> 4; - return ptr->version; -} - -/* ========================================================================= - Local helper function: - - basic test of "connectivity", writes/reads to/from PAX-Lo register - ========================================================================= */ - -static int -zr36016_basic_test (struct zr36016 *ptr) -{ - if (debug) { - int i; - zr36016_writei(ptr, ZR016I_PAX_LO, 0x55); - dprintk(1, KERN_INFO "%s: registers: ", ptr->name); - for (i = 0; i <= 0x0b; i++) - dprintk(1, "%02x ", zr36016_readi(ptr, i)); - dprintk(1, "\n"); - } - // for testing just write 0, then the default value to a register and read - // it back in both cases - zr36016_writei(ptr, ZR016I_PAX_LO, 0x00); - if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0) { - dprintk(1, - KERN_ERR - "%s: attach failed, can't connect to vfe processor!\n", - ptr->name); - return -ENXIO; - } - zr36016_writei(ptr, ZR016I_PAX_LO, 0x0d0); - if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0d0) { - dprintk(1, - KERN_ERR - "%s: attach failed, can't connect to vfe processor!\n", - ptr->name); - return -ENXIO; - } - // we allow version numbers from 0-3, should be enough, though - zr36016_read_version(ptr); - if (ptr->version & 0x0c) { - dprintk(1, - KERN_ERR - "%s: attach failed, suspicious version %d found...\n", - ptr->name, ptr->version); - return -ENXIO; - } - - return 0; /* looks good! */ -} - -/* ========================================================================= - Local helper function: - - simple loop for pushing the init datasets - NO USE -- - ========================================================================= */ - -#if 0 -static int zr36016_pushit (struct zr36016 *ptr, - u16 startreg, - u16 len, - const char *data) -{ - int i=0; - - dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", - ptr->name, startreg,len); - while (i<len) { - zr36016_writei(ptr, startreg++, data[i++]); - } - - return i; -} -#endif - -/* ========================================================================= - Basic datasets & init: - - //TODO// - ========================================================================= */ - -static void -zr36016_init (struct zr36016 *ptr) -{ - // stop any processing - zr36016_write(ptr, ZR016_GOSTOP, 0); - - // mode setup (yuv422 in and out, compression/expansuon due to mode) - zr36016_write(ptr, ZR016_MODE, - ZR016_YUV422 | ZR016_YUV422_YUV422 | - (ptr->mode == CODEC_DO_COMPRESSION ? - ZR016_COMPRESSION : ZR016_EXPANSION)); - - // misc setup - zr36016_writei(ptr, ZR016I_SETUP1, - (ptr->xdec ? (ZR016_HRFL | ZR016_HORZ) : 0) | - (ptr->ydec ? ZR016_VERT : 0) | ZR016_CNTI); - zr36016_writei(ptr, ZR016I_SETUP2, ZR016_CCIR); - - // Window setup - // (no extra offset for now, norm defines offset, default width height) - zr36016_writei(ptr, ZR016I_PAX_HI, ptr->width >> 8); - zr36016_writei(ptr, ZR016I_PAX_LO, ptr->width & 0xFF); - zr36016_writei(ptr, ZR016I_PAY_HI, ptr->height >> 8); - zr36016_writei(ptr, ZR016I_PAY_LO, ptr->height & 0xFF); - zr36016_writei(ptr, ZR016I_NAX_HI, ptr->xoff >> 8); - zr36016_writei(ptr, ZR016I_NAX_LO, ptr->xoff & 0xFF); - zr36016_writei(ptr, ZR016I_NAY_HI, ptr->yoff >> 8); - zr36016_writei(ptr, ZR016I_NAY_LO, ptr->yoff & 0xFF); - - /* shall we continue now, please? */ - zr36016_write(ptr, ZR016_GOSTOP, 1); -} - -/* ========================================================================= - CODEC API FUNCTIONS - - this functions are accessed by the master via the API structure - ========================================================================= */ - -/* set compression/expansion mode and launches codec - - this should be the last call from the master before starting processing */ -static int -zr36016_set_mode (struct videocodec *codec, - int mode) -{ - struct zr36016 *ptr = (struct zr36016 *) codec->data; - - dprintk(2, "%s: set_mode %d call\n", ptr->name, mode); - - if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION)) - return -EINVAL; - - ptr->mode = mode; - zr36016_init(ptr); - - return 0; -} - -/* set picture size */ -static int -zr36016_set_video (struct videocodec *codec, - struct tvnorm *norm, - struct vfe_settings *cap, - struct vfe_polarity *pol) -{ - struct zr36016 *ptr = (struct zr36016 *) codec->data; - - dprintk(2, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n", - ptr->name, norm->HStart, norm->VStart, - cap->x, cap->y, cap->width, cap->height, - cap->decimation); - - /* if () return -EINVAL; - * trust the master driver that it knows what it does - so - * we allow invalid startx/y for now ... */ - ptr->width = cap->width; - ptr->height = cap->height; - /* (Ronald) This is ugly. zoran_device.c, line 387 - * already mentions what happens if HStart is even - * (blue faces, etc., cr/cb inversed). There's probably - * some good reason why HStart is 0 instead of 1, so I'm - * leaving it to this for now, but really... This can be - * done a lot simpler */ - ptr->xoff = (norm->HStart ? norm->HStart : 1) + cap->x; - /* Something to note here (I don't understand it), setting - * VStart too high will cause the codec to 'not work'. I - * really don't get it. values of 16 (VStart) already break - * it here. Just '0' seems to work. More testing needed! */ - ptr->yoff = norm->VStart + cap->y; - /* (Ronald) dzjeeh, can't this thing do hor_decimation = 4? */ - ptr->xdec = ((cap->decimation & 0xff) == 1) ? 0 : 1; - ptr->ydec = (((cap->decimation >> 8) & 0xff) == 1) ? 0 : 1; - - return 0; -} - -/* additional control functions */ -static int -zr36016_control (struct videocodec *codec, - int type, - int size, - void *data) -{ - struct zr36016 *ptr = (struct zr36016 *) codec->data; - int *ival = (int *) data; - - dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type, - size); - - switch (type) { - case CODEC_G_STATUS: /* get last status - we don't know it ... */ - if (size != sizeof(int)) - return -EFAULT; - *ival = 0; - break; - - case CODEC_G_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - *ival = 0; - break; - - case CODEC_S_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - if (*ival != 0) - return -EINVAL; - /* not needed, do nothing */ - return 0; - - case CODEC_G_VFE: - case CODEC_S_VFE: - return 0; - - case CODEC_S_MMAP: - /* not available, give an error */ - return -ENXIO; - - default: - return -EINVAL; - } - - return size; -} - -/* ========================================================================= - Exit and unregister function: - - Deinitializes Zoran's JPEG processor - ========================================================================= */ - -static int -zr36016_unset (struct videocodec *codec) -{ - struct zr36016 *ptr = codec->data; - - if (ptr) { - /* do wee need some codec deinit here, too ???? */ - - dprintk(1, "%s: finished codec #%d\n", ptr->name, - ptr->num); - kfree(ptr); - codec->data = NULL; - - zr36016_codecs--; - return 0; - } - - return -EFAULT; -} - -/* ========================================================================= - Setup and registry function: - - Initializes Zoran's JPEG processor - - Also sets pixel size, average code size, mode (compr./decompr.) - (the given size is determined by the processor with the video interface) - ========================================================================= */ - -static int -zr36016_setup (struct videocodec *codec) -{ - struct zr36016 *ptr; - int res; - - dprintk(2, "zr36016: initializing VFE subsystem #%d.\n", - zr36016_codecs); - - if (zr36016_codecs == MAX_CODECS) { - dprintk(1, - KERN_ERR "zr36016: Can't attach more codecs!\n"); - return -ENOSPC; - } - //mem structure init - codec->data = ptr = kzalloc(sizeof(struct zr36016), GFP_KERNEL); - if (NULL == ptr) { - dprintk(1, KERN_ERR "zr36016: Can't get enough memory!\n"); - return -ENOMEM; - } - - snprintf(ptr->name, sizeof(ptr->name), "zr36016[%d]", - zr36016_codecs); - ptr->num = zr36016_codecs++; - ptr->codec = codec; - - //testing - res = zr36016_basic_test(ptr); - if (res < 0) { - zr36016_unset(codec); - return res; - } - //final setup - ptr->mode = CODEC_DO_COMPRESSION; - ptr->width = 768; - ptr->height = 288; - ptr->xdec = 1; - ptr->ydec = 0; - zr36016_init(ptr); - - dprintk(1, KERN_INFO "%s: codec v%d attached and running\n", - ptr->name, ptr->version); - - return 0; -} - -static const struct videocodec zr36016_codec = { - .owner = THIS_MODULE, - .name = "zr36016", - .magic = 0L, // magic not used - .flags = - CODEC_FLAG_HARDWARE | CODEC_FLAG_VFE | CODEC_FLAG_ENCODER | - CODEC_FLAG_DECODER, - .type = CODEC_TYPE_ZR36016, - .setup = zr36016_setup, // functionality - .unset = zr36016_unset, - .set_mode = zr36016_set_mode, - .set_video = zr36016_set_video, - .control = zr36016_control, - // others are not used -}; - -/* ========================================================================= - HOOK IN DRIVER AS KERNEL MODULE - ========================================================================= */ - -static int __init -zr36016_init_module (void) -{ - //dprintk(1, "ZR36016 driver %s\n",ZR016_VERSION); - zr36016_codecs = 0; - return videocodec_register(&zr36016_codec); -} - -static void __exit -zr36016_cleanup_module (void) -{ - if (zr36016_codecs) { - dprintk(1, - "zr36016: something's wrong - %d codecs left somehow.\n", - zr36016_codecs); - } - videocodec_unregister(&zr36016_codec); -} - -module_init(zr36016_init_module); -module_exit(zr36016_cleanup_module); - -MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>"); -MODULE_DESCRIPTION("Driver module for ZR36016 video frontends " - ZR016_VERSION); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/zoran/zr36016.h b/drivers/media/pci/zoran/zr36016.h deleted file mode 100644 index 784bcf5727b8..000000000000 --- a/drivers/media/pci/zoran/zr36016.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Zoran ZR36016 basic configuration functions - header file - * - * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at> - * - * $Id: zr36016.h,v 1.1.2.3 2003/01/14 21:18:07 rbultje Exp $ - * - * ------------------------------------------------------------------------ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * ------------------------------------------------------------------------ - */ - -#ifndef ZR36016_H -#define ZR36016_H - -/* data stored for each zoran jpeg codec chip */ -struct zr36016 { - char name[32]; - int num; - /* io datastructure */ - struct videocodec *codec; - // coder status - __u8 version; - // actual coder setup - int mode; - - __u16 xoff; - __u16 yoff; - __u16 width; - __u16 height; - __u16 xdec; - __u16 ydec; -}; - -/* direct register addresses */ -#define ZR016_GOSTOP 0x00 -#define ZR016_MODE 0x01 -#define ZR016_IADDR 0x02 -#define ZR016_IDATA 0x03 - -/* indirect register addresses */ -#define ZR016I_SETUP1 0x00 -#define ZR016I_SETUP2 0x01 -#define ZR016I_NAX_LO 0x02 -#define ZR016I_NAX_HI 0x03 -#define ZR016I_PAX_LO 0x04 -#define ZR016I_PAX_HI 0x05 -#define ZR016I_NAY_LO 0x06 -#define ZR016I_NAY_HI 0x07 -#define ZR016I_PAY_LO 0x08 -#define ZR016I_PAY_HI 0x09 -#define ZR016I_NOL_LO 0x0a -#define ZR016I_NOL_HI 0x0b - -/* possible values for mode register */ -#define ZR016_RGB444_YUV444 0x00 -#define ZR016_RGB444_YUV422 0x01 -#define ZR016_RGB444_YUV411 0x02 -#define ZR016_RGB444_Y400 0x03 -#define ZR016_RGB444_RGB444 0x04 -#define ZR016_YUV444_YUV444 0x08 -#define ZR016_YUV444_YUV422 0x09 -#define ZR016_YUV444_YUV411 0x0a -#define ZR016_YUV444_Y400 0x0b -#define ZR016_YUV444_RGB444 0x0c -#define ZR016_YUV422_YUV422 0x11 -#define ZR016_YUV422_YUV411 0x12 -#define ZR016_YUV422_Y400 0x13 -#define ZR016_YUV411_YUV411 0x16 -#define ZR016_YUV411_Y400 0x17 -#define ZR016_4444_4444 0x19 -#define ZR016_100_100 0x1b - -#define ZR016_RGB444 0x00 -#define ZR016_YUV444 0x20 -#define ZR016_YUV422 0x40 - -#define ZR016_COMPRESSION 0x80 -#define ZR016_EXPANSION 0x80 - -/* possible values for setup 1 register */ -#define ZR016_CKRT 0x80 -#define ZR016_VERT 0x40 -#define ZR016_HORZ 0x20 -#define ZR016_HRFL 0x10 -#define ZR016_DSFL 0x08 -#define ZR016_SBFL 0x04 -#define ZR016_RSTR 0x02 -#define ZR016_CNTI 0x01 - -/* possible values for setup 2 register */ -#define ZR016_SYEN 0x40 -#define ZR016_CCIR 0x04 -#define ZR016_SIGN 0x02 -#define ZR016_YMCS 0x01 - -#endif /*fndef ZR36016_H */ diff --git a/drivers/media/pci/zoran/zr36050.c b/drivers/media/pci/zoran/zr36050.c deleted file mode 100644 index 5ebfc16672f3..000000000000 --- a/drivers/media/pci/zoran/zr36050.c +++ /dev/null @@ -1,896 +0,0 @@ -/* - * Zoran ZR36050 basic configuration functions - * - * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at> - * - * $Id: zr36050.c,v 1.1.2.11 2003/08/03 14:54:53 rbultje Exp $ - * - * ------------------------------------------------------------------------ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * ------------------------------------------------------------------------ - */ - -#define ZR050_VERSION "v0.7.1" - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/delay.h> - -#include <linux/types.h> -#include <linux/wait.h> - -/* I/O commands, error codes */ -#include <asm/io.h> - -/* headerfile of this module */ -#include "zr36050.h" - -/* codec io API */ -#include "videocodec.h" - -/* it doesn't make sense to have more than 20 or so, - just to prevent some unwanted loops */ -#define MAX_CODECS 20 - -/* amount of chips attached via this driver */ -static int zr36050_codecs; - -/* debugging is available via module parameter */ -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-4)"); - -#define dprintk(num, format, args...) \ - do { \ - if (debug >= num) \ - printk(format, ##args); \ - } while (0) - -/* ========================================================================= - Local hardware I/O functions: - - read/write via codec layer (registers are located in the master device) - ========================================================================= */ - -/* read and write functions */ -static u8 -zr36050_read (struct zr36050 *ptr, - u16 reg) -{ - u8 value = 0; - - // just in case something is wrong... - if (ptr->codec->master_data->readreg) - value = (ptr->codec->master_data->readreg(ptr->codec, - reg)) & 0xFF; - else - dprintk(1, - KERN_ERR "%s: invalid I/O setup, nothing read!\n", - ptr->name); - - dprintk(4, "%s: reading from 0x%04x: %02x\n", ptr->name, reg, - value); - - return value; -} - -static void -zr36050_write (struct zr36050 *ptr, - u16 reg, - u8 value) -{ - dprintk(4, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value, - reg); - - // just in case something is wrong... - if (ptr->codec->master_data->writereg) - ptr->codec->master_data->writereg(ptr->codec, reg, value); - else - dprintk(1, - KERN_ERR - "%s: invalid I/O setup, nothing written!\n", - ptr->name); -} - -/* ========================================================================= - Local helper function: - - status read - ========================================================================= */ - -/* status is kept in datastructure */ -static u8 -zr36050_read_status1 (struct zr36050 *ptr) -{ - ptr->status1 = zr36050_read(ptr, ZR050_STATUS_1); - - zr36050_read(ptr, 0); - return ptr->status1; -} - -/* ========================================================================= - Local helper function: - - scale factor read - ========================================================================= */ - -/* scale factor is kept in datastructure */ -static u16 -zr36050_read_scalefactor (struct zr36050 *ptr) -{ - ptr->scalefact = (zr36050_read(ptr, ZR050_SF_HI) << 8) | - (zr36050_read(ptr, ZR050_SF_LO) & 0xFF); - - /* leave 0 selected for an eventually GO from master */ - zr36050_read(ptr, 0); - return ptr->scalefact; -} - -/* ========================================================================= - Local helper function: - - wait if codec is ready to proceed (end of processing) or time is over - ========================================================================= */ - -static void -zr36050_wait_end (struct zr36050 *ptr) -{ - int i = 0; - - while (!(zr36050_read_status1(ptr) & 0x4)) { - udelay(1); - if (i++ > 200000) { // 200ms, there is for sure something wrong!!! - dprintk(1, - "%s: timeout at wait_end (last status: 0x%02x)\n", - ptr->name, ptr->status1); - break; - } - } -} - -/* ========================================================================= - Local helper function: - - basic test of "connectivity", writes/reads to/from memory the SOF marker - ========================================================================= */ - -static int -zr36050_basic_test (struct zr36050 *ptr) -{ - zr36050_write(ptr, ZR050_SOF_IDX, 0x00); - zr36050_write(ptr, ZR050_SOF_IDX + 1, 0x00); - if ((zr36050_read(ptr, ZR050_SOF_IDX) | - zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0x0000) { - dprintk(1, - KERN_ERR - "%s: attach failed, can't connect to jpeg processor!\n", - ptr->name); - return -ENXIO; - } - zr36050_write(ptr, ZR050_SOF_IDX, 0xff); - zr36050_write(ptr, ZR050_SOF_IDX + 1, 0xc0); - if (((zr36050_read(ptr, ZR050_SOF_IDX) << 8) | - zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0xffc0) { - dprintk(1, - KERN_ERR - "%s: attach failed, can't connect to jpeg processor!\n", - ptr->name); - return -ENXIO; - } - - zr36050_wait_end(ptr); - if ((ptr->status1 & 0x4) == 0) { - dprintk(1, - KERN_ERR - "%s: attach failed, jpeg processor failed (end flag)!\n", - ptr->name); - return -EBUSY; - } - - return 0; /* looks good! */ -} - -/* ========================================================================= - Local helper function: - - simple loop for pushing the init datasets - ========================================================================= */ - -static int -zr36050_pushit (struct zr36050 *ptr, - u16 startreg, - u16 len, - const char *data) -{ - int i = 0; - - dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ptr->name, - startreg, len); - while (i < len) { - zr36050_write(ptr, startreg++, data[i++]); - } - - return i; -} - -/* ========================================================================= - Basic datasets: - - jpeg baseline setup data (you find it on lots places in internet, or just - extract it from any regular .jpg image...) - - Could be variable, but until it's not needed it they are just fixed to save - memory. Otherwise expand zr36050 structure with arrays, push the values to - it and initialize from there, as e.g. the linux zr36057/60 driver does it. - ========================================================================= */ - -static const char zr36050_dqt[0x86] = { - 0xff, 0xdb, //Marker: DQT - 0x00, 0x84, //Length: 2*65+2 - 0x00, //Pq,Tq first table - 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, - 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, - 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, - 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, - 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, - 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, - 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, - 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, - 0x01, //Pq,Tq second table - 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, - 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 -}; - -static const char zr36050_dht[0x1a4] = { - 0xff, 0xc4, //Marker: DHT - 0x01, 0xa2, //Length: 2*AC, 2*DC - 0x00, //DC first table - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0x01, //DC second table - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0x10, //AC first table - 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, - 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, - 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, - 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, - 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, - 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, - 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, - 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, - 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, - 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, - 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, - 0x11, //AC second table - 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, - 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, - 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, - 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, - 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, - 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, - 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, - 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, - 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, - 0xF9, 0xFA -}; - -/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */ -#define NO_OF_COMPONENTS 0x3 //Y,U,V -#define BASELINE_PRECISION 0x8 //MCU size (?) -static const char zr36050_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT -static const char zr36050_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC -static const char zr36050_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC - -/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */ -static const char zr36050_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 }; -static const char zr36050_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 }; - -/* ========================================================================= - Local helper functions: - - calculation and setup of parameter-dependent JPEG baseline segments - (needed for compression only) - ========================================================================= */ - -/* ------------------------------------------------------------------------- */ - -/* SOF (start of frame) segment depends on width, height and sampling ratio - of each color component */ - -static int -zr36050_set_sof (struct zr36050 *ptr) -{ - char sof_data[34]; // max. size of register set - int i; - - dprintk(3, "%s: write SOF (%dx%d, %d components)\n", ptr->name, - ptr->width, ptr->height, NO_OF_COMPONENTS); - sof_data[0] = 0xff; - sof_data[1] = 0xc0; - sof_data[2] = 0x00; - sof_data[3] = (3 * NO_OF_COMPONENTS) + 8; - sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36050 - sof_data[5] = (ptr->height) >> 8; - sof_data[6] = (ptr->height) & 0xff; - sof_data[7] = (ptr->width) >> 8; - sof_data[8] = (ptr->width) & 0xff; - sof_data[9] = NO_OF_COMPONENTS; - for (i = 0; i < NO_OF_COMPONENTS; i++) { - sof_data[10 + (i * 3)] = i; // index identifier - sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | (ptr->v_samp_ratio[i]); // sampling ratios - sof_data[12 + (i * 3)] = zr36050_tq[i]; // Q table selection - } - return zr36050_pushit(ptr, ZR050_SOF_IDX, - (3 * NO_OF_COMPONENTS) + 10, sof_data); -} - -/* ------------------------------------------------------------------------- */ - -/* SOS (start of scan) segment depends on the used scan components - of each color component */ - -static int -zr36050_set_sos (struct zr36050 *ptr) -{ - char sos_data[16]; // max. size of register set - int i; - - dprintk(3, "%s: write SOS\n", ptr->name); - sos_data[0] = 0xff; - sos_data[1] = 0xda; - sos_data[2] = 0x00; - sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3; - sos_data[4] = NO_OF_COMPONENTS; - for (i = 0; i < NO_OF_COMPONENTS; i++) { - sos_data[5 + (i * 2)] = i; // index - sos_data[6 + (i * 2)] = (zr36050_td[i] << 4) | zr36050_ta[i]; // AC/DC tbl.sel. - } - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3F; - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00; - return zr36050_pushit(ptr, ZR050_SOS1_IDX, - 4 + 1 + (2 * NO_OF_COMPONENTS) + 3, - sos_data); -} - -/* ------------------------------------------------------------------------- */ - -/* DRI (define restart interval) */ - -static int -zr36050_set_dri (struct zr36050 *ptr) -{ - char dri_data[6]; // max. size of register set - - dprintk(3, "%s: write DRI\n", ptr->name); - dri_data[0] = 0xff; - dri_data[1] = 0xdd; - dri_data[2] = 0x00; - dri_data[3] = 0x04; - dri_data[4] = ptr->dri >> 8; - dri_data[5] = ptr->dri & 0xff; - return zr36050_pushit(ptr, ZR050_DRI_IDX, 6, dri_data); -} - -/* ========================================================================= - Setup function: - - Setup compression/decompression of Zoran's JPEG processor - ( see also zoran 36050 manual ) - - ... sorry for the spaghetti code ... - ========================================================================= */ -static void -zr36050_init (struct zr36050 *ptr) -{ - int sum = 0; - long bitcnt, tmp; - - if (ptr->mode == CODEC_DO_COMPRESSION) { - dprintk(2, "%s: COMPRESSION SETUP\n", ptr->name); - - /* 050 communicates with 057 in master mode */ - zr36050_write(ptr, ZR050_HARDWARE, ZR050_HW_MSTR); - - /* encoding table preload for compression */ - zr36050_write(ptr, ZR050_MODE, - ZR050_MO_COMP | ZR050_MO_TLM); - zr36050_write(ptr, ZR050_OPTIONS, 0); - - /* disable all IRQs */ - zr36050_write(ptr, ZR050_INT_REQ_0, 0); - zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1 - - /* volume control settings */ - /*zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol);*/ - zr36050_write(ptr, ZR050_SF_HI, ptr->scalefact >> 8); - zr36050_write(ptr, ZR050_SF_LO, ptr->scalefact & 0xff); - - zr36050_write(ptr, ZR050_AF_HI, 0xff); - zr36050_write(ptr, ZR050_AF_M, 0xff); - zr36050_write(ptr, ZR050_AF_LO, 0xff); - - /* setup the variable jpeg tables */ - sum += zr36050_set_sof(ptr); - sum += zr36050_set_sos(ptr); - sum += zr36050_set_dri(ptr); - - /* setup the fixed jpeg tables - maybe variable, though - - * (see table init section above) */ - dprintk(3, "%s: write DQT, DHT, APP\n", ptr->name); - sum += zr36050_pushit(ptr, ZR050_DQT_IDX, - sizeof(zr36050_dqt), zr36050_dqt); - sum += zr36050_pushit(ptr, ZR050_DHT_IDX, - sizeof(zr36050_dht), zr36050_dht); - zr36050_write(ptr, ZR050_APP_IDX, 0xff); - zr36050_write(ptr, ZR050_APP_IDX + 1, 0xe0 + ptr->app.appn); - zr36050_write(ptr, ZR050_APP_IDX + 2, 0x00); - zr36050_write(ptr, ZR050_APP_IDX + 3, ptr->app.len + 2); - sum += zr36050_pushit(ptr, ZR050_APP_IDX + 4, 60, - ptr->app.data) + 4; - zr36050_write(ptr, ZR050_COM_IDX, 0xff); - zr36050_write(ptr, ZR050_COM_IDX + 1, 0xfe); - zr36050_write(ptr, ZR050_COM_IDX + 2, 0x00); - zr36050_write(ptr, ZR050_COM_IDX + 3, ptr->com.len + 2); - sum += zr36050_pushit(ptr, ZR050_COM_IDX + 4, 60, - ptr->com.data) + 4; - - /* do the internal huffman table preload */ - zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI); - - zr36050_write(ptr, ZR050_GO, 1); // launch codec - zr36050_wait_end(ptr); - dprintk(2, "%s: Status after table preload: 0x%02x\n", - ptr->name, ptr->status1); - - if ((ptr->status1 & 0x4) == 0) { - dprintk(1, KERN_ERR "%s: init aborted!\n", - ptr->name); - return; // something is wrong, its timed out!!!! - } - - /* setup misc. data for compression (target code sizes) */ - - /* size of compressed code to reach without header data */ - sum = ptr->real_code_vol - sum; - bitcnt = sum << 3; /* need the size in bits */ - - tmp = bitcnt >> 16; - dprintk(3, - "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n", - ptr->name, sum, ptr->real_code_vol, bitcnt, tmp); - zr36050_write(ptr, ZR050_TCV_NET_HI, tmp >> 8); - zr36050_write(ptr, ZR050_TCV_NET_MH, tmp & 0xff); - tmp = bitcnt & 0xffff; - zr36050_write(ptr, ZR050_TCV_NET_ML, tmp >> 8); - zr36050_write(ptr, ZR050_TCV_NET_LO, tmp & 0xff); - - bitcnt -= bitcnt >> 7; // bits without stuffing - bitcnt -= ((bitcnt * 5) >> 6); // bits without eob - - tmp = bitcnt >> 16; - dprintk(3, "%s: code: nettobit=%ld, highnettobits=%ld\n", - ptr->name, bitcnt, tmp); - zr36050_write(ptr, ZR050_TCV_DATA_HI, tmp >> 8); - zr36050_write(ptr, ZR050_TCV_DATA_MH, tmp & 0xff); - tmp = bitcnt & 0xffff; - zr36050_write(ptr, ZR050_TCV_DATA_ML, tmp >> 8); - zr36050_write(ptr, ZR050_TCV_DATA_LO, tmp & 0xff); - - /* compression setup with or without bitrate control */ - zr36050_write(ptr, ZR050_MODE, - ZR050_MO_COMP | ZR050_MO_PASS2 | - (ptr->bitrate_ctrl ? ZR050_MO_BRC : 0)); - - /* this headers seem to deliver "valid AVI" jpeg frames */ - zr36050_write(ptr, ZR050_MARKERS_EN, - ZR050_ME_DQT | ZR050_ME_DHT | - ((ptr->app.len > 0) ? ZR050_ME_APP : 0) | - ((ptr->com.len > 0) ? ZR050_ME_COM : 0)); - } else { - dprintk(2, "%s: EXPANSION SETUP\n", ptr->name); - - /* 050 communicates with 055 in master mode */ - zr36050_write(ptr, ZR050_HARDWARE, - ZR050_HW_MSTR | ZR050_HW_CFIS_2_CLK); - - /* encoding table preload */ - zr36050_write(ptr, ZR050_MODE, ZR050_MO_TLM); - - /* disable all IRQs */ - zr36050_write(ptr, ZR050_INT_REQ_0, 0); - zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1 - - dprintk(3, "%s: write DHT\n", ptr->name); - zr36050_pushit(ptr, ZR050_DHT_IDX, sizeof(zr36050_dht), - zr36050_dht); - - /* do the internal huffman table preload */ - zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI); - - zr36050_write(ptr, ZR050_GO, 1); // launch codec - zr36050_wait_end(ptr); - dprintk(2, "%s: Status after table preload: 0x%02x\n", - ptr->name, ptr->status1); - - if ((ptr->status1 & 0x4) == 0) { - dprintk(1, KERN_ERR "%s: init aborted!\n", - ptr->name); - return; // something is wrong, its timed out!!!! - } - - /* setup misc. data for expansion */ - zr36050_write(ptr, ZR050_MODE, 0); - zr36050_write(ptr, ZR050_MARKERS_EN, 0); - } - - /* adr on selected, to allow GO from master */ - zr36050_read(ptr, 0); -} - -/* ========================================================================= - CODEC API FUNCTIONS - - this functions are accessed by the master via the API structure - ========================================================================= */ - -/* set compression/expansion mode and launches codec - - this should be the last call from the master before starting processing */ -static int -zr36050_set_mode (struct videocodec *codec, - int mode) -{ - struct zr36050 *ptr = (struct zr36050 *) codec->data; - - dprintk(2, "%s: set_mode %d call\n", ptr->name, mode); - - if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION)) - return -EINVAL; - - ptr->mode = mode; - zr36050_init(ptr); - - return 0; -} - -/* set picture size (norm is ignored as the codec doesn't know about it) */ -static int -zr36050_set_video (struct videocodec *codec, - struct tvnorm *norm, - struct vfe_settings *cap, - struct vfe_polarity *pol) -{ - struct zr36050 *ptr = (struct zr36050 *) codec->data; - int size; - - dprintk(2, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) q%d call\n", - ptr->name, norm->HStart, norm->VStart, - cap->x, cap->y, cap->width, cap->height, - cap->decimation, cap->quality); - /* if () return -EINVAL; - * trust the master driver that it knows what it does - so - * we allow invalid startx/y and norm for now ... */ - ptr->width = cap->width / (cap->decimation & 0xff); - ptr->height = cap->height / ((cap->decimation >> 8) & 0xff); - - /* (KM) JPEG quality */ - size = ptr->width * ptr->height; - size *= 16; /* size in bits */ - /* apply quality setting */ - size = size * cap->quality / 200; - - /* Minimum: 1kb */ - if (size < 8192) - size = 8192; - /* Maximum: 7/8 of code buffer */ - if (size > ptr->total_code_vol * 7) - size = ptr->total_code_vol * 7; - - ptr->real_code_vol = size >> 3; /* in bytes */ - - /* Set max_block_vol here (previously in zr36050_init, moved - * here for consistency with zr36060 code */ - zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol); - - return 0; -} - -/* additional control functions */ -static int -zr36050_control (struct videocodec *codec, - int type, - int size, - void *data) -{ - struct zr36050 *ptr = (struct zr36050 *) codec->data; - int *ival = (int *) data; - - dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type, - size); - - switch (type) { - case CODEC_G_STATUS: /* get last status */ - if (size != sizeof(int)) - return -EFAULT; - zr36050_read_status1(ptr); - *ival = ptr->status1; - break; - - case CODEC_G_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - *ival = CODEC_MODE_BJPG; - break; - - case CODEC_S_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - if (*ival != CODEC_MODE_BJPG) - return -EINVAL; - /* not needed, do nothing */ - return 0; - - case CODEC_G_VFE: - case CODEC_S_VFE: - /* not needed, do nothing */ - return 0; - - case CODEC_S_MMAP: - /* not available, give an error */ - return -ENXIO; - - case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */ - if (size != sizeof(int)) - return -EFAULT; - *ival = ptr->total_code_vol; - break; - - case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */ - if (size != sizeof(int)) - return -EFAULT; - ptr->total_code_vol = *ival; - /* (Kieran Morrissey) - * code copied from zr36060.c to ensure proper bitrate */ - ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; - break; - - case CODEC_G_JPEG_SCALE: /* get scaling factor */ - if (size != sizeof(int)) - return -EFAULT; - *ival = zr36050_read_scalefactor(ptr); - break; - - case CODEC_S_JPEG_SCALE: /* set scaling factor */ - if (size != sizeof(int)) - return -EFAULT; - ptr->scalefact = *ival; - break; - - case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */ - struct jpeg_app_marker *app = data; - - if (size != sizeof(struct jpeg_app_marker)) - return -EFAULT; - - *app = ptr->app; - break; - } - - case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */ - struct jpeg_app_marker *app = data; - - if (size != sizeof(struct jpeg_app_marker)) - return -EFAULT; - - ptr->app = *app; - break; - } - - case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */ - struct jpeg_com_marker *com = data; - - if (size != sizeof(struct jpeg_com_marker)) - return -EFAULT; - - *com = ptr->com; - break; - } - - case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */ - struct jpeg_com_marker *com = data; - - if (size != sizeof(struct jpeg_com_marker)) - return -EFAULT; - - ptr->com = *com; - break; - } - - default: - return -EINVAL; - } - - return size; -} - -/* ========================================================================= - Exit and unregister function: - - Deinitializes Zoran's JPEG processor - ========================================================================= */ - -static int -zr36050_unset (struct videocodec *codec) -{ - struct zr36050 *ptr = codec->data; - - if (ptr) { - /* do wee need some codec deinit here, too ???? */ - - dprintk(1, "%s: finished codec #%d\n", ptr->name, - ptr->num); - kfree(ptr); - codec->data = NULL; - - zr36050_codecs--; - return 0; - } - - return -EFAULT; -} - -/* ========================================================================= - Setup and registry function: - - Initializes Zoran's JPEG processor - - Also sets pixel size, average code size, mode (compr./decompr.) - (the given size is determined by the processor with the video interface) - ========================================================================= */ - -static int -zr36050_setup (struct videocodec *codec) -{ - struct zr36050 *ptr; - int res; - - dprintk(2, "zr36050: initializing MJPEG subsystem #%d.\n", - zr36050_codecs); - - if (zr36050_codecs == MAX_CODECS) { - dprintk(1, - KERN_ERR "zr36050: Can't attach more codecs!\n"); - return -ENOSPC; - } - //mem structure init - codec->data = ptr = kzalloc(sizeof(struct zr36050), GFP_KERNEL); - if (NULL == ptr) { - dprintk(1, KERN_ERR "zr36050: Can't get enough memory!\n"); - return -ENOMEM; - } - - snprintf(ptr->name, sizeof(ptr->name), "zr36050[%d]", - zr36050_codecs); - ptr->num = zr36050_codecs++; - ptr->codec = codec; - - //testing - res = zr36050_basic_test(ptr); - if (res < 0) { - zr36050_unset(codec); - return res; - } - //final setup - memcpy(ptr->h_samp_ratio, zr36050_decimation_h, 8); - memcpy(ptr->v_samp_ratio, zr36050_decimation_v, 8); - - ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag - * (what is the difference?) */ - ptr->mode = CODEC_DO_COMPRESSION; - ptr->width = 384; - ptr->height = 288; - ptr->total_code_vol = 16000; - ptr->max_block_vol = 240; - ptr->scalefact = 0x100; - ptr->dri = 1; - - /* no app/com marker by default */ - ptr->app.appn = 0; - ptr->app.len = 0; - ptr->com.len = 0; - - zr36050_init(ptr); - - dprintk(1, KERN_INFO "%s: codec attached and running\n", - ptr->name); - - return 0; -} - -static const struct videocodec zr36050_codec = { - .owner = THIS_MODULE, - .name = "zr36050", - .magic = 0L, // magic not used - .flags = - CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER | - CODEC_FLAG_DECODER, - .type = CODEC_TYPE_ZR36050, - .setup = zr36050_setup, // functionality - .unset = zr36050_unset, - .set_mode = zr36050_set_mode, - .set_video = zr36050_set_video, - .control = zr36050_control, - // others are not used -}; - -/* ========================================================================= - HOOK IN DRIVER AS KERNEL MODULE - ========================================================================= */ - -static int __init -zr36050_init_module (void) -{ - //dprintk(1, "ZR36050 driver %s\n",ZR050_VERSION); - zr36050_codecs = 0; - return videocodec_register(&zr36050_codec); -} - -static void __exit -zr36050_cleanup_module (void) -{ - if (zr36050_codecs) { - dprintk(1, - "zr36050: something's wrong - %d codecs left somehow.\n", - zr36050_codecs); - } - videocodec_unregister(&zr36050_codec); -} - -module_init(zr36050_init_module); -module_exit(zr36050_cleanup_module); - -MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>"); -MODULE_DESCRIPTION("Driver module for ZR36050 jpeg processors " - ZR050_VERSION); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/zoran/zr36050.h b/drivers/media/pci/zoran/zr36050.h deleted file mode 100644 index 9236486d3c2b..000000000000 --- a/drivers/media/pci/zoran/zr36050.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Zoran ZR36050 basic configuration functions - header file - * - * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at> - * - * $Id: zr36050.h,v 1.1.2.2 2003/01/14 21:18:22 rbultje Exp $ - * - * ------------------------------------------------------------------------ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * ------------------------------------------------------------------------ - */ - -#ifndef ZR36050_H -#define ZR36050_H - -#include "videocodec.h" - -/* data stored for each zoran jpeg codec chip */ -struct zr36050 { - char name[32]; - int num; - /* io datastructure */ - struct videocodec *codec; - // last coder status - __u8 status1; - // actual coder setup - int mode; - - __u16 width; - __u16 height; - - __u16 bitrate_ctrl; - - __u32 total_code_vol; - __u32 real_code_vol; - __u16 max_block_vol; - - __u8 h_samp_ratio[8]; - __u8 v_samp_ratio[8]; - __u16 scalefact; - __u16 dri; - - /* com/app marker */ - struct jpeg_com_marker com; - struct jpeg_app_marker app; -}; - -/* zr36050 register addresses */ -#define ZR050_GO 0x000 -#define ZR050_HARDWARE 0x002 -#define ZR050_MODE 0x003 -#define ZR050_OPTIONS 0x004 -#define ZR050_MBCV 0x005 -#define ZR050_MARKERS_EN 0x006 -#define ZR050_INT_REQ_0 0x007 -#define ZR050_INT_REQ_1 0x008 -#define ZR050_TCV_NET_HI 0x009 -#define ZR050_TCV_NET_MH 0x00a -#define ZR050_TCV_NET_ML 0x00b -#define ZR050_TCV_NET_LO 0x00c -#define ZR050_TCV_DATA_HI 0x00d -#define ZR050_TCV_DATA_MH 0x00e -#define ZR050_TCV_DATA_ML 0x00f -#define ZR050_TCV_DATA_LO 0x010 -#define ZR050_SF_HI 0x011 -#define ZR050_SF_LO 0x012 -#define ZR050_AF_HI 0x013 -#define ZR050_AF_M 0x014 -#define ZR050_AF_LO 0x015 -#define ZR050_ACV_HI 0x016 -#define ZR050_ACV_MH 0x017 -#define ZR050_ACV_ML 0x018 -#define ZR050_ACV_LO 0x019 -#define ZR050_ACT_HI 0x01a -#define ZR050_ACT_MH 0x01b -#define ZR050_ACT_ML 0x01c -#define ZR050_ACT_LO 0x01d -#define ZR050_ACV_TRUN_HI 0x01e -#define ZR050_ACV_TRUN_MH 0x01f -#define ZR050_ACV_TRUN_ML 0x020 -#define ZR050_ACV_TRUN_LO 0x021 -#define ZR050_STATUS_0 0x02e -#define ZR050_STATUS_1 0x02f - -#define ZR050_SOF_IDX 0x040 -#define ZR050_SOS1_IDX 0x07a -#define ZR050_SOS2_IDX 0x08a -#define ZR050_SOS3_IDX 0x09a -#define ZR050_SOS4_IDX 0x0aa -#define ZR050_DRI_IDX 0x0c0 -#define ZR050_DNL_IDX 0x0c6 -#define ZR050_DQT_IDX 0x0cc -#define ZR050_DHT_IDX 0x1d4 -#define ZR050_APP_IDX 0x380 -#define ZR050_COM_IDX 0x3c0 - -/* zr36050 hardware register bits */ - -#define ZR050_HW_BSWD 0x80 -#define ZR050_HW_MSTR 0x40 -#define ZR050_HW_DMA 0x20 -#define ZR050_HW_CFIS_1_CLK 0x00 -#define ZR050_HW_CFIS_2_CLK 0x04 -#define ZR050_HW_CFIS_3_CLK 0x08 -#define ZR050_HW_CFIS_4_CLK 0x0C -#define ZR050_HW_CFIS_5_CLK 0x10 -#define ZR050_HW_CFIS_6_CLK 0x14 -#define ZR050_HW_CFIS_7_CLK 0x18 -#define ZR050_HW_CFIS_8_CLK 0x1C -#define ZR050_HW_BELE 0x01 - -/* zr36050 mode register bits */ - -#define ZR050_MO_COMP 0x80 -#define ZR050_MO_ATP 0x40 -#define ZR050_MO_PASS2 0x20 -#define ZR050_MO_TLM 0x10 -#define ZR050_MO_DCONLY 0x08 -#define ZR050_MO_BRC 0x04 - -#define ZR050_MO_ATP 0x40 -#define ZR050_MO_PASS2 0x20 -#define ZR050_MO_TLM 0x10 -#define ZR050_MO_DCONLY 0x08 - -/* zr36050 option register bits */ - -#define ZR050_OP_NSCN_1 0x00 -#define ZR050_OP_NSCN_2 0x20 -#define ZR050_OP_NSCN_3 0x40 -#define ZR050_OP_NSCN_4 0x60 -#define ZR050_OP_NSCN_5 0x80 -#define ZR050_OP_NSCN_6 0xA0 -#define ZR050_OP_NSCN_7 0xC0 -#define ZR050_OP_NSCN_8 0xE0 -#define ZR050_OP_OVF 0x10 - - -/* zr36050 markers-enable register bits */ - -#define ZR050_ME_APP 0x80 -#define ZR050_ME_COM 0x40 -#define ZR050_ME_DRI 0x20 -#define ZR050_ME_DQT 0x10 -#define ZR050_ME_DHT 0x08 -#define ZR050_ME_DNL 0x04 -#define ZR050_ME_DQTI 0x02 -#define ZR050_ME_DHTI 0x01 - -/* zr36050 status0/1 register bit masks */ - -#define ZR050_ST_RST_MASK 0x20 -#define ZR050_ST_SOF_MASK 0x02 -#define ZR050_ST_SOS_MASK 0x02 -#define ZR050_ST_DATRDY_MASK 0x80 -#define ZR050_ST_MRKDET_MASK 0x40 -#define ZR050_ST_RFM_MASK 0x10 -#define ZR050_ST_RFD_MASK 0x08 -#define ZR050_ST_END_MASK 0x04 -#define ZR050_ST_TCVOVF_MASK 0x02 -#define ZR050_ST_DATOVF_MASK 0x01 - -/* pixel component idx */ - -#define ZR050_Y_COMPONENT 0 -#define ZR050_U_COMPONENT 1 -#define ZR050_V_COMPONENT 2 - -#endif /*fndef ZR36050_H */ diff --git a/drivers/media/pci/zoran/zr36057.h b/drivers/media/pci/zoran/zr36057.h deleted file mode 100644 index c8acb21dcb5c..000000000000 --- a/drivers/media/pci/zoran/zr36057.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * zr36057.h - zr36057 register offsets - * - * Copyright (C) 1998 Dave Perks <dperks@ibm.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _ZR36057_H_ -#define _ZR36057_H_ - - -/* Zoran ZR36057 registers */ - -#define ZR36057_VFEHCR 0x000 /* Video Front End, Horizontal Configuration Register */ -#define ZR36057_VFEHCR_HSPol (1<<30) -#define ZR36057_VFEHCR_HStart 10 -#define ZR36057_VFEHCR_HEnd 0 -#define ZR36057_VFEHCR_Hmask 0x3ff - -#define ZR36057_VFEVCR 0x004 /* Video Front End, Vertical Configuration Register */ -#define ZR36057_VFEVCR_VSPol (1<<30) -#define ZR36057_VFEVCR_VStart 10 -#define ZR36057_VFEVCR_VEnd 0 -#define ZR36057_VFEVCR_Vmask 0x3ff - -#define ZR36057_VFESPFR 0x008 /* Video Front End, Scaler and Pixel Format Register */ -#define ZR36057_VFESPFR_ExtFl (1<<26) -#define ZR36057_VFESPFR_TopField (1<<25) -#define ZR36057_VFESPFR_VCLKPol (1<<24) -#define ZR36057_VFESPFR_HFilter 21 -#define ZR36057_VFESPFR_HorDcm 14 -#define ZR36057_VFESPFR_VerDcm 8 -#define ZR36057_VFESPFR_DispMode 6 -#define ZR36057_VFESPFR_YUV422 (0<<3) -#define ZR36057_VFESPFR_RGB888 (1<<3) -#define ZR36057_VFESPFR_RGB565 (2<<3) -#define ZR36057_VFESPFR_RGB555 (3<<3) -#define ZR36057_VFESPFR_ErrDif (1<<2) -#define ZR36057_VFESPFR_Pack24 (1<<1) -#define ZR36057_VFESPFR_LittleEndian (1<<0) - -#define ZR36057_VDTR 0x00c /* Video Display "Top" Register */ - -#define ZR36057_VDBR 0x010 /* Video Display "Bottom" Register */ - -#define ZR36057_VSSFGR 0x014 /* Video Stride, Status, and Frame Grab Register */ -#define ZR36057_VSSFGR_DispStride 16 -#define ZR36057_VSSFGR_VidOvf (1<<8) -#define ZR36057_VSSFGR_SnapShot (1<<1) -#define ZR36057_VSSFGR_FrameGrab (1<<0) - -#define ZR36057_VDCR 0x018 /* Video Display Configuration Register */ -#define ZR36057_VDCR_VidEn (1<<31) -#define ZR36057_VDCR_MinPix 24 -#define ZR36057_VDCR_Triton (1<<24) -#define ZR36057_VDCR_VidWinHt 12 -#define ZR36057_VDCR_VidWinWid 0 - -#define ZR36057_MMTR 0x01c /* Masking Map "Top" Register */ - -#define ZR36057_MMBR 0x020 /* Masking Map "Bottom" Register */ - -#define ZR36057_OCR 0x024 /* Overlay Control Register */ -#define ZR36057_OCR_OvlEnable (1 << 15) -#define ZR36057_OCR_MaskStride 0 - -#define ZR36057_SPGPPCR 0x028 /* System, PCI, and General Purpose Pins Control Register */ -#define ZR36057_SPGPPCR_SoftReset (1<<24) - -#define ZR36057_GPPGCR1 0x02c /* General Purpose Pins and GuestBus Control Register (1) */ - -#define ZR36057_MCSAR 0x030 /* MPEG Code Source Address Register */ - -#define ZR36057_MCTCR 0x034 /* MPEG Code Transfer Control Register */ -#define ZR36057_MCTCR_CodTime (1 << 30) -#define ZR36057_MCTCR_CEmpty (1 << 29) -#define ZR36057_MCTCR_CFlush (1 << 28) -#define ZR36057_MCTCR_CodGuestID 20 -#define ZR36057_MCTCR_CodGuestReg 16 - -#define ZR36057_MCMPR 0x038 /* MPEG Code Memory Pointer Register */ - -#define ZR36057_ISR 0x03c /* Interrupt Status Register */ -#define ZR36057_ISR_GIRQ1 (1<<30) -#define ZR36057_ISR_GIRQ0 (1<<29) -#define ZR36057_ISR_CodRepIRQ (1<<28) -#define ZR36057_ISR_JPEGRepIRQ (1<<27) - -#define ZR36057_ICR 0x040 /* Interrupt Control Register */ -#define ZR36057_ICR_GIRQ1 (1<<30) -#define ZR36057_ICR_GIRQ0 (1<<29) -#define ZR36057_ICR_CodRepIRQ (1<<28) -#define ZR36057_ICR_JPEGRepIRQ (1<<27) -#define ZR36057_ICR_IntPinEn (1<<24) - -#define ZR36057_I2CBR 0x044 /* I2C Bus Register */ -#define ZR36057_I2CBR_SDA (1<<1) -#define ZR36057_I2CBR_SCL (1<<0) - -#define ZR36057_JMC 0x100 /* JPEG Mode and Control */ -#define ZR36057_JMC_JPG (1 << 31) -#define ZR36057_JMC_JPGExpMode (0 << 29) -#define ZR36057_JMC_JPGCmpMode (1 << 29) -#define ZR36057_JMC_MJPGExpMode (2 << 29) -#define ZR36057_JMC_MJPGCmpMode (3 << 29) -#define ZR36057_JMC_RTBUSY_FB (1 << 6) -#define ZR36057_JMC_Go_en (1 << 5) -#define ZR36057_JMC_SyncMstr (1 << 4) -#define ZR36057_JMC_Fld_per_buff (1 << 3) -#define ZR36057_JMC_VFIFO_FB (1 << 2) -#define ZR36057_JMC_CFIFO_FB (1 << 1) -#define ZR36057_JMC_Stll_LitEndian (1 << 0) - -#define ZR36057_JPC 0x104 /* JPEG Process Control */ -#define ZR36057_JPC_P_Reset (1 << 7) -#define ZR36057_JPC_CodTrnsEn (1 << 5) -#define ZR36057_JPC_Active (1 << 0) - -#define ZR36057_VSP 0x108 /* Vertical Sync Parameters */ -#define ZR36057_VSP_VsyncSize 16 -#define ZR36057_VSP_FrmTot 0 - -#define ZR36057_HSP 0x10c /* Horizontal Sync Parameters */ -#define ZR36057_HSP_HsyncStart 16 -#define ZR36057_HSP_LineTot 0 - -#define ZR36057_FHAP 0x110 /* Field Horizontal Active Portion */ -#define ZR36057_FHAP_NAX 16 -#define ZR36057_FHAP_PAX 0 - -#define ZR36057_FVAP 0x114 /* Field Vertical Active Portion */ -#define ZR36057_FVAP_NAY 16 -#define ZR36057_FVAP_PAY 0 - -#define ZR36057_FPP 0x118 /* Field Process Parameters */ -#define ZR36057_FPP_Odd_Even (1 << 0) - -#define ZR36057_JCBA 0x11c /* JPEG Code Base Address */ - -#define ZR36057_JCFT 0x120 /* JPEG Code FIFO Threshold */ - -#define ZR36057_JCGI 0x124 /* JPEG Codec Guest ID */ -#define ZR36057_JCGI_JPEGuestID 4 -#define ZR36057_JCGI_JPEGuestReg 0 - -#define ZR36057_GCR2 0x12c /* GuestBus Control Register (2) */ - -#define ZR36057_POR 0x200 /* Post Office Register */ -#define ZR36057_POR_POPen (1<<25) -#define ZR36057_POR_POTime (1<<24) -#define ZR36057_POR_PODir (1<<23) - -#define ZR36057_STR 0x300 /* "Still" Transfer Register */ - -#endif diff --git a/drivers/media/pci/zoran/zr36060.c b/drivers/media/pci/zoran/zr36060.c deleted file mode 100644 index 2c2e8130fc96..000000000000 --- a/drivers/media/pci/zoran/zr36060.c +++ /dev/null @@ -1,1006 +0,0 @@ -/* - * Zoran ZR36060 basic configuration functions - * - * Copyright (C) 2002 Laurent Pinchart <laurent.pinchart@skynet.be> - * - * $Id: zr36060.c,v 1.1.2.22 2003/05/06 09:35:36 rbultje Exp $ - * - * ------------------------------------------------------------------------ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * ------------------------------------------------------------------------ - */ - -#define ZR060_VERSION "v0.7" - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/delay.h> - -#include <linux/types.h> -#include <linux/wait.h> - -/* I/O commands, error codes */ -#include <asm/io.h> - -/* headerfile of this module */ -#include "zr36060.h" - -/* codec io API */ -#include "videocodec.h" - -/* it doesn't make sense to have more than 20 or so, - just to prevent some unwanted loops */ -#define MAX_CODECS 20 - -/* amount of chips attached via this driver */ -static int zr36060_codecs; - -static bool low_bitrate; -module_param(low_bitrate, bool, 0); -MODULE_PARM_DESC(low_bitrate, "Buz compatibility option, halves bitrate"); - -/* debugging is available via module parameter */ -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-4)"); - -#define dprintk(num, format, args...) \ - do { \ - if (debug >= num) \ - printk(format, ##args); \ - } while (0) - -/* ========================================================================= - Local hardware I/O functions: - - read/write via codec layer (registers are located in the master device) - ========================================================================= */ - -/* read and write functions */ -static u8 -zr36060_read (struct zr36060 *ptr, - u16 reg) -{ - u8 value = 0; - - // just in case something is wrong... - if (ptr->codec->master_data->readreg) - value = (ptr->codec->master_data->readreg(ptr->codec, - reg)) & 0xff; - else - dprintk(1, - KERN_ERR "%s: invalid I/O setup, nothing read!\n", - ptr->name); - - //dprintk(4, "%s: reading from 0x%04x: %02x\n",ptr->name,reg,value); - - return value; -} - -static void -zr36060_write(struct zr36060 *ptr, - u16 reg, - u8 value) -{ - //dprintk(4, "%s: writing 0x%02x to 0x%04x\n",ptr->name,value,reg); - dprintk(4, "0x%02x @0x%04x\n", value, reg); - - // just in case something is wrong... - if (ptr->codec->master_data->writereg) - ptr->codec->master_data->writereg(ptr->codec, reg, value); - else - dprintk(1, - KERN_ERR - "%s: invalid I/O setup, nothing written!\n", - ptr->name); -} - -/* ========================================================================= - Local helper function: - - status read - ========================================================================= */ - -/* status is kept in datastructure */ -static u8 -zr36060_read_status (struct zr36060 *ptr) -{ - ptr->status = zr36060_read(ptr, ZR060_CFSR); - - zr36060_read(ptr, 0); - return ptr->status; -} - -/* ========================================================================= - Local helper function: - - scale factor read - ========================================================================= */ - -/* scale factor is kept in datastructure */ -static u16 -zr36060_read_scalefactor (struct zr36060 *ptr) -{ - ptr->scalefact = (zr36060_read(ptr, ZR060_SF_HI) << 8) | - (zr36060_read(ptr, ZR060_SF_LO) & 0xFF); - - /* leave 0 selected for an eventually GO from master */ - zr36060_read(ptr, 0); - return ptr->scalefact; -} - -/* ========================================================================= - Local helper function: - - wait if codec is ready to proceed (end of processing) or time is over - ========================================================================= */ - -static void -zr36060_wait_end (struct zr36060 *ptr) -{ - int i = 0; - - while (zr36060_read_status(ptr) & ZR060_CFSR_Busy) { - udelay(1); - if (i++ > 200000) { // 200ms, there is for sure something wrong!!! - dprintk(1, - "%s: timeout at wait_end (last status: 0x%02x)\n", - ptr->name, ptr->status); - break; - } - } -} - -/* ========================================================================= - Local helper function: - - basic test of "connectivity", writes/reads to/from memory the SOF marker - ========================================================================= */ - -static int -zr36060_basic_test (struct zr36060 *ptr) -{ - if ((zr36060_read(ptr, ZR060_IDR_DEV) != 0x33) && - (zr36060_read(ptr, ZR060_IDR_REV) != 0x01)) { - dprintk(1, - KERN_ERR - "%s: attach failed, can't connect to jpeg processor!\n", - ptr->name); - return -ENXIO; - } - - zr36060_wait_end(ptr); - if (ptr->status & ZR060_CFSR_Busy) { - dprintk(1, - KERN_ERR - "%s: attach failed, jpeg processor failed (end flag)!\n", - ptr->name); - return -EBUSY; - } - - return 0; /* looks good! */ -} - -/* ========================================================================= - Local helper function: - - simple loop for pushing the init datasets - ========================================================================= */ - -static int -zr36060_pushit (struct zr36060 *ptr, - u16 startreg, - u16 len, - const char *data) -{ - int i = 0; - - dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ptr->name, - startreg, len); - while (i < len) { - zr36060_write(ptr, startreg++, data[i++]); - } - - return i; -} - -/* ========================================================================= - Basic datasets: - - jpeg baseline setup data (you find it on lots places in internet, or just - extract it from any regular .jpg image...) - - Could be variable, but until it's not needed it they are just fixed to save - memory. Otherwise expand zr36060 structure with arrays, push the values to - it and initialize from there, as e.g. the linux zr36057/60 driver does it. - ========================================================================= */ - -static const char zr36060_dqt[0x86] = { - 0xff, 0xdb, //Marker: DQT - 0x00, 0x84, //Length: 2*65+2 - 0x00, //Pq,Tq first table - 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, - 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, - 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, - 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, - 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, - 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, - 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, - 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, - 0x01, //Pq,Tq second table - 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, - 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 -}; - -static const char zr36060_dht[0x1a4] = { - 0xff, 0xc4, //Marker: DHT - 0x01, 0xa2, //Length: 2*AC, 2*DC - 0x00, //DC first table - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0x01, //DC second table - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0x10, //AC first table - 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, - 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, - 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, - 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, - 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, - 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, - 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, - 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, - 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, - 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, - 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, - 0x11, //AC second table - 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, - 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, - 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, - 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, - 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, - 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, - 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, - 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, - 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, - 0xF9, 0xFA -}; - -/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */ -#define NO_OF_COMPONENTS 0x3 //Y,U,V -#define BASELINE_PRECISION 0x8 //MCU size (?) -static const char zr36060_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT -static const char zr36060_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC -static const char zr36060_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC - -/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */ -static const char zr36060_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 }; -static const char zr36060_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 }; - -/* ========================================================================= - Local helper functions: - - calculation and setup of parameter-dependent JPEG baseline segments - (needed for compression only) - ========================================================================= */ - -/* ------------------------------------------------------------------------- */ - -/* SOF (start of frame) segment depends on width, height and sampling ratio - of each color component */ - -static int -zr36060_set_sof (struct zr36060 *ptr) -{ - char sof_data[34]; // max. size of register set - int i; - - dprintk(3, "%s: write SOF (%dx%d, %d components)\n", ptr->name, - ptr->width, ptr->height, NO_OF_COMPONENTS); - sof_data[0] = 0xff; - sof_data[1] = 0xc0; - sof_data[2] = 0x00; - sof_data[3] = (3 * NO_OF_COMPONENTS) + 8; - sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36060 - sof_data[5] = (ptr->height) >> 8; - sof_data[6] = (ptr->height) & 0xff; - sof_data[7] = (ptr->width) >> 8; - sof_data[8] = (ptr->width) & 0xff; - sof_data[9] = NO_OF_COMPONENTS; - for (i = 0; i < NO_OF_COMPONENTS; i++) { - sof_data[10 + (i * 3)] = i; // index identifier - sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | - (ptr->v_samp_ratio[i]); // sampling ratios - sof_data[12 + (i * 3)] = zr36060_tq[i]; // Q table selection - } - return zr36060_pushit(ptr, ZR060_SOF_IDX, - (3 * NO_OF_COMPONENTS) + 10, sof_data); -} - -/* ------------------------------------------------------------------------- */ - -/* SOS (start of scan) segment depends on the used scan components - of each color component */ - -static int -zr36060_set_sos (struct zr36060 *ptr) -{ - char sos_data[16]; // max. size of register set - int i; - - dprintk(3, "%s: write SOS\n", ptr->name); - sos_data[0] = 0xff; - sos_data[1] = 0xda; - sos_data[2] = 0x00; - sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3; - sos_data[4] = NO_OF_COMPONENTS; - for (i = 0; i < NO_OF_COMPONENTS; i++) { - sos_data[5 + (i * 2)] = i; // index - sos_data[6 + (i * 2)] = (zr36060_td[i] << 4) | - zr36060_ta[i]; // AC/DC tbl.sel. - } - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3f; - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00; - return zr36060_pushit(ptr, ZR060_SOS_IDX, - 4 + 1 + (2 * NO_OF_COMPONENTS) + 3, - sos_data); -} - -/* ------------------------------------------------------------------------- */ - -/* DRI (define restart interval) */ - -static int -zr36060_set_dri (struct zr36060 *ptr) -{ - char dri_data[6]; // max. size of register set - - dprintk(3, "%s: write DRI\n", ptr->name); - dri_data[0] = 0xff; - dri_data[1] = 0xdd; - dri_data[2] = 0x00; - dri_data[3] = 0x04; - dri_data[4] = (ptr->dri) >> 8; - dri_data[5] = (ptr->dri) & 0xff; - return zr36060_pushit(ptr, ZR060_DRI_IDX, 6, dri_data); -} - -/* ========================================================================= - Setup function: - - Setup compression/decompression of Zoran's JPEG processor - ( see also zoran 36060 manual ) - - ... sorry for the spaghetti code ... - ========================================================================= */ -static void -zr36060_init (struct zr36060 *ptr) -{ - int sum = 0; - long bitcnt, tmp; - - if (ptr->mode == CODEC_DO_COMPRESSION) { - dprintk(2, "%s: COMPRESSION SETUP\n", ptr->name); - - zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst); - - /* 060 communicates with 067 in master mode */ - zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CodeMstr); - - /* Compression with or without variable scale factor */ - /*FIXME: What about ptr->bitrate_ctrl? */ - zr36060_write(ptr, ZR060_CMR, - ZR060_CMR_Comp | ZR060_CMR_Pass2 | - ZR060_CMR_BRB); - - /* Must be zero */ - zr36060_write(ptr, ZR060_MBZ, 0x00); - zr36060_write(ptr, ZR060_TCR_HI, 0x00); - zr36060_write(ptr, ZR060_TCR_LO, 0x00); - - /* Disable all IRQs - no DataErr means autoreset */ - zr36060_write(ptr, ZR060_IMR, 0); - - /* volume control settings */ - zr36060_write(ptr, ZR060_SF_HI, ptr->scalefact >> 8); - zr36060_write(ptr, ZR060_SF_LO, ptr->scalefact & 0xff); - - zr36060_write(ptr, ZR060_AF_HI, 0xff); - zr36060_write(ptr, ZR060_AF_M, 0xff); - zr36060_write(ptr, ZR060_AF_LO, 0xff); - - /* setup the variable jpeg tables */ - sum += zr36060_set_sof(ptr); - sum += zr36060_set_sos(ptr); - sum += zr36060_set_dri(ptr); - - /* setup the fixed jpeg tables - maybe variable, though - - * (see table init section above) */ - sum += - zr36060_pushit(ptr, ZR060_DQT_IDX, sizeof(zr36060_dqt), - zr36060_dqt); - sum += - zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht), - zr36060_dht); - zr36060_write(ptr, ZR060_APP_IDX, 0xff); - zr36060_write(ptr, ZR060_APP_IDX + 1, 0xe0 + ptr->app.appn); - zr36060_write(ptr, ZR060_APP_IDX + 2, 0x00); - zr36060_write(ptr, ZR060_APP_IDX + 3, ptr->app.len + 2); - sum += zr36060_pushit(ptr, ZR060_APP_IDX + 4, 60, - ptr->app.data) + 4; - zr36060_write(ptr, ZR060_COM_IDX, 0xff); - zr36060_write(ptr, ZR060_COM_IDX + 1, 0xfe); - zr36060_write(ptr, ZR060_COM_IDX + 2, 0x00); - zr36060_write(ptr, ZR060_COM_IDX + 3, ptr->com.len + 2); - sum += zr36060_pushit(ptr, ZR060_COM_IDX + 4, 60, - ptr->com.data) + 4; - - /* setup misc. data for compression (target code sizes) */ - - /* size of compressed code to reach without header data */ - sum = ptr->real_code_vol - sum; - bitcnt = sum << 3; /* need the size in bits */ - - tmp = bitcnt >> 16; - dprintk(3, - "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n", - ptr->name, sum, ptr->real_code_vol, bitcnt, tmp); - zr36060_write(ptr, ZR060_TCV_NET_HI, tmp >> 8); - zr36060_write(ptr, ZR060_TCV_NET_MH, tmp & 0xff); - tmp = bitcnt & 0xffff; - zr36060_write(ptr, ZR060_TCV_NET_ML, tmp >> 8); - zr36060_write(ptr, ZR060_TCV_NET_LO, tmp & 0xff); - - bitcnt -= bitcnt >> 7; // bits without stuffing - bitcnt -= ((bitcnt * 5) >> 6); // bits without eob - - tmp = bitcnt >> 16; - dprintk(3, "%s: code: nettobit=%ld, highnettobits=%ld\n", - ptr->name, bitcnt, tmp); - zr36060_write(ptr, ZR060_TCV_DATA_HI, tmp >> 8); - zr36060_write(ptr, ZR060_TCV_DATA_MH, tmp & 0xff); - tmp = bitcnt & 0xffff; - zr36060_write(ptr, ZR060_TCV_DATA_ML, tmp >> 8); - zr36060_write(ptr, ZR060_TCV_DATA_LO, tmp & 0xff); - - /* JPEG markers to be included in the compressed stream */ - zr36060_write(ptr, ZR060_MER, - ZR060_MER_DQT | ZR060_MER_DHT | - ((ptr->com.len > 0) ? ZR060_MER_Com : 0) | - ((ptr->app.len > 0) ? ZR060_MER_App : 0)); - - /* Setup the Video Frontend */ - /* Limit pixel range to 16..235 as per CCIR-601 */ - zr36060_write(ptr, ZR060_VCR, ZR060_VCR_Range); - - } else { - dprintk(2, "%s: EXPANSION SETUP\n", ptr->name); - - zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst); - - /* 060 communicates with 067 in master mode */ - zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CodeMstr); - - /* Decompression */ - zr36060_write(ptr, ZR060_CMR, 0); - - /* Must be zero */ - zr36060_write(ptr, ZR060_MBZ, 0x00); - zr36060_write(ptr, ZR060_TCR_HI, 0x00); - zr36060_write(ptr, ZR060_TCR_LO, 0x00); - - /* Disable all IRQs - no DataErr means autoreset */ - zr36060_write(ptr, ZR060_IMR, 0); - - /* setup misc. data for expansion */ - zr36060_write(ptr, ZR060_MER, 0); - - /* setup the fixed jpeg tables - maybe variable, though - - * (see table init section above) */ - zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht), - zr36060_dht); - - /* Setup the Video Frontend */ - //zr36060_write(ptr, ZR060_VCR, ZR060_VCR_FIExt); - //this doesn't seem right and doesn't work... - zr36060_write(ptr, ZR060_VCR, ZR060_VCR_Range); - } - - /* Load the tables */ - zr36060_write(ptr, ZR060_LOAD, - ZR060_LOAD_SyncRst | ZR060_LOAD_Load); - zr36060_wait_end(ptr); - dprintk(2, "%s: Status after table preload: 0x%02x\n", ptr->name, - ptr->status); - - if (ptr->status & ZR060_CFSR_Busy) { - dprintk(1, KERN_ERR "%s: init aborted!\n", ptr->name); - return; // something is wrong, its timed out!!!! - } -} - -/* ========================================================================= - CODEC API FUNCTIONS - - this functions are accessed by the master via the API structure - ========================================================================= */ - -/* set compression/expansion mode and launches codec - - this should be the last call from the master before starting processing */ -static int -zr36060_set_mode (struct videocodec *codec, - int mode) -{ - struct zr36060 *ptr = (struct zr36060 *) codec->data; - - dprintk(2, "%s: set_mode %d call\n", ptr->name, mode); - - if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION)) - return -EINVAL; - - ptr->mode = mode; - zr36060_init(ptr); - - return 0; -} - -/* set picture size (norm is ignored as the codec doesn't know about it) */ -static int -zr36060_set_video (struct videocodec *codec, - struct tvnorm *norm, - struct vfe_settings *cap, - struct vfe_polarity *pol) -{ - struct zr36060 *ptr = (struct zr36060 *) codec->data; - u32 reg; - int size; - - dprintk(2, "%s: set_video %d/%d-%dx%d (%%%d) call\n", ptr->name, - cap->x, cap->y, cap->width, cap->height, cap->decimation); - - /* if () return -EINVAL; - * trust the master driver that it knows what it does - so - * we allow invalid startx/y and norm for now ... */ - ptr->width = cap->width / (cap->decimation & 0xff); - ptr->height = cap->height / (cap->decimation >> 8); - - zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst); - - /* Note that VSPol/HSPol bits in zr36060 have the opposite - * meaning of their zr360x7 counterparts with the same names - * N.b. for VSPol this is only true if FIVEdge = 0 (default, - * left unchanged here - in accordance with datasheet). - */ - reg = (!pol->vsync_pol ? ZR060_VPR_VSPol : 0) - | (!pol->hsync_pol ? ZR060_VPR_HSPol : 0) - | (pol->field_pol ? ZR060_VPR_FIPol : 0) - | (pol->blank_pol ? ZR060_VPR_BLPol : 0) - | (pol->subimg_pol ? ZR060_VPR_SImgPol : 0) - | (pol->poe_pol ? ZR060_VPR_PoePol : 0) - | (pol->pvalid_pol ? ZR060_VPR_PValPol : 0) - | (pol->vclk_pol ? ZR060_VPR_VCLKPol : 0); - zr36060_write(ptr, ZR060_VPR, reg); - - reg = 0; - switch (cap->decimation & 0xff) { - default: - case 1: - break; - - case 2: - reg |= ZR060_SR_HScale2; - break; - - case 4: - reg |= ZR060_SR_HScale4; - break; - } - - switch (cap->decimation >> 8) { - default: - case 1: - break; - - case 2: - reg |= ZR060_SR_VScale; - break; - } - zr36060_write(ptr, ZR060_SR, reg); - - zr36060_write(ptr, ZR060_BCR_Y, 0x00); - zr36060_write(ptr, ZR060_BCR_U, 0x80); - zr36060_write(ptr, ZR060_BCR_V, 0x80); - - /* sync generator */ - - reg = norm->Ht - 1; /* Vtotal */ - zr36060_write(ptr, ZR060_SGR_VTOTAL_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SGR_VTOTAL_LO, (reg >> 0) & 0xff); - - reg = norm->Wt - 1; /* Htotal */ - zr36060_write(ptr, ZR060_SGR_HTOTAL_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SGR_HTOTAL_LO, (reg >> 0) & 0xff); - - reg = 6 - 1; /* VsyncSize */ - zr36060_write(ptr, ZR060_SGR_VSYNC, reg); - - //reg = 30 - 1; /* HsyncSize */ -///*CP*/ reg = (zr->params.norm == 1 ? 57 : 68); - reg = 68; - zr36060_write(ptr, ZR060_SGR_HSYNC, reg); - - reg = norm->VStart - 1; /* BVstart */ - zr36060_write(ptr, ZR060_SGR_BVSTART, reg); - - reg += norm->Ha / 2; /* BVend */ - zr36060_write(ptr, ZR060_SGR_BVEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SGR_BVEND_LO, (reg >> 0) & 0xff); - - reg = norm->HStart - 1; /* BHstart */ - zr36060_write(ptr, ZR060_SGR_BHSTART, reg); - - reg += norm->Wa; /* BHend */ - zr36060_write(ptr, ZR060_SGR_BHEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SGR_BHEND_LO, (reg >> 0) & 0xff); - - /* active area */ - reg = cap->y + norm->VStart; /* Vstart */ - zr36060_write(ptr, ZR060_AAR_VSTART_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_AAR_VSTART_LO, (reg >> 0) & 0xff); - - reg += cap->height; /* Vend */ - zr36060_write(ptr, ZR060_AAR_VEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_AAR_VEND_LO, (reg >> 0) & 0xff); - - reg = cap->x + norm->HStart; /* Hstart */ - zr36060_write(ptr, ZR060_AAR_HSTART_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_AAR_HSTART_LO, (reg >> 0) & 0xff); - - reg += cap->width; /* Hend */ - zr36060_write(ptr, ZR060_AAR_HEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_AAR_HEND_LO, (reg >> 0) & 0xff); - - /* subimage area */ - reg = norm->VStart - 4; /* SVstart */ - zr36060_write(ptr, ZR060_SWR_VSTART_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SWR_VSTART_LO, (reg >> 0) & 0xff); - - reg += norm->Ha / 2 + 8; /* SVend */ - zr36060_write(ptr, ZR060_SWR_VEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SWR_VEND_LO, (reg >> 0) & 0xff); - - reg = norm->HStart /*+ 64 */ - 4; /* SHstart */ - zr36060_write(ptr, ZR060_SWR_HSTART_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SWR_HSTART_LO, (reg >> 0) & 0xff); - - reg += norm->Wa + 8; /* SHend */ - zr36060_write(ptr, ZR060_SWR_HEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SWR_HEND_LO, (reg >> 0) & 0xff); - - size = ptr->width * ptr->height; - /* Target compressed field size in bits: */ - size = size * 16; /* uncompressed size in bits */ - /* (Ronald) by default, quality = 100 is a compression - * ratio 1:2. Setting low_bitrate (insmod option) sets - * it to 1:4 (instead of 1:2, zr36060 max) as limit because the - * buz can't handle more at decimation=1... Use low_bitrate if - * you have a Buz, unless you know what you're doing */ - size = size * cap->quality / (low_bitrate ? 400 : 200); - /* Lower limit (arbitrary, 1 KB) */ - if (size < 8192) - size = 8192; - /* Upper limit: 7/8 of the code buffers */ - if (size > ptr->total_code_vol * 7) - size = ptr->total_code_vol * 7; - - ptr->real_code_vol = size >> 3; /* in bytes */ - - /* the MBCVR is the *maximum* block volume, according to the - * JPEG ISO specs, this shouldn't be used, since that allows - * for the best encoding quality. So set it to it's max value */ - reg = ptr->max_block_vol; - zr36060_write(ptr, ZR060_MBCVR, reg); - - return 0; -} - -/* additional control functions */ -static int -zr36060_control (struct videocodec *codec, - int type, - int size, - void *data) -{ - struct zr36060 *ptr = (struct zr36060 *) codec->data; - int *ival = (int *) data; - - dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type, - size); - - switch (type) { - case CODEC_G_STATUS: /* get last status */ - if (size != sizeof(int)) - return -EFAULT; - zr36060_read_status(ptr); - *ival = ptr->status; - break; - - case CODEC_G_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - *ival = CODEC_MODE_BJPG; - break; - - case CODEC_S_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - if (*ival != CODEC_MODE_BJPG) - return -EINVAL; - /* not needed, do nothing */ - return 0; - - case CODEC_G_VFE: - case CODEC_S_VFE: - /* not needed, do nothing */ - return 0; - - case CODEC_S_MMAP: - /* not available, give an error */ - return -ENXIO; - - case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */ - if (size != sizeof(int)) - return -EFAULT; - *ival = ptr->total_code_vol; - break; - - case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */ - if (size != sizeof(int)) - return -EFAULT; - ptr->total_code_vol = *ival; - ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; - break; - - case CODEC_G_JPEG_SCALE: /* get scaling factor */ - if (size != sizeof(int)) - return -EFAULT; - *ival = zr36060_read_scalefactor(ptr); - break; - - case CODEC_S_JPEG_SCALE: /* set scaling factor */ - if (size != sizeof(int)) - return -EFAULT; - ptr->scalefact = *ival; - break; - - case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */ - struct jpeg_app_marker *app = data; - - if (size != sizeof(struct jpeg_app_marker)) - return -EFAULT; - - *app = ptr->app; - break; - } - - case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */ - struct jpeg_app_marker *app = data; - - if (size != sizeof(struct jpeg_app_marker)) - return -EFAULT; - - ptr->app = *app; - break; - } - - case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */ - struct jpeg_com_marker *com = data; - - if (size != sizeof(struct jpeg_com_marker)) - return -EFAULT; - - *com = ptr->com; - break; - } - - case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */ - struct jpeg_com_marker *com = data; - - if (size != sizeof(struct jpeg_com_marker)) - return -EFAULT; - - ptr->com = *com; - break; - } - - default: - return -EINVAL; - } - - return size; -} - -/* ========================================================================= - Exit and unregister function: - - Deinitializes Zoran's JPEG processor - ========================================================================= */ - -static int -zr36060_unset (struct videocodec *codec) -{ - struct zr36060 *ptr = codec->data; - - if (ptr) { - /* do wee need some codec deinit here, too ???? */ - - dprintk(1, "%s: finished codec #%d\n", ptr->name, - ptr->num); - kfree(ptr); - codec->data = NULL; - - zr36060_codecs--; - return 0; - } - - return -EFAULT; -} - -/* ========================================================================= - Setup and registry function: - - Initializes Zoran's JPEG processor - - Also sets pixel size, average code size, mode (compr./decompr.) - (the given size is determined by the processor with the video interface) - ========================================================================= */ - -static int -zr36060_setup (struct videocodec *codec) -{ - struct zr36060 *ptr; - int res; - - dprintk(2, "zr36060: initializing MJPEG subsystem #%d.\n", - zr36060_codecs); - - if (zr36060_codecs == MAX_CODECS) { - dprintk(1, - KERN_ERR "zr36060: Can't attach more codecs!\n"); - return -ENOSPC; - } - //mem structure init - codec->data = ptr = kzalloc(sizeof(struct zr36060), GFP_KERNEL); - if (NULL == ptr) { - dprintk(1, KERN_ERR "zr36060: Can't get enough memory!\n"); - return -ENOMEM; - } - - snprintf(ptr->name, sizeof(ptr->name), "zr36060[%d]", - zr36060_codecs); - ptr->num = zr36060_codecs++; - ptr->codec = codec; - - //testing - res = zr36060_basic_test(ptr); - if (res < 0) { - zr36060_unset(codec); - return res; - } - //final setup - memcpy(ptr->h_samp_ratio, zr36060_decimation_h, 8); - memcpy(ptr->v_samp_ratio, zr36060_decimation_v, 8); - - ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag - * (what is the difference?) */ - ptr->mode = CODEC_DO_COMPRESSION; - ptr->width = 384; - ptr->height = 288; - ptr->total_code_vol = 16000; /* CHECKME */ - ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; - ptr->max_block_vol = 240; /* CHECKME, was 120 is 240 */ - ptr->scalefact = 0x100; - ptr->dri = 1; /* CHECKME, was 8 is 1 */ - - /* by default, no COM or APP markers - app should set those */ - ptr->com.len = 0; - ptr->app.appn = 0; - ptr->app.len = 0; - - zr36060_init(ptr); - - dprintk(1, KERN_INFO "%s: codec attached and running\n", - ptr->name); - - return 0; -} - -static const struct videocodec zr36060_codec = { - .owner = THIS_MODULE, - .name = "zr36060", - .magic = 0L, // magic not used - .flags = - CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER | - CODEC_FLAG_DECODER | CODEC_FLAG_VFE, - .type = CODEC_TYPE_ZR36060, - .setup = zr36060_setup, // functionality - .unset = zr36060_unset, - .set_mode = zr36060_set_mode, - .set_video = zr36060_set_video, - .control = zr36060_control, - // others are not used -}; - -/* ========================================================================= - HOOK IN DRIVER AS KERNEL MODULE - ========================================================================= */ - -static int __init -zr36060_init_module (void) -{ - //dprintk(1, "zr36060 driver %s\n",ZR060_VERSION); - zr36060_codecs = 0; - return videocodec_register(&zr36060_codec); -} - -static void __exit -zr36060_cleanup_module (void) -{ - if (zr36060_codecs) { - dprintk(1, - "zr36060: something's wrong - %d codecs left somehow.\n", - zr36060_codecs); - } - - /* however, we can't just stay alive */ - videocodec_unregister(&zr36060_codec); -} - -module_init(zr36060_init_module); -module_exit(zr36060_cleanup_module); - -MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@skynet.be>"); -MODULE_DESCRIPTION("Driver module for ZR36060 jpeg processors " - ZR060_VERSION); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/zoran/zr36060.h b/drivers/media/pci/zoran/zr36060.h deleted file mode 100644 index 82911757ba78..000000000000 --- a/drivers/media/pci/zoran/zr36060.h +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Zoran ZR36060 basic configuration functions - header file - * - * Copyright (C) 2002 Laurent Pinchart <laurent.pinchart@skynet.be> - * - * $Id: zr36060.h,v 1.1.1.1.2.3 2003/01/14 21:18:47 rbultje Exp $ - * - * ------------------------------------------------------------------------ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * ------------------------------------------------------------------------ - */ - -#ifndef ZR36060_H -#define ZR36060_H - -#include "videocodec.h" - -/* data stored for each zoran jpeg codec chip */ -struct zr36060 { - char name[32]; - int num; - /* io datastructure */ - struct videocodec *codec; - // last coder status - __u8 status; - // actual coder setup - int mode; - - __u16 width; - __u16 height; - - __u16 bitrate_ctrl; - - __u32 total_code_vol; - __u32 real_code_vol; - __u16 max_block_vol; - - __u8 h_samp_ratio[8]; - __u8 v_samp_ratio[8]; - __u16 scalefact; - __u16 dri; - - /* app/com marker data */ - struct jpeg_app_marker app; - struct jpeg_com_marker com; -}; - -/* ZR36060 register addresses */ -#define ZR060_LOAD 0x000 -#define ZR060_CFSR 0x001 -#define ZR060_CIR 0x002 -#define ZR060_CMR 0x003 -#define ZR060_MBZ 0x004 -#define ZR060_MBCVR 0x005 -#define ZR060_MER 0x006 -#define ZR060_IMR 0x007 -#define ZR060_ISR 0x008 -#define ZR060_TCV_NET_HI 0x009 -#define ZR060_TCV_NET_MH 0x00a -#define ZR060_TCV_NET_ML 0x00b -#define ZR060_TCV_NET_LO 0x00c -#define ZR060_TCV_DATA_HI 0x00d -#define ZR060_TCV_DATA_MH 0x00e -#define ZR060_TCV_DATA_ML 0x00f -#define ZR060_TCV_DATA_LO 0x010 -#define ZR060_SF_HI 0x011 -#define ZR060_SF_LO 0x012 -#define ZR060_AF_HI 0x013 -#define ZR060_AF_M 0x014 -#define ZR060_AF_LO 0x015 -#define ZR060_ACV_HI 0x016 -#define ZR060_ACV_MH 0x017 -#define ZR060_ACV_ML 0x018 -#define ZR060_ACV_LO 0x019 -#define ZR060_ACT_HI 0x01a -#define ZR060_ACT_MH 0x01b -#define ZR060_ACT_ML 0x01c -#define ZR060_ACT_LO 0x01d -#define ZR060_ACV_TRUN_HI 0x01e -#define ZR060_ACV_TRUN_MH 0x01f -#define ZR060_ACV_TRUN_ML 0x020 -#define ZR060_ACV_TRUN_LO 0x021 -#define ZR060_IDR_DEV 0x022 -#define ZR060_IDR_REV 0x023 -#define ZR060_TCR_HI 0x024 -#define ZR060_TCR_LO 0x025 -#define ZR060_VCR 0x030 -#define ZR060_VPR 0x031 -#define ZR060_SR 0x032 -#define ZR060_BCR_Y 0x033 -#define ZR060_BCR_U 0x034 -#define ZR060_BCR_V 0x035 -#define ZR060_SGR_VTOTAL_HI 0x036 -#define ZR060_SGR_VTOTAL_LO 0x037 -#define ZR060_SGR_HTOTAL_HI 0x038 -#define ZR060_SGR_HTOTAL_LO 0x039 -#define ZR060_SGR_VSYNC 0x03a -#define ZR060_SGR_HSYNC 0x03b -#define ZR060_SGR_BVSTART 0x03c -#define ZR060_SGR_BHSTART 0x03d -#define ZR060_SGR_BVEND_HI 0x03e -#define ZR060_SGR_BVEND_LO 0x03f -#define ZR060_SGR_BHEND_HI 0x040 -#define ZR060_SGR_BHEND_LO 0x041 -#define ZR060_AAR_VSTART_HI 0x042 -#define ZR060_AAR_VSTART_LO 0x043 -#define ZR060_AAR_VEND_HI 0x044 -#define ZR060_AAR_VEND_LO 0x045 -#define ZR060_AAR_HSTART_HI 0x046 -#define ZR060_AAR_HSTART_LO 0x047 -#define ZR060_AAR_HEND_HI 0x048 -#define ZR060_AAR_HEND_LO 0x049 -#define ZR060_SWR_VSTART_HI 0x04a -#define ZR060_SWR_VSTART_LO 0x04b -#define ZR060_SWR_VEND_HI 0x04c -#define ZR060_SWR_VEND_LO 0x04d -#define ZR060_SWR_HSTART_HI 0x04e -#define ZR060_SWR_HSTART_LO 0x04f -#define ZR060_SWR_HEND_HI 0x050 -#define ZR060_SWR_HEND_LO 0x051 - -#define ZR060_SOF_IDX 0x060 -#define ZR060_SOS_IDX 0x07a -#define ZR060_DRI_IDX 0x0c0 -#define ZR060_DQT_IDX 0x0cc -#define ZR060_DHT_IDX 0x1d4 -#define ZR060_APP_IDX 0x380 -#define ZR060_COM_IDX 0x3c0 - -/* ZR36060 LOAD register bits */ - -#define ZR060_LOAD_Load (1 << 7) -#define ZR060_LOAD_SyncRst (1 << 0) - -/* ZR36060 Code FIFO Status register bits */ - -#define ZR060_CFSR_Busy (1 << 7) -#define ZR060_CFSR_CBusy (1 << 2) -#define ZR060_CFSR_CFIFO (3 << 0) - -/* ZR36060 Code Interface register */ - -#define ZR060_CIR_Code16 (1 << 7) -#define ZR060_CIR_Endian (1 << 6) -#define ZR060_CIR_CFIS (1 << 2) -#define ZR060_CIR_CodeMstr (1 << 0) - -/* ZR36060 Codec Mode register */ - -#define ZR060_CMR_Comp (1 << 7) -#define ZR060_CMR_ATP (1 << 6) -#define ZR060_CMR_Pass2 (1 << 5) -#define ZR060_CMR_TLM (1 << 4) -#define ZR060_CMR_BRB (1 << 2) -#define ZR060_CMR_FSF (1 << 1) - -/* ZR36060 Markers Enable register */ - -#define ZR060_MER_App (1 << 7) -#define ZR060_MER_Com (1 << 6) -#define ZR060_MER_DRI (1 << 5) -#define ZR060_MER_DQT (1 << 4) -#define ZR060_MER_DHT (1 << 3) - -/* ZR36060 Interrupt Mask register */ - -#define ZR060_IMR_EOAV (1 << 3) -#define ZR060_IMR_EOI (1 << 2) -#define ZR060_IMR_End (1 << 1) -#define ZR060_IMR_DataErr (1 << 0) - -/* ZR36060 Interrupt Status register */ - -#define ZR060_ISR_ProCnt (3 << 6) -#define ZR060_ISR_EOAV (1 << 3) -#define ZR060_ISR_EOI (1 << 2) -#define ZR060_ISR_End (1 << 1) -#define ZR060_ISR_DataErr (1 << 0) - -/* ZR36060 Video Control register */ - -#define ZR060_VCR_Video8 (1 << 7) -#define ZR060_VCR_Range (1 << 6) -#define ZR060_VCR_FIDet (1 << 3) -#define ZR060_VCR_FIVedge (1 << 2) -#define ZR060_VCR_FIExt (1 << 1) -#define ZR060_VCR_SyncMstr (1 << 0) - -/* ZR36060 Video Polarity register */ - -#define ZR060_VPR_VCLKPol (1 << 7) -#define ZR060_VPR_PValPol (1 << 6) -#define ZR060_VPR_PoePol (1 << 5) -#define ZR060_VPR_SImgPol (1 << 4) -#define ZR060_VPR_BLPol (1 << 3) -#define ZR060_VPR_FIPol (1 << 2) -#define ZR060_VPR_HSPol (1 << 1) -#define ZR060_VPR_VSPol (1 << 0) - -/* ZR36060 Scaling register */ - -#define ZR060_SR_VScale (1 << 2) -#define ZR060_SR_HScale2 (1 << 0) -#define ZR060_SR_HScale4 (2 << 0) - -#endif /*fndef ZR36060_H */ diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index c7a1cf8a1b01..2728376b04b5 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -26,6 +26,7 @@ config VIDEO_VIA_CAMERA # # Platform multimedia device configuration # +source "drivers/media/platform/cadence/Kconfig" source "drivers/media/platform/davinci/Kconfig" @@ -34,7 +35,7 @@ source "drivers/media/platform/omap/Kconfig" config VIDEO_SH_VOU tristate "SuperH VOU video output driver" depends on MEDIA_CAMERA_SUPPORT - depends on VIDEO_DEV && I2C && HAS_DMA + depends on VIDEO_DEV && I2C depends on ARCH_SHMOBILE || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG help @@ -42,7 +43,7 @@ config VIDEO_SH_VOU config VIDEO_VIU tristate "Freescale VIU Video Driver" - depends on VIDEO_V4L2 && PPC_MPC512x + depends on VIDEO_V4L2 && (PPC_MPC512x || COMPILE_TEST) && I2C select VIDEOBUF_DMA_CONTIG default y ---help--- @@ -62,10 +63,10 @@ config VIDEO_MUX config VIDEO_OMAP3 tristate "OMAP 3 Camera support" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 - depends on HAS_DMA && OF - depends on OMAP_IOMMU - select ARM_DMA_USE_IOMMU + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on (ARCH_OMAP3 && OMAP_IOMMU) || COMPILE_TEST + depends on COMMON_CLK && OF + select ARM_DMA_USE_IOMMU if OMAP_IOMMU select VIDEOBUF2_DMA_CONTIG select MFD_SYSCON select V4L2_FWNODE @@ -80,7 +81,7 @@ config VIDEO_OMAP3_DEBUG config VIDEO_PXA27x tristate "PXA27x Quick Capture Interface driver" - depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA + depends on VIDEO_DEV && VIDEO_V4L2 depends on PXA27x || COMPILE_TEST select VIDEOBUF2_DMA_SG select SG_SPLIT @@ -90,7 +91,7 @@ config VIDEO_PXA27x config VIDEO_QCOM_CAMSS tristate "Qualcomm 8x16 V4L2 Camera Subsystem driver" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST select VIDEOBUF2_DMA_SG select V4L2_FWNODE @@ -100,7 +101,6 @@ config VIDEO_S3C_CAMIF depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API depends on PM depends on ARCH_S3C64XX || PLAT_S3C24XX || COMPILE_TEST - depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG ---help--- This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera @@ -111,7 +111,7 @@ config VIDEO_S3C_CAMIF config VIDEO_STM32_DCMI tristate "STM32 Digital Camera Memory Interface (DCMI) support" - depends on VIDEO_V4L2 && OF && HAS_DMA + depends on VIDEO_V4L2 && OF depends on ARCH_STM32 || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE @@ -124,7 +124,7 @@ config VIDEO_STM32_DCMI config VIDEO_RENESAS_CEU tristate "Renesas Capture Engine Unit (CEU) driver" - depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA + depends on VIDEO_DEV && VIDEO_V4L2 depends on ARCH_SHMOBILE || ARCH_R7S72100 || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE @@ -142,7 +142,6 @@ config VIDEO_TI_CAL tristate "TI CAL (Camera Adaptation Layer) driver" depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on SOC_DRA7XX || COMPILE_TEST - depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE default n @@ -170,7 +169,6 @@ if V4L_MEM2MEM_DRIVERS config VIDEO_CODA tristate "Chips&Media Coda multi-standard codec IP" depends on VIDEO_DEV && VIDEO_V4L2 && (ARCH_MXC || COMPILE_TEST) - depends on HAS_DMA select SRAM select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_VMALLOC @@ -188,7 +186,6 @@ config VIDEO_MEDIATEK_JPEG depends on MTK_IOMMU_V1 || COMPILE_TEST depends on VIDEO_DEV && VIDEO_V4L2 depends on ARCH_MEDIATEK || COMPILE_TEST - depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV ---help--- @@ -200,7 +197,7 @@ config VIDEO_MEDIATEK_JPEG config VIDEO_MEDIATEK_VPU tristate "Mediatek Video Processor Unit" - depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA + depends on VIDEO_DEV && VIDEO_V4L2 depends on ARCH_MEDIATEK || COMPILE_TEST ---help--- This driver provides downloading VPU firmware and @@ -216,7 +213,6 @@ config VIDEO_MEDIATEK_MDP depends on MTK_IOMMU || COMPILE_TEST depends on VIDEO_DEV && VIDEO_V4L2 depends on ARCH_MEDIATEK || COMPILE_TEST - depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV select VIDEO_MEDIATEK_VPU @@ -231,7 +227,7 @@ config VIDEO_MEDIATEK_MDP config VIDEO_MEDIATEK_VCODEC tristate "Mediatek Video Codec driver" depends on MTK_IOMMU || COMPILE_TEST - depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA + depends on VIDEO_DEV && VIDEO_V4L2 depends on ARCH_MEDIATEK || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV @@ -247,7 +243,7 @@ config VIDEO_MEDIATEK_VCODEC config VIDEO_MEM2MEM_DEINTERLACE tristate "Deinterlace support" - depends on VIDEO_DEV && VIDEO_V4L2 && DMA_ENGINE + depends on VIDEO_DEV && VIDEO_V4L2 depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV @@ -258,7 +254,6 @@ config VIDEO_SAMSUNG_S5P_G2D tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver" depends on VIDEO_DEV && VIDEO_V4L2 depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST - depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV default n @@ -270,7 +265,6 @@ config VIDEO_SAMSUNG_S5P_JPEG tristate "Samsung S5P/Exynos3250/Exynos4 JPEG codec driver" depends on VIDEO_DEV && VIDEO_V4L2 depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST - depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV ---help--- @@ -281,7 +275,6 @@ config VIDEO_SAMSUNG_S5P_MFC tristate "Samsung S5P MFC Video Codec" depends on VIDEO_DEV && VIDEO_V4L2 depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST - depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG default n help @@ -291,7 +284,6 @@ config VIDEO_MX2_EMMAPRP tristate "MX2 eMMa-PrP support" depends on VIDEO_DEV && VIDEO_V4L2 depends on SOC_IMX27 || COMPILE_TEST - depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV help @@ -303,7 +295,6 @@ config VIDEO_SAMSUNG_EXYNOS_GSC tristate "Samsung Exynos G-Scaler driver" depends on VIDEO_DEV && VIDEO_V4L2 depends on ARCH_EXYNOS || COMPILE_TEST - depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV help @@ -312,7 +303,6 @@ config VIDEO_SAMSUNG_EXYNOS_GSC config VIDEO_STI_BDISP tristate "STMicroelectronics BDISP 2D blitter driver" depends on VIDEO_DEV && VIDEO_V4L2 - depends on HAS_DMA depends on ARCH_STI || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV @@ -322,7 +312,6 @@ config VIDEO_STI_BDISP config VIDEO_STI_HVA tristate "STMicroelectronics HVA multi-format video encoder V4L2 driver" depends on VIDEO_DEV && VIDEO_V4L2 - depends on HAS_DMA depends on ARCH_STI || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV @@ -349,7 +338,6 @@ config VIDEO_STI_DELTA tristate "STMicroelectronics DELTA multi-format video decoder V4L2 driver" depends on VIDEO_DEV && VIDEO_V4L2 depends on ARCH_STI || COMPILE_TEST - depends on HAS_DMA help This V4L2 driver enables DELTA multi-format video decoder of STMicroelectronics STiH4xx SoC series allowing hardware @@ -395,7 +383,7 @@ config VIDEO_SH_VEU config VIDEO_RENESAS_FDP1 tristate "Renesas Fine Display Processor" - depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA + depends on VIDEO_DEV && VIDEO_V4L2 depends on ARCH_SHMOBILE || COMPILE_TEST depends on (!ARCH_RENESAS && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP select VIDEOBUF2_DMA_CONTIG @@ -409,7 +397,7 @@ config VIDEO_RENESAS_FDP1 config VIDEO_RENESAS_JPU tristate "Renesas JPEG Processing Unit" - depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA + depends on VIDEO_DEV && VIDEO_V4L2 depends on ARCH_RENESAS || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV @@ -434,8 +422,8 @@ config VIDEO_RENESAS_FCP config VIDEO_RENESAS_VSP1 tristate "Renesas VSP1 Video Processing Engine" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA - depends on (ARCH_RENESAS && OF) || COMPILE_TEST + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on ARCH_RENESAS || COMPILE_TEST depends on (!ARM64 && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_VMALLOC @@ -447,7 +435,7 @@ config VIDEO_RENESAS_VSP1 config VIDEO_ROCKCHIP_RGA tristate "Rockchip Raster 2d Graphic Acceleration Unit" - depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA + depends on VIDEO_DEV && VIDEO_V4L2 depends on ARCH_ROCKCHIP || COMPILE_TEST select VIDEOBUF2_DMA_SG select V4L2_MEM2MEM_DEV @@ -464,7 +452,6 @@ config VIDEO_TI_VPE tristate "TI VPE (Video Processing Engine) driver" depends on VIDEO_DEV && VIDEO_V4L2 depends on SOC_DRA7XX || COMPILE_TEST - depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV select VIDEO_TI_VPDMA @@ -483,7 +470,7 @@ config VIDEO_TI_VPE_DEBUG config VIDEO_QCOM_VENUS tristate "Qualcomm Venus V4L2 encoder/decoder driver" - depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA + depends on VIDEO_DEV && VIDEO_V4L2 depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST select QCOM_MDT_LOADER if ARCH_QCOM select QCOM_SCM if ARCH_QCOM @@ -558,7 +545,7 @@ config VIDEO_MESON_AO_CEC config CEC_GPIO tristate "Generic GPIO-based CEC driver" - depends on PREEMPT + depends on PREEMPT || COMPILE_TEST select CEC_CORE select CEC_PIN select GPIOLIB @@ -625,7 +612,7 @@ if SDR_PLATFORM_DRIVERS config VIDEO_RCAR_DRIF tristate "Renesas Digitial Radio Interface (DRIF)" - depends on VIDEO_V4L2 && HAS_DMA + depends on VIDEO_V4L2 depends on ARCH_RENESAS || COMPILE_TEST select VIDEOBUF2_VMALLOC ---help--- diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 932515df4477..04bc1502a30e 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -3,6 +3,7 @@ # Makefile for the video capture/playback device drivers. # +obj-$(CONFIG_VIDEO_CADENCE) += cadence/ obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/ diff --git a/drivers/media/platform/am437x/Kconfig b/drivers/media/platform/am437x/Kconfig index 160e77e9a0fb..f4ce1176e4dc 100644 --- a/drivers/media/platform/am437x/Kconfig +++ b/drivers/media/platform/am437x/Kconfig @@ -1,6 +1,6 @@ config VIDEO_AM437X_VPFE tristate "TI AM437x VPFE video capture driver" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on SOC_AM43XX || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index 601ae6487617..58ebc2220d0e 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -2662,8 +2662,7 @@ static void vpfe_save_context(struct vpfe_ccdc *ccdc) static int vpfe_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct vpfe_device *vpfe = platform_get_drvdata(pdev); + struct vpfe_device *vpfe = dev_get_drvdata(dev); struct vpfe_ccdc *ccdc = &vpfe->ccdc; /* if streaming has not started we don't care */ @@ -2720,8 +2719,7 @@ static void vpfe_restore_context(struct vpfe_ccdc *ccdc) static int vpfe_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct vpfe_device *vpfe = platform_get_drvdata(pdev); + struct vpfe_device *vpfe = dev_get_drvdata(dev); struct vpfe_ccdc *ccdc = &vpfe->ccdc; /* if streaming has not started we don't care */ diff --git a/drivers/media/platform/atmel/Kconfig b/drivers/media/platform/atmel/Kconfig index 55de751e5f51..a211ef20f77e 100644 --- a/drivers/media/platform/atmel/Kconfig +++ b/drivers/media/platform/atmel/Kconfig @@ -1,6 +1,6 @@ config VIDEO_ATMEL_ISC tristate "ATMEL Image Sensor Controller (ISC) support" - depends on VIDEO_V4L2 && COMMON_CLK && VIDEO_V4L2_SUBDEV_API && HAS_DMA + depends on VIDEO_V4L2 && COMMON_CLK && VIDEO_V4L2_SUBDEV_API depends on ARCH_AT91 || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select REGMAP_MMIO @@ -11,7 +11,7 @@ config VIDEO_ATMEL_ISC config VIDEO_ATMEL_ISI tristate "ATMEL Image Sensor Interface (ISI) support" - depends on VIDEO_V4L2 && OF && HAS_DMA + depends on VIDEO_V4L2 && OF depends on ARCH_AT91 || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE diff --git a/drivers/media/platform/cadence/Kconfig b/drivers/media/platform/cadence/Kconfig new file mode 100644 index 000000000000..3bf0f2454384 --- /dev/null +++ b/drivers/media/platform/cadence/Kconfig @@ -0,0 +1,34 @@ +config VIDEO_CADENCE + bool "Cadence Video Devices" + help + If you have a media device designed by Cadence, say Y. + + Note that this option doesn't include new drivers in the kernel: + saying N will just cause Kconfig to skip all the questions about + Cadence media devices. + +if VIDEO_CADENCE + +config VIDEO_CADENCE_CSI2RX + tristate "Cadence MIPI-CSI2 RX Controller" + depends on MEDIA_CONTROLLER + depends on VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + Support for the Cadence MIPI CSI2 Receiver controller. + + To compile this driver as a module, choose M here: the module will be + called cdns-csi2rx. + +config VIDEO_CADENCE_CSI2TX + tristate "Cadence MIPI-CSI2 TX Controller" + depends on MEDIA_CONTROLLER + depends on VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + Support for the Cadence MIPI CSI2 Transceiver controller. + + To compile this driver as a module, choose M here: the module will be + called cdns-csi2tx. + +endif diff --git a/drivers/media/platform/cadence/Makefile b/drivers/media/platform/cadence/Makefile new file mode 100644 index 000000000000..be59a8728a01 --- /dev/null +++ b/drivers/media/platform/cadence/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_VIDEO_CADENCE_CSI2RX) += cdns-csi2rx.o +obj-$(CONFIG_VIDEO_CADENCE_CSI2TX) += cdns-csi2tx.o diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c new file mode 100644 index 000000000000..a0f02916006b --- /dev/null +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -0,0 +1,498 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Cadence MIPI-CSI2 RX Controller v1.3 + * + * Copyright (C) 2017 Cadence Design Systems Inc. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define CSI2RX_DEVICE_CFG_REG 0x000 + +#define CSI2RX_SOFT_RESET_REG 0x004 +#define CSI2RX_SOFT_RESET_PROTOCOL BIT(1) +#define CSI2RX_SOFT_RESET_FRONT BIT(0) + +#define CSI2RX_STATIC_CFG_REG 0x008 +#define CSI2RX_STATIC_CFG_DLANE_MAP(llane, plane) ((plane) << (16 + (llane) * 4)) +#define CSI2RX_STATIC_CFG_LANES_MASK GENMASK(11, 8) + +#define CSI2RX_STREAM_BASE(n) (((n) + 1) * 0x100) + +#define CSI2RX_STREAM_CTRL_REG(n) (CSI2RX_STREAM_BASE(n) + 0x000) +#define CSI2RX_STREAM_CTRL_START BIT(0) + +#define CSI2RX_STREAM_DATA_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x008) +#define CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT BIT(31) +#define CSI2RX_STREAM_DATA_CFG_VC_SELECT(n) BIT((n) + 16) + +#define CSI2RX_STREAM_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x00c) +#define CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF (1 << 8) + +#define CSI2RX_LANES_MAX 4 +#define CSI2RX_STREAMS_MAX 4 + +enum csi2rx_pads { + CSI2RX_PAD_SINK, + CSI2RX_PAD_SOURCE_STREAM0, + CSI2RX_PAD_SOURCE_STREAM1, + CSI2RX_PAD_SOURCE_STREAM2, + CSI2RX_PAD_SOURCE_STREAM3, + CSI2RX_PAD_MAX, +}; + +struct csi2rx_priv { + struct device *dev; + unsigned int count; + + /* + * Used to prevent race conditions between multiple, + * concurrent calls to start and stop. + */ + struct mutex lock; + + void __iomem *base; + struct clk *sys_clk; + struct clk *p_clk; + struct clk *pixel_clk[CSI2RX_STREAMS_MAX]; + struct phy *dphy; + + u8 lanes[CSI2RX_LANES_MAX]; + u8 num_lanes; + u8 max_lanes; + u8 max_streams; + bool has_internal_dphy; + + struct v4l2_subdev subdev; + struct v4l2_async_notifier notifier; + struct media_pad pads[CSI2RX_PAD_MAX]; + + /* Remote source */ + struct v4l2_async_subdev asd; + struct v4l2_subdev *source_subdev; + int source_pad; +}; + +static inline +struct csi2rx_priv *v4l2_subdev_to_csi2rx(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct csi2rx_priv, subdev); +} + +static void csi2rx_reset(struct csi2rx_priv *csi2rx) +{ + writel(CSI2RX_SOFT_RESET_PROTOCOL | CSI2RX_SOFT_RESET_FRONT, + csi2rx->base + CSI2RX_SOFT_RESET_REG); + + udelay(10); + + writel(0, csi2rx->base + CSI2RX_SOFT_RESET_REG); +} + +static int csi2rx_start(struct csi2rx_priv *csi2rx) +{ + unsigned int i; + unsigned long lanes_used = 0; + u32 reg; + int ret; + + ret = clk_prepare_enable(csi2rx->p_clk); + if (ret) + return ret; + + csi2rx_reset(csi2rx); + + reg = csi2rx->num_lanes << 8; + for (i = 0; i < csi2rx->num_lanes; i++) { + reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, csi2rx->lanes[i]); + set_bit(csi2rx->lanes[i], &lanes_used); + } + + /* + * Even the unused lanes need to be mapped. In order to avoid + * to map twice to the same physical lane, keep the lanes used + * in the previous loop, and only map unused physical lanes to + * the rest of our logical lanes. + */ + for (i = csi2rx->num_lanes; i < csi2rx->max_lanes; i++) { + unsigned int idx = find_first_zero_bit(&lanes_used, + sizeof(lanes_used)); + set_bit(idx, &lanes_used); + reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, i + 1); + } + + writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG); + + ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true); + if (ret) + goto err_disable_pclk; + + /* + * Create a static mapping between the CSI virtual channels + * and the output stream. + * + * This should be enhanced, but v4l2 lacks the support for + * changing that mapping dynamically. + * + * We also cannot enable and disable independent streams here, + * hence the reference counting. + */ + for (i = 0; i < csi2rx->max_streams; i++) { + ret = clk_prepare_enable(csi2rx->pixel_clk[i]); + if (ret) + goto err_disable_pixclk; + + writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF, + csi2rx->base + CSI2RX_STREAM_CFG_REG(i)); + + writel(CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT | + CSI2RX_STREAM_DATA_CFG_VC_SELECT(i), + csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)); + + writel(CSI2RX_STREAM_CTRL_START, + csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); + } + + ret = clk_prepare_enable(csi2rx->sys_clk); + if (ret) + goto err_disable_pixclk; + + clk_disable_unprepare(csi2rx->p_clk); + + return 0; + +err_disable_pixclk: + for (; i > 0; i--) + clk_disable_unprepare(csi2rx->pixel_clk[i - 1]); + +err_disable_pclk: + clk_disable_unprepare(csi2rx->p_clk); + + return ret; +} + +static void csi2rx_stop(struct csi2rx_priv *csi2rx) +{ + unsigned int i; + + clk_prepare_enable(csi2rx->p_clk); + clk_disable_unprepare(csi2rx->sys_clk); + + for (i = 0; i < csi2rx->max_streams; i++) { + writel(0, csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); + + clk_disable_unprepare(csi2rx->pixel_clk[i]); + } + + clk_disable_unprepare(csi2rx->p_clk); + + if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false)) + dev_warn(csi2rx->dev, "Couldn't disable our subdev\n"); +} + +static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + int ret = 0; + + mutex_lock(&csi2rx->lock); + + if (enable) { + /* + * If we're not the first users, there's no need to + * enable the whole controller. + */ + if (!csi2rx->count) { + ret = csi2rx_start(csi2rx); + if (ret) + goto out; + } + + csi2rx->count++; + } else { + csi2rx->count--; + + /* + * Let the last user turn off the lights. + */ + if (!csi2rx->count) + csi2rx_stop(csi2rx); + } + +out: + mutex_unlock(&csi2rx->lock); + return ret; +} + +static const struct v4l2_subdev_video_ops csi2rx_video_ops = { + .s_stream = csi2rx_s_stream, +}; + +static const struct v4l2_subdev_ops csi2rx_subdev_ops = { + .video = &csi2rx_video_ops, +}; + +static int csi2rx_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *s_subdev, + struct v4l2_async_subdev *asd) +{ + struct v4l2_subdev *subdev = notifier->sd; + struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + + csi2rx->source_pad = media_entity_get_fwnode_pad(&s_subdev->entity, + s_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (csi2rx->source_pad < 0) { + dev_err(csi2rx->dev, "Couldn't find output pad for subdev %s\n", + s_subdev->name); + return csi2rx->source_pad; + } + + csi2rx->source_subdev = s_subdev; + + dev_dbg(csi2rx->dev, "Bound %s pad: %d\n", s_subdev->name, + csi2rx->source_pad); + + return media_create_pad_link(&csi2rx->source_subdev->entity, + csi2rx->source_pad, + &csi2rx->subdev.entity, 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +} + +static const struct v4l2_async_notifier_operations csi2rx_notifier_ops = { + .bound = csi2rx_async_bound, +}; + +static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, + struct platform_device *pdev) +{ + struct resource *res; + unsigned char i; + u32 dev_cfg; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + csi2rx->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(csi2rx->base)) + return PTR_ERR(csi2rx->base); + + csi2rx->sys_clk = devm_clk_get(&pdev->dev, "sys_clk"); + if (IS_ERR(csi2rx->sys_clk)) { + dev_err(&pdev->dev, "Couldn't get sys clock\n"); + return PTR_ERR(csi2rx->sys_clk); + } + + csi2rx->p_clk = devm_clk_get(&pdev->dev, "p_clk"); + if (IS_ERR(csi2rx->p_clk)) { + dev_err(&pdev->dev, "Couldn't get P clock\n"); + return PTR_ERR(csi2rx->p_clk); + } + + csi2rx->dphy = devm_phy_optional_get(&pdev->dev, "dphy"); + if (IS_ERR(csi2rx->dphy)) { + dev_err(&pdev->dev, "Couldn't get external D-PHY\n"); + return PTR_ERR(csi2rx->dphy); + } + + /* + * FIXME: Once we'll have external D-PHY support, the check + * will need to be removed. + */ + if (csi2rx->dphy) { + dev_err(&pdev->dev, "External D-PHY not supported yet\n"); + return -EINVAL; + } + + clk_prepare_enable(csi2rx->p_clk); + dev_cfg = readl(csi2rx->base + CSI2RX_DEVICE_CFG_REG); + clk_disable_unprepare(csi2rx->p_clk); + + csi2rx->max_lanes = dev_cfg & 7; + if (csi2rx->max_lanes > CSI2RX_LANES_MAX) { + dev_err(&pdev->dev, "Invalid number of lanes: %u\n", + csi2rx->max_lanes); + return -EINVAL; + } + + csi2rx->max_streams = (dev_cfg >> 4) & 7; + if (csi2rx->max_streams > CSI2RX_STREAMS_MAX) { + dev_err(&pdev->dev, "Invalid number of streams: %u\n", + csi2rx->max_streams); + return -EINVAL; + } + + csi2rx->has_internal_dphy = dev_cfg & BIT(3) ? true : false; + + /* + * FIXME: Once we'll have internal D-PHY support, the check + * will need to be removed. + */ + if (csi2rx->has_internal_dphy) { + dev_err(&pdev->dev, "Internal D-PHY not supported yet\n"); + return -EINVAL; + } + + for (i = 0; i < csi2rx->max_streams; i++) { + char clk_name[16]; + + snprintf(clk_name, sizeof(clk_name), "pixel_if%u_clk", i); + csi2rx->pixel_clk[i] = devm_clk_get(&pdev->dev, clk_name); + if (IS_ERR(csi2rx->pixel_clk[i])) { + dev_err(&pdev->dev, "Couldn't get clock %s\n", clk_name); + return PTR_ERR(csi2rx->pixel_clk[i]); + } + } + + return 0; +} + +static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx) +{ + struct v4l2_fwnode_endpoint v4l2_ep; + struct fwnode_handle *fwh; + struct device_node *ep; + int ret; + + ep = of_graph_get_endpoint_by_regs(csi2rx->dev->of_node, 0, 0); + if (!ep) + return -EINVAL; + + fwh = of_fwnode_handle(ep); + ret = v4l2_fwnode_endpoint_parse(fwh, &v4l2_ep); + if (ret) { + dev_err(csi2rx->dev, "Could not parse v4l2 endpoint\n"); + of_node_put(ep); + return ret; + } + + if (v4l2_ep.bus_type != V4L2_MBUS_CSI2) { + dev_err(csi2rx->dev, "Unsupported media bus type: 0x%x\n", + v4l2_ep.bus_type); + of_node_put(ep); + return -EINVAL; + } + + memcpy(csi2rx->lanes, v4l2_ep.bus.mipi_csi2.data_lanes, + sizeof(csi2rx->lanes)); + csi2rx->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; + if (csi2rx->num_lanes > csi2rx->max_lanes) { + dev_err(csi2rx->dev, "Unsupported number of data-lanes: %d\n", + csi2rx->num_lanes); + of_node_put(ep); + return -EINVAL; + } + + csi2rx->asd.match.fwnode = fwnode_graph_get_remote_port_parent(fwh); + csi2rx->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + of_node_put(ep); + + csi2rx->notifier.subdevs = devm_kzalloc(csi2rx->dev, + sizeof(*csi2rx->notifier.subdevs), + GFP_KERNEL); + if (!csi2rx->notifier.subdevs) + return -ENOMEM; + + csi2rx->notifier.subdevs[0] = &csi2rx->asd; + csi2rx->notifier.num_subdevs = 1; + csi2rx->notifier.ops = &csi2rx_notifier_ops; + + return v4l2_async_subdev_notifier_register(&csi2rx->subdev, + &csi2rx->notifier); +} + +static int csi2rx_probe(struct platform_device *pdev) +{ + struct csi2rx_priv *csi2rx; + unsigned int i; + int ret; + + csi2rx = kzalloc(sizeof(*csi2rx), GFP_KERNEL); + if (!csi2rx) + return -ENOMEM; + platform_set_drvdata(pdev, csi2rx); + csi2rx->dev = &pdev->dev; + mutex_init(&csi2rx->lock); + + ret = csi2rx_get_resources(csi2rx, pdev); + if (ret) + goto err_free_priv; + + ret = csi2rx_parse_dt(csi2rx); + if (ret) + goto err_free_priv; + + csi2rx->subdev.owner = THIS_MODULE; + csi2rx->subdev.dev = &pdev->dev; + v4l2_subdev_init(&csi2rx->subdev, &csi2rx_subdev_ops); + v4l2_set_subdevdata(&csi2rx->subdev, &pdev->dev); + snprintf(csi2rx->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.%s", + KBUILD_MODNAME, dev_name(&pdev->dev)); + + /* Create our media pads */ + csi2rx->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + csi2rx->pads[CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) + csi2rx->pads[i].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&csi2rx->subdev.entity, CSI2RX_PAD_MAX, + csi2rx->pads); + if (ret) + goto err_free_priv; + + ret = v4l2_async_register_subdev(&csi2rx->subdev); + if (ret < 0) + goto err_free_priv; + + dev_info(&pdev->dev, + "Probed CSI2RX with %u/%u lanes, %u streams, %s D-PHY\n", + csi2rx->num_lanes, csi2rx->max_lanes, csi2rx->max_streams, + csi2rx->has_internal_dphy ? "internal" : "no"); + + return 0; + +err_free_priv: + kfree(csi2rx); + return ret; +} + +static int csi2rx_remove(struct platform_device *pdev) +{ + struct csi2rx_priv *csi2rx = platform_get_drvdata(pdev); + + v4l2_async_unregister_subdev(&csi2rx->subdev); + kfree(csi2rx); + + return 0; +} + +static const struct of_device_id csi2rx_of_table[] = { + { .compatible = "cdns,csi2rx" }, + { }, +}; +MODULE_DEVICE_TABLE(of, csi2rx_of_table); + +static struct platform_driver csi2rx_driver = { + .probe = csi2rx_probe, + .remove = csi2rx_remove, + + .driver = { + .name = "cdns-csi2rx", + .of_match_table = csi2rx_of_table, + }, +}; +module_platform_driver(csi2rx_driver); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>"); +MODULE_DESCRIPTION("Cadence CSI2-RX controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/cadence/cdns-csi2tx.c b/drivers/media/platform/cadence/cdns-csi2tx.c new file mode 100644 index 000000000000..dfa1d88d955b --- /dev/null +++ b/drivers/media/platform/cadence/cdns-csi2tx.c @@ -0,0 +1,563 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Cadence MIPI-CSI2 TX Controller + * + * Copyright (C) 2017-2018 Cadence Design Systems Inc. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define CSI2TX_DEVICE_CONFIG_REG 0x00 +#define CSI2TX_DEVICE_CONFIG_STREAMS_MASK GENMASK(6, 4) +#define CSI2TX_DEVICE_CONFIG_HAS_DPHY BIT(3) +#define CSI2TX_DEVICE_CONFIG_LANES_MASK GENMASK(2, 0) + +#define CSI2TX_CONFIG_REG 0x20 +#define CSI2TX_CONFIG_CFG_REQ BIT(2) +#define CSI2TX_CONFIG_SRST_REQ BIT(1) + +#define CSI2TX_DPHY_CFG_REG 0x28 +#define CSI2TX_DPHY_CFG_CLK_RESET BIT(16) +#define CSI2TX_DPHY_CFG_LANE_RESET(n) BIT((n) + 12) +#define CSI2TX_DPHY_CFG_MODE_MASK GENMASK(9, 8) +#define CSI2TX_DPHY_CFG_MODE_LPDT (2 << 8) +#define CSI2TX_DPHY_CFG_MODE_HS (1 << 8) +#define CSI2TX_DPHY_CFG_MODE_ULPS (0 << 8) +#define CSI2TX_DPHY_CFG_CLK_ENABLE BIT(4) +#define CSI2TX_DPHY_CFG_LANE_ENABLE(n) BIT(n) + +#define CSI2TX_DPHY_CLK_WAKEUP_REG 0x2c +#define CSI2TX_DPHY_CLK_WAKEUP_ULPS_CYCLES(n) ((n) & 0xffff) + +#define CSI2TX_DT_CFG_REG(n) (0x80 + (n) * 8) +#define CSI2TX_DT_CFG_DT(n) (((n) & 0x3f) << 2) + +#define CSI2TX_DT_FORMAT_REG(n) (0x84 + (n) * 8) +#define CSI2TX_DT_FORMAT_BYTES_PER_LINE(n) (((n) & 0xffff) << 16) +#define CSI2TX_DT_FORMAT_MAX_LINE_NUM(n) ((n) & 0xffff) + +#define CSI2TX_STREAM_IF_CFG_REG(n) (0x100 + (n) * 4) +#define CSI2TX_STREAM_IF_CFG_FILL_LEVEL(n) ((n) & 0x1f) + +#define CSI2TX_LANES_MAX 4 +#define CSI2TX_STREAMS_MAX 4 + +enum csi2tx_pads { + CSI2TX_PAD_SOURCE, + CSI2TX_PAD_SINK_STREAM0, + CSI2TX_PAD_SINK_STREAM1, + CSI2TX_PAD_SINK_STREAM2, + CSI2TX_PAD_SINK_STREAM3, + CSI2TX_PAD_MAX, +}; + +struct csi2tx_fmt { + u32 mbus; + u32 dt; + u32 bpp; +}; + +struct csi2tx_priv { + struct device *dev; + unsigned int count; + + /* + * Used to prevent race conditions between multiple, + * concurrent calls to start and stop. + */ + struct mutex lock; + + void __iomem *base; + + struct clk *esc_clk; + struct clk *p_clk; + struct clk *pixel_clk[CSI2TX_STREAMS_MAX]; + + struct v4l2_subdev subdev; + struct media_pad pads[CSI2TX_PAD_MAX]; + struct v4l2_mbus_framefmt pad_fmts[CSI2TX_PAD_MAX]; + + bool has_internal_dphy; + u8 lanes[CSI2TX_LANES_MAX]; + unsigned int num_lanes; + unsigned int max_lanes; + unsigned int max_streams; +}; + +static const struct csi2tx_fmt csi2tx_formats[] = { + { + .mbus = MEDIA_BUS_FMT_UYVY8_1X16, + .bpp = 2, + .dt = 0x1e, + }, + { + .mbus = MEDIA_BUS_FMT_RGB888_1X24, + .bpp = 3, + .dt = 0x24, + }, +}; + +static const struct v4l2_mbus_framefmt fmt_default = { + .width = 1280, + .height = 720, + .code = MEDIA_BUS_FMT_RGB888_1X24, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_DEFAULT, +}; + +static inline +struct csi2tx_priv *v4l2_subdev_to_csi2tx(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct csi2tx_priv, subdev); +} + +static const struct csi2tx_fmt *csi2tx_get_fmt_from_mbus(u32 mbus) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(csi2tx_formats); i++) + if (csi2tx_formats[i].mbus == mbus) + return &csi2tx_formats[i]; + + return NULL; +} + +static int csi2tx_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad || code->index >= ARRAY_SIZE(csi2tx_formats)) + return -EINVAL; + + code->code = csi2tx_formats[code->index].mbus; + + return 0; +} + +static struct v4l2_mbus_framefmt * +__csi2tx_get_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct csi2tx_priv *csi2tx = v4l2_subdev_to_csi2tx(subdev); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(subdev, cfg, + fmt->pad); + + return &csi2tx->pad_fmts[fmt->pad]; +} + +static int csi2tx_get_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + const struct v4l2_mbus_framefmt *format; + + /* Multiplexed pad? */ + if (fmt->pad == CSI2TX_PAD_SOURCE) + return -EINVAL; + + format = __csi2tx_get_pad_format(subdev, cfg, fmt); + if (!format) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +static int csi2tx_set_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + const struct v4l2_mbus_framefmt *src_format = &fmt->format; + struct v4l2_mbus_framefmt *dst_format; + + /* Multiplexed pad? */ + if (fmt->pad == CSI2TX_PAD_SOURCE) + return -EINVAL; + + if (!csi2tx_get_fmt_from_mbus(fmt->format.code)) + src_format = &fmt_default; + + dst_format = __csi2tx_get_pad_format(subdev, cfg, fmt); + if (!dst_format) + return -EINVAL; + + *dst_format = *src_format; + + return 0; +} + +static const struct v4l2_subdev_pad_ops csi2tx_pad_ops = { + .enum_mbus_code = csi2tx_enum_mbus_code, + .get_fmt = csi2tx_get_pad_format, + .set_fmt = csi2tx_set_pad_format, +}; + +static void csi2tx_reset(struct csi2tx_priv *csi2tx) +{ + writel(CSI2TX_CONFIG_SRST_REQ, csi2tx->base + CSI2TX_CONFIG_REG); + + udelay(10); +} + +static int csi2tx_start(struct csi2tx_priv *csi2tx) +{ + struct media_entity *entity = &csi2tx->subdev.entity; + struct media_link *link; + unsigned int i; + u32 reg; + + csi2tx_reset(csi2tx); + + writel(CSI2TX_CONFIG_CFG_REQ, csi2tx->base + CSI2TX_CONFIG_REG); + + udelay(10); + + /* Configure our PPI interface with the D-PHY */ + writel(CSI2TX_DPHY_CLK_WAKEUP_ULPS_CYCLES(32), + csi2tx->base + CSI2TX_DPHY_CLK_WAKEUP_REG); + + /* Put our lanes (clock and data) out of reset */ + reg = CSI2TX_DPHY_CFG_CLK_RESET | CSI2TX_DPHY_CFG_MODE_LPDT; + for (i = 0; i < csi2tx->num_lanes; i++) + reg |= CSI2TX_DPHY_CFG_LANE_RESET(csi2tx->lanes[i]); + writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG); + + udelay(10); + + /* Enable our (clock and data) lanes */ + reg |= CSI2TX_DPHY_CFG_CLK_ENABLE; + for (i = 0; i < csi2tx->num_lanes; i++) + reg |= CSI2TX_DPHY_CFG_LANE_ENABLE(csi2tx->lanes[i]); + writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG); + + udelay(10); + + /* Switch to HS mode */ + reg &= ~CSI2TX_DPHY_CFG_MODE_MASK; + writel(reg | CSI2TX_DPHY_CFG_MODE_HS, + csi2tx->base + CSI2TX_DPHY_CFG_REG); + + udelay(10); + + /* + * Create a static mapping between the CSI virtual channels + * and the input streams. + * + * This should be enhanced, but v4l2 lacks the support for + * changing that mapping dynamically at the moment. + * + * We're protected from the userspace setting up links at the + * same time by the upper layer having called + * media_pipeline_start(). + */ + list_for_each_entry(link, &entity->links, list) { + struct v4l2_mbus_framefmt *mfmt; + const struct csi2tx_fmt *fmt; + unsigned int stream; + int pad_idx = -1; + + /* Only consider our enabled input pads */ + for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++) { + struct media_pad *pad = &csi2tx->pads[i]; + + if ((pad == link->sink) && + (link->flags & MEDIA_LNK_FL_ENABLED)) { + pad_idx = i; + break; + } + } + + if (pad_idx < 0) + continue; + + mfmt = &csi2tx->pad_fmts[pad_idx]; + fmt = csi2tx_get_fmt_from_mbus(mfmt->code); + if (!fmt) + continue; + + stream = pad_idx - CSI2TX_PAD_SINK_STREAM0; + + /* + * We use the stream ID there, but it's wrong. + * + * A stream could very well send a data type that is + * not equal to its stream ID. We need to find a + * proper way to address it. + */ + writel(CSI2TX_DT_CFG_DT(fmt->dt), + csi2tx->base + CSI2TX_DT_CFG_REG(stream)); + + writel(CSI2TX_DT_FORMAT_BYTES_PER_LINE(mfmt->width * fmt->bpp) | + CSI2TX_DT_FORMAT_MAX_LINE_NUM(mfmt->height + 1), + csi2tx->base + CSI2TX_DT_FORMAT_REG(stream)); + + /* + * TODO: This needs to be calculated based on the + * output CSI2 clock rate. + */ + writel(CSI2TX_STREAM_IF_CFG_FILL_LEVEL(4), + csi2tx->base + CSI2TX_STREAM_IF_CFG_REG(stream)); + } + + /* Disable the configuration mode */ + writel(0, csi2tx->base + CSI2TX_CONFIG_REG); + + return 0; +} + +static void csi2tx_stop(struct csi2tx_priv *csi2tx) +{ + writel(CSI2TX_CONFIG_CFG_REQ | CSI2TX_CONFIG_SRST_REQ, + csi2tx->base + CSI2TX_CONFIG_REG); +} + +static int csi2tx_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct csi2tx_priv *csi2tx = v4l2_subdev_to_csi2tx(subdev); + int ret = 0; + + mutex_lock(&csi2tx->lock); + + if (enable) { + /* + * If we're not the first users, there's no need to + * enable the whole controller. + */ + if (!csi2tx->count) { + ret = csi2tx_start(csi2tx); + if (ret) + goto out; + } + + csi2tx->count++; + } else { + csi2tx->count--; + + /* + * Let the last user turn off the lights. + */ + if (!csi2tx->count) + csi2tx_stop(csi2tx); + } + +out: + mutex_unlock(&csi2tx->lock); + return ret; +} + +static const struct v4l2_subdev_video_ops csi2tx_video_ops = { + .s_stream = csi2tx_s_stream, +}; + +static const struct v4l2_subdev_ops csi2tx_subdev_ops = { + .pad = &csi2tx_pad_ops, + .video = &csi2tx_video_ops, +}; + +static int csi2tx_get_resources(struct csi2tx_priv *csi2tx, + struct platform_device *pdev) +{ + struct resource *res; + unsigned int i; + u32 dev_cfg; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + csi2tx->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(csi2tx->base)) + return PTR_ERR(csi2tx->base); + + csi2tx->p_clk = devm_clk_get(&pdev->dev, "p_clk"); + if (IS_ERR(csi2tx->p_clk)) { + dev_err(&pdev->dev, "Couldn't get p_clk\n"); + return PTR_ERR(csi2tx->p_clk); + } + + csi2tx->esc_clk = devm_clk_get(&pdev->dev, "esc_clk"); + if (IS_ERR(csi2tx->esc_clk)) { + dev_err(&pdev->dev, "Couldn't get the esc_clk\n"); + return PTR_ERR(csi2tx->esc_clk); + } + + clk_prepare_enable(csi2tx->p_clk); + dev_cfg = readl(csi2tx->base + CSI2TX_DEVICE_CONFIG_REG); + clk_disable_unprepare(csi2tx->p_clk); + + csi2tx->max_lanes = dev_cfg & CSI2TX_DEVICE_CONFIG_LANES_MASK; + if (csi2tx->max_lanes > CSI2TX_LANES_MAX) { + dev_err(&pdev->dev, "Invalid number of lanes: %u\n", + csi2tx->max_lanes); + return -EINVAL; + } + + csi2tx->max_streams = (dev_cfg & CSI2TX_DEVICE_CONFIG_STREAMS_MASK) >> 4; + if (csi2tx->max_streams > CSI2TX_STREAMS_MAX) { + dev_err(&pdev->dev, "Invalid number of streams: %u\n", + csi2tx->max_streams); + return -EINVAL; + } + + csi2tx->has_internal_dphy = !!(dev_cfg & CSI2TX_DEVICE_CONFIG_HAS_DPHY); + + for (i = 0; i < csi2tx->max_streams; i++) { + char clk_name[16]; + + snprintf(clk_name, sizeof(clk_name), "pixel_if%u_clk", i); + csi2tx->pixel_clk[i] = devm_clk_get(&pdev->dev, clk_name); + if (IS_ERR(csi2tx->pixel_clk[i])) { + dev_err(&pdev->dev, "Couldn't get clock %s\n", + clk_name); + return PTR_ERR(csi2tx->pixel_clk[i]); + } + } + + return 0; +} + +static int csi2tx_check_lanes(struct csi2tx_priv *csi2tx) +{ + struct v4l2_fwnode_endpoint v4l2_ep; + struct device_node *ep; + int ret; + + ep = of_graph_get_endpoint_by_regs(csi2tx->dev->of_node, 0, 0); + if (!ep) + return -EINVAL; + + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep); + if (ret) { + dev_err(csi2tx->dev, "Could not parse v4l2 endpoint\n"); + goto out; + } + + if (v4l2_ep.bus_type != V4L2_MBUS_CSI2) { + dev_err(csi2tx->dev, "Unsupported media bus type: 0x%x\n", + v4l2_ep.bus_type); + ret = -EINVAL; + goto out; + } + + csi2tx->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; + if (csi2tx->num_lanes > csi2tx->max_lanes) { + dev_err(csi2tx->dev, + "Current configuration uses more lanes than supported\n"); + ret = -EINVAL; + goto out; + } + + memcpy(csi2tx->lanes, v4l2_ep.bus.mipi_csi2.data_lanes, + sizeof(csi2tx->lanes)); + +out: + of_node_put(ep); + return ret; +} + +static int csi2tx_probe(struct platform_device *pdev) +{ + struct csi2tx_priv *csi2tx; + unsigned int i; + int ret; + + csi2tx = kzalloc(sizeof(*csi2tx), GFP_KERNEL); + if (!csi2tx) + return -ENOMEM; + platform_set_drvdata(pdev, csi2tx); + mutex_init(&csi2tx->lock); + csi2tx->dev = &pdev->dev; + + ret = csi2tx_get_resources(csi2tx, pdev); + if (ret) + goto err_free_priv; + + v4l2_subdev_init(&csi2tx->subdev, &csi2tx_subdev_ops); + csi2tx->subdev.owner = THIS_MODULE; + csi2tx->subdev.dev = &pdev->dev; + csi2tx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(csi2tx->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.%s", + KBUILD_MODNAME, dev_name(&pdev->dev)); + + ret = csi2tx_check_lanes(csi2tx); + if (ret) + goto err_free_priv; + + /* Create our media pads */ + csi2tx->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + csi2tx->pads[CSI2TX_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++) + csi2tx->pads[i].flags = MEDIA_PAD_FL_SINK; + + /* + * Only the input pads are considered to have a format at the + * moment. The CSI link can multiplex various streams with + * different formats, and we can't expose this in v4l2 right + * now. + */ + for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++) + csi2tx->pad_fmts[i] = fmt_default; + + ret = media_entity_pads_init(&csi2tx->subdev.entity, CSI2TX_PAD_MAX, + csi2tx->pads); + if (ret) + goto err_free_priv; + + ret = v4l2_async_register_subdev(&csi2tx->subdev); + if (ret < 0) + goto err_free_priv; + + dev_info(&pdev->dev, + "Probed CSI2TX with %u/%u lanes, %u streams, %s D-PHY\n", + csi2tx->num_lanes, csi2tx->max_lanes, csi2tx->max_streams, + csi2tx->has_internal_dphy ? "internal" : "no"); + + return 0; + +err_free_priv: + kfree(csi2tx); + return ret; +} + +static int csi2tx_remove(struct platform_device *pdev) +{ + struct csi2tx_priv *csi2tx = platform_get_drvdata(pdev); + + v4l2_async_unregister_subdev(&csi2tx->subdev); + kfree(csi2tx); + + return 0; +} + +static const struct of_device_id csi2tx_of_table[] = { + { .compatible = "cdns,csi2tx" }, + { }, +}; +MODULE_DEVICE_TABLE(of, csi2tx_of_table); + +static struct platform_driver csi2tx_driver = { + .probe = csi2tx_probe, + .remove = csi2tx_remove, + + .driver = { + .name = "cdns-csi2tx", + .of_match_table = csi2tx_of_table, + }, +}; +module_platform_driver(csi2tx_driver); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>"); +MODULE_DESCRIPTION("Cadence CSI2-TX controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/cec-gpio/cec-gpio.c b/drivers/media/platform/cec-gpio/cec-gpio.c index f1f28cf5c751..69f8242209c2 100644 --- a/drivers/media/platform/cec-gpio/cec-gpio.c +++ b/drivers/media/platform/cec-gpio/cec-gpio.c @@ -158,7 +158,7 @@ static int cec_gpio_probe(struct platform_device *pdev) cec->dev = dev; - cec->cec_gpio = devm_gpiod_get(dev, "cec", GPIOD_IN); + cec->cec_gpio = devm_gpiod_get(dev, "cec", GPIOD_OUT_HIGH_OPEN_DRAIN); if (IS_ERR(cec->cec_gpio)) return PTR_ERR(cec->cec_gpio); cec->cec_irq = gpiod_to_irq(cec->cec_gpio); diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 04e35d70ce2e..c7631e117dd3 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -779,16 +779,27 @@ static int coda_s_fmt_vid_cap(struct file *file, void *priv, r.width = q_data_src->width; r.height = q_data_src->height; - return coda_s_fmt(ctx, f, &r); + ret = coda_s_fmt(ctx, f, &r); + if (ret) + return ret; + + if (ctx->inst_type != CODA_INST_ENCODER) + return 0; + + ctx->colorspace = f->fmt.pix.colorspace; + ctx->xfer_func = f->fmt.pix.xfer_func; + ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc; + ctx->quantization = f->fmt.pix.quantization; + + return 0; } static int coda_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { struct coda_ctx *ctx = fh_to_ctx(priv); - struct coda_q_data *q_data_src; struct v4l2_format f_cap; - struct v4l2_rect r; + struct vb2_queue *dst_vq; int ret; ret = coda_try_fmt_vid_out(file, priv, f); @@ -799,28 +810,34 @@ static int coda_s_fmt_vid_out(struct file *file, void *priv, if (ret) return ret; + if (ctx->inst_type != CODA_INST_DECODER) + return 0; + ctx->colorspace = f->fmt.pix.colorspace; ctx->xfer_func = f->fmt.pix.xfer_func; ctx->ycbcr_enc = f->fmt.pix.ycbcr_enc; ctx->quantization = f->fmt.pix.quantization; + dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (!dst_vq) + return -EINVAL; + + /* + * Setting the capture queue format is not possible while the capture + * queue is still busy. This is not an error, but the user will have to + * make sure themselves that the capture format is set correctly before + * starting the output queue again. + */ + if (vb2_is_busy(dst_vq)) + return 0; + memset(&f_cap, 0, sizeof(f_cap)); f_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; coda_g_fmt(file, priv, &f_cap); f_cap.fmt.pix.width = f->fmt.pix.width; f_cap.fmt.pix.height = f->fmt.pix.height; - ret = coda_try_fmt_vid_cap(file, priv, &f_cap); - if (ret) - return ret; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); - r.left = 0; - r.top = 0; - r.width = q_data_src->width; - r.height = q_data_src->height; - - return coda_s_fmt(ctx, &f_cap, &r); + return coda_s_fmt_vid_cap(file, priv, &f_cap); } static int coda_reqbufs(struct file *file, void *priv, diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig index 55982e681d77..06b5e581f25f 100644 --- a/drivers/media/platform/davinci/Kconfig +++ b/drivers/media/platform/davinci/Kconfig @@ -2,7 +2,6 @@ config VIDEO_DAVINCI_VPIF_DISPLAY tristate "TI DaVinci VPIF V4L2-Display driver" depends on VIDEO_V4L2 depends on ARCH_DAVINCI || COMPILE_TEST - depends on HAS_DMA depends on I2C select VIDEOBUF2_DMA_CONTIG select VIDEO_ADV7343 if MEDIA_SUBDRV_AUTOSELECT @@ -19,7 +18,6 @@ config VIDEO_DAVINCI_VPIF_CAPTURE tristate "TI DaVinci VPIF video capture driver" depends on VIDEO_V4L2 depends on ARCH_DAVINCI || COMPILE_TEST - depends on HAS_DMA depends on I2C select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE @@ -35,7 +33,6 @@ config VIDEO_DM6446_CCDC tristate "TI DM6446 CCDC video capture driver" depends on VIDEO_V4L2 depends on ARCH_DAVINCI || COMPILE_TEST - depends on HAS_DMA depends on I2C select VIDEOBUF_DMA_CONTIG help @@ -52,7 +49,6 @@ config VIDEO_DM355_CCDC tristate "TI DM355 CCDC video capture driver" depends on VIDEO_V4L2 depends on ARCH_DAVINCI || COMPILE_TEST - depends on HAS_DMA depends on I2C select VIDEOBUF_DMA_CONTIG help @@ -67,8 +63,8 @@ config VIDEO_DM355_CCDC config VIDEO_DM365_ISIF tristate "TI DM365 ISIF video capture driver" - depends on VIDEO_V4L2 && ARCH_DAVINCI - depends on HAS_DMA + depends on VIDEO_V4L2 + depends on ARCH_DAVINCI || COMPILE_TEST depends on I2C select VIDEOBUF_DMA_CONTIG help @@ -81,8 +77,8 @@ config VIDEO_DM365_ISIF config VIDEO_DAVINCI_VPBE_DISPLAY tristate "TI DaVinci VPBE V4L2-Display driver" - depends on VIDEO_V4L2 && ARCH_DAVINCI - depends on HAS_DMA + depends on VIDEO_V4L2 + depends on ARCH_DAVINCI || COMPILE_TEST depends on I2C select VIDEOBUF2_DMA_CONTIG help diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c index d5ff58494c1e..f924e76e2fbf 100644 --- a/drivers/media/platform/davinci/isif.c +++ b/drivers/media/platform/davinci/isif.c @@ -31,8 +31,6 @@ #include <linux/err.h> #include <linux/module.h> -#include <mach/mux.h> - #include <media/davinci/isif.h> #include <media/davinci/vpss.h> @@ -1029,7 +1027,7 @@ static int isif_probe(struct platform_device *pdev) { void (*setup_pinmux)(void); struct resource *res; - void *__iomem addr; + void __iomem *addr; int status = 0, i; /* Platform data holds setup_pinmux function ptr */ diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c index 7f6462562579..18c035ef84cf 100644 --- a/drivers/media/platform/davinci/vpbe.c +++ b/drivers/media/platform/davinci/vpbe.c @@ -51,7 +51,7 @@ MODULE_AUTHOR("Texas Instruments"); /** * vpbe_current_encoder_info - Get config info for current encoder - * @vpbe_dev - vpbe device ptr + * @vpbe_dev: vpbe device ptr * * Return ptr to current encoder config info */ @@ -68,8 +68,8 @@ vpbe_current_encoder_info(struct vpbe_device *vpbe_dev) /** * vpbe_find_encoder_sd_index - Given a name find encoder sd index * - * @vpbe_config - ptr to vpbe cfg - * @output_index - index used by application + * @cfg: ptr to vpbe cfg + * @index: index used by application * * Return sd index of the encoder */ @@ -94,8 +94,8 @@ static int vpbe_find_encoder_sd_index(struct vpbe_config *cfg, /** * vpbe_g_cropcap - Get crop capabilities of the display - * @vpbe_dev - vpbe device ptr - * @cropcap - cropcap is a ptr to struct v4l2_cropcap + * @vpbe_dev: vpbe device ptr + * @cropcap: cropcap is a ptr to struct v4l2_cropcap * * Update the crop capabilities in crop cap for current * mode @@ -116,8 +116,8 @@ static int vpbe_g_cropcap(struct vpbe_device *vpbe_dev, /** * vpbe_enum_outputs - enumerate outputs - * @vpbe_dev - vpbe device ptr - * @output - ptr to v4l2_output structure + * @vpbe_dev: vpbe device ptr + * @output: ptr to v4l2_output structure * * Enumerates the outputs available at the vpbe display * returns the status, -EINVAL if end of output list @@ -212,8 +212,8 @@ static int vpbe_get_std_info_by_name(struct vpbe_device *vpbe_dev, /** * vpbe_set_output - Set output - * @vpbe_dev - vpbe device ptr - * @index - index of output + * @vpbe_dev: vpbe device ptr + * @index: index of output * * Set vpbe output to the output specified by the index */ @@ -308,7 +308,7 @@ static int vpbe_set_default_output(struct vpbe_device *vpbe_dev) /** * vpbe_get_output - Get output - * @vpbe_dev - vpbe device ptr + * @vpbe_dev: vpbe device ptr * * return current vpbe output to the the index */ @@ -317,7 +317,7 @@ static unsigned int vpbe_get_output(struct vpbe_device *vpbe_dev) return vpbe_dev->current_out_index; } -/** +/* * vpbe_s_dv_timings - Set the given preset timings in the encoder * * Sets the timings if supported by the current encoder. Return the status. @@ -369,7 +369,7 @@ static int vpbe_s_dv_timings(struct vpbe_device *vpbe_dev, return ret; } -/** +/* * vpbe_g_dv_timings - Get the timings in the current encoder * * Get the timings in the current encoder. Return the status. 0 - success @@ -394,7 +394,7 @@ static int vpbe_g_dv_timings(struct vpbe_device *vpbe_dev, return -EINVAL; } -/** +/* * vpbe_enum_dv_timings - Enumerate the dv timings in the current encoder * * Get the timings in the current encoder. Return the status. 0 - success @@ -426,7 +426,7 @@ static int vpbe_enum_dv_timings(struct vpbe_device *vpbe_dev, return 0; } -/** +/* * vpbe_s_std - Set the given standard in the encoder * * Sets the standard if supported by the current encoder. Return the status. @@ -465,7 +465,7 @@ static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id std_id) return ret; } -/** +/* * vpbe_g_std - Get the standard in the current encoder * * Get the standard in the current encoder. Return the status. 0 - success @@ -488,7 +488,7 @@ static int vpbe_g_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id) return -EINVAL; } -/** +/* * vpbe_set_mode - Set mode in the current encoder using mode info * * Use the mode string to decide what timings to set in the encoder @@ -572,7 +572,8 @@ static int platform_device_get(struct device *dev, void *data) /** * vpbe_initialize() - Initialize the vpbe display controller - * @vpbe_dev - vpbe device ptr + * @dev: Master and slave device ptr + * @vpbe_dev: vpbe device ptr * * Master frame buffer device drivers calls this to initialize vpbe * display controller. This will then registers v4l2 device and the sub @@ -769,7 +770,8 @@ fail_mutex_unlock: /** * vpbe_deinitialize() - de-initialize the vpbe display controller - * @dev - Master and slave device ptr + * @dev: Master and slave device ptr + * @vpbe_dev: vpbe device ptr * * vpbe_master and slave frame buffer devices calls this to de-initialize * the display controller. It is called when master and slave device diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 6aabd21fe69f..b0eb3d899eb4 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -26,7 +26,10 @@ #include <linux/slab.h> #include <asm/pgtable.h> + +#ifdef CONFIG_ARCH_DAVINCI #include <mach/cputype.h> +#endif #include <media/v4l2-dev.h> #include <media/v4l2-common.h> @@ -53,8 +56,7 @@ static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev, static int venc_is_second_field(struct vpbe_display *disp_dev) { struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; - int ret; - int val; + int ret, val; ret = v4l2_subdev_call(vpbe_dev->venc, core, @@ -64,6 +66,7 @@ static int venc_is_second_field(struct vpbe_display *disp_dev) if (ret < 0) { v4l2_err(&vpbe_dev->v4l2_dev, "Error in getting Field ID 0\n"); + return 1; } return val; } @@ -282,7 +285,7 @@ static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count) struct osd_state *osd_device = layer->disp_dev->osd_device; int ret; - osd_device->ops.disable_layer(osd_device, layer->layer_info.id); + osd_device->ops.disable_layer(osd_device, layer->layer_info.id); /* Get the next frame from the buffer queue */ layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next, @@ -564,7 +567,7 @@ static void vpbe_disp_check_window_params(struct vpbe_display *disp_dev, } -/** +/* * vpbe_try_format() * If user application provides width and height, and have bytesperline set * to zero, driver calculates bytesperline and sizeimage based on hardware @@ -929,7 +932,7 @@ static int vpbe_display_try_fmt(struct file *file, void *priv, } -/** +/* * vpbe_display_s_std - Set the given standard in the encoder * * Sets the standard if supported by the current encoder. Return the status. @@ -961,7 +964,7 @@ static int vpbe_display_s_std(struct file *file, void *priv, return 0; } -/** +/* * vpbe_display_g_std - Get the standard in the current encoder * * Get the standard in the current encoder. Return the status. 0 - success @@ -984,7 +987,7 @@ static int vpbe_display_g_std(struct file *file, void *priv, return -EINVAL; } -/** +/* * vpbe_display_enum_output - enumerate outputs * * Enumerates the outputs available at the vpbe display @@ -1013,7 +1016,7 @@ static int vpbe_display_enum_output(struct file *file, void *priv, return 0; } -/** +/* * vpbe_display_s_output - Set output to * the output specified by the index */ @@ -1042,7 +1045,7 @@ static int vpbe_display_s_output(struct file *file, void *priv, return 0; } -/** +/* * vpbe_display_g_output - Get output from subdevice * for a given by the index */ @@ -1059,7 +1062,7 @@ static int vpbe_display_g_output(struct file *file, void *priv, return 0; } -/** +/* * vpbe_display_enum_dv_timings - Enumerate the dv timings * * enum the timings in the current encoder. Return the status. 0 - success @@ -1089,7 +1092,7 @@ vpbe_display_enum_dv_timings(struct file *file, void *priv, return 0; } -/** +/* * vpbe_display_s_dv_timings - Set the dv timings * * Set the timings in the current encoder. Return the status. 0 - success @@ -1122,7 +1125,7 @@ vpbe_display_s_dv_timings(struct file *file, void *priv, return 0; } -/** +/* * vpbe_display_g_dv_timings - Set the dv timings * * Get the timings in the current encoder. Return the status. 0 - success @@ -1351,9 +1354,9 @@ static int register_device(struct vpbe_layer *vpbe_display_layer, v4l2_info(&disp_dev->vpbe_dev->v4l2_dev, "Trying to register VPBE display device.\n"); v4l2_info(&disp_dev->vpbe_dev->v4l2_dev, - "layer=%x,layer->video_dev=%x\n", - (int)vpbe_display_layer, - (int)&vpbe_display_layer->video_dev); + "layer=%p,layer->video_dev=%p\n", + vpbe_display_layer, + &vpbe_display_layer->video_dev); vpbe_display_layer->video_dev.queue = &vpbe_display_layer->buffer_queue; err = video_register_device(&vpbe_display_layer->video_dev, diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c index 66449791c70c..7f610320426d 100644 --- a/drivers/media/platform/davinci/vpbe_osd.c +++ b/drivers/media/platform/davinci/vpbe_osd.c @@ -24,8 +24,10 @@ #include <linux/clk.h> #include <linux/slab.h> +#ifdef CONFIG_ARCH_DAVINCI #include <mach/cputype.h> #include <mach/hardware.h> +#endif #include <media/davinci/vpss.h> #include <media/v4l2-device.h> @@ -122,10 +124,10 @@ static inline u32 osd_modify(struct osd_state *sd, u32 mask, u32 val, /** * _osd_dm6446_vid0_pingpong() - field inversion fix for DM6446 - * @sd - ptr to struct osd_state - * @field_inversion - inversion flag - * @fb_base_phys - frame buffer address - * @lconfig - ptr to layer config + * @sd: ptr to struct osd_state + * @field_inversion: inversion flag + * @fb_base_phys: frame buffer address + * @lconfig: ptr to layer config * * This routine implements a workaround for the field signal inversion silicon * erratum described in Advisory 1.3.8 for the DM6446. The fb_base_phys and @@ -782,9 +784,9 @@ static void osd_get_layer_config(struct osd_state *sd, enum osd_layer layer, /** * try_layer_config() - Try a specific configuration for the layer - * @sd - ptr to struct osd_state - * @layer - layer to configure - * @lconfig - layer configuration to try + * @sd: ptr to struct osd_state + * @layer: layer to configure + * @lconfig: layer configuration to try * * If the requested lconfig is completely rejected and the value of lconfig on * exit is the current lconfig, then try_layer_config() returns 1. Otherwise, @@ -844,9 +846,10 @@ static int try_layer_config(struct osd_state *sd, enum osd_layer layer, /* DM6446: */ /* only one OSD window at a time can use RGB pixel formats */ - if ((osd->vpbe_type == VPBE_VERSION_1) && - is_osd_win(layer) && is_rgb_pixfmt(lconfig->pixfmt)) { + if ((osd->vpbe_type == VPBE_VERSION_1) && + is_osd_win(layer) && is_rgb_pixfmt(lconfig->pixfmt)) { enum osd_pix_format pixfmt; + if (layer == WIN_OSD0) pixfmt = osd->win[WIN_OSD1].lconfig.pixfmt; else diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c index 3a4e78595149..ba157827192c 100644 --- a/drivers/media/platform/davinci/vpbe_venc.c +++ b/drivers/media/platform/davinci/vpbe_venc.c @@ -21,8 +21,11 @@ #include <linux/videodev2.h> #include <linux/slab.h> +#ifdef CONFIG_ARCH_DAVINCI #include <mach/hardware.h> #include <mach/mux.h> +#endif + #include <linux/platform_data/i2c-davinci.h> #include <linux/io.h> @@ -224,7 +227,6 @@ venc_enable_vpss_clock(int venc_type, */ static int venc_set_ntsc(struct v4l2_subdev *sd) { - u32 val; struct venc_state *venc = to_state(sd); struct venc_platform_data *pdata = venc->pdata; @@ -241,7 +243,7 @@ static int venc_set_ntsc(struct v4l2_subdev *sd) if (venc->venc_type == VPBE_VERSION_3) { venc_write(sd, VENC_CLKCTL, 0x01); venc_write(sd, VENC_VIDCTL, 0); - val = vdaccfg_write(sd, VDAC_CONFIG_SD_V3); + vdaccfg_write(sd, VDAC_CONFIG_SD_V3); } else if (venc->venc_type == VPBE_VERSION_2) { venc_write(sd, VENC_CLKCTL, 0x01); venc_write(sd, VENC_VIDCTL, 0); @@ -604,10 +606,9 @@ static int venc_device_get(struct device *dev, void *data) struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev, const char *venc_name) { - struct venc_state *venc; - int err; + struct venc_state *venc = NULL; - err = bus_for_each_dev(&platform_bus_type, NULL, &venc, + bus_for_each_dev(&platform_bus_type, NULL, &venc, venc_device_get); if (venc == NULL) return NULL; diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index 6f44abf7fa31..8613358ed245 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -1509,7 +1509,7 @@ static int vpfe_streamon(struct file *file, void *priv, unlock_out: mutex_unlock(&vpfe_dev->lock); streamoff: - ret = videobuf_streamoff(&vpfe_dev->buffer_queue); + videobuf_streamoff(&vpfe_dev->buffer_queue); return ret; } diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig index 7b2c49e5a592..c8e5ad8f8294 100644 --- a/drivers/media/platform/exynos4-is/Kconfig +++ b/drivers/media/platform/exynos4-is/Kconfig @@ -41,11 +41,10 @@ config VIDEO_S5P_MIPI_CSIS To compile this driver as a module, choose M here: the module will be called s5p-csis. -if SOC_EXYNOS4412 || SOC_EXYNOS5250 - config VIDEO_EXYNOS_FIMC_LITE tristate "EXYNOS FIMC-LITE camera interface driver" depends on I2C + depends on SOC_EXYNOS4412 || SOC_EXYNOS5250 || COMPILE_TEST depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select VIDEO_EXYNOS4_IS_COMMON @@ -55,7 +54,6 @@ config VIDEO_EXYNOS_FIMC_LITE To compile this driver as a module, choose M here: the module will be called exynos-fimc-lite. -endif config VIDEO_EXYNOS4_FIMC_IS tristate "EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver" diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c index f0acc550d065..16565a0b4bf1 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.c @@ -254,7 +254,7 @@ void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f) /* Maximum output pixel size */ cfg = readl(dev->regs + FLITE_REG_CIOCAN); cfg &= ~FLITE_REG_CIOCAN_MASK; - cfg = (f->f_height << 16) | f->f_width; + cfg |= (f->f_height << 16) | f->f_width; writel(cfg, dev->regs + FLITE_REG_CIOCAN); /* DMA offsets */ diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 200c47c69a75..e41510ce69a4 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -36,6 +36,12 @@ #define DRV_NAME "fsl_viu" #define VIU_VERSION "0.5.1" +/* Allow building this driver with COMPILE_TEST */ +#ifndef CONFIG_PPC +#define out_be32(v, a) iowrite32be(a, (void __iomem *)v) +#define in_be32(a) ioread32be((void __iomem *)a) +#endif + #define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ #define VIU_VID_MEM_LIMIT 4 /* Video memory limit, in Mb */ @@ -128,7 +134,7 @@ struct viu_dev { int dma_done; /* Hardware register area */ - struct viu_reg *vr; + struct viu_reg __iomem *vr; /* Interrupt vector */ int irq; @@ -229,7 +235,7 @@ enum status_config { static irqreturn_t viu_intr(int irq, void *dev_id); -struct viu_fmt *format_by_fourcc(int fourcc) +static struct viu_fmt *format_by_fourcc(int fourcc) { int i; @@ -242,9 +248,9 @@ struct viu_fmt *format_by_fourcc(int fourcc) return NULL; } -void viu_start_dma(struct viu_dev *dev) +static void viu_start_dma(struct viu_dev *dev) { - struct viu_reg *vr = dev->vr; + struct viu_reg __iomem *vr = dev->vr; dev->field = 0; @@ -253,9 +259,9 @@ void viu_start_dma(struct viu_dev *dev) out_be32(&vr->status_cfg, INT_FIELD_EN); } -void viu_stop_dma(struct viu_dev *dev) +static void viu_stop_dma(struct viu_dev *dev) { - struct viu_reg *vr = dev->vr; + struct viu_reg __iomem *vr = dev->vr; int cnt = 100; u32 status_cfg; @@ -290,7 +296,7 @@ static int restart_video_queue(struct viu_dmaqueue *vidq) { struct viu_buf *buf, *prev; - dprintk(1, "%s vidq=0x%08lx\n", __func__, (unsigned long)vidq); + dprintk(1, "%s vidq=%p\n", __func__, vidq); if (!list_empty(&vidq->active)) { buf = list_entry(vidq->active.next, struct viu_buf, vb.queue); dprintk(2, "restart_queue [%p/%d]: restart dma\n", @@ -395,7 +401,7 @@ static void free_buffer(struct videobuf_queue *vq, struct viu_buf *buf) inline int buffer_activate(struct viu_dev *dev, struct viu_buf *buf) { - struct viu_reg *vr = dev->vr; + struct viu_reg __iomem *vr = dev->vr; int bpp; /* setup the DMA base address */ @@ -497,8 +503,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) struct viu_buf *prev; if (!list_empty(&vidq->queued)) { - dprintk(1, "adding vb queue=0x%08lx\n", - (unsigned long)&buf->vb.queue); + dprintk(1, "adding vb queue=%p\n", &buf->vb.queue); dprintk(1, "vidq pointer 0x%p, queued 0x%p\n", vidq, &vidq->queued); dprintk(1, "dev %p, queued: self %p, next %p, head %p\n", @@ -509,8 +514,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, buf->vb.i); } else if (list_empty(&vidq->active)) { - dprintk(1, "adding vb active=0x%08lx\n", - (unsigned long)&buf->vb.queue); + dprintk(1, "adding vb active=%p\n", &buf->vb.queue); list_add_tail(&buf->vb.queue, &vidq->active); buf->vb.state = VIDEOBUF_ACTIVE; mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); @@ -519,8 +523,7 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) buffer_activate(dev, buf); } else { - dprintk(1, "adding vb queue2=0x%08lx\n", - (unsigned long)&buf->vb.queue); + dprintk(1, "adding vb queue2=%p\n", &buf->vb.queue); prev = list_entry(vidq->active.prev, struct viu_buf, vb.queue); if (prev->vb.width == buf->vb.width && prev->vb.height == buf->vb.height && @@ -703,10 +706,8 @@ static int verify_preview(struct viu_dev *dev, struct v4l2_window *win) return 0; } -inline void viu_activate_overlay(struct viu_reg *viu_reg) +inline void viu_activate_overlay(struct viu_reg __iomem *vr) { - struct viu_reg *vr = viu_reg; - out_be32(&vr->field_base_addr, reg_val.field_base_addr); out_be32(&vr->dma_inc, reg_val.dma_inc); out_be32(&vr->picture_count, reg_val.picture_count); @@ -749,7 +750,7 @@ static int viu_setup_preview(struct viu_dev *dev, struct viu_fh *fh) reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN; /* setup the base address of the overlay buffer */ - reg_val.field_base_addr = (u32)dev->ovbuf.base; + reg_val.field_base_addr = (u32)(long)dev->ovbuf.base; return 0; } @@ -802,7 +803,7 @@ static int vidioc_overlay(struct file *file, void *priv, unsigned int on) return 0; } -int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg) +static int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg) { struct viu_fh *fh = priv; struct viu_dev *dev = fh->dev; @@ -813,7 +814,7 @@ int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg) return 0; } -int vidioc_s_fbuf(struct file *file, void *priv, const struct v4l2_framebuffer *arg) +static int vidioc_s_fbuf(struct file *file, void *priv, const struct v4l2_framebuffer *arg) { struct viu_fh *fh = priv; struct viu_dev *dev = fh->dev; @@ -985,10 +986,8 @@ inline void viu_activate_next_buf(struct viu_dev *dev, } } -inline void viu_default_settings(struct viu_reg *viu_reg) +inline void viu_default_settings(struct viu_reg __iomem *vr) { - struct viu_reg *vr = viu_reg; - out_be32(&vr->luminance, 0x9512A254); out_be32(&vr->chroma_r, 0x03310000); out_be32(&vr->chroma_g, 0x06600F38); @@ -1001,7 +1000,7 @@ inline void viu_default_settings(struct viu_reg *viu_reg) static void viu_overlay_intr(struct viu_dev *dev, u32 status) { - struct viu_reg *vr = dev->vr; + struct viu_reg __iomem *vr = dev->vr; if (status & INT_DMA_END_STATUS) dev->dma_done = 1; @@ -1032,7 +1031,7 @@ static void viu_overlay_intr(struct viu_dev *dev, u32 status) static void viu_capture_intr(struct viu_dev *dev, u32 status) { struct viu_dmaqueue *vidq = &dev->vidq; - struct viu_reg *vr = dev->vr; + struct viu_reg __iomem *vr = dev->vr; struct viu_buf *buf; int field_num; int need_two; @@ -1104,7 +1103,7 @@ static void viu_capture_intr(struct viu_dev *dev, u32 status) static irqreturn_t viu_intr(int irq, void *dev_id) { struct viu_dev *dev = (struct viu_dev *)dev_id; - struct viu_reg *vr = dev->vr; + struct viu_reg __iomem *vr = dev->vr; u32 status; u32 error; @@ -1169,7 +1168,7 @@ static int viu_open(struct file *file) struct video_device *vdev = video_devdata(file); struct viu_dev *dev = video_get_drvdata(vdev); struct viu_fh *fh; - struct viu_reg *vr; + struct viu_reg __iomem *vr; int minor = vdev->minor; u32 status_cfg; @@ -1210,9 +1209,7 @@ static int viu_open(struct file *file) dev->crop_current.width = fh->width; dev->crop_current.height = fh->height; - dprintk(1, "Open: fh=0x%08lx, dev=0x%08lx, dev->vidq=0x%08lx\n", - (unsigned long)fh, (unsigned long)dev, - (unsigned long)&dev->vidq); + dprintk(1, "Open: fh=%p, dev=%p, dev->vidq=%p\n", fh, dev, &dev->vidq); dprintk(1, "Open: list_empty queued=%d\n", list_empty(&dev->vidq.queued)); dprintk(1, "Open: list_empty active=%d\n", @@ -1305,7 +1302,7 @@ static int viu_release(struct file *file) return 0; } -void viu_reset(struct viu_reg *reg) +static void viu_reset(struct viu_reg __iomem *reg) { out_be32(®->status_cfg, 0); out_be32(®->luminance, 0x9512a254); @@ -1325,7 +1322,7 @@ static int viu_mmap(struct file *file, struct vm_area_struct *vma) struct viu_dev *dev = fh->dev; int ret; - dprintk(1, "mmap called, vma=0x%08lx\n", (unsigned long)vma); + dprintk(1, "mmap called, vma=%p\n", vma); if (mutex_lock_interruptible(&dev->lock)) return -ERESTARTSYS; @@ -1407,7 +1404,7 @@ static int viu_of_probe(struct platform_device *op) } viu_irq = irq_of_parse_and_map(op->dev.of_node, 0); - if (viu_irq == NO_IRQ) { + if (!viu_irq) { dev_err(&op->dev, "Error while mapping the irq\n"); return -EINVAL; } diff --git a/drivers/media/platform/marvell-ccic/Kconfig b/drivers/media/platform/marvell-ccic/Kconfig index 4bf5bd1e90d6..cf12e077203a 100644 --- a/drivers/media/platform/marvell-ccic/Kconfig +++ b/drivers/media/platform/marvell-ccic/Kconfig @@ -1,7 +1,6 @@ config VIDEO_CAFE_CCIC tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support" depends on PCI && I2C && VIDEO_V4L2 - depends on HAS_DMA select VIDEO_OV7670 select VIDEOBUF2_VMALLOC select VIDEOBUF2_DMA_CONTIG @@ -13,10 +12,12 @@ config VIDEO_CAFE_CCIC config VIDEO_MMP_CAMERA tristate "Marvell Armada 610 integrated camera controller support" - depends on ARCH_MMP && I2C && VIDEO_V4L2 - depends on HAS_DMA && BROKEN + depends on I2C && VIDEO_V4L2 + depends on ARCH_MMP || COMPILE_TEST select VIDEO_OV7670 select I2C_GPIO + select VIDEOBUF2_VMALLOC + select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_DMA_SG ---help--- This is a Video4Linux2 driver for the integrated camera diff --git a/drivers/media/platform/marvell-ccic/Makefile b/drivers/media/platform/marvell-ccic/Makefile index 05a792c579a2..b3a4d0cdccb8 100644 --- a/drivers/media/platform/marvell-ccic/Makefile +++ b/drivers/media/platform/marvell-ccic/Makefile @@ -1,6 +1,5 @@ -obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o -cafe_ccic-y := cafe-driver.o mcam-core.o - -obj-$(CONFIG_VIDEO_MMP_CAMERA) += mmp_camera.o -mmp_camera-y := mmp-driver.o mcam-core.o +obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o mcam-core.o +cafe_ccic-y := cafe-driver.o +obj-$(CONFIG_VIDEO_MMP_CAMERA) += mmp_camera.o mcam-core.o +mmp_camera-y := mmp-driver.o diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 80670eeee142..dfdbd4354b74 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1720,6 +1720,7 @@ int mccic_irq(struct mcam_camera *cam, unsigned int irqs) } return handled; } +EXPORT_SYMBOL_GPL(mccic_irq); /* ---------------------------------------------------------------------- */ /* @@ -1830,7 +1831,7 @@ out_unregister: v4l2_device_unregister(&cam->v4l2_dev); return ret; } - +EXPORT_SYMBOL_GPL(mccic_register); void mccic_shutdown(struct mcam_camera *cam) { @@ -1850,6 +1851,7 @@ void mccic_shutdown(struct mcam_camera *cam) v4l2_ctrl_handler_free(&cam->ctrl_handler); v4l2_device_unregister(&cam->v4l2_dev); } +EXPORT_SYMBOL_GPL(mccic_shutdown); /* * Power management @@ -1868,6 +1870,7 @@ void mccic_suspend(struct mcam_camera *cam) } mutex_unlock(&cam->s_mutex); } +EXPORT_SYMBOL_GPL(mccic_suspend); int mccic_resume(struct mcam_camera *cam) { @@ -1898,4 +1901,8 @@ int mccic_resume(struct mcam_camera *cam) } return ret; } +EXPORT_SYMBOL_GPL(mccic_resume); #endif /* CONFIG_PM */ + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index 816f4b6a7b8e..3cf300072348 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -37,7 +37,7 @@ MODULE_LICENSE("GPL"); static char *mcam_clks[] = {"CCICAXICLK", "CCICFUNCLK", "CCICPHYCLK"}; struct mmp_camera { - void *power_regs; + void __iomem *power_regs; struct platform_device *pdev; struct mcam_camera mcam; struct list_head devlist; @@ -183,7 +183,7 @@ static void mmpcam_power_down(struct mcam_camera *mcam) mcam_clk_disable(mcam); } -void mcam_ctlr_reset(struct mcam_camera *mcam) +static void mcam_ctlr_reset(struct mcam_camera *mcam) { unsigned long val; struct mmp_camera *cam = mcam_to_cam(mcam); @@ -214,7 +214,7 @@ void mcam_ctlr_reset(struct mcam_camera *mcam) * CSI2_DPHY3 and CSI2_DPHY6 can be set with a default value * or be calculated dynamically */ -void mmpcam_calc_dphy(struct mcam_camera *mcam) +static void mmpcam_calc_dphy(struct mcam_camera *mcam) { struct mmp_camera *cam = mcam_to_cam(mcam); struct mmp_camera_platform_data *pdata = cam->pdev->dev.platform_data; diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c index af17aaa21f58..328e8f650d9b 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c @@ -1084,10 +1084,7 @@ static int mtk_jpeg_clk_init(struct mtk_jpeg_dev *jpeg) return PTR_ERR(jpeg->clk_jdec); jpeg->clk_jdec_smi = devm_clk_get(jpeg->dev, "jpgdec-smi"); - if (IS_ERR(jpeg->clk_jdec_smi)) - return PTR_ERR(jpeg->clk_jdec_smi); - - return 0; + return PTR_ERR_OR_ZERO(jpeg->clk_jdec_smi); } static int mtk_jpeg_probe(struct platform_device *pdev) diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig index e8e2db181a7a..d827b6c285a6 100644 --- a/drivers/media/platform/omap/Kconfig +++ b/drivers/media/platform/omap/Kconfig @@ -1,15 +1,16 @@ config VIDEO_OMAP2_VOUT_VRFB bool + default y + depends on VIDEO_OMAP2_VOUT && (OMAP2_VRFB || COMPILE_TEST) config VIDEO_OMAP2_VOUT tristate "OMAP2/OMAP3 V4L2-Display driver" depends on MMU - depends on ARCH_OMAP2 || ARCH_OMAP3 - depends on FB_OMAP2 + depends on FB_OMAP2 || (COMPILE_TEST && FB_OMAP2=n) + depends on ARCH_OMAP2 || ARCH_OMAP3 || COMPILE_TEST select VIDEOBUF_GEN select VIDEOBUF_DMA_CONTIG select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 - select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB select FRAME_VECTOR default n ---help--- diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index a795a9fae899..5700b7818621 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -198,7 +198,7 @@ static int omap_vout_try_format(struct v4l2_pix_format *pix) * omap_vout_get_userptr: Convert user space virtual address to physical * address. */ -static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp, +static int omap_vout_get_userptr(struct videobuf_buffer *vb, long virtp, u32 *physp) { struct frame_vector *vec; @@ -702,19 +702,18 @@ static int omap_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count, virt_addr = omap_vout_alloc_buffer(vout->buffer_size, &phy_addr); if (!virt_addr) { - if (ovid->rotation_type == VOUT_ROT_NONE) { + if (ovid->rotation_type == VOUT_ROT_NONE) break; - } else { - if (!is_rotation_enabled(vout)) - break; + + if (!is_rotation_enabled(vout)) + break; + /* Free the VRFB buffers if no space for V4L2 buffers */ for (j = i; j < *count; j++) { - omap_vout_free_buffer( - vout->smsshado_virt_addr[j], - vout->smsshado_size); + omap_vout_free_buffer(vout->smsshado_virt_addr[j], + vout->smsshado_size); vout->smsshado_virt_addr[j] = 0; vout->smsshado_phy_addr[j] = 0; - } } } vout->buf_virt_addr[i] = virt_addr; diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c index 1d8508237220..29e3f5da59c1 100644 --- a/drivers/media/platform/omap/omap_vout_vrfb.c +++ b/drivers/media/platform/omap/omap_vout_vrfb.c @@ -54,8 +54,8 @@ static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout, *count = 0; return -ENOMEM; } - memset((void *) vout->smsshado_virt_addr[i], 0, - vout->smsshado_size); + memset((void *)(long)vout->smsshado_virt_addr[i], 0, + vout->smsshado_size); } return 0; } diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 8eb000e3d8fd..f22cf351e3ee 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -61,7 +61,9 @@ #include <linux/sched.h> #include <linux/vmalloc.h> +#ifdef CONFIG_ARM_DMA_USE_IOMMU #include <asm/dma-iommu.h> +#endif #include <media/v4l2-common.h> #include <media/v4l2-fwnode.h> @@ -284,13 +286,6 @@ static const struct clk_ops isp_xclk_ops = { static const char *isp_xclk_parent_name = "cam_mclk"; -static const struct clk_init_data isp_xclk_init_data = { - .name = "cam_xclk", - .ops = &isp_xclk_ops, - .parent_names = &isp_xclk_parent_name, - .num_parents = 1, -}; - static struct clk *isp_xclk_src_get(struct of_phandle_args *clkspec, void *data) { unsigned int idx = clkspec->args[0]; @@ -1945,12 +1940,16 @@ error_csi2: static void isp_detach_iommu(struct isp_device *isp) { +#ifdef CONFIG_ARM_DMA_USE_IOMMU + arm_iommu_detach_device(isp->dev); arm_iommu_release_mapping(isp->mapping); isp->mapping = NULL; +#endif } static int isp_attach_iommu(struct isp_device *isp) { +#ifdef CONFIG_ARM_DMA_USE_IOMMU struct dma_iommu_mapping *mapping; int ret; @@ -1961,8 +1960,7 @@ static int isp_attach_iommu(struct isp_device *isp) mapping = arm_iommu_create_mapping(&platform_bus_type, SZ_1G, SZ_2G); if (IS_ERR(mapping)) { dev_err(isp->dev, "failed to create ARM IOMMU mapping\n"); - ret = PTR_ERR(mapping); - goto error; + return PTR_ERR(mapping); } isp->mapping = mapping; @@ -1977,8 +1975,12 @@ static int isp_attach_iommu(struct isp_device *isp) return 0; error: - isp_detach_iommu(isp); + arm_iommu_release_mapping(isp->mapping); + isp->mapping = NULL; return ret; +#else + return -ENODEV; +#endif } /* diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index b66276ab5765..77b73e27a274 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/omap3isp/ispccdc.c @@ -735,7 +735,7 @@ static int ccdc_config(struct isp_ccdc_device *ccdc, return -ENOMEM; if (copy_from_user(fpc_new.addr, - (__force void __user *)fpc.fpcaddr, + (__force void __user *)(long)fpc.fpcaddr, size)) { dma_free_coherent(isp->dev, size, fpc_new.addr, fpc_new.dma); diff --git a/drivers/media/platform/omap3isp/isph3a_aewb.c b/drivers/media/platform/omap3isp/isph3a_aewb.c index d44626f20ac6..3c82dea4d375 100644 --- a/drivers/media/platform/omap3isp/isph3a_aewb.c +++ b/drivers/media/platform/omap3isp/isph3a_aewb.c @@ -250,6 +250,8 @@ static long h3a_aewb_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) return omap3isp_stat_config(stat, arg); case VIDIOC_OMAP3ISP_STAT_REQ: return omap3isp_stat_request_statistics(stat, arg); + case VIDIOC_OMAP3ISP_STAT_REQ_TIME32: + return omap3isp_stat_request_statistics_time32(stat, arg); case VIDIOC_OMAP3ISP_STAT_EN: { unsigned long *en = arg; return omap3isp_stat_enable(stat, !!*en); diff --git a/drivers/media/platform/omap3isp/isph3a_af.c b/drivers/media/platform/omap3isp/isph3a_af.c index 99bd6cc21d86..4da25c84f0c6 100644 --- a/drivers/media/platform/omap3isp/isph3a_af.c +++ b/drivers/media/platform/omap3isp/isph3a_af.c @@ -314,6 +314,8 @@ static long h3a_af_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) return omap3isp_stat_config(stat, arg); case VIDIOC_OMAP3ISP_STAT_REQ: return omap3isp_stat_request_statistics(stat, arg); + case VIDIOC_OMAP3ISP_STAT_REQ_TIME32: + return omap3isp_stat_request_statistics_time32(stat, arg); case VIDIOC_OMAP3ISP_STAT_EN: { int *en = arg; return omap3isp_stat_enable(stat, !!*en); diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/omap3isp/isphist.c index a4ed5d140d48..d4be3d0e06f9 100644 --- a/drivers/media/platform/omap3isp/isphist.c +++ b/drivers/media/platform/omap3isp/isphist.c @@ -435,6 +435,8 @@ static long hist_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) return omap3isp_stat_config(stat, arg); case VIDIOC_OMAP3ISP_STAT_REQ: return omap3isp_stat_request_statistics(stat, arg); + case VIDIOC_OMAP3ISP_STAT_REQ_TIME32: + return omap3isp_stat_request_statistics_time32(stat, arg); case VIDIOC_OMAP3ISP_STAT_EN: { int *en = arg; return omap3isp_stat_enable(stat, !!*en); diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c index ac30a0f83780..3195f7c8b8b7 100644 --- a/drivers/media/platform/omap3isp/isppreview.c +++ b/drivers/media/platform/omap3isp/isppreview.c @@ -25,7 +25,7 @@ #include "isppreview.h" /* Default values in Office Fluorescent Light for RGBtoRGB Blending */ -static struct omap3isp_prev_rgbtorgb flr_rgb2rgb = { +static const struct omap3isp_prev_rgbtorgb flr_rgb2rgb = { { /* RGB-RGB Matrix */ {0x01E2, 0x0F30, 0x0FEE}, {0x0F9B, 0x01AC, 0x0FB9}, @@ -35,7 +35,7 @@ static struct omap3isp_prev_rgbtorgb flr_rgb2rgb = { }; /* Default values in Office Fluorescent Light for RGB to YUV Conversion*/ -static struct omap3isp_prev_csc flr_prev_csc = { +static const struct omap3isp_prev_csc flr_prev_csc = { { /* CSC Coef Matrix */ {66, 129, 25}, {-38, -75, 112}, @@ -890,7 +890,7 @@ static int preview_config(struct isp_prev_device *prev, params = &prev->params.params[!!(active & bit)]; if (cfg->flag & bit) { - void __user *from = *(void * __user *) + void __user *from = *(void __user **) ((void *)cfg + attr->config_offset); void *to = (void *)params + attr->param_offset; size_t size = attr->param_size; diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c index 47cbc7e3d825..47353fee26c3 100644 --- a/drivers/media/platform/omap3isp/ispstat.c +++ b/drivers/media/platform/omap3isp/ispstat.c @@ -17,6 +17,7 @@ #include <linux/dma-mapping.h> #include <linux/slab.h> +#include <linux/timekeeping.h> #include <linux/uaccess.h> #include "isp.h" @@ -237,7 +238,7 @@ static int isp_stat_buf_queue(struct ispstat *stat) if (!stat->active_buf) return STAT_NO_BUF; - v4l2_get_timestamp(&stat->active_buf->ts); + ktime_get_ts64(&stat->active_buf->ts); stat->active_buf->buf_size = stat->buf_size; if (isp_stat_buf_check_magic(stat, stat->active_buf)) { @@ -370,7 +371,7 @@ static int isp_stat_bufs_alloc_one(struct device *dev, int ret; buf->virt_addr = dma_alloc_coherent(dev, size, &buf->dma_addr, - GFP_KERNEL | GFP_DMA); + GFP_KERNEL); if (!buf->virt_addr) return -ENOMEM; @@ -449,10 +450,8 @@ static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size) buf->empty = 1; dev_dbg(stat->isp->dev, - "%s: buffer[%u] allocated. dma=0x%08lx virt=0x%08lx", - stat->subdev.name, i, - (unsigned long)buf->dma_addr, - (unsigned long)buf->virt_addr); + "%s: buffer[%u] allocated. dma=%pad virt=%p", + stat->subdev.name, i, &buf->dma_addr, buf->virt_addr); } return 0; @@ -500,7 +499,8 @@ int omap3isp_stat_request_statistics(struct ispstat *stat, return PTR_ERR(buf); } - data->ts = buf->ts; + data->ts.tv_sec = buf->ts.tv_sec; + data->ts.tv_usec = buf->ts.tv_nsec / NSEC_PER_USEC; data->config_counter = buf->config_counter; data->frame_number = buf->frame_number; data->buf_size = buf->buf_size; @@ -512,6 +512,23 @@ int omap3isp_stat_request_statistics(struct ispstat *stat, return 0; } +int omap3isp_stat_request_statistics_time32(struct ispstat *stat, + struct omap3isp_stat_data_time32 *data) +{ + struct omap3isp_stat_data data64; + int ret; + + ret = omap3isp_stat_request_statistics(stat, &data64); + if (ret) + return ret; + + data->ts.tv_sec = data64.ts.tv_sec; + data->ts.tv_usec = data64.ts.tv_usec; + memcpy(&data->buf, &data64.buf, sizeof(*data) - sizeof(data->ts)); + + return 0; +} + /* * omap3isp_stat_config - Receives new statistic engine configuration. * @new_conf: Pointer to config structure. @@ -527,12 +544,6 @@ int omap3isp_stat_config(struct ispstat *stat, void *new_conf) struct ispstat_generic_config *user_cfg = new_conf; u32 buf_size = user_cfg->buf_size; - if (!new_conf) { - dev_dbg(stat->isp->dev, "%s: configuration is NULL\n", - stat->subdev.name); - return -EINVAL; - } - mutex_lock(&stat->ioctl_lock); dev_dbg(stat->isp->dev, diff --git a/drivers/media/platform/omap3isp/ispstat.h b/drivers/media/platform/omap3isp/ispstat.h index 6d9b0244f320..923b38cfc682 100644 --- a/drivers/media/platform/omap3isp/ispstat.h +++ b/drivers/media/platform/omap3isp/ispstat.h @@ -39,7 +39,7 @@ struct ispstat_buffer { struct sg_table sgt; void *virt_addr; dma_addr_t dma_addr; - struct timeval ts; + struct timespec64 ts; u32 buf_size; u32 frame_number; u16 config_counter; @@ -130,6 +130,8 @@ struct ispstat_generic_config { int omap3isp_stat_config(struct ispstat *stat, void *new_conf); int omap3isp_stat_request_statistics(struct ispstat *stat, struct omap3isp_stat_data *data); +int omap3isp_stat_request_statistics_time32(struct ispstat *stat, + struct omap3isp_stat_data_time32 *data); int omap3isp_stat_init(struct ispstat *stat, const char *name, const struct v4l2_subdev_ops *sd_ops); void omap3isp_stat_cleanup(struct ispstat *stat); diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index a751c89a3ea8..9d228eac24ea 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -1403,7 +1403,7 @@ static int isp_video_mmap(struct file *file, struct vm_area_struct *vma) return vb2_mmap(&vfh->queue, vma); } -static struct v4l2_file_operations isp_video_fops = { +static const struct v4l2_file_operations isp_video_fops = { .owner = THIS_MODULE, .unlocked_ioctl = video_ioctl2, .open = isp_video_open, diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index c71a00736541..4d5a26b4cdda 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -2030,6 +2030,22 @@ static int pxac_vidioc_s_input(struct file *file, void *priv, unsigned int i) return 0; } +static int pxac_sensor_set_power(struct pxa_camera_dev *pcdev, int on) +{ + int ret; + + ret = sensor_call(pcdev, core, s_power, on); + if (ret == -ENOIOCTLCMD) + ret = 0; + if (ret) { + dev_warn(pcdev_to_dev(pcdev), + "Failed to put subdevice in %s mode: %d\n", + on ? "normal operation" : "power saving", ret); + } + + return ret; +} + static int pxac_fops_camera_open(struct file *filp) { struct pxa_camera_dev *pcdev = video_drvdata(filp); @@ -2040,7 +2056,10 @@ static int pxac_fops_camera_open(struct file *filp) if (ret < 0) goto out; - ret = sensor_call(pcdev, core, s_power, 1); + if (!v4l2_fh_is_singular_file(filp)) + goto out; + + ret = pxac_sensor_set_power(pcdev, 1); if (ret) v4l2_fh_release(filp); out: @@ -2052,13 +2071,17 @@ static int pxac_fops_camera_release(struct file *filp) { struct pxa_camera_dev *pcdev = video_drvdata(filp); int ret; - - ret = vb2_fop_release(filp); - if (ret < 0) - return ret; + bool fh_singular; mutex_lock(&pcdev->mlock); - ret = sensor_call(pcdev, core, s_power, 0); + + fh_singular = v4l2_fh_is_singular_file(filp); + + ret = _vb2_fop_release(filp, NULL); + + if (fh_singular) + ret = pxac_sensor_set_power(pcdev, 0); + mutex_unlock(&pcdev->mlock); return ret; @@ -2160,7 +2183,7 @@ static int pxa_camera_sensor_bound(struct v4l2_async_notifier *notifier, pix->pixelformat = pcdev->current_fmt->host_fmt->fourcc; v4l2_fill_mbus_format(mf, pix, pcdev->current_fmt->code); - err = sensor_call(pcdev, core, s_power, 1); + err = pxac_sensor_set_power(pcdev, 1); if (err) goto out; @@ -2187,7 +2210,7 @@ static int pxa_camera_sensor_bound(struct v4l2_async_notifier *notifier, } out_sensor_poweroff: - err = sensor_call(pcdev, core, s_power, 0); + err = pxac_sensor_set_power(pcdev, 0); out: mutex_unlock(&pcdev->mlock); return err; @@ -2242,11 +2265,8 @@ static int pxa_camera_suspend(struct device *dev) pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR3); pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR4); - if (pcdev->sensor) { - ret = sensor_call(pcdev, core, s_power, 0); - if (ret == -ENOIOCTLCMD) - ret = 0; - } + if (pcdev->sensor) + ret = pxac_sensor_set_power(pcdev, 0); return ret; } @@ -2263,9 +2283,7 @@ static int pxa_camera_resume(struct device *dev) __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR4); if (pcdev->sensor) { - ret = sensor_call(pcdev, core, s_power, 1); - if (ret == -ENOIOCTLCMD) - ret = 0; + ret = pxac_sensor_set_power(pcdev, 1); } /* Restart frame capture if active buffer exists */ diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/rcar-vin/Kconfig index af4c98b44d2e..baf4eafff6e3 100644 --- a/drivers/media/platform/rcar-vin/Kconfig +++ b/drivers/media/platform/rcar-vin/Kconfig @@ -1,12 +1,24 @@ +config VIDEO_RCAR_CSI2 + tristate "R-Car MIPI CSI-2 Receiver" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF + depends on ARCH_RENESAS || COMPILE_TEST + select V4L2_FWNODE + help + Support for Renesas R-Car MIPI CSI-2 receiver. + Supports R-Car Gen3 SoCs. + + To compile this driver as a module, choose M here: the + module will be called rcar-csi2. + config VIDEO_RCAR_VIN tristate "R-Car Video Input (VIN) Driver" - depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && HAS_DMA && MEDIA_CONTROLLER + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && OF && MEDIA_CONTROLLER depends on ARCH_RENESAS || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE ---help--- Support for Renesas R-Car Video Input (VIN) driver. - Supports R-Car Gen2 SoCs. + Supports R-Car Gen2 and Gen3 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/Makefile b/drivers/media/platform/rcar-vin/Makefile index 48c5632c21dc..5ab803d3e7c1 100644 --- a/drivers/media/platform/rcar-vin/Makefile +++ b/drivers/media/platform/rcar-vin/Makefile @@ -1,3 +1,4 @@ rcar-vin-objs = rcar-core.o rcar-dma.o rcar-v4l2.o +obj-$(CONFIG_VIDEO_RCAR_CSI2) += rcar-csi2.o obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin.o diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index f1fc7978d6d1..d3072e166a1c 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -20,12 +20,341 @@ #include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/sys_soc.h> #include <media/v4l2-async.h> #include <media/v4l2-fwnode.h> +#include <media/v4l2-mc.h> #include "rcar-vin.h" +/* + * The companion CSI-2 receiver driver (rcar-csi2) is known + * and we know it has one source pad (pad 0) and four sink + * pads (pad 1-4). So to translate a pad on the remote + * CSI-2 receiver to/from the VIN internal channel number simply + * subtract/add one from the pad/channel number. + */ +#define rvin_group_csi_pad_to_channel(pad) ((pad) - 1) +#define rvin_group_csi_channel_to_pad(channel) ((channel) + 1) + +/* + * Not all VINs are created equal, master VINs control the + * routing for other VIN's. We can figure out which VIN is + * master by looking at a VINs id. + */ +#define rvin_group_id_to_master(vin) ((vin) < 4 ? 0 : 4) + +/* ----------------------------------------------------------------------------- + * Media Controller link notification + */ + +/* group lock should be held when calling this function. */ +static int rvin_group_entity_to_csi_id(struct rvin_group *group, + struct media_entity *entity) +{ + struct v4l2_subdev *sd; + unsigned int i; + + sd = media_entity_to_v4l2_subdev(entity); + + for (i = 0; i < RVIN_CSI_MAX; i++) + if (group->csi[i].subdev == sd) + return i; + + return -ENODEV; +} + +static unsigned int rvin_group_get_mask(struct rvin_dev *vin, + enum rvin_csi_id csi_id, + unsigned char channel) +{ + const struct rvin_group_route *route; + unsigned int mask = 0; + + for (route = vin->info->routes; route->mask; route++) { + if (route->vin == vin->id && + route->csi == csi_id && + route->channel == channel) { + vin_dbg(vin, + "Adding route: vin: %d csi: %d channel: %d\n", + route->vin, route->csi, route->channel); + mask |= route->mask; + } + } + + return mask; +} + +/* + * Link setup for the links between a VIN and a CSI-2 receiver is a bit + * complex. The reason for this is that the register controlling routing + * is not present in each VIN instance. There are special VINs which + * control routing for themselves and other VINs. There are not many + * different possible links combinations that can be enabled at the same + * time, therefor all already enabled links which are controlled by a + * master VIN need to be taken into account when making the decision + * if a new link can be enabled or not. + * + * 1. Find out which VIN the link the user tries to enable is connected to. + * 2. Lookup which master VIN controls the links for this VIN. + * 3. Start with a bitmask with all bits set. + * 4. For each previously enabled link from the master VIN bitwise AND its + * route mask (see documentation for mask in struct rvin_group_route) + * with the bitmask. + * 5. Bitwise AND the mask for the link the user tries to enable to the bitmask. + * 6. If the bitmask is not empty at this point the new link can be enabled + * while keeping all previous links enabled. Update the CHSEL value of the + * master VIN and inform the user that the link could be enabled. + * + * Please note that no link can be enabled if any VIN in the group is + * currently open. + */ +static int rvin_group_link_notify(struct media_link *link, u32 flags, + unsigned int notification) +{ + struct rvin_group *group = container_of(link->graph_obj.mdev, + struct rvin_group, mdev); + unsigned int master_id, channel, mask_new, i; + unsigned int mask = ~0; + struct media_entity *entity; + struct video_device *vdev; + struct media_pad *csi_pad; + struct rvin_dev *vin = NULL; + int csi_id, ret; + + ret = v4l2_pipeline_link_notify(link, flags, notification); + if (ret) + return ret; + + /* Only care about link enablement for VIN nodes. */ + if (!(flags & MEDIA_LNK_FL_ENABLED) || + !is_media_entity_v4l2_video_device(link->sink->entity)) + return 0; + + /* If any entity is in use don't allow link changes. */ + media_device_for_each_entity(entity, &group->mdev) + if (entity->use_count) + return -EBUSY; + + mutex_lock(&group->lock); + + /* Find the master VIN that controls the routes. */ + vdev = media_entity_to_video_device(link->sink->entity); + vin = container_of(vdev, struct rvin_dev, vdev); + master_id = rvin_group_id_to_master(vin->id); + + if (WARN_ON(!group->vin[master_id])) { + ret = -ENODEV; + goto out; + } + + /* Build a mask for already enabled links. */ + for (i = master_id; i < master_id + 4; i++) { + if (!group->vin[i]) + continue; + + /* Get remote CSI-2, if any. */ + csi_pad = media_entity_remote_pad( + &group->vin[i]->vdev.entity.pads[0]); + if (!csi_pad) + continue; + + csi_id = rvin_group_entity_to_csi_id(group, csi_pad->entity); + channel = rvin_group_csi_pad_to_channel(csi_pad->index); + + mask &= rvin_group_get_mask(group->vin[i], csi_id, channel); + } + + /* Add the new link to the existing mask and check if it works. */ + csi_id = rvin_group_entity_to_csi_id(group, link->source->entity); + channel = rvin_group_csi_pad_to_channel(link->source->index); + mask_new = mask & rvin_group_get_mask(vin, csi_id, channel); + + vin_dbg(vin, "Try link change mask: 0x%x new: 0x%x\n", mask, mask_new); + + if (!mask_new) { + ret = -EMLINK; + goto out; + } + + /* New valid CHSEL found, set the new value. */ + ret = rvin_set_channel_routing(group->vin[master_id], __ffs(mask_new)); +out: + mutex_unlock(&group->lock); + + return ret; +} + +static const struct media_device_ops rvin_media_ops = { + .link_notify = rvin_group_link_notify, +}; + +/* ----------------------------------------------------------------------------- + * Gen3 CSI2 Group Allocator + */ + +/* FIXME: This should if we find a system that supports more + * than one group for the whole system be replaced with a linked + * list of groups. And eventually all of this should be replaced + * with a global device allocator API. + * + * But for now this works as on all supported systems there will + * be only one group for all instances. + */ + +static DEFINE_MUTEX(rvin_group_lock); +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); +} + +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); + + /* Count number of VINs in the system */ + group->count = 0; + for_each_matching_node(np, vin->dev->driver->of_match_table) + if (of_device_is_available(np)) + group->count++; + + vin_dbg(vin, "found %u enabled VIN's in DT", group->count); + + mdev->dev = vin->dev; + mdev->ops = &rvin_media_ops; + + match = of_match_node(vin->dev->driver->of_match_table, + vin->dev->of_node); + + strlcpy(mdev->driver_name, KBUILD_MODNAME, sizeof(mdev->driver_name)); + strlcpy(mdev->model, match->compatible, sizeof(mdev->model)); + snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", + dev_name(mdev->dev)); + + media_device_init(mdev); + + ret = media_device_register(&group->mdev); + if (ret) + rvin_group_cleanup(group); + + return ret; +} + +static void rvin_group_release(struct kref *kref) +{ + struct rvin_group *group = + container_of(kref, struct rvin_group, refcount); + + mutex_lock(&rvin_group_lock); + + rvin_group_data = NULL; + + rvin_group_cleanup(group); + + kfree(group); + + mutex_unlock(&rvin_group_lock); +} + +static int rvin_group_get(struct rvin_dev *vin) +{ + struct rvin_group *group; + u32 id; + int ret; + + /* Make sure VIN id is present and sane */ + ret = of_property_read_u32(vin->dev->of_node, "renesas,id", &id); + if (ret) { + vin_err(vin, "%pOF: No renesas,id property found\n", + vin->dev->of_node); + return -EINVAL; + } + + if (id >= RCAR_VIN_NUM) { + vin_err(vin, "%pOF: Invalid renesas,id '%u'\n", + vin->dev->of_node, id); + return -EINVAL; + } + + /* Join or create a VIN group */ + mutex_lock(&rvin_group_lock); + if (rvin_group_data) { + group = rvin_group_data; + kref_get(&group->refcount); + } else { + group = kzalloc(sizeof(*group), GFP_KERNEL); + if (!group) { + ret = -ENOMEM; + goto err_group; + } + + ret = rvin_group_init(group, vin); + if (ret) { + kfree(group); + vin_err(vin, "Failed to initialize group\n"); + goto err_group; + } + + kref_init(&group->refcount); + + rvin_group_data = group; + } + mutex_unlock(&rvin_group_lock); + + /* Add VIN to group */ + mutex_lock(&group->lock); + + if (group->vin[id]) { + vin_err(vin, "Duplicate renesas,id property value %u\n", id); + mutex_unlock(&group->lock); + kref_put(&group->refcount, rvin_group_release); + return -EINVAL; + } + + group->vin[id] = vin; + + vin->id = id; + vin->group = group; + vin->v4l2_dev.mdev = &group->mdev; + + mutex_unlock(&group->lock); + + return 0; +err_group: + mutex_unlock(&rvin_group_lock); + return ret; +} + +static void rvin_group_put(struct rvin_dev *vin) +{ + struct rvin_group *group = vin->group; + + mutex_lock(&group->lock); + + vin->group = NULL; + vin->v4l2_dev.mdev = NULL; + + if (WARN_ON(group->vin[vin->id] != vin)) + goto out; + + group->vin[vin->id] = NULL; +out: + mutex_unlock(&group->lock); + + kref_put(&group->refcount, rvin_group_release); +} + /* ----------------------------------------------------------------------------- * Async notifier */ @@ -46,30 +375,93 @@ static int rvin_find_pad(struct v4l2_subdev *sd, int direction) return -EINVAL; } -static bool rvin_mbus_supported(struct rvin_graph_entity *entity) +/* ----------------------------------------------------------------------------- + * Digital async notifier + */ + +/* The vin lock should be held when calling the subdevice attach and detach */ +static int rvin_digital_subdevice_attach(struct rvin_dev *vin, + struct v4l2_subdev *subdev) { - struct v4l2_subdev *sd = entity->subdev; struct v4l2_subdev_mbus_code_enum code = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; + int ret; + /* Find source and sink pad of remote subdevice */ + ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE); + if (ret < 0) + return ret; + vin->digital->source_pad = ret; + + ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK); + vin->digital->sink_pad = ret < 0 ? 0 : ret; + + /* Find compatible subdevices mbus format */ + vin->mbus_code = 0; code.index = 0; - code.pad = entity->source_pad; - while (!v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code)) { + code.pad = vin->digital->source_pad; + while (!vin->mbus_code && + !v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code)) { code.index++; switch (code.code) { case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_UYVY8_1X16: case MEDIA_BUS_FMT_UYVY8_2X8: case MEDIA_BUS_FMT_UYVY10_2X10: case MEDIA_BUS_FMT_RGB888_1X24: - entity->code = code.code; - return true; + vin->mbus_code = code.code; + vin_dbg(vin, "Found media bus format for %s: %d\n", + subdev->name, vin->mbus_code); + break; default: break; } } - return false; + if (!vin->mbus_code) { + vin_err(vin, "Unsupported media bus format for %s\n", + subdev->name); + return -EINVAL; + } + + /* Read tvnorms */ + ret = v4l2_subdev_call(subdev, video, g_tvnorms, &vin->vdev.tvnorms); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + /* Read standard */ + vin->std = V4L2_STD_UNKNOWN; + ret = v4l2_subdev_call(subdev, video, g_std, &vin->std); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + /* Add the controls */ + ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16); + if (ret < 0) + return ret; + + ret = v4l2_ctrl_add_handler(&vin->ctrl_handler, subdev->ctrl_handler, + NULL); + if (ret < 0) { + v4l2_ctrl_handler_free(&vin->ctrl_handler); + return ret; + } + + vin->vdev.ctrl_handler = &vin->ctrl_handler; + + vin->digital->subdev = subdev; + + return 0; +} + +static void rvin_digital_subdevice_detach(struct rvin_dev *vin) +{ + rvin_v4l2_unregister(vin); + v4l2_ctrl_handler_free(&vin->ctrl_handler); + + vin->vdev.ctrl_handler = NULL; + vin->digital->subdev = NULL; } static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier) @@ -77,23 +469,13 @@ static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier) struct rvin_dev *vin = notifier_to_vin(notifier); int ret; - /* Verify subdevices mbus format */ - if (!rvin_mbus_supported(vin->digital)) { - vin_err(vin, "Unsupported media bus format for %s\n", - vin->digital->subdev->name); - return -EINVAL; - } - - vin_dbg(vin, "Found media bus format for %s: %d\n", - vin->digital->subdev->name, vin->digital->code); - ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); if (ret < 0) { vin_err(vin, "Failed to register subdev nodes\n"); return ret; } - return rvin_v4l2_probe(vin); + return rvin_v4l2_register(vin); } static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier, @@ -103,8 +485,10 @@ static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier, struct rvin_dev *vin = notifier_to_vin(notifier); vin_dbg(vin, "unbind digital subdev %s\n", subdev->name); - rvin_v4l2_remove(vin); - vin->digital->subdev = NULL; + + mutex_lock(&vin->lock); + rvin_digital_subdevice_detach(vin); + mutex_unlock(&vin->lock); } static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, @@ -114,19 +498,13 @@ static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, struct rvin_dev *vin = notifier_to_vin(notifier); int ret; - v4l2_set_subdev_hostdata(subdev, vin); - - /* Find source and sink pad of remote subdevice */ - - ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE); - if (ret < 0) + mutex_lock(&vin->lock); + ret = rvin_digital_subdevice_attach(vin, subdev); + mutex_unlock(&vin->lock); + if (ret) return ret; - vin->digital->source_pad = ret; - ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK); - vin->digital->sink_pad = ret < 0 ? 0 : ret; - - vin->digital->subdev = subdev; + v4l2_set_subdev_hostdata(subdev, vin); vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n", subdev->name, vin->digital->source_pad, @@ -134,13 +512,13 @@ static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, return 0; } + static const struct v4l2_async_notifier_operations rvin_digital_notify_ops = { .bound = rvin_digital_notify_bound, .unbind = rvin_digital_notify_unbind, .complete = rvin_digital_notify_complete, }; - static int rvin_digital_parse_v4l2(struct device *dev, struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd) @@ -152,16 +530,16 @@ static int rvin_digital_parse_v4l2(struct device *dev, if (vep->base.port || vep->base.id) return -ENOTCONN; - rvge->mbus_cfg.type = vep->bus_type; + vin->mbus_cfg.type = vep->bus_type; - switch (rvge->mbus_cfg.type) { + switch (vin->mbus_cfg.type) { case V4L2_MBUS_PARALLEL: vin_dbg(vin, "Found PARALLEL media bus\n"); - rvge->mbus_cfg.flags = vep->bus.parallel.flags; + vin->mbus_cfg.flags = vep->bus.parallel.flags; break; case V4L2_MBUS_BT656: vin_dbg(vin, "Found BT656 media bus\n"); - rvge->mbus_cfg.flags = 0; + vin->mbus_cfg.flags = 0; break; default: vin_err(vin, "Unknown media bus type\n"); @@ -200,24 +578,477 @@ static int rvin_digital_graph_init(struct rvin_dev *vin) } /* ----------------------------------------------------------------------------- + * Group async notifier + */ + +static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct rvin_dev *vin = notifier_to_vin(notifier); + const struct rvin_group_route *route; + unsigned int i; + int ret; + + ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev); + if (ret) { + vin_err(vin, "Failed to register subdev nodes\n"); + return ret; + } + + /* Register all video nodes for the group. */ + for (i = 0; i < RCAR_VIN_NUM; i++) { + if (vin->group->vin[i]) { + ret = rvin_v4l2_register(vin->group->vin[i]); + if (ret) + return ret; + } + } + + /* Create all media device links between VINs and CSI-2's. */ + mutex_lock(&vin->group->lock); + for (route = vin->info->routes; route->mask; route++) { + struct media_pad *source_pad, *sink_pad; + struct media_entity *source, *sink; + unsigned int source_idx; + + /* Check that VIN is part of the group. */ + if (!vin->group->vin[route->vin]) + continue; + + /* Check that VIN' master is part of the group. */ + if (!vin->group->vin[rvin_group_id_to_master(route->vin)]) + continue; + + /* Check that CSI-2 is part of the group. */ + if (!vin->group->csi[route->csi].subdev) + continue; + + source = &vin->group->csi[route->csi].subdev->entity; + source_idx = rvin_group_csi_channel_to_pad(route->channel); + source_pad = &source->pads[source_idx]; + + sink = &vin->group->vin[route->vin]->vdev.entity; + sink_pad = &sink->pads[0]; + + /* Skip if link already exists. */ + if (media_entity_find_link(source_pad, sink_pad)) + continue; + + ret = media_create_pad_link(source, source_idx, sink, 0, 0); + if (ret) { + vin_err(vin, "Error adding link from %s to %s\n", + source->name, sink->name); + break; + } + } + mutex_unlock(&vin->group->lock); + + return ret; +} + +static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct rvin_dev *vin = notifier_to_vin(notifier); + unsigned int i; + + for (i = 0; i < RCAR_VIN_NUM; i++) + if (vin->group->vin[i]) + rvin_v4l2_unregister(vin->group->vin[i]); + + mutex_lock(&vin->group->lock); + + for (i = 0; i < RVIN_CSI_MAX; i++) { + if (vin->group->csi[i].fwnode != asd->match.fwnode) + continue; + vin->group->csi[i].subdev = NULL; + vin_dbg(vin, "Unbind CSI-2 %s from slot %u\n", subdev->name, i); + break; + } + + mutex_unlock(&vin->group->lock); +} + +static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct rvin_dev *vin = notifier_to_vin(notifier); + unsigned int i; + + mutex_lock(&vin->group->lock); + + for (i = 0; i < RVIN_CSI_MAX; i++) { + if (vin->group->csi[i].fwnode != asd->match.fwnode) + continue; + vin->group->csi[i].subdev = subdev; + vin_dbg(vin, "Bound CSI-2 %s to slot %u\n", subdev->name, i); + break; + } + + mutex_unlock(&vin->group->lock); + + return 0; +} + +static const struct v4l2_async_notifier_operations rvin_group_notify_ops = { + .bound = rvin_group_notify_bound, + .unbind = rvin_group_notify_unbind, + .complete = rvin_group_notify_complete, +}; + +static int rvin_mc_parse_of_endpoint(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd) +{ + struct rvin_dev *vin = dev_get_drvdata(dev); + + if (vep->base.port != 1 || vep->base.id >= RVIN_CSI_MAX) + return -EINVAL; + + if (!of_device_is_available(to_of_node(asd->match.fwnode))) { + + vin_dbg(vin, "OF device %pOF disabled, ignoring\n", + to_of_node(asd->match.fwnode)); + return -ENOTCONN; + + } + + if (vin->group->csi[vep->base.id].fwnode) { + vin_dbg(vin, "OF device %pOF already handled\n", + to_of_node(asd->match.fwnode)); + return -ENOTCONN; + } + + vin->group->csi[vep->base.id].fwnode = asd->match.fwnode; + + vin_dbg(vin, "Add group OF device %pOF to slot %u\n", + to_of_node(asd->match.fwnode), vep->base.id); + + return 0; +} + +static int rvin_mc_parse_of_graph(struct rvin_dev *vin) +{ + unsigned int count = 0; + unsigned int i; + int ret; + + mutex_lock(&vin->group->lock); + + /* If there already is a notifier something has gone wrong, bail out. */ + if (WARN_ON(vin->group->notifier)) { + mutex_unlock(&vin->group->lock); + return -EINVAL; + } + + /* If not all VIN's are registered don't register the notifier. */ + for (i = 0; i < RCAR_VIN_NUM; i++) + if (vin->group->vin[i]) + count++; + + if (vin->group->count != count) { + mutex_unlock(&vin->group->lock); + return 0; + } + + /* + * Have all VIN's look for subdevices. Some subdevices will overlap + * but the parser function can handle it, so each subdevice will + * only be registered once with the notifier. + */ + + vin->group->notifier = &vin->notifier; + + for (i = 0; i < RCAR_VIN_NUM; i++) { + if (!vin->group->vin[i]) + continue; + + ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port( + vin->group->vin[i]->dev, vin->group->notifier, + sizeof(struct v4l2_async_subdev), 1, + rvin_mc_parse_of_endpoint); + if (ret) { + mutex_unlock(&vin->group->lock); + return ret; + } + } + + mutex_unlock(&vin->group->lock); + + vin->group->notifier->ops = &rvin_group_notify_ops; + + ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); + if (ret < 0) { + vin_err(vin, "Notifier registration failed\n"); + return ret; + } + + return 0; +} + +static int rvin_mc_init(struct rvin_dev *vin) +{ + int ret; + + /* All our sources are CSI-2 */ + vin->mbus_cfg.type = V4L2_MBUS_CSI2; + vin->mbus_cfg.flags = 0; + + vin->pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad); + if (ret) + return ret; + + ret = rvin_group_get(vin); + if (ret) + return ret; + + ret = rvin_mc_parse_of_graph(vin); + if (ret) + rvin_group_put(vin); + + return ret; +} + +/* ----------------------------------------------------------------------------- * Platform Device Driver */ +static const struct rvin_info rcar_info_h1 = { + .model = RCAR_H1, + .use_mc = false, + .max_width = 2048, + .max_height = 2048, +}; + +static const struct rvin_info rcar_info_m1 = { + .model = RCAR_M1, + .use_mc = false, + .max_width = 2048, + .max_height = 2048, +}; + +static const struct rvin_info rcar_info_gen2 = { + .model = RCAR_GEN2, + .use_mc = false, + .max_width = 2048, + .max_height = 2048, +}; + +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) }, + { .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_CSI41, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) }, + { .csi = RVIN_CSI41, .channel = 1, .vin = 4, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) }, + { .csi = RVIN_CSI41, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) }, + { .csi = RVIN_CSI41, .channel = 0, .vin = 5, .mask = BIT(2) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) }, + { .csi = RVIN_CSI41, .channel = 0, .vin = 6, .mask = BIT(1) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) }, + { .csi = RVIN_CSI41, .channel = 2, .vin = 6, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) }, + { .csi = RVIN_CSI41, .channel = 1, .vin = 7, .mask = BIT(0) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) }, + { .csi = RVIN_CSI41, .channel = 3, .vin = 7, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) }, + { /* Sentinel */ } +}; + +static const struct rvin_info rcar_info_r8a7795 = { + .model = RCAR_GEN3, + .use_mc = true, + .max_width = 4096, + .max_height = 4096, + .routes = rcar_info_r8a7795_routes, +}; + +static const struct rvin_group_route rcar_info_r8a7795es1_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_CSI21, .channel = 0, .vin = 0, .mask = BIT(2) | BIT(5) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) }, + { .csi = RVIN_CSI21, .channel = 0, .vin = 1, .mask = BIT(1) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) }, + { .csi = RVIN_CSI21, .channel = 1, .vin = 1, .mask = BIT(5) }, + { .csi = RVIN_CSI21, .channel = 0, .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_CSI21, .channel = 2, .vin = 2, .mask = BIT(5) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) }, + { .csi = RVIN_CSI21, .channel = 1, .vin = 3, .mask = BIT(2) }, + { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) }, + { .csi = RVIN_CSI21, .channel = 3, .vin = 3, .mask = BIT(5) }, + { .csi = RVIN_CSI41, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) }, + { .csi = RVIN_CSI21, .channel = 0, .vin = 4, .mask = BIT(2) | BIT(5) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) }, + { .csi = RVIN_CSI21, .channel = 0, .vin = 5, .mask = BIT(1) }, + { .csi = RVIN_CSI41, .channel = 0, .vin = 5, .mask = BIT(2) }, + { .csi = RVIN_CSI41, .channel = 1, .vin = 5, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) }, + { .csi = RVIN_CSI21, .channel = 1, .vin = 5, .mask = BIT(5) }, + { .csi = RVIN_CSI21, .channel = 0, .vin = 6, .mask = BIT(0) }, + { .csi = RVIN_CSI41, .channel = 0, .vin = 6, .mask = BIT(1) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) }, + { .csi = RVIN_CSI41, .channel = 2, .vin = 6, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) }, + { .csi = RVIN_CSI21, .channel = 2, .vin = 6, .mask = BIT(5) }, + { .csi = RVIN_CSI41, .channel = 1, .vin = 7, .mask = BIT(0) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) }, + { .csi = RVIN_CSI21, .channel = 1, .vin = 7, .mask = BIT(2) }, + { .csi = RVIN_CSI41, .channel = 3, .vin = 7, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) }, + { .csi = RVIN_CSI21, .channel = 3, .vin = 7, .mask = BIT(5) }, + { /* Sentinel */ } +}; + +static const struct rvin_info rcar_info_r8a7795es1 = { + .model = RCAR_GEN3, + .use_mc = true, + .max_width = 4096, + .max_height = 4096, + .routes = rcar_info_r8a7795es1_routes, +}; + +static const struct rvin_group_route rcar_info_r8a7796_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_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) }, + { .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) }, + { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 5, .mask = BIT(2) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 5, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 6, .mask = BIT(1) }, + { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) }, + { .csi = RVIN_CSI40, .channel = 2, .vin = 6, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 7, .mask = BIT(0) }, + { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) }, + { .csi = RVIN_CSI40, .channel = 3, .vin = 7, .mask = BIT(3) }, + { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) }, + { /* Sentinel */ } +}; + +static const struct rvin_info rcar_info_r8a7796 = { + .model = RCAR_GEN3, + .use_mc = true, + .max_width = 4096, + .max_height = 4096, + .routes = rcar_info_r8a7796_routes, +}; + +static const struct rvin_group_route _rcar_info_r8a77970_routes[] = { + { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) }, + { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) }, + { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) }, + { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) }, + { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) }, + { /* Sentinel */ } +}; + +static const struct rvin_info rcar_info_r8a77970 = { + .model = RCAR_GEN3, + .use_mc = true, + .max_width = 4096, + .max_height = 4096, + .routes = _rcar_info_r8a77970_routes, +}; + static const struct of_device_id rvin_of_id_table[] = { - { .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 }, - { .compatible = "renesas,vin-r8a7793", .data = (void *)RCAR_GEN2 }, - { .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 }, - { .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 }, - { .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 }, - { .compatible = "renesas,vin-r8a7778", .data = (void *)RCAR_M1 }, - { .compatible = "renesas,rcar-gen2-vin", .data = (void *)RCAR_GEN2 }, - { }, + { + .compatible = "renesas,vin-r8a7778", + .data = &rcar_info_m1, + }, + { + .compatible = "renesas,vin-r8a7779", + .data = &rcar_info_h1, + }, + { + .compatible = "renesas,vin-r8a7790", + .data = &rcar_info_gen2, + }, + { + .compatible = "renesas,vin-r8a7791", + .data = &rcar_info_gen2, + }, + { + .compatible = "renesas,vin-r8a7793", + .data = &rcar_info_gen2, + }, + { + .compatible = "renesas,vin-r8a7794", + .data = &rcar_info_gen2, + }, + { + .compatible = "renesas,rcar-gen2-vin", + .data = &rcar_info_gen2, + }, + { + .compatible = "renesas,vin-r8a7795", + .data = &rcar_info_r8a7795, + }, + { + .compatible = "renesas,vin-r8a7796", + .data = &rcar_info_r8a7796, + }, + { + .compatible = "renesas,vin-r8a77970", + .data = &rcar_info_r8a77970, + }, + { /* Sentinel */ }, }; MODULE_DEVICE_TABLE(of, rvin_of_id_table); +static const struct soc_device_attribute r8a7795es1[] = { + { + .soc_id = "r8a7795", .revision = "ES1.*", + .data = &rcar_info_r8a7795es1, + }, + { /* Sentinel */ } +}; + static int rcar_vin_probe(struct platform_device *pdev) { - const struct of_device_id *match; + const struct soc_device_attribute *attr; struct rvin_dev *vin; struct resource *mem; int irq, ret; @@ -226,12 +1057,16 @@ static int rcar_vin_probe(struct platform_device *pdev) if (!vin) return -ENOMEM; - match = of_match_device(of_match_ptr(rvin_of_id_table), &pdev->dev); - if (!match) - return -ENODEV; - vin->dev = &pdev->dev; - vin->chip = (enum chip_id)match->data; + vin->info = of_device_get_match_data(&pdev->dev); + + /* + * Special care is needed on r8a7795 ES1.x since it + * uses different routing than r8a7795 ES2.0. + */ + attr = soc_device_match(r8a7795es1); + if (attr) + vin->info = attr->data; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (mem == NULL) @@ -245,13 +1080,15 @@ static int rcar_vin_probe(struct platform_device *pdev) if (irq < 0) return irq; - ret = rvin_dma_probe(vin, irq); + ret = rvin_dma_register(vin, irq); if (ret) return ret; platform_set_drvdata(pdev, vin); - - ret = rvin_digital_graph_init(vin); + if (vin->info->use_mc) + ret = rvin_mc_init(vin); + else + ret = rvin_digital_graph_init(vin); if (ret < 0) goto error; @@ -260,7 +1097,7 @@ static int rcar_vin_probe(struct platform_device *pdev) return 0; error: - rvin_dma_remove(vin); + rvin_dma_unregister(vin); v4l2_async_notifier_cleanup(&vin->notifier); return ret; @@ -272,10 +1109,22 @@ static int rcar_vin_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); + rvin_v4l2_unregister(vin); + v4l2_async_notifier_unregister(&vin->notifier); v4l2_async_notifier_cleanup(&vin->notifier); - rvin_dma_remove(vin); + if (vin->info->use_mc) { + mutex_lock(&vin->group->lock); + if (vin->group->notifier == &vin->notifier) + vin->group->notifier = NULL; + mutex_unlock(&vin->group->lock); + rvin_group_put(vin); + } else { + v4l2_ctrl_handler_free(&vin->ctrl_handler); + } + + rvin_dma_unregister(vin); return 0; } diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c new file mode 100644 index 000000000000..daef72d410a3 --- /dev/null +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -0,0 +1,1084 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Renesas R-Car MIPI CSI-2 Receiver + * + * Copyright (C) 2018 Renesas Electronics Corp. + */ + +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/sys_soc.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-mc.h> +#include <media/v4l2-subdev.h> + +struct rcar_csi2; + +/* Register offsets and bits */ + +/* Control Timing Select */ +#define TREF_REG 0x00 +#define TREF_TREF BIT(0) + +/* Software Reset */ +#define SRST_REG 0x04 +#define SRST_SRST BIT(0) + +/* PHY Operation Control */ +#define PHYCNT_REG 0x08 +#define PHYCNT_SHUTDOWNZ BIT(17) +#define PHYCNT_RSTZ BIT(16) +#define PHYCNT_ENABLECLK BIT(4) +#define PHYCNT_ENABLE_3 BIT(3) +#define PHYCNT_ENABLE_2 BIT(2) +#define PHYCNT_ENABLE_1 BIT(1) +#define PHYCNT_ENABLE_0 BIT(0) + +/* Checksum Control */ +#define CHKSUM_REG 0x0c +#define CHKSUM_ECC_EN BIT(1) +#define CHKSUM_CRC_EN BIT(0) + +/* + * Channel Data Type Select + * VCDT[0-15]: Channel 1 VCDT[16-31]: Channel 2 + * VCDT2[0-15]: Channel 3 VCDT2[16-31]: Channel 4 + */ +#define VCDT_REG 0x10 +#define VCDT2_REG 0x14 +#define VCDT_VCDTN_EN BIT(15) +#define VCDT_SEL_VC(n) (((n) & 0x3) << 8) +#define VCDT_SEL_DTN_ON BIT(6) +#define VCDT_SEL_DT(n) (((n) & 0x3f) << 0) + +/* Frame Data Type Select */ +#define FRDT_REG 0x18 + +/* Field Detection Control */ +#define FLD_REG 0x1c +#define FLD_FLD_NUM(n) (((n) & 0xff) << 16) +#define FLD_FLD_EN4 BIT(3) +#define FLD_FLD_EN3 BIT(2) +#define FLD_FLD_EN2 BIT(1) +#define FLD_FLD_EN BIT(0) + +/* Automatic Standby Control */ +#define ASTBY_REG 0x20 + +/* Long Data Type Setting 0 */ +#define LNGDT0_REG 0x28 + +/* Long Data Type Setting 1 */ +#define LNGDT1_REG 0x2c + +/* Interrupt Enable */ +#define INTEN_REG 0x30 + +/* Interrupt Source Mask */ +#define INTCLOSE_REG 0x34 + +/* Interrupt Status Monitor */ +#define INTSTATE_REG 0x38 +#define INTSTATE_INT_ULPS_START BIT(7) +#define INTSTATE_INT_ULPS_END BIT(6) + +/* Interrupt Error Status Monitor */ +#define INTERRSTATE_REG 0x3c + +/* Short Packet Data */ +#define SHPDAT_REG 0x40 + +/* Short Packet Count */ +#define SHPCNT_REG 0x44 + +/* LINK Operation Control */ +#define LINKCNT_REG 0x48 +#define LINKCNT_MONITOR_EN BIT(31) +#define LINKCNT_REG_MONI_PACT_EN BIT(25) +#define LINKCNT_ICLK_NONSTOP BIT(24) + +/* Lane Swap */ +#define LSWAP_REG 0x4c +#define LSWAP_L3SEL(n) (((n) & 0x3) << 6) +#define LSWAP_L2SEL(n) (((n) & 0x3) << 4) +#define LSWAP_L1SEL(n) (((n) & 0x3) << 2) +#define LSWAP_L0SEL(n) (((n) & 0x3) << 0) + +/* PHY Test Interface Write Register */ +#define PHTW_REG 0x50 +#define PHTW_DWEN BIT(24) +#define PHTW_TESTDIN_DATA(n) (((n & 0xff)) << 16) +#define PHTW_CWEN BIT(8) +#define PHTW_TESTDIN_CODE(n) ((n & 0xff)) + +struct phtw_value { + u16 data; + u16 code; +}; + +struct rcsi2_mbps_reg { + u16 mbps; + u16 reg; +}; + +static const struct rcsi2_mbps_reg phtw_mbps_h3_v3h_m3n[] = { + { .mbps = 80, .reg = 0x86 }, + { .mbps = 90, .reg = 0x86 }, + { .mbps = 100, .reg = 0x87 }, + { .mbps = 110, .reg = 0x87 }, + { .mbps = 120, .reg = 0x88 }, + { .mbps = 130, .reg = 0x88 }, + { .mbps = 140, .reg = 0x89 }, + { .mbps = 150, .reg = 0x89 }, + { .mbps = 160, .reg = 0x8a }, + { .mbps = 170, .reg = 0x8a }, + { .mbps = 180, .reg = 0x8b }, + { .mbps = 190, .reg = 0x8b }, + { .mbps = 205, .reg = 0x8c }, + { .mbps = 220, .reg = 0x8d }, + { .mbps = 235, .reg = 0x8e }, + { .mbps = 250, .reg = 0x8e }, + { /* sentinel */ }, +}; + +static const struct rcsi2_mbps_reg phtw_mbps_v3m_e3[] = { + { .mbps = 80, .reg = 0x00 }, + { .mbps = 90, .reg = 0x20 }, + { .mbps = 100, .reg = 0x40 }, + { .mbps = 110, .reg = 0x02 }, + { .mbps = 130, .reg = 0x22 }, + { .mbps = 140, .reg = 0x42 }, + { .mbps = 150, .reg = 0x04 }, + { .mbps = 170, .reg = 0x24 }, + { .mbps = 180, .reg = 0x44 }, + { .mbps = 200, .reg = 0x06 }, + { .mbps = 220, .reg = 0x26 }, + { .mbps = 240, .reg = 0x46 }, + { .mbps = 250, .reg = 0x08 }, + { .mbps = 270, .reg = 0x28 }, + { .mbps = 300, .reg = 0x0a }, + { .mbps = 330, .reg = 0x2a }, + { .mbps = 360, .reg = 0x4a }, + { .mbps = 400, .reg = 0x0c }, + { .mbps = 450, .reg = 0x2c }, + { .mbps = 500, .reg = 0x0e }, + { .mbps = 550, .reg = 0x2e }, + { .mbps = 600, .reg = 0x10 }, + { .mbps = 650, .reg = 0x30 }, + { .mbps = 700, .reg = 0x12 }, + { .mbps = 750, .reg = 0x32 }, + { .mbps = 800, .reg = 0x52 }, + { .mbps = 850, .reg = 0x72 }, + { .mbps = 900, .reg = 0x14 }, + { .mbps = 950, .reg = 0x34 }, + { .mbps = 1000, .reg = 0x54 }, + { .mbps = 1050, .reg = 0x74 }, + { .mbps = 1125, .reg = 0x16 }, + { /* sentinel */ }, +}; + +/* PHY Test Interface Clear */ +#define PHTC_REG 0x58 +#define PHTC_TESTCLR BIT(0) + +/* PHY Frequency Control */ +#define PHYPLL_REG 0x68 +#define PHYPLL_HSFREQRANGE(n) ((n) << 16) + +static const struct rcsi2_mbps_reg hsfreqrange_h3_v3h_m3n[] = { + { .mbps = 80, .reg = 0x00 }, + { .mbps = 90, .reg = 0x10 }, + { .mbps = 100, .reg = 0x20 }, + { .mbps = 110, .reg = 0x30 }, + { .mbps = 120, .reg = 0x01 }, + { .mbps = 130, .reg = 0x11 }, + { .mbps = 140, .reg = 0x21 }, + { .mbps = 150, .reg = 0x31 }, + { .mbps = 160, .reg = 0x02 }, + { .mbps = 170, .reg = 0x12 }, + { .mbps = 180, .reg = 0x22 }, + { .mbps = 190, .reg = 0x32 }, + { .mbps = 205, .reg = 0x03 }, + { .mbps = 220, .reg = 0x13 }, + { .mbps = 235, .reg = 0x23 }, + { .mbps = 250, .reg = 0x33 }, + { .mbps = 275, .reg = 0x04 }, + { .mbps = 300, .reg = 0x14 }, + { .mbps = 325, .reg = 0x25 }, + { .mbps = 350, .reg = 0x35 }, + { .mbps = 400, .reg = 0x05 }, + { .mbps = 450, .reg = 0x16 }, + { .mbps = 500, .reg = 0x26 }, + { .mbps = 550, .reg = 0x37 }, + { .mbps = 600, .reg = 0x07 }, + { .mbps = 650, .reg = 0x18 }, + { .mbps = 700, .reg = 0x28 }, + { .mbps = 750, .reg = 0x39 }, + { .mbps = 800, .reg = 0x09 }, + { .mbps = 850, .reg = 0x19 }, + { .mbps = 900, .reg = 0x29 }, + { .mbps = 950, .reg = 0x3a }, + { .mbps = 1000, .reg = 0x0a }, + { .mbps = 1050, .reg = 0x1a }, + { .mbps = 1100, .reg = 0x2a }, + { .mbps = 1150, .reg = 0x3b }, + { .mbps = 1200, .reg = 0x0b }, + { .mbps = 1250, .reg = 0x1b }, + { .mbps = 1300, .reg = 0x2b }, + { .mbps = 1350, .reg = 0x3c }, + { .mbps = 1400, .reg = 0x0c }, + { .mbps = 1450, .reg = 0x1c }, + { .mbps = 1500, .reg = 0x2c }, + { /* sentinel */ }, +}; + +static const struct rcsi2_mbps_reg hsfreqrange_m3w_h3es1[] = { + { .mbps = 80, .reg = 0x00 }, + { .mbps = 90, .reg = 0x10 }, + { .mbps = 100, .reg = 0x20 }, + { .mbps = 110, .reg = 0x30 }, + { .mbps = 120, .reg = 0x01 }, + { .mbps = 130, .reg = 0x11 }, + { .mbps = 140, .reg = 0x21 }, + { .mbps = 150, .reg = 0x31 }, + { .mbps = 160, .reg = 0x02 }, + { .mbps = 170, .reg = 0x12 }, + { .mbps = 180, .reg = 0x22 }, + { .mbps = 190, .reg = 0x32 }, + { .mbps = 205, .reg = 0x03 }, + { .mbps = 220, .reg = 0x13 }, + { .mbps = 235, .reg = 0x23 }, + { .mbps = 250, .reg = 0x33 }, + { .mbps = 275, .reg = 0x04 }, + { .mbps = 300, .reg = 0x14 }, + { .mbps = 325, .reg = 0x05 }, + { .mbps = 350, .reg = 0x15 }, + { .mbps = 400, .reg = 0x25 }, + { .mbps = 450, .reg = 0x06 }, + { .mbps = 500, .reg = 0x16 }, + { .mbps = 550, .reg = 0x07 }, + { .mbps = 600, .reg = 0x17 }, + { .mbps = 650, .reg = 0x08 }, + { .mbps = 700, .reg = 0x18 }, + { .mbps = 750, .reg = 0x09 }, + { .mbps = 800, .reg = 0x19 }, + { .mbps = 850, .reg = 0x29 }, + { .mbps = 900, .reg = 0x39 }, + { .mbps = 950, .reg = 0x0a }, + { .mbps = 1000, .reg = 0x1a }, + { .mbps = 1050, .reg = 0x2a }, + { .mbps = 1100, .reg = 0x3a }, + { .mbps = 1150, .reg = 0x0b }, + { .mbps = 1200, .reg = 0x1b }, + { .mbps = 1250, .reg = 0x2b }, + { .mbps = 1300, .reg = 0x3b }, + { .mbps = 1350, .reg = 0x0c }, + { .mbps = 1400, .reg = 0x1c }, + { .mbps = 1450, .reg = 0x2c }, + { .mbps = 1500, .reg = 0x3c }, + { /* sentinel */ }, +}; + +/* PHY ESC Error Monitor */ +#define PHEERM_REG 0x74 + +/* PHY Clock Lane Monitor */ +#define PHCLM_REG 0x78 +#define PHCLM_STOPSTATECKL BIT(0) + +/* PHY Data Lane Monitor */ +#define PHDLM_REG 0x7c + +/* CSI0CLK Frequency Configuration Preset Register */ +#define CSI0CLKFCPR_REG 0x260 +#define CSI0CLKFREQRANGE(n) ((n & 0x3f) << 16) + +struct rcar_csi2_format { + u32 code; + unsigned int datatype; + unsigned int bpp; +}; + +static const struct rcar_csi2_format rcar_csi2_formats[] = { + { .code = MEDIA_BUS_FMT_RGB888_1X24, .datatype = 0x24, .bpp = 24 }, + { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .bpp = 16 }, + { .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 }, +}; + +static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(rcar_csi2_formats); i++) + if (rcar_csi2_formats[i].code == code) + return &rcar_csi2_formats[i]; + + return NULL; +} + +enum rcar_csi2_pads { + RCAR_CSI2_SINK, + RCAR_CSI2_SOURCE_VC0, + RCAR_CSI2_SOURCE_VC1, + RCAR_CSI2_SOURCE_VC2, + RCAR_CSI2_SOURCE_VC3, + NR_OF_RCAR_CSI2_PAD, +}; + +struct rcar_csi2_info { + int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps); + const struct rcsi2_mbps_reg *hsfreqrange; + unsigned int csi0clkfreqrange; + bool clear_ulps; +}; + +struct rcar_csi2 { + struct device *dev; + void __iomem *base; + const struct rcar_csi2_info *info; + + struct v4l2_subdev subdev; + struct media_pad pads[NR_OF_RCAR_CSI2_PAD]; + + struct v4l2_async_notifier notifier; + struct v4l2_async_subdev asd; + struct v4l2_subdev *remote; + + struct v4l2_mbus_framefmt mf; + + struct mutex lock; + int stream_count; + + unsigned short lanes; + unsigned char lane_swap[4]; +}; + +static inline struct rcar_csi2 *sd_to_csi2(struct v4l2_subdev *sd) +{ + return container_of(sd, struct rcar_csi2, subdev); +} + +static inline struct rcar_csi2 *notifier_to_csi2(struct v4l2_async_notifier *n) +{ + return container_of(n, struct rcar_csi2, notifier); +} + +static u32 rcsi2_read(struct rcar_csi2 *priv, unsigned int reg) +{ + return ioread32(priv->base + reg); +} + +static void rcsi2_write(struct rcar_csi2 *priv, unsigned int reg, u32 data) +{ + iowrite32(data, priv->base + reg); +} + +static void rcsi2_reset(struct rcar_csi2 *priv) +{ + rcsi2_write(priv, SRST_REG, SRST_SRST); + usleep_range(100, 150); + rcsi2_write(priv, SRST_REG, 0); +} + +static int rcsi2_wait_phy_start(struct rcar_csi2 *priv) +{ + 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; + + if ((rcsi2_read(priv, PHCLM_REG) & PHCLM_STOPSTATECKL) && + (rcsi2_read(priv, PHDLM_REG) & lane_mask) == lane_mask) + return 0; + + usleep_range(1000, 2000); + } + + dev_err(priv->dev, "Timeout waiting for LP-11 state\n"); + + return -ETIMEDOUT; +} + +static int rcsi2_set_phypll(struct rcar_csi2 *priv, unsigned int mbps) +{ + const struct rcsi2_mbps_reg *hsfreq; + + for (hsfreq = priv->info->hsfreqrange; hsfreq->mbps != 0; hsfreq++) + if (hsfreq->mbps >= mbps) + break; + + if (!hsfreq->mbps) { + dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)", mbps); + return -ERANGE; + } + + rcsi2_write(priv, PHYPLL_REG, PHYPLL_HSFREQRANGE(hsfreq->reg)); + + return 0; +} + +static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp) +{ + struct v4l2_subdev *source; + struct v4l2_ctrl *ctrl; + u64 mbps; + + if (!priv->remote) + return -ENODEV; + + source = priv->remote; + + /* Read the pixel rate control from remote. */ + ctrl = v4l2_ctrl_find(source->ctrl_handler, V4L2_CID_PIXEL_RATE); + if (!ctrl) { + dev_err(priv->dev, "no pixel rate control in subdev %s\n", + source->name); + return -EINVAL; + } + + /* + * Calculate the phypll in mbps. + * link_freq = (pixel_rate * bits_per_sample) / (2 * nr_of_lanes) + * bps = link_freq * 2 + */ + mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * bpp; + do_div(mbps, priv->lanes * 1000000); + + return mbps; +} + +static int rcsi2_start(struct rcar_csi2 *priv) +{ + const struct rcar_csi2_format *format; + u32 phycnt, vcdt = 0, vcdt2 = 0; + unsigned int i; + int mbps, ret; + + dev_dbg(priv->dev, "Input size (%ux%u%c)\n", + priv->mf.width, priv->mf.height, + priv->mf.field == V4L2_FIELD_NONE ? 'p' : 'i'); + + /* Code is validated in set_fmt. */ + format = rcsi2_code_to_fmt(priv->mf.code); + + /* + * Enable all Virtual Channels. + * + * NOTE: It's not possible to get individual datatype for each + * source virtual channel. Once this is possible in V4L2 + * it should be used here. + */ + for (i = 0; i < 4; i++) { + u32 vcdt_part; + + vcdt_part = VCDT_SEL_VC(i) | VCDT_VCDTN_EN | VCDT_SEL_DTN_ON | + VCDT_SEL_DT(format->datatype); + + /* Store in correct reg and offset. */ + if (i < 2) + vcdt |= vcdt_part << ((i % 2) * 16); + else + vcdt2 |= vcdt_part << ((i % 2) * 16); + } + + phycnt = PHYCNT_ENABLECLK; + phycnt |= (1 << priv->lanes) - 1; + + mbps = rcsi2_calc_mbps(priv, format->bpp); + if (mbps < 0) + return mbps; + + /* Init */ + rcsi2_write(priv, TREF_REG, TREF_TREF); + rcsi2_reset(priv); + rcsi2_write(priv, PHTC_REG, 0); + + /* Configure */ + rcsi2_write(priv, FLD_REG, FLD_FLD_NUM(2) | FLD_FLD_EN4 | + FLD_FLD_EN3 | FLD_FLD_EN2 | FLD_FLD_EN); + rcsi2_write(priv, VCDT_REG, vcdt); + rcsi2_write(priv, VCDT2_REG, vcdt2); + /* Lanes are zero indexed. */ + rcsi2_write(priv, LSWAP_REG, + LSWAP_L0SEL(priv->lane_swap[0] - 1) | + LSWAP_L1SEL(priv->lane_swap[1] - 1) | + LSWAP_L2SEL(priv->lane_swap[2] - 1) | + LSWAP_L3SEL(priv->lane_swap[3] - 1)); + + /* Start */ + if (priv->info->init_phtw) { + ret = priv->info->init_phtw(priv, mbps); + if (ret) + return ret; + } + + if (priv->info->hsfreqrange) { + ret = rcsi2_set_phypll(priv, mbps); + if (ret) + return ret; + } + + if (priv->info->csi0clkfreqrange) + rcsi2_write(priv, CSI0CLKFCPR_REG, + CSI0CLKFREQRANGE(priv->info->csi0clkfreqrange)); + + rcsi2_write(priv, PHYCNT_REG, phycnt); + rcsi2_write(priv, LINKCNT_REG, LINKCNT_MONITOR_EN | + LINKCNT_REG_MONI_PACT_EN | LINKCNT_ICLK_NONSTOP); + rcsi2_write(priv, PHYCNT_REG, phycnt | PHYCNT_SHUTDOWNZ); + rcsi2_write(priv, PHYCNT_REG, phycnt | PHYCNT_SHUTDOWNZ | PHYCNT_RSTZ); + + ret = rcsi2_wait_phy_start(priv); + if (ret) + return ret; + + /* Clear Ultra Low Power interrupt. */ + if (priv->info->clear_ulps) + rcsi2_write(priv, INTSTATE_REG, + INTSTATE_INT_ULPS_START | + INTSTATE_INT_ULPS_END); + return 0; +} + +static void rcsi2_stop(struct rcar_csi2 *priv) +{ + rcsi2_write(priv, PHYCNT_REG, 0); + + rcsi2_reset(priv); + + rcsi2_write(priv, PHTC_REG, PHTC_TESTCLR); +} + +static int rcsi2_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct rcar_csi2 *priv = sd_to_csi2(sd); + struct v4l2_subdev *nextsd; + int ret = 0; + + mutex_lock(&priv->lock); + + if (!priv->remote) { + ret = -ENODEV; + goto out; + } + + nextsd = priv->remote; + + if (enable && priv->stream_count == 0) { + pm_runtime_get_sync(priv->dev); + + ret = rcsi2_start(priv); + if (ret) { + pm_runtime_put(priv->dev); + goto out; + } + + ret = v4l2_subdev_call(nextsd, video, s_stream, 1); + if (ret) { + rcsi2_stop(priv); + pm_runtime_put(priv->dev); + goto out; + } + } else if (!enable && priv->stream_count == 1) { + rcsi2_stop(priv); + v4l2_subdev_call(nextsd, video, s_stream, 0); + pm_runtime_put(priv->dev); + } + + priv->stream_count += enable ? 1 : -1; +out: + mutex_unlock(&priv->lock); + + return ret; +} + +static int rcsi2_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct rcar_csi2 *priv = sd_to_csi2(sd); + struct v4l2_mbus_framefmt *framefmt; + + if (!rcsi2_code_to_fmt(format->format.code)) + format->format.code = rcar_csi2_formats[0].code; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + priv->mf = format->format; + } else { + framefmt = v4l2_subdev_get_try_format(sd, cfg, 0); + *framefmt = format->format; + } + + return 0; +} + +static int rcsi2_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct rcar_csi2 *priv = sd_to_csi2(sd); + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + format->format = priv->mf; + else + format->format = *v4l2_subdev_get_try_format(sd, cfg, 0); + + return 0; +} + +static const struct v4l2_subdev_video_ops rcar_csi2_video_ops = { + .s_stream = rcsi2_s_stream, +}; + +static const struct v4l2_subdev_pad_ops rcar_csi2_pad_ops = { + .set_fmt = rcsi2_set_pad_format, + .get_fmt = rcsi2_get_pad_format, +}; + +static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = { + .video = &rcar_csi2_video_ops, + .pad = &rcar_csi2_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Async handling and registration of subdevices and links. + */ + +static int rcsi2_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct rcar_csi2 *priv = notifier_to_csi2(notifier); + int pad; + + pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode, + MEDIA_PAD_FL_SOURCE); + if (pad < 0) { + dev_err(priv->dev, "Failed to find pad for %s\n", subdev->name); + return pad; + } + + priv->remote = subdev; + + dev_dbg(priv->dev, "Bound %s pad: %d\n", subdev->name, pad); + + return media_create_pad_link(&subdev->entity, pad, + &priv->subdev.entity, 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +} + +static void rcsi2_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct rcar_csi2 *priv = notifier_to_csi2(notifier); + + priv->remote = NULL; + + dev_dbg(priv->dev, "Unbind %s\n", subdev->name); +} + +static const struct v4l2_async_notifier_operations rcar_csi2_notify_ops = { + .bound = rcsi2_notify_bound, + .unbind = rcsi2_notify_unbind, +}; + +static int rcsi2_parse_v4l2(struct rcar_csi2 *priv, + struct v4l2_fwnode_endpoint *vep) +{ + unsigned int i; + + /* Only port 0 endpoint 0 is valid. */ + if (vep->base.port || vep->base.id) + return -ENOTCONN; + + if (vep->bus_type != V4L2_MBUS_CSI2) { + dev_err(priv->dev, "Unsupported bus: %u\n", vep->bus_type); + return -EINVAL; + } + + priv->lanes = vep->bus.mipi_csi2.num_data_lanes; + if (priv->lanes != 1 && priv->lanes != 2 && priv->lanes != 4) { + dev_err(priv->dev, "Unsupported number of data-lanes: %u\n", + priv->lanes); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(priv->lane_swap); i++) { + priv->lane_swap[i] = i < priv->lanes ? + vep->bus.mipi_csi2.data_lanes[i] : i; + + /* Check for valid lane number. */ + if (priv->lane_swap[i] < 1 || priv->lane_swap[i] > 4) { + dev_err(priv->dev, "data-lanes must be in 1-4 range\n"); + return -EINVAL; + } + } + + return 0; +} + +static int rcsi2_parse_dt(struct rcar_csi2 *priv) +{ + struct device_node *ep; + struct v4l2_fwnode_endpoint v4l2_ep; + int ret; + + ep = of_graph_get_endpoint_by_regs(priv->dev->of_node, 0, 0); + if (!ep) { + dev_err(priv->dev, "Not connected to subdevice\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep); + if (ret) { + dev_err(priv->dev, "Could not parse v4l2 endpoint\n"); + of_node_put(ep); + return -EINVAL; + } + + ret = rcsi2_parse_v4l2(priv, &v4l2_ep); + if (ret) { + of_node_put(ep); + return ret; + } + + priv->asd.match.fwnode = + fwnode_graph_get_remote_endpoint(of_fwnode_handle(ep)); + priv->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + + of_node_put(ep); + + priv->notifier.subdevs = devm_kzalloc(priv->dev, + sizeof(*priv->notifier.subdevs), + GFP_KERNEL); + if (!priv->notifier.subdevs) + return -ENOMEM; + + priv->notifier.num_subdevs = 1; + priv->notifier.subdevs[0] = &priv->asd; + priv->notifier.ops = &rcar_csi2_notify_ops; + + dev_dbg(priv->dev, "Found '%pOF'\n", + to_of_node(priv->asd.match.fwnode)); + + return v4l2_async_subdev_notifier_register(&priv->subdev, + &priv->notifier); +} + +/* ----------------------------------------------------------------------------- + * PHTW initialization sequences. + * + * NOTE: Magic values are from the datasheet and lack documentation. + */ + +static int rcsi2_phtw_write(struct rcar_csi2 *priv, u16 data, u16 code) +{ + unsigned int timeout; + + rcsi2_write(priv, PHTW_REG, + PHTW_DWEN | PHTW_TESTDIN_DATA(data) | + PHTW_CWEN | PHTW_TESTDIN_CODE(code)); + + /* Wait for DWEN and CWEN to be cleared by hardware. */ + for (timeout = 0; timeout <= 20; timeout++) { + if (!(rcsi2_read(priv, PHTW_REG) & (PHTW_DWEN | PHTW_CWEN))) + return 0; + + usleep_range(1000, 2000); + } + + dev_err(priv->dev, "Timeout waiting for PHTW_DWEN and/or PHTW_CWEN\n"); + + return -ETIMEDOUT; +} + +static int rcsi2_phtw_write_array(struct rcar_csi2 *priv, + const struct phtw_value *values) +{ + const struct phtw_value *value; + int ret; + + for (value = values; value->data || value->code; value++) { + ret = rcsi2_phtw_write(priv, value->data, value->code); + if (ret) + return ret; + } + + return 0; +} + +static int rcsi2_phtw_write_mbps(struct rcar_csi2 *priv, unsigned int mbps, + const struct rcsi2_mbps_reg *values, u16 code) +{ + const struct rcsi2_mbps_reg *value; + + for (value = values; value->mbps; value++) + if (value->mbps >= mbps) + break; + + if (!value->mbps) { + dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)", mbps); + return -ERANGE; + } + + return rcsi2_phtw_write(priv, value->reg, code); +} + +static int rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, unsigned int mbps) +{ + static const struct phtw_value step1[] = { + { .data = 0xcc, .code = 0xe2 }, + { .data = 0x01, .code = 0xe3 }, + { .data = 0x11, .code = 0xe4 }, + { .data = 0x01, .code = 0xe5 }, + { .data = 0x10, .code = 0x04 }, + { /* sentinel */ }, + }; + + static const struct phtw_value step2[] = { + { .data = 0x38, .code = 0x08 }, + { .data = 0x01, .code = 0x00 }, + { .data = 0x4b, .code = 0xac }, + { .data = 0x03, .code = 0x00 }, + { .data = 0x80, .code = 0x07 }, + { /* sentinel */ }, + }; + + int ret; + + ret = rcsi2_phtw_write_array(priv, step1); + if (ret) + return ret; + + if (mbps <= 250) { + ret = rcsi2_phtw_write(priv, 0x39, 0x05); + if (ret) + return ret; + + ret = rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_h3_v3h_m3n, + 0xf1); + if (ret) + return ret; + } + + return rcsi2_phtw_write_array(priv, step2); +} + +static int rcsi2_init_phtw_v3m_e3(struct rcar_csi2 *priv, unsigned int mbps) +{ + static const struct phtw_value step1[] = { + { .data = 0xed, .code = 0x34 }, + { .data = 0xed, .code = 0x44 }, + { .data = 0xed, .code = 0x54 }, + { .data = 0xed, .code = 0x84 }, + { .data = 0xed, .code = 0x94 }, + { /* sentinel */ }, + }; + + int ret; + + ret = rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3m_e3, 0x44); + if (ret) + return ret; + + return rcsi2_phtw_write_array(priv, step1); +} + +/* ----------------------------------------------------------------------------- + * Platform Device Driver. + */ + +static const struct media_entity_operations rcar_csi2_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int rcsi2_probe_resources(struct rcar_csi2 *priv, + struct platform_device *pdev) +{ + struct resource *res; + int irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + return 0; +} + +static const struct rcar_csi2_info rcar_csi2_info_r8a7795 = { + .init_phtw = rcsi2_init_phtw_h3_v3h_m3n, + .hsfreqrange = hsfreqrange_h3_v3h_m3n, + .csi0clkfreqrange = 0x20, + .clear_ulps = true, +}; + +static const struct rcar_csi2_info rcar_csi2_info_r8a7795es1 = { + .hsfreqrange = hsfreqrange_m3w_h3es1, +}; + +static const struct rcar_csi2_info rcar_csi2_info_r8a7796 = { + .hsfreqrange = hsfreqrange_m3w_h3es1, +}; + +static const struct rcar_csi2_info rcar_csi2_info_r8a77965 = { + .init_phtw = rcsi2_init_phtw_h3_v3h_m3n, + .hsfreqrange = hsfreqrange_h3_v3h_m3n, + .csi0clkfreqrange = 0x20, + .clear_ulps = true, +}; + +static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = { + .init_phtw = rcsi2_init_phtw_v3m_e3, +}; + +static const struct of_device_id rcar_csi2_of_table[] = { + { + .compatible = "renesas,r8a7795-csi2", + .data = &rcar_csi2_info_r8a7795, + }, + { + .compatible = "renesas,r8a7796-csi2", + .data = &rcar_csi2_info_r8a7796, + }, + { + .compatible = "renesas,r8a77965-csi2", + .data = &rcar_csi2_info_r8a77965, + }, + { + .compatible = "renesas,r8a77970-csi2", + .data = &rcar_csi2_info_r8a77970, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, rcar_csi2_of_table); + +static const struct soc_device_attribute r8a7795es1[] = { + { + .soc_id = "r8a7795", .revision = "ES1.*", + .data = &rcar_csi2_info_r8a7795es1, + }, + { /* sentinel */ }, +}; + +static int rcsi2_probe(struct platform_device *pdev) +{ + const struct soc_device_attribute *attr; + struct rcar_csi2 *priv; + unsigned int i; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->info = of_device_get_match_data(&pdev->dev); + + /* + * r8a7795 ES1.x behaves differently than the ES2.0+ but doesn't + * have it's own compatible string. + */ + attr = soc_device_match(r8a7795es1); + if (attr) + priv->info = attr->data; + + priv->dev = &pdev->dev; + + mutex_init(&priv->lock); + priv->stream_count = 0; + + ret = rcsi2_probe_resources(priv, pdev); + if (ret) { + dev_err(priv->dev, "Failed to get resources\n"); + return ret; + } + + platform_set_drvdata(pdev, priv); + + ret = rcsi2_parse_dt(priv); + if (ret) + return ret; + + priv->subdev.owner = THIS_MODULE; + priv->subdev.dev = &pdev->dev; + v4l2_subdev_init(&priv->subdev, &rcar_csi2_subdev_ops); + v4l2_set_subdevdata(&priv->subdev, &pdev->dev); + snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s %s", + KBUILD_MODNAME, dev_name(&pdev->dev)); + priv->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + + priv->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + priv->subdev.entity.ops = &rcar_csi2_entity_ops; + + priv->pads[RCAR_CSI2_SINK].flags = MEDIA_PAD_FL_SINK; + for (i = RCAR_CSI2_SOURCE_VC0; i < NR_OF_RCAR_CSI2_PAD; i++) + priv->pads[i].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&priv->subdev.entity, NR_OF_RCAR_CSI2_PAD, + priv->pads); + if (ret) + goto error; + + pm_runtime_enable(&pdev->dev); + + ret = v4l2_async_register_subdev(&priv->subdev); + if (ret < 0) + goto error; + + dev_info(priv->dev, "%d lanes found\n", priv->lanes); + + return 0; + +error: + v4l2_async_notifier_unregister(&priv->notifier); + v4l2_async_notifier_cleanup(&priv->notifier); + + return ret; +} + +static int rcsi2_remove(struct platform_device *pdev) +{ + struct rcar_csi2 *priv = platform_get_drvdata(pdev); + + v4l2_async_notifier_unregister(&priv->notifier); + v4l2_async_notifier_cleanup(&priv->notifier); + v4l2_async_unregister_subdev(&priv->subdev); + + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static struct platform_driver rcar_csi2_pdrv = { + .remove = rcsi2_remove, + .probe = rcsi2_probe, + .driver = { + .name = "rcar-csi2", + .of_match_table = rcar_csi2_of_table, + }, +}; + +module_platform_driver(rcar_csi2_pdrv); + +MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>"); +MODULE_DESCRIPTION("Renesas R-Car MIPI CSI-2 receiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c index 4a40e6ad1be7..ac07f99e3516 100644 --- a/drivers/media/platform/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/rcar-vin/rcar-dma.c @@ -16,6 +16,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/pm_runtime.h> #include <media/videobuf2-dma-contig.h> @@ -33,21 +34,23 @@ #define VNELPRC_REG 0x10 /* Video n End Line Pre-Clip Register */ #define VNSPPRC_REG 0x14 /* Video n Start Pixel Pre-Clip Register */ #define VNEPPRC_REG 0x18 /* Video n End Pixel Pre-Clip Register */ -#define VNSLPOC_REG 0x1C /* Video n Start Line Post-Clip Register */ -#define VNELPOC_REG 0x20 /* Video n End Line Post-Clip Register */ -#define VNSPPOC_REG 0x24 /* Video n Start Pixel Post-Clip Register */ -#define VNEPPOC_REG 0x28 /* Video n End Pixel Post-Clip Register */ #define VNIS_REG 0x2C /* Video n Image Stride Register */ #define VNMB_REG(m) (0x30 + ((m) << 2)) /* Video n Memory Base m Register */ #define VNIE_REG 0x40 /* Video n Interrupt Enable Register */ #define VNINTS_REG 0x44 /* Video n Interrupt Status Register */ #define VNSI_REG 0x48 /* Video n Scanline Interrupt Register */ #define VNMTC_REG 0x4C /* Video n Memory Transfer Control Register */ -#define VNYS_REG 0x50 /* Video n Y Scale Register */ -#define VNXS_REG 0x54 /* Video n X Scale Register */ #define VNDMR_REG 0x58 /* Video n Data Mode Register */ #define VNDMR2_REG 0x5C /* Video n Data Mode Register 2 */ #define VNUVAOF_REG 0x60 /* Video n UV Address Offset Register */ + +/* Register offsets specific for Gen2 */ +#define VNSLPOC_REG 0x1C /* Video n Start Line Post-Clip Register */ +#define VNELPOC_REG 0x20 /* Video n End Line Post-Clip Register */ +#define VNSPPOC_REG 0x24 /* Video n Start Pixel Post-Clip Register */ +#define VNEPPOC_REG 0x28 /* Video n End Pixel Post-Clip Register */ +#define VNYS_REG 0x50 /* Video n Y Scale Register */ +#define VNXS_REG 0x54 /* Video n X Scale Register */ #define VNC1A_REG 0x80 /* Video n Coefficient Set C1A Register */ #define VNC1B_REG 0x84 /* Video n Coefficient Set C1B Register */ #define VNC1C_REG 0x88 /* Video n Coefficient Set C1C Register */ @@ -73,9 +76,13 @@ #define VNC8B_REG 0xF4 /* Video n Coefficient Set C8B Register */ #define VNC8C_REG 0xF8 /* Video n Coefficient Set C8C Register */ +/* Register offsets specific for Gen3 */ +#define VNCSI_IFMD_REG 0x20 /* Video n CSI2 Interface Mode Register */ /* Register bit fields for R-Car VIN */ /* Video n Main Control Register bits */ +#define VNMC_DPINE (1 << 27) /* Gen3 specific */ +#define VNMC_SCLE (1 << 26) /* Gen3 specific */ #define VNMC_FOC (1 << 21) #define VNMC_YCAL (1 << 19) #define VNMC_INF_YUV8_BT656 (0 << 16) @@ -119,6 +126,12 @@ #define VNDMR2_FTEV (1 << 17) #define VNDMR2_VLV(n) ((n & 0xf) << 12) +/* Video n CSI2 Interface Mode Register (Gen3) */ +#define VNCSI_IFMD_DES1 (1 << 26) +#define VNCSI_IFMD_DES0 (1 << 25) +#define VNCSI_IFMD_CSI_CHSEL(n) (((n) & 0xf) << 0) +#define VNCSI_IFMD_CSI_CHSEL_MASK 0xf + struct rvin_buffer { struct vb2_v4l2_buffer vb; struct list_head list; @@ -138,267 +151,6 @@ static u32 rvin_read(struct rvin_dev *vin, u32 offset) return ioread32(vin->base + offset); } -static int rvin_setup(struct rvin_dev *vin) -{ - u32 vnmc, dmr, dmr2, interrupts; - v4l2_std_id std; - bool progressive = false, output_is_yuv = false, input_is_yuv = false; - - switch (vin->format.field) { - case V4L2_FIELD_TOP: - vnmc = VNMC_IM_ODD; - break; - case V4L2_FIELD_BOTTOM: - vnmc = VNMC_IM_EVEN; - break; - case V4L2_FIELD_INTERLACED: - /* Default to TB */ - vnmc = VNMC_IM_FULL; - /* Use BT if video standard can be read and is 60 Hz format */ - if (!v4l2_subdev_call(vin_to_source(vin), video, g_std, &std)) { - if (std & V4L2_STD_525_60) - vnmc = VNMC_IM_FULL | VNMC_FOC; - } - break; - case V4L2_FIELD_INTERLACED_TB: - vnmc = VNMC_IM_FULL; - break; - case V4L2_FIELD_INTERLACED_BT: - vnmc = VNMC_IM_FULL | VNMC_FOC; - break; - case V4L2_FIELD_ALTERNATE: - case V4L2_FIELD_NONE: - vnmc = VNMC_IM_ODD_EVEN; - progressive = true; - break; - default: - vnmc = VNMC_IM_ODD; - break; - } - - /* - * Input interface - */ - switch (vin->digital->code) { - case MEDIA_BUS_FMT_YUYV8_1X16: - /* BT.601/BT.1358 16bit YCbCr422 */ - vnmc |= VNMC_INF_YUV16; - input_is_yuv = true; - break; - case MEDIA_BUS_FMT_UYVY8_2X8: - /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ - vnmc |= vin->digital->mbus_cfg.type == V4L2_MBUS_BT656 ? - VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; - input_is_yuv = true; - break; - case MEDIA_BUS_FMT_RGB888_1X24: - vnmc |= VNMC_INF_RGB888; - break; - case MEDIA_BUS_FMT_UYVY10_2X10: - /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ - vnmc |= vin->digital->mbus_cfg.type == V4L2_MBUS_BT656 ? - VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; - input_is_yuv = true; - break; - default: - break; - } - - /* Enable VSYNC Field Toogle mode after one VSYNC input */ - dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1); - - /* Hsync Signal Polarity Select */ - if (!(vin->digital->mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) - dmr2 |= VNDMR2_HPS; - - /* Vsync Signal Polarity Select */ - if (!(vin->digital->mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) - dmr2 |= VNDMR2_VPS; - - /* - * Output format - */ - switch (vin->format.pixelformat) { - case V4L2_PIX_FMT_NV16: - rvin_write(vin, - ALIGN(vin->format.width * vin->format.height, 0x80), - VNUVAOF_REG); - dmr = VNDMR_DTMD_YCSEP; - output_is_yuv = true; - break; - case V4L2_PIX_FMT_YUYV: - dmr = VNDMR_BPSM; - output_is_yuv = true; - break; - case V4L2_PIX_FMT_UYVY: - dmr = 0; - output_is_yuv = true; - break; - case V4L2_PIX_FMT_XRGB555: - dmr = VNDMR_DTMD_ARGB1555; - break; - case V4L2_PIX_FMT_RGB565: - dmr = 0; - break; - case V4L2_PIX_FMT_XBGR32: - /* Note: not supported on M1 */ - dmr = VNDMR_EXRGB; - break; - default: - vin_err(vin, "Invalid pixelformat (0x%x)\n", - vin->format.pixelformat); - return -EINVAL; - } - - /* Always update on field change */ - vnmc |= VNMC_VUP; - - /* If input and output use the same colorspace, use bypass mode */ - if (input_is_yuv == output_is_yuv) - vnmc |= VNMC_BPS; - - /* Progressive or interlaced mode */ - interrupts = progressive ? VNIE_FIE : VNIE_EFE; - - /* Ack interrupts */ - rvin_write(vin, interrupts, VNINTS_REG); - /* Enable interrupts */ - rvin_write(vin, interrupts, VNIE_REG); - /* Start capturing */ - rvin_write(vin, dmr, VNDMR_REG); - rvin_write(vin, dmr2, VNDMR2_REG); - - /* Enable module */ - rvin_write(vin, vnmc | VNMC_ME, VNMC_REG); - - return 0; -} - -static void rvin_disable_interrupts(struct rvin_dev *vin) -{ - rvin_write(vin, 0, VNIE_REG); -} - -static u32 rvin_get_interrupt_status(struct rvin_dev *vin) -{ - return rvin_read(vin, VNINTS_REG); -} - -static void rvin_ack_interrupt(struct rvin_dev *vin) -{ - rvin_write(vin, rvin_read(vin, VNINTS_REG), VNINTS_REG); -} - -static bool rvin_capture_active(struct rvin_dev *vin) -{ - return rvin_read(vin, VNMS_REG) & VNMS_CA; -} - -static enum v4l2_field rvin_get_active_field(struct rvin_dev *vin, u32 vnms) -{ - if (vin->format.field == V4L2_FIELD_ALTERNATE) { - /* If FS is set it's a Even field */ - if (vnms & VNMS_FS) - return V4L2_FIELD_BOTTOM; - return V4L2_FIELD_TOP; - } - - return vin->format.field; -} - -static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr) -{ - const struct rvin_video_format *fmt; - int offsetx, offsety; - dma_addr_t offset; - - fmt = rvin_format_from_pixel(vin->format.pixelformat); - - /* - * There is no HW support for composition do the beast we can - * by modifying the buffer offset - */ - offsetx = vin->compose.left * fmt->bpp; - offsety = vin->compose.top * vin->format.bytesperline; - offset = addr + offsetx + offsety; - - /* - * The address needs to be 128 bytes aligned. Driver should never accept - * settings that do not satisfy this in the first place... - */ - if (WARN_ON((offsetx | offsety | offset) & HW_BUFFER_MASK)) - return; - - rvin_write(vin, offset, VNMB_REG(slot)); -} - -/* - * Moves a buffer from the queue to the HW slot. If no buffer is - * available use the scratch buffer. The scratch buffer is never - * returned to userspace, its only function is to enable the capture - * loop to keep running. - */ -static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot) -{ - struct rvin_buffer *buf; - struct vb2_v4l2_buffer *vbuf; - dma_addr_t phys_addr; - - /* A already populated slot shall never be overwritten. */ - if (WARN_ON(vin->queue_buf[slot] != NULL)) - return; - - vin_dbg(vin, "Filling HW slot: %d\n", slot); - - if (list_empty(&vin->buf_list)) { - vin->queue_buf[slot] = NULL; - phys_addr = vin->scratch_phys; - } else { - /* Keep track of buffer we give to HW */ - buf = list_entry(vin->buf_list.next, struct rvin_buffer, list); - vbuf = &buf->vb; - list_del_init(to_buf_list(vbuf)); - vin->queue_buf[slot] = vbuf; - - /* Setup DMA */ - phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); - } - - rvin_set_slot_addr(vin, slot, phys_addr); -} - -static int rvin_capture_start(struct rvin_dev *vin) -{ - int slot, ret; - - for (slot = 0; slot < HW_BUFFER_NUM; slot++) - rvin_fill_hw_slot(vin, slot); - - rvin_crop_scale_comp(vin); - - ret = rvin_setup(vin); - if (ret) - return ret; - - vin_dbg(vin, "Starting to capture\n"); - - /* Continuous Frame Capture Mode */ - rvin_write(vin, VNFC_C_FRAME, VNFC_REG); - - vin->state = RUNNING; - - return 0; -} - -static void rvin_capture_stop(struct rvin_dev *vin) -{ - /* Set continuous & single transfer off */ - rvin_write(vin, 0, VNFC_REG); - - /* Disable module */ - rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG); -} - /* ----------------------------------------------------------------------------- * Crop and Scaling Gen2 */ @@ -775,28 +527,10 @@ static void rvin_set_coeff(struct rvin_dev *vin, unsigned short xs) rvin_write(vin, p_set->coeff_set[23], VNC8C_REG); } -void rvin_crop_scale_comp(struct rvin_dev *vin) +static void rvin_crop_scale_comp_gen2(struct rvin_dev *vin) { u32 xs, ys; - /* Set Start/End Pixel/Line Pre-Clip */ - rvin_write(vin, vin->crop.left, VNSPPRC_REG); - rvin_write(vin, vin->crop.left + vin->crop.width - 1, VNEPPRC_REG); - switch (vin->format.field) { - case V4L2_FIELD_INTERLACED: - case V4L2_FIELD_INTERLACED_TB: - case V4L2_FIELD_INTERLACED_BT: - rvin_write(vin, vin->crop.top / 2, VNSLPRC_REG); - rvin_write(vin, (vin->crop.top + vin->crop.height) / 2 - 1, - VNELPRC_REG); - break; - default: - rvin_write(vin, vin->crop.top, VNSLPRC_REG); - rvin_write(vin, vin->crop.top + vin->crop.height - 1, - VNELPRC_REG); - break; - } - /* Set scaling coefficient */ ys = 0; if (vin->crop.height != vin->compose.height) @@ -834,11 +568,6 @@ void rvin_crop_scale_comp(struct rvin_dev *vin) break; } - if (vin->format.pixelformat == V4L2_PIX_FMT_NV16) - rvin_write(vin, ALIGN(vin->format.width, 0x20), VNIS_REG); - else - rvin_write(vin, ALIGN(vin->format.width, 0x10), VNIS_REG); - vin_dbg(vin, "Pre-Clip: %ux%u@%u:%u YS: %d XS: %d Post-Clip: %ux%u@%u:%u\n", vin->crop.width, vin->crop.height, vin->crop.left, @@ -846,12 +575,299 @@ void rvin_crop_scale_comp(struct rvin_dev *vin) 0, 0); } -void rvin_scale_try(struct rvin_dev *vin, struct v4l2_pix_format *pix, - u32 width, u32 height) +void rvin_crop_scale_comp(struct rvin_dev *vin) { - /* All VIN channels on Gen2 have scalers */ - pix->width = width; - pix->height = height; + /* Set Start/End Pixel/Line Pre-Clip */ + rvin_write(vin, vin->crop.left, VNSPPRC_REG); + rvin_write(vin, vin->crop.left + vin->crop.width - 1, VNEPPRC_REG); + + switch (vin->format.field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + rvin_write(vin, vin->crop.top / 2, VNSLPRC_REG); + rvin_write(vin, (vin->crop.top + vin->crop.height) / 2 - 1, + VNELPRC_REG); + break; + default: + rvin_write(vin, vin->crop.top, VNSLPRC_REG); + rvin_write(vin, vin->crop.top + vin->crop.height - 1, + VNELPRC_REG); + break; + } + + /* TODO: Add support for the UDS scaler. */ + if (vin->info->model != RCAR_GEN3) + rvin_crop_scale_comp_gen2(vin); + + if (vin->format.pixelformat == V4L2_PIX_FMT_NV16) + rvin_write(vin, ALIGN(vin->format.width, 0x20), VNIS_REG); + else + rvin_write(vin, ALIGN(vin->format.width, 0x10), VNIS_REG); +} + +/* ----------------------------------------------------------------------------- + * Hardware setup + */ + +static int rvin_setup(struct rvin_dev *vin) +{ + u32 vnmc, dmr, dmr2, interrupts; + bool progressive = false, output_is_yuv = false, input_is_yuv = false; + + switch (vin->format.field) { + case V4L2_FIELD_TOP: + vnmc = VNMC_IM_ODD; + break; + case V4L2_FIELD_BOTTOM: + vnmc = VNMC_IM_EVEN; + break; + case V4L2_FIELD_INTERLACED: + /* Default to TB */ + vnmc = VNMC_IM_FULL; + /* Use BT if video standard can be read and is 60 Hz format */ + if (!vin->info->use_mc && vin->std & V4L2_STD_525_60) + vnmc = VNMC_IM_FULL | VNMC_FOC; + break; + case V4L2_FIELD_INTERLACED_TB: + vnmc = VNMC_IM_FULL; + break; + case V4L2_FIELD_INTERLACED_BT: + vnmc = VNMC_IM_FULL | VNMC_FOC; + break; + case V4L2_FIELD_NONE: + vnmc = VNMC_IM_ODD_EVEN; + progressive = true; + break; + default: + vnmc = VNMC_IM_ODD; + break; + } + + /* + * Input interface + */ + switch (vin->mbus_code) { + case MEDIA_BUS_FMT_YUYV8_1X16: + /* BT.601/BT.1358 16bit YCbCr422 */ + vnmc |= VNMC_INF_YUV16; + input_is_yuv = true; + break; + case MEDIA_BUS_FMT_UYVY8_1X16: + vnmc |= VNMC_INF_YUV16 | VNMC_YCAL; + input_is_yuv = true; + break; + case MEDIA_BUS_FMT_UYVY8_2X8: + /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ + vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? + VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; + input_is_yuv = true; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + vnmc |= VNMC_INF_RGB888; + break; + case MEDIA_BUS_FMT_UYVY10_2X10: + /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ + vnmc |= vin->mbus_cfg.type == V4L2_MBUS_BT656 ? + VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; + input_is_yuv = true; + break; + default: + break; + } + + /* Enable VSYNC Field Toogle mode after one VSYNC input */ + if (vin->info->model == RCAR_GEN3) + dmr2 = VNDMR2_FTEV; + else + dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1); + + /* Hsync Signal Polarity Select */ + if (!(vin->mbus_cfg.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) + dmr2 |= VNDMR2_HPS; + + /* Vsync Signal Polarity Select */ + if (!(vin->mbus_cfg.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) + dmr2 |= VNDMR2_VPS; + + /* + * Output format + */ + switch (vin->format.pixelformat) { + case V4L2_PIX_FMT_NV16: + rvin_write(vin, + ALIGN(vin->format.width * vin->format.height, 0x80), + VNUVAOF_REG); + dmr = VNDMR_DTMD_YCSEP; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_YUYV: + dmr = VNDMR_BPSM; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_UYVY: + dmr = 0; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_XRGB555: + dmr = VNDMR_DTMD_ARGB1555; + break; + case V4L2_PIX_FMT_RGB565: + dmr = 0; + break; + case V4L2_PIX_FMT_XBGR32: + /* Note: not supported on M1 */ + dmr = VNDMR_EXRGB; + break; + default: + vin_err(vin, "Invalid pixelformat (0x%x)\n", + vin->format.pixelformat); + return -EINVAL; + } + + /* Always update on field change */ + vnmc |= VNMC_VUP; + + /* If input and output use the same colorspace, use bypass mode */ + if (input_is_yuv == output_is_yuv) + vnmc |= VNMC_BPS; + + if (vin->info->model == RCAR_GEN3) { + /* Select between CSI-2 and Digital input */ + if (vin->mbus_cfg.type == V4L2_MBUS_CSI2) + vnmc &= ~VNMC_DPINE; + else + vnmc |= VNMC_DPINE; + } + + /* Progressive or interlaced mode */ + interrupts = progressive ? VNIE_FIE : VNIE_EFE; + + /* Ack interrupts */ + rvin_write(vin, interrupts, VNINTS_REG); + /* Enable interrupts */ + rvin_write(vin, interrupts, VNIE_REG); + /* Start capturing */ + rvin_write(vin, dmr, VNDMR_REG); + rvin_write(vin, dmr2, VNDMR2_REG); + + /* Enable module */ + rvin_write(vin, vnmc | VNMC_ME, VNMC_REG); + + return 0; +} + +static void rvin_disable_interrupts(struct rvin_dev *vin) +{ + rvin_write(vin, 0, VNIE_REG); +} + +static u32 rvin_get_interrupt_status(struct rvin_dev *vin) +{ + return rvin_read(vin, VNINTS_REG); +} + +static void rvin_ack_interrupt(struct rvin_dev *vin) +{ + rvin_write(vin, rvin_read(vin, VNINTS_REG), VNINTS_REG); +} + +static bool rvin_capture_active(struct rvin_dev *vin) +{ + return rvin_read(vin, VNMS_REG) & VNMS_CA; +} + +static void rvin_set_slot_addr(struct rvin_dev *vin, int slot, dma_addr_t addr) +{ + const struct rvin_video_format *fmt; + int offsetx, offsety; + dma_addr_t offset; + + fmt = rvin_format_from_pixel(vin->format.pixelformat); + + /* + * There is no HW support for composition do the beast we can + * by modifying the buffer offset + */ + offsetx = vin->compose.left * fmt->bpp; + offsety = vin->compose.top * vin->format.bytesperline; + offset = addr + offsetx + offsety; + + /* + * The address needs to be 128 bytes aligned. Driver should never accept + * settings that do not satisfy this in the first place... + */ + if (WARN_ON((offsetx | offsety | offset) & HW_BUFFER_MASK)) + return; + + rvin_write(vin, offset, VNMB_REG(slot)); +} + +/* + * Moves a buffer from the queue to the HW slot. If no buffer is + * available use the scratch buffer. The scratch buffer is never + * returned to userspace, its only function is to enable the capture + * loop to keep running. + */ +static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot) +{ + struct rvin_buffer *buf; + struct vb2_v4l2_buffer *vbuf; + dma_addr_t phys_addr; + + /* A already populated slot shall never be overwritten. */ + if (WARN_ON(vin->queue_buf[slot] != NULL)) + return; + + vin_dbg(vin, "Filling HW slot: %d\n", slot); + + if (list_empty(&vin->buf_list)) { + vin->queue_buf[slot] = NULL; + phys_addr = vin->scratch_phys; + } else { + /* Keep track of buffer we give to HW */ + buf = list_entry(vin->buf_list.next, struct rvin_buffer, list); + vbuf = &buf->vb; + list_del_init(to_buf_list(vbuf)); + vin->queue_buf[slot] = vbuf; + + /* Setup DMA */ + phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); + } + + rvin_set_slot_addr(vin, slot, phys_addr); +} + +static int rvin_capture_start(struct rvin_dev *vin) +{ + int slot, ret; + + for (slot = 0; slot < HW_BUFFER_NUM; slot++) + rvin_fill_hw_slot(vin, slot); + + rvin_crop_scale_comp(vin); + + ret = rvin_setup(vin); + if (ret) + return ret; + + vin_dbg(vin, "Starting to capture\n"); + + /* Continuous Frame Capture Mode */ + rvin_write(vin, VNFC_C_FRAME, VNFC_REG); + + vin->state = RUNNING; + + return 0; +} + +static void rvin_capture_stop(struct rvin_dev *vin) +{ + /* Set continuous & single transfer off */ + rvin_write(vin, 0, VNFC_REG); + + /* Disable module */ + rvin_write(vin, rvin_read(vin, VNMC_REG) & ~VNMC_ME, VNMC_REG); } /* ----------------------------------------------------------------------------- @@ -896,7 +912,7 @@ static irqreturn_t rvin_irq(int irq, void *data) /* Capture frame */ if (vin->queue_buf[slot]) { - vin->queue_buf[slot]->field = rvin_get_active_field(vin, vnms); + vin->queue_buf[slot]->field = vin->format.field; vin->queue_buf[slot]->sequence = vin->sequence; vin->queue_buf[slot]->vb2_buf.timestamp = ktime_get_ns(); vb2_buffer_done(&vin->queue_buf[slot]->vb2_buf, @@ -984,10 +1000,127 @@ static void rvin_buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&vin->qlock, flags); } +static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd, + struct media_pad *pad) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + fmt.pad = pad->index; + if (v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt)) + return -EPIPE; + + switch (fmt.format.code) { + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_UYVY10_2X10: + case MEDIA_BUS_FMT_RGB888_1X24: + vin->mbus_code = fmt.format.code; + break; + default: + return -EPIPE; + } + + switch (fmt.format.field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_NONE: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: + /* Supported natively */ + break; + case V4L2_FIELD_ALTERNATE: + switch (vin->format.field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_NONE: + break; + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_SEQ_TB: + case V4L2_FIELD_SEQ_BT: + /* Use VIN hardware to combine the two fields */ + fmt.format.height *= 2; + break; + default: + return -EPIPE; + } + break; + default: + return -EPIPE; + } + + if (fmt.format.width != vin->format.width || + fmt.format.height != vin->format.height || + fmt.format.code != vin->mbus_code) + return -EPIPE; + + return 0; +} + +static int rvin_set_stream(struct rvin_dev *vin, int on) +{ + struct media_pipeline *pipe; + struct media_device *mdev; + struct v4l2_subdev *sd; + struct media_pad *pad; + int ret; + + /* No media controller used, simply pass operation to subdevice. */ + if (!vin->info->use_mc) { + ret = v4l2_subdev_call(vin->digital->subdev, video, s_stream, + on); + + return ret == -ENOIOCTLCMD ? 0 : ret; + } + + pad = media_entity_remote_pad(&vin->pad); + if (!pad) + return -EPIPE; + + sd = media_entity_to_v4l2_subdev(pad->entity); + + if (!on) { + media_pipeline_stop(&vin->vdev.entity); + return v4l2_subdev_call(sd, video, s_stream, 0); + } + + ret = rvin_mc_validate_format(vin, sd, pad); + if (ret) + return ret; + + /* + * The graph lock needs to be taken to protect concurrent + * starts of multiple VIN instances as they might share + * a common subdevice down the line and then should use + * the same pipe. + */ + mdev = vin->vdev.entity.graph_obj.mdev; + mutex_lock(&mdev->graph_mutex); + pipe = sd->entity.pipe ? sd->entity.pipe : &vin->vdev.pipe; + ret = __media_pipeline_start(&vin->vdev.entity, pipe); + mutex_unlock(&mdev->graph_mutex); + if (ret) + return ret; + + ret = v4l2_subdev_call(sd, video, s_stream, 1); + if (ret == -ENOIOCTLCMD) + ret = 0; + if (ret) + media_pipeline_stop(&vin->vdev.entity); + + return ret; +} + static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count) { struct rvin_dev *vin = vb2_get_drv_priv(vq); - struct v4l2_subdev *sd; unsigned long flags; int ret; @@ -1002,8 +1135,13 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count) return -ENOMEM; } - sd = vin_to_source(vin); - v4l2_subdev_call(sd, video, s_stream, 1); + ret = rvin_set_stream(vin, 1); + if (ret) { + spin_lock_irqsave(&vin->qlock, flags); + return_all_buffers(vin, VB2_BUF_STATE_QUEUED); + spin_unlock_irqrestore(&vin->qlock, flags); + goto out; + } spin_lock_irqsave(&vin->qlock, flags); @@ -1012,11 +1150,11 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count) ret = rvin_capture_start(vin); if (ret) { return_all_buffers(vin, VB2_BUF_STATE_QUEUED); - v4l2_subdev_call(sd, video, s_stream, 0); + rvin_set_stream(vin, 0); } spin_unlock_irqrestore(&vin->qlock, flags); - +out: if (ret) dma_free_coherent(vin->dev, vin->format.sizeimage, vin->scratch, vin->scratch_phys); @@ -1027,7 +1165,6 @@ static int rvin_start_streaming(struct vb2_queue *vq, unsigned int count) static void rvin_stop_streaming(struct vb2_queue *vq) { struct rvin_dev *vin = vb2_get_drv_priv(vq); - struct v4l2_subdev *sd; unsigned long flags; int retries = 0; @@ -1066,8 +1203,7 @@ static void rvin_stop_streaming(struct vb2_queue *vq) spin_unlock_irqrestore(&vin->qlock, flags); - sd = vin_to_source(vin); - v4l2_subdev_call(sd, video, s_stream, 0); + rvin_set_stream(vin, 0); /* disable interrupts */ rvin_disable_interrupts(vin); @@ -1087,14 +1223,14 @@ static const struct vb2_ops rvin_qops = { .wait_finish = vb2_ops_wait_finish, }; -void rvin_dma_remove(struct rvin_dev *vin) +void rvin_dma_unregister(struct rvin_dev *vin) { mutex_destroy(&vin->lock); v4l2_device_unregister(&vin->v4l2_dev); } -int rvin_dma_probe(struct rvin_dev *vin, int irq) +int rvin_dma_register(struct rvin_dev *vin, int irq) { struct vb2_queue *q = &vin->queue; int i, ret; @@ -1142,7 +1278,43 @@ int rvin_dma_probe(struct rvin_dev *vin, int irq) return 0; error: - rvin_dma_remove(vin); + rvin_dma_unregister(vin); + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Gen3 CHSEL manipulation + */ + +/* + * There is no need to have locking around changing the routing + * as it's only possible to do so when no VIN in the group is + * streaming so nothing can race with the VNMC register. + */ +int rvin_set_channel_routing(struct rvin_dev *vin, u8 chsel) +{ + u32 ifmd, vnmc; + int ret; + + ret = pm_runtime_get_sync(vin->dev); + if (ret < 0) + return ret; + + /* Make register writes take effect immediately. */ + vnmc = rvin_read(vin, VNMC_REG); + rvin_write(vin, vnmc & ~VNMC_VUP, VNMC_REG); + + ifmd = VNCSI_IFMD_DES1 | VNCSI_IFMD_DES0 | VNCSI_IFMD_CSI_CHSEL(chsel); + + rvin_write(vin, ifmd, VNCSI_IFMD_REG); + + vin_dbg(vin, "Set IFMD 0x%x\n", ifmd); + + /* Restore VNMC. */ + rvin_write(vin, vnmc, VNMC_REG); + + pm_runtime_put(vin->dev); return ret; } diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index b479b882da12..e78fba84d590 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -18,13 +18,16 @@ #include <media/v4l2-event.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-mc.h> #include <media/v4l2-rect.h> #include "rcar-vin.h" #define RVIN_DEFAULT_FORMAT V4L2_PIX_FMT_YUYV -#define RVIN_MAX_WIDTH 2048 -#define RVIN_MAX_HEIGHT 2048 +#define RVIN_DEFAULT_WIDTH 800 +#define RVIN_DEFAULT_HEIGHT 600 +#define RVIN_DEFAULT_FIELD V4L2_FIELD_NONE +#define RVIN_DEFAULT_COLORSPACE V4L2_COLORSPACE_SRGB /* ----------------------------------------------------------------------------- * Format Conversions @@ -88,99 +91,111 @@ static u32 rvin_format_sizeimage(struct v4l2_pix_format *pix) return pix->bytesperline * pix->height; } -/* ----------------------------------------------------------------------------- - * V4L2 - */ - -static void rvin_reset_crop_compose(struct rvin_dev *vin) +static void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix) { - vin->crop.top = vin->crop.left = 0; - vin->crop.width = vin->source.width; - vin->crop.height = vin->source.height; + u32 walign; - vin->compose.top = vin->compose.left = 0; - vin->compose.width = vin->format.width; - vin->compose.height = vin->format.height; + if (!rvin_format_from_pixel(pix->pixelformat) || + (vin->info->model == RCAR_M1 && + pix->pixelformat == V4L2_PIX_FMT_XBGR32)) + pix->pixelformat = RVIN_DEFAULT_FORMAT; + + switch (pix->field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_NONE: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + case V4L2_FIELD_INTERLACED: + break; + case V4L2_FIELD_ALTERNATE: + /* + * Driver does not (yet) support outputting ALTERNATE to a + * userspace. It does support outputting INTERLACED so use + * the VIN hardware to combine the two fields. + */ + pix->field = V4L2_FIELD_INTERLACED; + pix->height *= 2; + break; + default: + pix->field = RVIN_DEFAULT_FIELD; + break; + } + + /* HW limit width to a multiple of 32 (2^5) for NV16 else 2 (2^1) */ + walign = vin->format.pixelformat == V4L2_PIX_FMT_NV16 ? 5 : 1; + + /* Limit to VIN capabilities */ + v4l_bound_align_image(&pix->width, 2, vin->info->max_width, walign, + &pix->height, 4, vin->info->max_height, 2, 0); + + pix->bytesperline = rvin_format_bytesperline(pix); + pix->sizeimage = rvin_format_sizeimage(pix); + + vin_dbg(vin, "Format %ux%u bpl: %u size: %u\n", + pix->width, pix->height, pix->bytesperline, pix->sizeimage); } +/* ----------------------------------------------------------------------------- + * V4L2 + */ + static int rvin_reset_format(struct rvin_dev *vin) { struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = vin->digital->source_pad, }; - struct v4l2_mbus_framefmt *mf = &fmt.format; int ret; - fmt.pad = vin->digital->source_pad; - ret = v4l2_subdev_call(vin_to_source(vin), pad, get_fmt, NULL, &fmt); if (ret) return ret; - vin->format.width = mf->width; - vin->format.height = mf->height; - vin->format.colorspace = mf->colorspace; - vin->format.field = mf->field; + v4l2_fill_pix_format(&vin->format, &fmt.format); - /* - * If the subdevice uses ALTERNATE field mode and G_STD is - * implemented use the VIN HW to combine the two fields to - * one INTERLACED frame. The ALTERNATE field mode can still - * be requested in S_FMT and be respected, this is just the - * default which is applied at probing or when S_STD is called. - */ - if (vin->format.field == V4L2_FIELD_ALTERNATE && - v4l2_subdev_has_op(vin_to_source(vin), video, g_std)) - vin->format.field = V4L2_FIELD_INTERLACED; + rvin_format_align(vin, &vin->format); - switch (vin->format.field) { - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - case V4L2_FIELD_ALTERNATE: - vin->format.height /= 2; - break; - case V4L2_FIELD_NONE: - case V4L2_FIELD_INTERLACED_TB: - case V4L2_FIELD_INTERLACED_BT: - case V4L2_FIELD_INTERLACED: - break; - default: - vin->format.field = V4L2_FIELD_NONE; - break; - } - - rvin_reset_crop_compose(vin); + vin->source.top = 0; + vin->source.left = 0; + vin->source.width = vin->format.width; + vin->source.height = vin->format.height; - vin->format.bytesperline = rvin_format_bytesperline(&vin->format); - vin->format.sizeimage = rvin_format_sizeimage(&vin->format); + vin->crop = vin->source; + vin->compose = vin->source; return 0; } -static int __rvin_try_format_source(struct rvin_dev *vin, - u32 which, - struct v4l2_pix_format *pix, - struct rvin_source_fmt *source) +static int rvin_try_format(struct rvin_dev *vin, u32 which, + struct v4l2_pix_format *pix, + struct v4l2_rect *crop, struct v4l2_rect *compose) { - struct v4l2_subdev *sd; + struct v4l2_subdev *sd = vin_to_source(vin); struct v4l2_subdev_pad_config *pad_cfg; struct v4l2_subdev_format format = { .which = which, + .pad = vin->digital->source_pad, }; enum v4l2_field field; + u32 width, height; int ret; - sd = vin_to_source(vin); - - v4l2_fill_mbus_format(&format.format, pix, vin->digital->code); - pad_cfg = v4l2_subdev_alloc_pad_config(sd); if (pad_cfg == NULL) return -ENOMEM; - format.pad = vin->digital->source_pad; + if (!rvin_format_from_pixel(pix->pixelformat) || + (vin->info->model == RCAR_M1 && + pix->pixelformat == V4L2_PIX_FMT_XBGR32)) + pix->pixelformat = RVIN_DEFAULT_FORMAT; + + v4l2_fill_mbus_format(&format.format, pix, vin->mbus_code); + /* Allow the video device to override field and to scale */ field = pix->field; + width = pix->width; + height = pix->height; ret = v4l2_subdev_call(sd, pad, set_fmt, pad_cfg, &format); if (ret < 0 && ret != -ENOIOCTLCMD) @@ -188,92 +203,36 @@ static int __rvin_try_format_source(struct rvin_dev *vin, v4l2_fill_pix_format(pix, &format.format); - pix->field = field; - - source->width = pix->width; - source->height = pix->height; - - vin_dbg(vin, "Source resolution: %ux%u\n", source->width, - source->height); + if (crop) { + crop->top = 0; + crop->left = 0; + crop->width = pix->width; + crop->height = pix->height; -done: - v4l2_subdev_free_pad_config(pad_cfg); - return ret; -} - -static int __rvin_try_format(struct rvin_dev *vin, - u32 which, - struct v4l2_pix_format *pix, - struct rvin_source_fmt *source) -{ - u32 rwidth, rheight, walign; - int ret; - - /* Requested */ - rwidth = pix->width; - rheight = pix->height; - - /* Keep current field if no specific one is asked for */ - if (pix->field == V4L2_FIELD_ANY) - pix->field = vin->format.field; - - /* If requested format is not supported fallback to the default */ - if (!rvin_format_from_pixel(pix->pixelformat)) { - vin_dbg(vin, "Format 0x%x not found, using default 0x%x\n", - pix->pixelformat, RVIN_DEFAULT_FORMAT); - pix->pixelformat = RVIN_DEFAULT_FORMAT; + /* + * If source is ALTERNATE the driver will use the VIN hardware + * to INTERLACE it. The crop height then needs to be doubled. + */ + if (pix->field == V4L2_FIELD_ALTERNATE) + crop->height *= 2; } - /* Always recalculate */ - pix->bytesperline = 0; - pix->sizeimage = 0; + if (field != V4L2_FIELD_ANY) + pix->field = field; - /* Limit to source capabilities */ - ret = __rvin_try_format_source(vin, which, pix, source); - if (ret) - return ret; + pix->width = width; + pix->height = height; - switch (pix->field) { - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - case V4L2_FIELD_ALTERNATE: - pix->height /= 2; - source->height /= 2; - break; - case V4L2_FIELD_NONE: - case V4L2_FIELD_INTERLACED_TB: - case V4L2_FIELD_INTERLACED_BT: - case V4L2_FIELD_INTERLACED: - break; - default: - pix->field = V4L2_FIELD_NONE; - break; - } - - /* If source can't match format try if VIN can scale */ - if (source->width != rwidth || source->height != rheight) - rvin_scale_try(vin, pix, rwidth, rheight); - - /* HW limit width to a multiple of 32 (2^5) for NV16 else 2 (2^1) */ - walign = vin->format.pixelformat == V4L2_PIX_FMT_NV16 ? 5 : 1; + rvin_format_align(vin, pix); - /* Limit to VIN capabilities */ - v4l_bound_align_image(&pix->width, 2, RVIN_MAX_WIDTH, walign, - &pix->height, 4, RVIN_MAX_HEIGHT, 2, 0); - - pix->bytesperline = max_t(u32, pix->bytesperline, - rvin_format_bytesperline(pix)); - pix->sizeimage = max_t(u32, pix->sizeimage, - rvin_format_sizeimage(pix)); - - if (vin->chip == RCAR_M1 && pix->pixelformat == V4L2_PIX_FMT_XBGR32) { - vin_err(vin, "pixel format XBGR32 not supported on M1\n"); - return -EINVAL; + if (compose) { + compose->top = 0; + compose->left = 0; + compose->width = pix->width; + compose->height = pix->height; } - - vin_dbg(vin, "Requested %ux%u Got %ux%u bpl: %d size: %d\n", - rwidth, rheight, pix->width, pix->height, - pix->bytesperline, pix->sizeimage); +done: + v4l2_subdev_free_pad_config(pad_cfg); return 0; } @@ -294,33 +253,30 @@ static int rvin_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct rvin_dev *vin = video_drvdata(file); - struct rvin_source_fmt source; - return __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, - &source); + return rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, NULL, + NULL); } static int rvin_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct rvin_dev *vin = video_drvdata(file); - struct rvin_source_fmt source; + struct v4l2_rect crop, compose; int ret; if (vb2_is_busy(&vin->queue)) return -EBUSY; - ret = __rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix, - &source); + ret = rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix, + &crop, &compose); if (ret) return ret; - vin->source.width = source.width; - vin->source.height = source.height; - vin->format = f->fmt.pix; - - rvin_reset_crop_compose(vin); + vin->crop = crop; + vin->compose = compose; + vin->source = crop; return 0; } @@ -405,8 +361,8 @@ static int rvin_s_selection(struct file *file, void *fh, max_rect.height = vin->source.height; v4l2_rect_map_inside(&r, &max_rect); - v4l_bound_align_image(&r.width, 2, vin->source.width, 1, - &r.height, 4, vin->source.height, 2, 0); + v4l_bound_align_image(&r.width, 6, vin->source.width, 0, + &r.height, 2, vin->source.height, 0, 0); r.top = clamp_t(s32, r.top, 0, vin->source.height - r.height); r.left = clamp_t(s32, r.left, 0, vin->source.width - r.width); @@ -523,6 +479,8 @@ static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a) if (ret < 0) return ret; + vin->std = a; + /* Changing the standard will change the width/height */ return rvin_reset_format(vin); } @@ -530,9 +488,13 @@ static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a) static int rvin_g_std(struct file *file, void *priv, v4l2_std_id *a) { struct rvin_dev *vin = video_drvdata(file); - struct v4l2_subdev *sd = vin_to_source(vin); - return v4l2_subdev_call(sd, video, g_std, a); + if (v4l2_subdev_has_op(vin_to_source(vin), pad, dv_timings_cap)) + return -ENOIOCTLCMD; + + *a = vin->std; + + return 0; } static int rvin_subscribe_event(struct v4l2_fh *fh, @@ -697,6 +659,97 @@ static const struct v4l2_ioctl_ops rvin_ioctl_ops = { }; /* ----------------------------------------------------------------------------- + * V4L2 Media Controller + */ + +static void rvin_mc_try_format(struct rvin_dev *vin, + struct v4l2_pix_format *pix) +{ + /* + * The V4L2 specification clearly documents the colorspace fields + * as being set by drivers for capture devices. Using the values + * supplied by userspace thus wouldn't comply with the API. Until + * the API is updated force fixed vaules. + */ + pix->colorspace = RVIN_DEFAULT_COLORSPACE; + pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace); + pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace); + pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, pix->colorspace, + pix->ycbcr_enc); + + rvin_format_align(vin, pix); +} + +static int rvin_mc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rvin_dev *vin = video_drvdata(file); + + rvin_mc_try_format(vin, &f->fmt.pix); + + return 0; +} + +static int rvin_mc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rvin_dev *vin = video_drvdata(file); + + if (vb2_is_busy(&vin->queue)) + return -EBUSY; + + rvin_mc_try_format(vin, &f->fmt.pix); + + vin->format = f->fmt.pix; + + vin->crop.top = 0; + vin->crop.left = 0; + vin->crop.width = vin->format.width; + vin->crop.height = vin->format.height; + vin->compose = vin->crop; + + return 0; +} + +static int rvin_mc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_CAMERA; + strlcpy(i->name, "Camera", sizeof(i->name)); + + return 0; +} + +static const struct v4l2_ioctl_ops rvin_mc_ioctl_ops = { + .vidioc_querycap = rvin_querycap, + .vidioc_try_fmt_vid_cap = rvin_mc_try_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = rvin_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = rvin_mc_s_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = rvin_enum_fmt_vid_cap, + + .vidioc_enum_input = rvin_mc_enum_input, + .vidioc_g_input = rvin_g_input, + .vidioc_s_input = rvin_s_input, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = rvin_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* ----------------------------------------------------------------------------- * File Operations */ @@ -839,14 +892,82 @@ static const struct v4l2_file_operations rvin_fops = { .read = vb2_fop_read, }; -void rvin_v4l2_remove(struct rvin_dev *vin) +/* ----------------------------------------------------------------------------- + * Media controller file operations + */ + +static int rvin_mc_open(struct file *file) { + struct rvin_dev *vin = video_drvdata(file); + int ret; + + ret = mutex_lock_interruptible(&vin->lock); + if (ret) + return ret; + + ret = pm_runtime_get_sync(vin->dev); + if (ret < 0) + goto err_unlock; + + ret = v4l2_pipeline_pm_use(&vin->vdev.entity, 1); + if (ret < 0) + goto err_pm; + + file->private_data = vin; + + ret = v4l2_fh_open(file); + if (ret) + goto err_v4l2pm; + + mutex_unlock(&vin->lock); + + return 0; +err_v4l2pm: + v4l2_pipeline_pm_use(&vin->vdev.entity, 0); +err_pm: + pm_runtime_put(vin->dev); +err_unlock: + mutex_unlock(&vin->lock); + + return ret; +} + +static int rvin_mc_release(struct file *file) +{ + struct rvin_dev *vin = video_drvdata(file); + int ret; + + mutex_lock(&vin->lock); + + /* the release helper will cleanup any on-going streaming. */ + ret = _vb2_fop_release(file, NULL); + + v4l2_pipeline_pm_use(&vin->vdev.entity, 0); + pm_runtime_put(vin->dev); + + mutex_unlock(&vin->lock); + + return ret; +} + +static const struct v4l2_file_operations rvin_mc_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = rvin_mc_open, + .release = rvin_mc_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .read = vb2_fop_read, +}; + +void rvin_v4l2_unregister(struct rvin_dev *vin) +{ + if (!video_is_registered(&vin->vdev)) + return; + v4l2_info(&vin->v4l2_dev, "Removing %s\n", video_device_node_name(&vin->vdev)); - /* Checks internaly if handlers have been init or not */ - v4l2_ctrl_handler_free(&vin->ctrl_handler); - /* Checks internaly if vdev have been init or not */ video_unregister_device(&vin->vdev); } @@ -866,58 +987,39 @@ static void rvin_notify(struct v4l2_subdev *sd, } } -int rvin_v4l2_probe(struct rvin_dev *vin) +int rvin_v4l2_register(struct rvin_dev *vin) { struct video_device *vdev = &vin->vdev; - struct v4l2_subdev *sd = vin_to_source(vin); int ret; - v4l2_set_subdev_hostdata(sd, vin); - vin->v4l2_dev.notify = rvin_notify; - ret = v4l2_subdev_call(sd, video, g_tvnorms, &vin->vdev.tvnorms); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - return ret; - - if (vin->vdev.tvnorms == 0) { - /* Disable the STD API if there are no tvnorms defined */ - v4l2_disable_ioctl(&vin->vdev, VIDIOC_G_STD); - v4l2_disable_ioctl(&vin->vdev, VIDIOC_S_STD); - v4l2_disable_ioctl(&vin->vdev, VIDIOC_QUERYSTD); - v4l2_disable_ioctl(&vin->vdev, VIDIOC_ENUMSTD); - } - - /* Add the controls */ - /* - * Currently the subdev with the largest number of controls (13) is - * ov6550. So let's pick 16 as a hint for the control handler. Note - * that this is a hint only: too large and you waste some memory, too - * small and there is a (very) small performance hit when looking up - * controls in the internal hash. - */ - ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16); - if (ret < 0) - return ret; - - ret = v4l2_ctrl_add_handler(&vin->ctrl_handler, sd->ctrl_handler, NULL); - if (ret < 0) - return ret; - /* video node */ - vdev->fops = &rvin_fops; vdev->v4l2_dev = &vin->v4l2_dev; vdev->queue = &vin->queue; - strlcpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name)); + snprintf(vdev->name, sizeof(vdev->name), "VIN%u output", vin->id); vdev->release = video_device_release_empty; - vdev->ioctl_ops = &rvin_ioctl_ops; vdev->lock = &vin->lock; - vdev->ctrl_handler = &vin->ctrl_handler; vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + /* Set a default format */ vin->format.pixelformat = RVIN_DEFAULT_FORMAT; - rvin_reset_format(vin); + vin->format.width = RVIN_DEFAULT_WIDTH; + vin->format.height = RVIN_DEFAULT_HEIGHT; + vin->format.field = RVIN_DEFAULT_FIELD; + vin->format.colorspace = RVIN_DEFAULT_COLORSPACE; + + if (vin->info->use_mc) { + vdev->fops = &rvin_mc_fops; + vdev->ioctl_ops = &rvin_mc_ioctl_ops; + } else { + vdev->fops = &rvin_fops; + vdev->ioctl_ops = &rvin_ioctl_ops; + rvin_reset_format(vin); + } + + rvin_format_align(vin, &vin->format); ret = video_register_device(&vin->vdev, VFL_TYPE_GRABBER, -1); if (ret) { diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h index 95897127cc41..c2aef789b491 100644 --- a/drivers/media/platform/rcar-vin/rcar-vin.h +++ b/drivers/media/platform/rcar-vin/rcar-vin.h @@ -17,6 +17,8 @@ #ifndef __RCAR_VIN__ #define __RCAR_VIN__ +#include <linux/kref.h> + #include <media/v4l2-async.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-dev.h> @@ -29,10 +31,24 @@ /* Address alignment mask for HW buffers */ #define HW_BUFFER_MASK 0x7f -enum chip_id { +/* Max number on VIN instances that can be in a system */ +#define RCAR_VIN_NUM 8 + +struct rvin_group; + +enum model_id { RCAR_H1, RCAR_M1, RCAR_GEN2, + RCAR_GEN3, +}; + +enum rvin_csi_id { + RVIN_CSI20, + RVIN_CSI21, + RVIN_CSI40, + RVIN_CSI41, + RVIN_CSI_MAX, }; /** @@ -47,16 +63,6 @@ enum rvin_dma_state { }; /** - * struct rvin_source_fmt - Source information - * @width: Width from source - * @height: Height from source - */ -struct rvin_source_fmt { - u32 width; - u32 height; -}; - -/** * struct rvin_video_format - Data format stored in memory * @fourcc: Pixelformat * @bpp: Bytes per pixel @@ -70,8 +76,6 @@ struct rvin_video_format { * struct rvin_graph_entity - Video endpoint from async framework * @asd: sub-device descriptor for async framework * @subdev: subdevice matched using async framework - * @code: Media bus format from source - * @mbus_cfg: Media bus format from DT * @source_pad: source pad of remote subdevice * @sink_pad: sink pad of remote subdevice */ @@ -79,18 +83,64 @@ struct rvin_graph_entity { struct v4l2_async_subdev asd; struct v4l2_subdev *subdev; - u32 code; - struct v4l2_mbus_config mbus_cfg; - unsigned int source_pad; unsigned int sink_pad; }; /** + * struct rvin_group_route - describes a route from a channel of a + * CSI-2 receiver to a VIN + * + * @csi: CSI-2 receiver ID. + * @channel: Output channel of the CSI-2 receiver. + * @vin: VIN ID. + * @mask: Bitmask of the different CHSEL register values that + * allow for a route from @csi + @chan to @vin. + * + * .. note:: + * Each R-Car CSI-2 receiver has four output channels facing the VIN + * devices, each channel can carry one CSI-2 Virtual Channel (VC). + * There is no correlation between channel number and CSI-2 VC. It's + * up to the CSI-2 receiver driver to configure which VC is output + * on which channel, the VIN devices only care about output channels. + * + * There are in some cases multiple CHSEL register settings which would + * allow for the same route from @csi + @channel to @vin. For example + * on R-Car H3 both the CHSEL values 0 and 3 allow for a route from + * CSI40/VC0 to VIN0. All possible CHSEL values for a route need to be + * recorded as a bitmask in @mask, in this example bit 0 and 3 should + * be set. + */ +struct rvin_group_route { + enum rvin_csi_id csi; + unsigned int channel; + unsigned int vin; + unsigned int mask; +}; + +/** + * struct rvin_info - Information about the particular VIN implementation + * @model: VIN model + * @use_mc: use media controller instead of controlling subdevice + * @max_width: max input width the VIN supports + * @max_height: max input height the VIN supports + * @routes: list of possible routes from the CSI-2 recivers to + * all VINs. The list mush be NULL terminated. + */ +struct rvin_info { + enum model_id model; + bool use_mc; + + unsigned int max_width; + unsigned int max_height; + const struct rvin_group_route *routes; +}; + +/** * struct rvin_dev - Renesas VIN device structure * @dev: (OF) device * @base: device I/O register space remapped to virtual memory - * @chip: type of VIN chip + * @info: info about VIN instance * * @vdev: V4L2 video device associated with VIN * @v4l2_dev: V4L2 device @@ -98,6 +148,10 @@ struct rvin_graph_entity { * @notifier: V4L2 asynchronous subdevs notifier * @digital: entity in the DT for local digital subdevice * + * @group: Gen3 CSI group + * @id: Gen3 group id for this VIN + * @pad: media pad for the video device entity + * * @lock: protects @queue * @queue: vb2 buffers queue * @scratch: cpu address for scratch buffer @@ -110,16 +164,19 @@ struct rvin_graph_entity { * @sequence: V4L2 buffers sequence number * @state: keeps track of operation state * - * @source: active format from the video source + * @mbus_cfg: media bus configuration from DT + * @mbus_code: media bus format code * @format: active V4L2 pixel format * * @crop: active cropping * @compose: active composing + * @source: active size of the video source + * @std: active video standard of the video source */ struct rvin_dev { struct device *dev; void __iomem *base; - enum chip_id chip; + const struct rvin_info *info; struct video_device vdev; struct v4l2_device v4l2_dev; @@ -127,6 +184,10 @@ struct rvin_dev { struct v4l2_async_notifier notifier; struct rvin_graph_entity *digital; + struct rvin_group *group; + unsigned int id; + struct media_pad pad; + struct mutex lock; struct vb2_queue queue; void *scratch; @@ -138,11 +199,14 @@ struct rvin_dev { unsigned int sequence; enum rvin_dma_state state; - struct rvin_source_fmt source; + struct v4l2_mbus_config mbus_cfg; + u32 mbus_code; struct v4l2_pix_format format; struct v4l2_rect crop; struct v4l2_rect compose; + struct v4l2_rect source; + v4l2_std_id std; }; #define vin_to_source(vin) ((vin)->digital->subdev) @@ -153,17 +217,47 @@ struct rvin_dev { #define vin_warn(d, fmt, arg...) dev_warn(d->dev, fmt, ##arg) #define vin_err(d, fmt, arg...) dev_err(d->dev, fmt, ##arg) -int rvin_dma_probe(struct rvin_dev *vin, int irq); -void rvin_dma_remove(struct rvin_dev *vin); +/** + * struct rvin_group - VIN CSI2 group information + * @refcount: number of VIN instances using the group + * + * @mdev: media device which represents the group + * + * @lock: protects the count, notifier, vin and csi members + * @count: number of enabled VIN instances found in DT + * @notifier: pointer to the notifier of a VIN which handles the + * groups async sub-devices. + * @vin: VIN instances which are part of the group + * @csi: array of pairs of fwnode and subdev pointers + * to all CSI-2 subdevices. + */ +struct rvin_group { + struct kref refcount; + + struct media_device mdev; -int rvin_v4l2_probe(struct rvin_dev *vin); -void rvin_v4l2_remove(struct rvin_dev *vin); + struct mutex lock; + unsigned int count; + struct v4l2_async_notifier *notifier; + struct rvin_dev *vin[RCAR_VIN_NUM]; + + struct { + struct fwnode_handle *fwnode; + struct v4l2_subdev *subdev; + } csi[RVIN_CSI_MAX]; +}; + +int rvin_dma_register(struct rvin_dev *vin, int irq); +void rvin_dma_unregister(struct rvin_dev *vin); + +int rvin_v4l2_register(struct rvin_dev *vin); +void rvin_v4l2_unregister(struct rvin_dev *vin); const struct rvin_video_format *rvin_format_from_pixel(u32 pixelformat); /* Cropping, composing and scaling */ -void rvin_scale_try(struct rvin_dev *vin, struct v4l2_pix_format *pix, - u32 width, u32 height); void rvin_crop_scale_comp(struct rvin_dev *vin); +int rvin_set_channel_routing(struct rvin_dev *vin, u8 chsel); + #endif diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index f6092ae45912..8b44a849ab41 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -1280,7 +1280,7 @@ static int jpu_open(struct file *file) /* ...issue software reset */ ret = jpu_reset(jpu); if (ret) - goto device_prepare_rollback; + goto jpu_reset_rollback; } jpu->ref_count++; @@ -1288,6 +1288,8 @@ static int jpu_open(struct file *file) mutex_unlock(&jpu->mutex); return 0; +jpu_reset_rollback: + clk_disable_unprepare(jpu->clk); device_prepare_rollback: mutex_unlock(&jpu->mutex); v4l_prepare_rollback: diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c index 6599dba5ab84..fe4fe944592d 100644 --- a/drivers/media/platform/renesas-ceu.c +++ b/drivers/media/platform/renesas-ceu.c @@ -777,8 +777,15 @@ static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) const struct ceu_fmt *ceu_fmt; int ret; + /* + * Set format on sensor sub device: bus format used to produce memory + * format is selected at initialization time. + */ struct v4l2_subdev_format sd_format = { - .which = V4L2_SUBDEV_FORMAT_TRY, + .which = V4L2_SUBDEV_FORMAT_TRY, + .format = { + .code = ceu_sd->mbus_fmt.mbus_code, + }, }; switch (pix->pixelformat) { @@ -800,10 +807,6 @@ static int ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) v4l_bound_align_image(&pix->width, 2, CEU_MAX_WIDTH, 4, &pix->height, 4, CEU_MAX_HEIGHT, 4, 0); - /* - * Set format on sensor sub device: bus format used to produce memory - * format is selected at initialization time. - */ v4l2_fill_mbus_format_mplane(&sd_format.format, pix); ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, &pad_cfg, &sd_format); if (ret) @@ -827,8 +830,15 @@ static int ceu_set_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt) struct v4l2_subdev *v4l2_sd = ceu_sd->v4l2_sd; int ret; + /* + * Set format on sensor sub device: bus format used to produce memory + * format is selected at initialization time. + */ struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .code = ceu_sd->mbus_fmt.mbus_code, + }, }; ret = ceu_try_fmt(ceudev, v4l2_fmt); @@ -1365,7 +1375,7 @@ static int ceu_notify_complete(struct v4l2_async_notifier *notifier) return ret; /* Register the video device. */ - strncpy(vdev->name, DRIVER_NAME, strlen(DRIVER_NAME)); + strlcpy(vdev->name, DRIVER_NAME, sizeof(vdev->name)); vdev->v4l2_dev = v4l2_dev; vdev->lock = &ceudev->mlock; vdev->queue = &ceudev->vb2_vq; @@ -1545,6 +1555,7 @@ static const struct ceu_data ceu_data_sh4 = { #if IS_ENABLED(CONFIG_OF) static const struct of_device_id ceu_of_match[] = { { .compatible = "renesas,r7s72100-ceu", .data = &ceu_data_rz }, + { .compatible = "renesas,r8a7740-ceu", .data = &ceu_data_rz }, { } }; MODULE_DEVICE_TABLE(of, ceu_of_match); diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c index 0974b9a7a584..0861842b2dfc 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c @@ -425,9 +425,9 @@ unsigned int exynos3250_jpeg_get_int_status(void __iomem *regs) } void exynos3250_jpeg_clear_int_status(void __iomem *regs, - unsigned int value) + unsigned int value) { - return writel(value, regs + EXYNOS3250_JPGINTST); + writel(value, regs + EXYNOS3250_JPGINTST); } unsigned int exynos3250_jpeg_operating(void __iomem *regs) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 5cf4d9921264..6a3cc4f86c5d 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -271,8 +271,8 @@ static int vidioc_querycap(struct file *file, void *priv, { struct s5p_mfc_dev *dev = video_drvdata(file); - strncpy(cap->driver, S5P_MFC_NAME, sizeof(cap->driver) - 1); - strncpy(cap->card, dev->vfd_dec->name, sizeof(cap->card) - 1); + strlcpy(cap->driver, S5P_MFC_NAME, sizeof(cap->driver)); + strlcpy(cap->card, dev->vfd_dec->name, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(&dev->plat_dev->dev)); /* diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 5c0462ca9993..570f391f2cfd 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -1313,8 +1313,8 @@ static int vidioc_querycap(struct file *file, void *priv, { struct s5p_mfc_dev *dev = video_drvdata(file); - strncpy(cap->driver, S5P_MFC_NAME, sizeof(cap->driver) - 1); - strncpy(cap->card, dev->vfd_enc->name, sizeof(cap->card) - 1); + strlcpy(cap->driver, S5P_MFC_NAME, sizeof(cap->driver)); + strlcpy(cap->card, dev->vfd_enc->name, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(&dev->plat_dev->dev)); /* diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index f5979c12ad61..669d116b8f09 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -18,9 +18,8 @@ config SOC_CAMERA_PLATFORM config VIDEO_SH_MOBILE_CEU tristate "SuperH Mobile CEU Interface driver" - depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK + depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK depends on ARCH_SHMOBILE || COMPILE_TEST - depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select SOC_CAMERA_SCALE_CROP ---help--- diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c index cb4986b8f798..ce00e90d4e3c 100644 --- a/drivers/media/platform/soc_camera/soc_camera_platform.c +++ b/drivers/media/platform/soc_camera/soc_camera_platform.c @@ -159,7 +159,8 @@ static int soc_camera_platform_probe(struct platform_device *pdev) v4l2_subdev_init(&priv->subdev, &platform_subdev_ops); v4l2_set_subdevdata(&priv->subdev, p); - strncpy(priv->subdev.name, dev_name(&pdev->dev), V4L2_SUBDEV_NAME_SIZE); + strlcpy(priv->subdev.name, dev_name(&pdev->dev), + sizeof(priv->subdev.name)); return v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev); } diff --git a/drivers/media/platform/sti/bdisp/bdisp-hw.c b/drivers/media/platform/sti/bdisp/bdisp-hw.c index a5eb592e12c0..26d9fa7aeb5f 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-hw.c +++ b/drivers/media/platform/sti/bdisp/bdisp-hw.c @@ -455,7 +455,7 @@ int bdisp_hw_alloc_nodes(struct bdisp_ctx *ctx) /* Allocate all the nodes within a single memory page */ base = dma_alloc_attrs(dev, node_size * MAX_NB_NODE, &paddr, - GFP_KERNEL | GFP_DMA, DMA_ATTR_WRITE_COMBINE); + GFP_KERNEL, DMA_ATTR_WRITE_COMBINE); if (!base) { dev_err(dev, "%s no mem\n", __func__); return -ENOMEM; diff --git a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c index bf4ca16db440..66b64096f5de 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c +++ b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c @@ -1297,6 +1297,10 @@ static int bdisp_probe(struct platform_device *pdev) if (!bdisp) return -ENOMEM; + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + bdisp->pdev = pdev; bdisp->dev = dev; platform_set_drvdata(pdev, bdisp); diff --git a/drivers/media/platform/sti/c8sectpfe/Kconfig b/drivers/media/platform/sti/c8sectpfe/Kconfig index 740190f8a3b6..7420a50572d3 100644 --- a/drivers/media/platform/sti/c8sectpfe/Kconfig +++ b/drivers/media/platform/sti/c8sectpfe/Kconfig @@ -1,6 +1,6 @@ config DVB_C8SECTPFE tristate "STMicroelectronics C8SECTPFE DVB support" - depends on PINCTRL && DVB_CORE && I2C && HAS_DMA + depends on PINCTRL && DVB_CORE && I2C depends on ARCH_STI || ARCH_MULTIPLATFORM || COMPILE_TEST select FW_LOADER select DEBUG_FS diff --git a/drivers/media/platform/sti/hva/hva-mem.c b/drivers/media/platform/sti/hva/hva-mem.c index caf50cd4bb77..68047b60b66c 100644 --- a/drivers/media/platform/sti/hva/hva-mem.c +++ b/drivers/media/platform/sti/hva/hva-mem.c @@ -22,7 +22,7 @@ int hva_mem_alloc(struct hva_ctx *ctx, u32 size, const char *name, return -ENOMEM; } - base = dma_alloc_attrs(dev, size, &paddr, GFP_KERNEL | GFP_DMA, + base = dma_alloc_attrs(dev, size, &paddr, GFP_KERNEL, DMA_ATTR_WRITE_COMBINE); if (!base) { dev_err(dev, "%s %s : dma_alloc_attrs failed for %s (size=%d)\n", diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/sti/hva/hva-v4l2.c index 2ab0b5cc5c22..15080cb00fa7 100644 --- a/drivers/media/platform/sti/hva/hva-v4l2.c +++ b/drivers/media/platform/sti/hva/hva-v4l2.c @@ -1355,6 +1355,10 @@ static int hva_probe(struct platform_device *pdev) goto err; } + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + hva->dev = dev; hva->pdev = pdev; platform_set_drvdata(pdev, hva); diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c index e9a02639554b..f01c3e813247 100644 --- a/drivers/media/platform/via-camera.c +++ b/drivers/media/platform/via-camera.c @@ -178,7 +178,7 @@ static int via_sensor_power_setup(struct via_camera *cam) cam->power_gpio = viafb_gpio_lookup("VGPIO3"); cam->reset_gpio = viafb_gpio_lookup("VGPIO2"); - if (cam->power_gpio < 0 || cam->reset_gpio < 0) { + if (!gpio_is_valid(cam->power_gpio) || !gpio_is_valid(cam->reset_gpio)) { dev_err(&cam->platdev->dev, "Unable to find GPIO lines\n"); return -EINVAL; } diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c index ee89ad76bee2..1fb887293337 100644 --- a/drivers/media/platform/video-mux.c +++ b/drivers/media/platform/video-mux.c @@ -45,6 +45,7 @@ static int video_mux_link_setup(struct media_entity *entity, { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); + u16 source_pad = entity->num_pads - 1; int ret = 0; /* @@ -74,6 +75,9 @@ static int video_mux_link_setup(struct media_entity *entity, if (ret < 0) goto out; vmux->active = local->index; + + /* Propagate the active format to the source */ + vmux->format_mbus[source_pad] = vmux->format_mbus[vmux->active]; } else { if (vmux->active != local->index) goto out; @@ -162,14 +166,20 @@ static int video_mux_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_format *sdformat) { struct video_mux *vmux = v4l2_subdev_to_video_mux(sd); - struct v4l2_mbus_framefmt *mbusformat; + struct v4l2_mbus_framefmt *mbusformat, *source_mbusformat; struct media_pad *pad = &vmux->pads[sdformat->pad]; + u16 source_pad = sd->entity.num_pads - 1; mbusformat = __video_mux_get_pad_format(sd, cfg, sdformat->pad, sdformat->which); if (!mbusformat) return -EINVAL; + source_mbusformat = __video_mux_get_pad_format(sd, cfg, source_pad, + sdformat->which); + if (!source_mbusformat) + return -EINVAL; + mutex_lock(&vmux->lock); /* Source pad mirrors active sink pad, no limitations on sink pads */ @@ -178,6 +188,10 @@ static int video_mux_set_format(struct v4l2_subdev *sd, *mbusformat = sdformat->format; + /* Propagate the format from an active sink to source */ + if ((pad->flags & MEDIA_PAD_FL_SINK) && (pad->index == vmux->active)) + *source_mbusformat = sdformat->format; + mutex_unlock(&vmux->lock); return 0; diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c index e5914be0e12d..be531caa2cdf 100644 --- a/drivers/media/platform/vivid/vivid-vid-common.c +++ b/drivers/media/platform/vivid/vivid-vid-common.c @@ -860,7 +860,7 @@ int vidioc_g_edid(struct file *file, void *_fh, return -ENODATA; if (edid->start_block >= dev->edid_blocks) return -EINVAL; - if (edid->start_block + edid->blocks > dev->edid_blocks) + if (edid->blocks > dev->edid_blocks - edid->start_block) edid->blocks = dev->edid_blocks - edid->start_block; if (adap) cec_set_edid_phys_addr(dev->edid, dev->edid_blocks * 128, adap->phys_addr); diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile index f5cd6f0491cb..4bb4dcbef7b5 100644 --- a/drivers/media/platform/vsp1/Makefile +++ b/drivers/media/platform/vsp1/Makefile @@ -3,8 +3,8 @@ vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_pipe.o vsp1-y += vsp1_dl.o vsp1_drm.o vsp1_video.o vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o vsp1-y += vsp1_clu.o vsp1_hsit.o vsp1_lut.o -vsp1-y += vsp1_bru.o vsp1_sru.o vsp1_uds.o +vsp1-y += vsp1_brx.o vsp1_sru.o vsp1_uds.o vsp1-y += vsp1_hgo.o vsp1_hgt.o vsp1_histo.o -vsp1-y += vsp1_lif.o +vsp1-y += vsp1_lif.o vsp1_uif.o obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 78ef838416b3..33f632331474 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1.h -- R-Car VSP1 Driver * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_H__ #define __VSP1_H__ @@ -30,7 +26,7 @@ struct rcar_fcp_device; struct vsp1_drm; struct vsp1_entity; struct vsp1_platform_data; -struct vsp1_bru; +struct vsp1_brx; struct vsp1_clu; struct vsp1_hgo; struct vsp1_hgt; @@ -40,10 +36,12 @@ struct vsp1_lut; struct vsp1_rwpf; struct vsp1_sru; struct vsp1_uds; +struct vsp1_uif; #define VSP1_MAX_LIF 2 #define VSP1_MAX_RPF 5 #define VSP1_MAX_UDS 3 +#define VSP1_MAX_UIF 2 #define VSP1_MAX_WPF 4 #define VSP1_HAS_LUT (1 << 1) @@ -64,6 +62,7 @@ struct vsp1_device_info { unsigned int lif_count; unsigned int rpf_count; unsigned int uds_count; + unsigned int uif_count; unsigned int wpf_count; unsigned int num_bru_inputs; bool uapi; @@ -78,8 +77,8 @@ struct vsp1_device { struct rcar_fcp_device *fcp; struct device *bus_master; - struct vsp1_bru *brs; - struct vsp1_bru *bru; + struct vsp1_brx *brs; + struct vsp1_brx *bru; struct vsp1_clu *clu; struct vsp1_hgo *hgo; struct vsp1_hgt *hgt; @@ -90,6 +89,7 @@ struct vsp1_device { struct vsp1_rwpf *rpf[VSP1_MAX_RPF]; struct vsp1_sru *sru; struct vsp1_uds *uds[VSP1_MAX_UDS]; + struct vsp1_uif *uif[VSP1_MAX_UIF]; struct vsp1_rwpf *wpf[VSP1_MAX_WPF]; struct list_head entities; diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h deleted file mode 100644 index c98ed96d8de6..000000000000 --- a/drivers/media/platform/vsp1/vsp1_bru.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * vsp1_bru.h -- R-Car VSP1 Blend ROP Unit - * - * Copyright (C) 2013 Renesas Corporation - * - * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ -#ifndef __VSP1_BRU_H__ -#define __VSP1_BRU_H__ - -#include <media/media-entity.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-subdev.h> - -#include "vsp1_entity.h" - -struct vsp1_device; -struct vsp1_rwpf; - -#define BRU_PAD_SINK(n) (n) - -struct vsp1_bru { - struct vsp1_entity entity; - unsigned int base; - - struct v4l2_ctrl_handler ctrls; - - struct { - struct vsp1_rwpf *rpf; - } inputs[VSP1_MAX_RPF]; - - u32 bgcolor; -}; - -static inline struct vsp1_bru *to_bru(struct v4l2_subdev *subdev) -{ - return container_of(subdev, struct vsp1_bru, entity.subdev); -} - -struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1, - enum vsp1_entity_type type); - -#endif /* __VSP1_BRU_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_brx.c index e8fd2ae3b3eb..359917b5d842 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_brx.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* - * vsp1_bru.c -- R-Car VSP1 Blend ROP Unit + * vsp1_brx.c -- R-Car VSP1 Blend ROP Unit (BRU and BRS) * * Copyright (C) 2013 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -17,45 +13,45 @@ #include <media/v4l2-subdev.h> #include "vsp1.h" -#include "vsp1_bru.h" +#include "vsp1_brx.h" #include "vsp1_dl.h" #include "vsp1_pipe.h" #include "vsp1_rwpf.h" #include "vsp1_video.h" -#define BRU_MIN_SIZE 1U -#define BRU_MAX_SIZE 8190U +#define BRX_MIN_SIZE 1U +#define BRX_MAX_SIZE 8190U /* ----------------------------------------------------------------------------- * Device Access */ -static inline void vsp1_bru_write(struct vsp1_bru *bru, struct vsp1_dl_list *dl, - u32 reg, u32 data) +static inline void vsp1_brx_write(struct vsp1_brx *brx, + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, bru->base + reg, data); + vsp1_dl_body_write(dlb, brx->base + reg, data); } /* ----------------------------------------------------------------------------- * Controls */ -static int bru_s_ctrl(struct v4l2_ctrl *ctrl) +static int brx_s_ctrl(struct v4l2_ctrl *ctrl) { - struct vsp1_bru *bru = - container_of(ctrl->handler, struct vsp1_bru, ctrls); + struct vsp1_brx *brx = + container_of(ctrl->handler, struct vsp1_brx, ctrls); switch (ctrl->id) { case V4L2_CID_BG_COLOR: - bru->bgcolor = ctrl->val; + brx->bgcolor = ctrl->val; break; } return 0; } -static const struct v4l2_ctrl_ops bru_ctrl_ops = { - .s_ctrl = bru_s_ctrl, +static const struct v4l2_ctrl_ops brx_ctrl_ops = { + .s_ctrl = brx_s_ctrl, }; /* ----------------------------------------------------------------------------- @@ -63,12 +59,12 @@ static const struct v4l2_ctrl_ops bru_ctrl_ops = { */ /* - * The BRU can't perform format conversion, all sink and source formats must be + * The BRx can't perform format conversion, all sink and source formats must be * identical. We pick the format on the first sink pad (pad 0) and propagate it * to all other pads. */ -static int bru_enum_mbus_code(struct v4l2_subdev *subdev, +static int brx_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { @@ -81,7 +77,7 @@ static int bru_enum_mbus_code(struct v4l2_subdev *subdev, ARRAY_SIZE(codes)); } -static int bru_enum_frame_size(struct v4l2_subdev *subdev, +static int brx_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { @@ -92,29 +88,29 @@ static int bru_enum_frame_size(struct v4l2_subdev *subdev, fse->code != MEDIA_BUS_FMT_AYUV8_1X32) return -EINVAL; - fse->min_width = BRU_MIN_SIZE; - fse->max_width = BRU_MAX_SIZE; - fse->min_height = BRU_MIN_SIZE; - fse->max_height = BRU_MAX_SIZE; + fse->min_width = BRX_MIN_SIZE; + fse->max_width = BRX_MAX_SIZE; + fse->min_height = BRX_MIN_SIZE; + fse->max_height = BRX_MAX_SIZE; return 0; } -static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru, +static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx, struct v4l2_subdev_pad_config *cfg, unsigned int pad) { - return v4l2_subdev_get_try_compose(&bru->entity.subdev, cfg, pad); + return v4l2_subdev_get_try_compose(&brx->entity.subdev, cfg, pad); } -static void bru_try_format(struct vsp1_bru *bru, +static void brx_try_format(struct vsp1_brx *brx, struct v4l2_subdev_pad_config *config, unsigned int pad, struct v4l2_mbus_framefmt *fmt) { struct v4l2_mbus_framefmt *format; switch (pad) { - case BRU_PAD_SINK(0): + case BRX_PAD_SINK(0): /* Default to YUV if the requested format is not supported. */ if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 && fmt->code != MEDIA_BUS_FMT_AYUV8_1X32) @@ -122,46 +118,46 @@ static void bru_try_format(struct vsp1_bru *bru, break; default: - /* The BRU can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&bru->entity, config, - BRU_PAD_SINK(0)); + /* The BRx can't perform format conversion. */ + format = vsp1_entity_get_pad_format(&brx->entity, config, + BRX_PAD_SINK(0)); fmt->code = format->code; break; } - fmt->width = clamp(fmt->width, BRU_MIN_SIZE, BRU_MAX_SIZE); - fmt->height = clamp(fmt->height, BRU_MIN_SIZE, BRU_MAX_SIZE); + fmt->width = clamp(fmt->width, BRX_MIN_SIZE, BRX_MAX_SIZE); + fmt->height = clamp(fmt->height, BRX_MIN_SIZE, BRX_MAX_SIZE); fmt->field = V4L2_FIELD_NONE; fmt->colorspace = V4L2_COLORSPACE_SRGB; } -static int bru_set_format(struct v4l2_subdev *subdev, +static int brx_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { - struct vsp1_bru *bru = to_bru(subdev); + struct vsp1_brx *brx = to_brx(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; int ret = 0; - mutex_lock(&bru->entity.lock); + mutex_lock(&brx->entity.lock); - config = vsp1_entity_get_pad_config(&bru->entity, cfg, fmt->which); + config = vsp1_entity_get_pad_config(&brx->entity, cfg, fmt->which); if (!config) { ret = -EINVAL; goto done; } - bru_try_format(bru, config, fmt->pad, &fmt->format); + brx_try_format(brx, config, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&bru->entity, config, fmt->pad); + format = vsp1_entity_get_pad_format(&brx->entity, config, fmt->pad); *format = fmt->format; /* Reset the compose rectangle */ - if (fmt->pad != bru->entity.source_pad) { + if (fmt->pad != brx->entity.source_pad) { struct v4l2_rect *compose; - compose = bru_get_compose(bru, config, fmt->pad); + compose = brx_get_compose(brx, config, fmt->pad); compose->left = 0; compose->top = 0; compose->width = format->width; @@ -169,48 +165,48 @@ static int bru_set_format(struct v4l2_subdev *subdev, } /* Propagate the format code to all pads */ - if (fmt->pad == BRU_PAD_SINK(0)) { + if (fmt->pad == BRX_PAD_SINK(0)) { unsigned int i; - for (i = 0; i <= bru->entity.source_pad; ++i) { - format = vsp1_entity_get_pad_format(&bru->entity, + for (i = 0; i <= brx->entity.source_pad; ++i) { + format = vsp1_entity_get_pad_format(&brx->entity, config, i); format->code = fmt->format.code; } } done: - mutex_unlock(&bru->entity.lock); + mutex_unlock(&brx->entity.lock); return ret; } -static int bru_get_selection(struct v4l2_subdev *subdev, +static int brx_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { - struct vsp1_bru *bru = to_bru(subdev); + struct vsp1_brx *brx = to_brx(subdev); struct v4l2_subdev_pad_config *config; - if (sel->pad == bru->entity.source_pad) + if (sel->pad == brx->entity.source_pad) return -EINVAL; switch (sel->target) { case V4L2_SEL_TGT_COMPOSE_BOUNDS: sel->r.left = 0; sel->r.top = 0; - sel->r.width = BRU_MAX_SIZE; - sel->r.height = BRU_MAX_SIZE; + sel->r.width = BRX_MAX_SIZE; + sel->r.height = BRX_MAX_SIZE; return 0; case V4L2_SEL_TGT_COMPOSE: - config = vsp1_entity_get_pad_config(&bru->entity, cfg, + config = vsp1_entity_get_pad_config(&brx->entity, cfg, sel->which); if (!config) return -EINVAL; - mutex_lock(&bru->entity.lock); - sel->r = *bru_get_compose(bru, config, sel->pad); - mutex_unlock(&bru->entity.lock); + mutex_lock(&brx->entity.lock); + sel->r = *brx_get_compose(brx, config, sel->pad); + mutex_unlock(&brx->entity.lock); return 0; default: @@ -218,25 +214,25 @@ static int bru_get_selection(struct v4l2_subdev *subdev, } } -static int bru_set_selection(struct v4l2_subdev *subdev, +static int brx_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { - struct vsp1_bru *bru = to_bru(subdev); + struct vsp1_brx *brx = to_brx(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; struct v4l2_rect *compose; int ret = 0; - if (sel->pad == bru->entity.source_pad) + if (sel->pad == brx->entity.source_pad) return -EINVAL; if (sel->target != V4L2_SEL_TGT_COMPOSE) return -EINVAL; - mutex_lock(&bru->entity.lock); + mutex_lock(&brx->entity.lock); - config = vsp1_entity_get_pad_config(&bru->entity, cfg, sel->which); + config = vsp1_entity_get_pad_config(&brx->entity, cfg, sel->which); if (!config) { ret = -EINVAL; goto done; @@ -246,8 +242,8 @@ static int bru_set_selection(struct v4l2_subdev *subdev, * The compose rectangle top left corner must be inside the output * frame. */ - format = vsp1_entity_get_pad_format(&bru->entity, config, - bru->entity.source_pad); + format = vsp1_entity_get_pad_format(&brx->entity, config, + brx->entity.source_pad); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); @@ -255,51 +251,47 @@ static int bru_set_selection(struct v4l2_subdev *subdev, * Scaling isn't supported, the compose rectangle size must be identical * to the sink format size. */ - format = vsp1_entity_get_pad_format(&bru->entity, config, sel->pad); + format = vsp1_entity_get_pad_format(&brx->entity, config, sel->pad); sel->r.width = format->width; sel->r.height = format->height; - compose = bru_get_compose(bru, config, sel->pad); + compose = brx_get_compose(brx, config, sel->pad); *compose = sel->r; done: - mutex_unlock(&bru->entity.lock); + mutex_unlock(&brx->entity.lock); return ret; } -static const struct v4l2_subdev_pad_ops bru_pad_ops = { +static const struct v4l2_subdev_pad_ops brx_pad_ops = { .init_cfg = vsp1_entity_init_cfg, - .enum_mbus_code = bru_enum_mbus_code, - .enum_frame_size = bru_enum_frame_size, + .enum_mbus_code = brx_enum_mbus_code, + .enum_frame_size = brx_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, - .set_fmt = bru_set_format, - .get_selection = bru_get_selection, - .set_selection = bru_set_selection, + .set_fmt = brx_set_format, + .get_selection = brx_get_selection, + .set_selection = brx_set_selection, }; -static const struct v4l2_subdev_ops bru_ops = { - .pad = &bru_pad_ops, +static const struct v4l2_subdev_ops brx_ops = { + .pad = &brx_pad_ops, }; /* ----------------------------------------------------------------------------- * VSP1 Entity Operations */ -static void bru_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void brx_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { - struct vsp1_bru *bru = to_bru(&entity->subdev); + struct vsp1_brx *brx = to_brx(&entity->subdev); struct v4l2_mbus_framefmt *format; unsigned int flags; unsigned int i; - if (params != VSP1_ENTITY_PARAMS_INIT) - return; - - format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config, - bru->entity.source_pad); + format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.config, + brx->entity.source_pad); /* * The hardware is extremely flexible but we have no userspace API to @@ -313,7 +305,7 @@ static void bru_configure(struct vsp1_entity *entity, * format at the pipeline output is premultiplied. */ flags = pipe->output ? pipe->output->format.flags : 0; - vsp1_bru_write(bru, dl, VI6_BRU_INCTRL, + vsp1_brx_write(brx, dlb, VI6_BRU_INCTRL, flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ? 0 : VI6_BRU_INCTRL_NRM); @@ -321,12 +313,12 @@ static void bru_configure(struct vsp1_entity *entity, * Set the background position to cover the whole output image and * configure its color. */ - vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_SIZE, + vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_SIZE, (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) | (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT)); - vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_LOC, 0); + vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_LOC, 0); - vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_COL, bru->bgcolor | + vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_COL, brx->bgcolor | (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT)); /* @@ -336,25 +328,25 @@ static void bru_configure(struct vsp1_entity *entity, * unit. */ if (entity->type == VSP1_ENTITY_BRU) - vsp1_bru_write(bru, dl, VI6_BRU_ROP, + vsp1_brx_write(brx, dlb, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) | VI6_BRU_ROP_CROP(VI6_ROP_NOP) | VI6_BRU_ROP_AROP(VI6_ROP_NOP)); - for (i = 0; i < bru->entity.source_pad; ++i) { + for (i = 0; i < brx->entity.source_pad; ++i) { bool premultiplied = false; u32 ctrl = 0; /* - * Configure all Blend/ROP units corresponding to an enabled BRU + * Configure all Blend/ROP units corresponding to an enabled BRx * input for alpha blending. Blend/ROP units corresponding to - * disabled BRU inputs are used in ROP NOP mode to ignore the + * disabled BRx inputs are used in ROP NOP mode to ignore the * SRC input. */ - if (bru->inputs[i].rpf) { + if (brx->inputs[i].rpf) { ctrl |= VI6_BRU_CTRL_RBC; - premultiplied = bru->inputs[i].rpf->format.flags + premultiplied = brx->inputs[i].rpf->format.flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA; } else { ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP) @@ -378,7 +370,7 @@ static void bru_configure(struct vsp1_entity *entity, if (!(entity->type == VSP1_ENTITY_BRU && i == 1)) ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i); - vsp1_bru_write(bru, dl, VI6_BRU_CTRL(i), ctrl); + vsp1_brx_write(brx, dlb, VI6_BRU_CTRL(i), ctrl); /* * Harcode the blending formula to @@ -393,7 +385,7 @@ static void bru_configure(struct vsp1_entity *entity, * * otherwise. */ - vsp1_bru_write(bru, dl, VI6_BRU_BLD(i), + vsp1_brx_write(brx, dlb, VI6_BRU_BLD(i), VI6_BRU_BLD_CCMDX_255_SRC_A | (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY : VI6_BRU_BLD_CCMDY_SRC_A) | @@ -403,29 +395,29 @@ static void bru_configure(struct vsp1_entity *entity, } } -static const struct vsp1_entity_operations bru_entity_ops = { - .configure = bru_configure, +static const struct vsp1_entity_operations brx_entity_ops = { + .configure_stream = brx_configure_stream, }; /* ----------------------------------------------------------------------------- * Initialization and Cleanup */ -struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1, +struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1, enum vsp1_entity_type type) { - struct vsp1_bru *bru; + struct vsp1_brx *brx; unsigned int num_pads; const char *name; int ret; - bru = devm_kzalloc(vsp1->dev, sizeof(*bru), GFP_KERNEL); - if (bru == NULL) + brx = devm_kzalloc(vsp1->dev, sizeof(*brx), GFP_KERNEL); + if (brx == NULL) return ERR_PTR(-ENOMEM); - bru->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE; - bru->entity.ops = &bru_entity_ops; - bru->entity.type = type; + brx->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE; + brx->entity.ops = &brx_entity_ops; + brx->entity.type = type; if (type == VSP1_ENTITY_BRU) { num_pads = vsp1->info->num_bru_inputs + 1; @@ -435,26 +427,26 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1, name = "brs"; } - ret = vsp1_entity_init(vsp1, &bru->entity, name, num_pads, &bru_ops, + ret = vsp1_entity_init(vsp1, &brx->entity, name, num_pads, &brx_ops, MEDIA_ENT_F_PROC_VIDEO_COMPOSER); if (ret < 0) return ERR_PTR(ret); /* Initialize the control handler. */ - v4l2_ctrl_handler_init(&bru->ctrls, 1); - v4l2_ctrl_new_std(&bru->ctrls, &bru_ctrl_ops, V4L2_CID_BG_COLOR, + v4l2_ctrl_handler_init(&brx->ctrls, 1); + v4l2_ctrl_new_std(&brx->ctrls, &brx_ctrl_ops, V4L2_CID_BG_COLOR, 0, 0xffffff, 1, 0); - bru->bgcolor = 0; + brx->bgcolor = 0; - bru->entity.subdev.ctrl_handler = &bru->ctrls; + brx->entity.subdev.ctrl_handler = &brx->ctrls; - if (bru->ctrls.error) { + if (brx->ctrls.error) { dev_err(vsp1->dev, "%s: failed to initialize controls\n", name); - ret = bru->ctrls.error; - vsp1_entity_destroy(&bru->entity); + ret = brx->ctrls.error; + vsp1_entity_destroy(&brx->entity); return ERR_PTR(ret); } - return bru; + return brx; } diff --git a/drivers/media/platform/vsp1/vsp1_brx.h b/drivers/media/platform/vsp1/vsp1_brx.h new file mode 100644 index 000000000000..6abbb8c3343c --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_brx.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * vsp1_brx.h -- R-Car VSP1 Blend ROP Unit (BRU and BRS) + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + */ +#ifndef __VSP1_BRX_H__ +#define __VSP1_BRX_H__ + +#include <media/media-entity.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "vsp1_entity.h" + +struct vsp1_device; +struct vsp1_rwpf; + +#define BRX_PAD_SINK(n) (n) + +struct vsp1_brx { + struct vsp1_entity entity; + unsigned int base; + + struct v4l2_ctrl_handler ctrls; + + struct { + struct vsp1_rwpf *rpf; + } inputs[VSP1_MAX_RPF]; + + u32 bgcolor; +}; + +static inline struct vsp1_brx *to_brx(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_brx, entity.subdev); +} + +struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1, + enum vsp1_entity_type type); + +#endif /* __VSP1_BRX_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_clu.c b/drivers/media/platform/vsp1/vsp1_clu.c index f2fb26e5ab4e..942fc14c19d1 100644 --- a/drivers/media/platform/vsp1/vsp1_clu.c +++ b/drivers/media/platform/vsp1/vsp1_clu.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_clu.c -- R-Car VSP1 Cubic Look-Up Table * * Copyright (C) 2015-2016 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -23,14 +19,16 @@ #define CLU_MIN_SIZE 4U #define CLU_MAX_SIZE 8190U +#define CLU_SIZE (17 * 17 * 17) + /* ----------------------------------------------------------------------------- * Device Access */ -static inline void vsp1_clu_write(struct vsp1_clu *clu, struct vsp1_dl_list *dl, - u32 reg, u32 data) +static inline void vsp1_clu_write(struct vsp1_clu *clu, + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg, data); + vsp1_dl_body_write(dlb, reg, data); } /* ----------------------------------------------------------------------------- @@ -47,19 +45,19 @@ static int clu_set_table(struct vsp1_clu *clu, struct v4l2_ctrl *ctrl) struct vsp1_dl_body *dlb; unsigned int i; - dlb = vsp1_dl_fragment_alloc(clu->entity.vsp1, 1 + 17 * 17 * 17); + dlb = vsp1_dl_body_get(clu->pool); if (!dlb) return -ENOMEM; - vsp1_dl_fragment_write(dlb, VI6_CLU_ADDR, 0); - for (i = 0; i < 17 * 17 * 17; ++i) - vsp1_dl_fragment_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]); + vsp1_dl_body_write(dlb, VI6_CLU_ADDR, 0); + for (i = 0; i < CLU_SIZE; ++i) + vsp1_dl_body_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]); spin_lock_irq(&clu->lock); swap(clu->clu, dlb); spin_unlock_irq(&clu->lock); - vsp1_dl_fragment_free(dlb); + vsp1_dl_body_put(dlb); return 0; } @@ -118,18 +116,18 @@ static const struct v4l2_ctrl_config clu_mode_control = { * V4L2 Subdevice Pad Operations */ +static const unsigned int clu_codes[] = { + MEDIA_BUS_FMT_ARGB8888_1X32, + MEDIA_BUS_FMT_AHSV8888_1X32, + MEDIA_BUS_FMT_AYUV8_1X32, +}; + static int clu_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { - static const unsigned int codes[] = { - MEDIA_BUS_FMT_ARGB8888_1X32, - MEDIA_BUS_FMT_AHSV8888_1X32, - MEDIA_BUS_FMT_AYUV8_1X32, - }; - - return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, - ARRAY_SIZE(codes)); + return vsp1_subdev_enum_mbus_code(subdev, cfg, code, clu_codes, + ARRAY_SIZE(clu_codes)); } static int clu_enum_frame_size(struct v4l2_subdev *subdev, @@ -145,51 +143,10 @@ static int clu_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { - struct vsp1_clu *clu = to_clu(subdev); - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - int ret = 0; - - mutex_lock(&clu->entity.lock); - - config = vsp1_entity_get_pad_config(&clu->entity, cfg, fmt->which); - if (!config) { - ret = -EINVAL; - goto done; - } - - /* Default to YUV if the requested format is not supported. */ - if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && - fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 && - fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) - fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - - format = vsp1_entity_get_pad_format(&clu->entity, config, fmt->pad); - - if (fmt->pad == CLU_PAD_SOURCE) { - /* The CLU output format can't be modified. */ - fmt->format = *format; - goto done; - } - - format->code = fmt->format.code; - format->width = clamp_t(unsigned int, fmt->format.width, - CLU_MIN_SIZE, CLU_MAX_SIZE); - format->height = clamp_t(unsigned int, fmt->format.height, - CLU_MIN_SIZE, CLU_MAX_SIZE); - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; - - fmt->format = *format; - - /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&clu->entity, config, - CLU_PAD_SOURCE); - *format = fmt->format; - -done: - mutex_unlock(&clu->entity.lock); - return ret; + return vsp1_subdev_set_pad_format(subdev, cfg, fmt, clu_codes, + ARRAY_SIZE(clu_codes), + CLU_MIN_SIZE, CLU_MIN_SIZE, + CLU_MAX_SIZE, CLU_MAX_SIZE); } /* ----------------------------------------------------------------------------- @@ -212,57 +169,65 @@ static const struct v4l2_subdev_ops clu_ops = { * VSP1 Entity Operations */ -static void clu_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void clu_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { struct vsp1_clu *clu = to_clu(&entity->subdev); - struct vsp1_dl_body *dlb; + struct v4l2_mbus_framefmt *format; + + /* + * The yuv_mode can't be changed during streaming. Cache it internally + * for future runtime configuration calls. + */ + format = vsp1_entity_get_pad_format(&clu->entity, + clu->entity.config, + CLU_PAD_SINK); + clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; +} + +static void clu_configure_frame(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + struct vsp1_clu *clu = to_clu(&entity->subdev); + struct vsp1_dl_body *clu_dlb; unsigned long flags; u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN; - switch (params) { - case VSP1_ENTITY_PARAMS_INIT: { - /* - * The format can't be changed during streaming, only verify it - * at setup time and store the information internally for future - * runtime configuration calls. - */ - struct v4l2_mbus_framefmt *format; - - format = vsp1_entity_get_pad_format(&clu->entity, - clu->entity.config, - CLU_PAD_SINK); - clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; - break; - } - - case VSP1_ENTITY_PARAMS_PARTITION: - break; + /* 2D mode can only be used with the YCbCr pixel encoding. */ + if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode) + ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D + | VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D + | VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D; - case VSP1_ENTITY_PARAMS_RUNTIME: - /* 2D mode can only be used with the YCbCr pixel encoding. */ - if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode) - ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D - | VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D - | VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D; + vsp1_clu_write(clu, dlb, VI6_CLU_CTRL, ctrl); - vsp1_clu_write(clu, dl, VI6_CLU_CTRL, ctrl); + spin_lock_irqsave(&clu->lock, flags); + clu_dlb = clu->clu; + clu->clu = NULL; + spin_unlock_irqrestore(&clu->lock, flags); - spin_lock_irqsave(&clu->lock, flags); - dlb = clu->clu; - clu->clu = NULL; - spin_unlock_irqrestore(&clu->lock, flags); + if (clu_dlb) { + vsp1_dl_list_add_body(dl, clu_dlb); - if (dlb) - vsp1_dl_list_add_fragment(dl, dlb); - break; + /* Release our local reference. */ + vsp1_dl_body_put(clu_dlb); } } +static void clu_destroy(struct vsp1_entity *entity) +{ + struct vsp1_clu *clu = to_clu(&entity->subdev); + + vsp1_dl_body_pool_destroy(clu->pool); +} + static const struct vsp1_entity_operations clu_entity_ops = { - .configure = clu_configure, + .configure_stream = clu_configure_stream, + .configure_frame = clu_configure_frame, + .destroy = clu_destroy, }; /* ----------------------------------------------------------------------------- @@ -288,6 +253,17 @@ struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1) if (ret < 0) return ERR_PTR(ret); + /* + * Pre-allocate a body pool, with 3 bodies allowing a userspace update + * before the hardware has committed a previous set of tables, handling + * both the queued and pending dl entries. One extra entry is added to + * the CLU_SIZE to allow for the VI6_CLU_ADDR header. + */ + clu->pool = vsp1_dl_body_pool_create(clu->entity.vsp1, 3, CLU_SIZE + 1, + 0); + if (!clu->pool) + return ERR_PTR(-ENOMEM); + /* Initialize the control handler. */ v4l2_ctrl_handler_init(&clu->ctrls, 2); v4l2_ctrl_new_custom(&clu->ctrls, &clu_table_control, NULL); diff --git a/drivers/media/platform/vsp1/vsp1_clu.h b/drivers/media/platform/vsp1/vsp1_clu.h index 036e0a2f1a42..cef2f44481ba 100644 --- a/drivers/media/platform/vsp1/vsp1_clu.h +++ b/drivers/media/platform/vsp1/vsp1_clu.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_clu.h -- R-Car VSP1 Cubic Look-Up Table * * Copyright (C) 2015 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_CLU_H__ #define __VSP1_CLU_H__ @@ -36,6 +32,7 @@ struct vsp1_clu { spinlock_t lock; unsigned int mode; struct vsp1_dl_body *clu; + struct vsp1_dl_body_pool *pool; }; static inline struct vsp1_clu *to_clu(struct v4l2_subdev *subdev) diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 0b86ed01e85d..d9b9cdd8fbe2 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -1,19 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ /* - * vsp1_dl.h -- R-Car VSP1 Display List + * vsp1_dl.c -- R-Car VSP1 Display List * * Copyright (C) 2015 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/gfp.h> +#include <linux/refcount.h> #include <linux/slab.h> #include <linux/workqueue.h> @@ -45,14 +42,22 @@ struct vsp1_dl_entry { /** * struct vsp1_dl_body - Display list body * @list: entry in the display list list of bodies + * @free: entry in the pool free body list + * @pool: pool to which this body belongs * @vsp1: the VSP1 device * @entries: array of entries * @dma: DMA address of the entries * @size: size of the DMA memory in bytes * @num_entries: number of stored entries + * @max_entries: number of entries available */ struct vsp1_dl_body { struct list_head list; + struct list_head free; + + refcount_t refcnt; + + struct vsp1_dl_body_pool *pool; struct vsp1_device *vsp1; struct vsp1_dl_entry *entries; @@ -60,6 +65,31 @@ struct vsp1_dl_body { size_t size; unsigned int num_entries; + unsigned int max_entries; +}; + +/** + * struct vsp1_dl_body_pool - display list body pool + * @dma: DMA address of the entries + * @size: size of the full DMA memory pool in bytes + * @mem: CPU memory pointer for the pool + * @bodies: Array of DLB structures for the pool + * @free: List of free DLB entries + * @lock: Protects the free list + * @vsp1: the VSP1 device + */ +struct vsp1_dl_body_pool { + /* DMA allocation */ + dma_addr_t dma; + size_t size; + void *mem; + + /* Body management */ + struct vsp1_dl_body *bodies; + struct list_head free; + spinlock_t lock; + + struct vsp1_device *vsp1; }; /** @@ -69,9 +99,10 @@ struct vsp1_dl_body { * @header: display list header, NULL for headerless lists * @dma: DMA address for the header * @body0: first display list body - * @fragments: list of extra display list bodies + * @bodies: list of extra display list bodies * @has_chain: if true, indicates that there's a partition chain * @chain: entry in the display list partition chain + * @internal: whether the display list is used for internal purpose */ struct vsp1_dl_list { struct list_head list; @@ -80,11 +111,13 @@ struct vsp1_dl_list { struct vsp1_dl_header *header; dma_addr_t dma; - struct vsp1_dl_body body0; - struct list_head fragments; + struct vsp1_dl_body *body0; + struct list_head bodies; bool has_chain; struct list_head chain; + + bool internal; }; enum vsp1_dl_mode { @@ -98,13 +131,12 @@ enum vsp1_dl_mode { * @mode: display list operation mode (header or headerless) * @singleshot: execute the display list in single-shot mode * @vsp1: the VSP1 device - * @lock: protects the free, active, queued, pending and gc_fragments lists + * @lock: protects the free, active, queued, and pending lists * @free: array of all free display lists * @active: list currently being processed (loaded) by hardware * @queued: list queued to the hardware (written to the DL registers) * @pending: list waiting to be queued to the hardware - * @gc_work: fragments garbage collector work struct - * @gc_fragments: array of display list fragments waiting to be freed + * @pool: body pool for the display list bodies */ struct vsp1_dl_manager { unsigned int index; @@ -118,109 +150,164 @@ struct vsp1_dl_manager { struct vsp1_dl_list *queued; struct vsp1_dl_list *pending; - struct work_struct gc_work; - struct list_head gc_fragments; + struct vsp1_dl_body_pool *pool; }; /* ----------------------------------------------------------------------------- * Display List Body Management */ -/* - * Initialize a display list body object and allocate DMA memory for the body - * data. The display list body object is expected to have been initialized to - * 0 when allocated. +/** + * vsp1_dl_body_pool_create - Create a pool of bodies from a single allocation + * @vsp1: The VSP1 device + * @num_bodies: The number of bodies to allocate + * @num_entries: The maximum number of entries that a body can contain + * @extra_size: Extra allocation provided for the bodies + * + * Allocate a pool of display list bodies each with enough memory to contain the + * requested number of entries plus the @extra_size. + * + * Return a pointer to a pool on success or NULL if memory can't be allocated. */ -static int vsp1_dl_body_init(struct vsp1_device *vsp1, - struct vsp1_dl_body *dlb, unsigned int num_entries, - size_t extra_size) +struct vsp1_dl_body_pool * +vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies, + unsigned int num_entries, size_t extra_size) { - size_t size = num_entries * sizeof(*dlb->entries) + extra_size; + struct vsp1_dl_body_pool *pool; + size_t dlb_size; + unsigned int i; - dlb->vsp1 = vsp1; - dlb->size = size; + pool = kzalloc(sizeof(*pool), GFP_KERNEL); + if (!pool) + return NULL; - dlb->entries = dma_alloc_wc(vsp1->bus_master, dlb->size, &dlb->dma, - GFP_KERNEL); - if (!dlb->entries) - return -ENOMEM; + pool->vsp1 = vsp1; - return 0; + /* + * TODO: 'extra_size' is only used by vsp1_dlm_create(), to allocate + * extra memory for the display list header. We need only one header per + * display list, not per display list body, thus this allocation is + * extraneous and should be reworked in the future. + */ + dlb_size = num_entries * sizeof(struct vsp1_dl_entry) + extra_size; + pool->size = dlb_size * num_bodies; + + pool->bodies = kcalloc(num_bodies, sizeof(*pool->bodies), GFP_KERNEL); + if (!pool->bodies) { + kfree(pool); + return NULL; + } + + pool->mem = dma_alloc_wc(vsp1->bus_master, pool->size, &pool->dma, + GFP_KERNEL); + if (!pool->mem) { + kfree(pool->bodies); + kfree(pool); + return NULL; + } + + spin_lock_init(&pool->lock); + INIT_LIST_HEAD(&pool->free); + + for (i = 0; i < num_bodies; ++i) { + struct vsp1_dl_body *dlb = &pool->bodies[i]; + + dlb->pool = pool; + dlb->max_entries = num_entries; + + dlb->dma = pool->dma + i * dlb_size; + dlb->entries = pool->mem + i * dlb_size; + + list_add_tail(&dlb->free, &pool->free); + } + + return pool; } -/* - * Cleanup a display list body and free allocated DMA memory allocated. +/** + * vsp1_dl_body_pool_destroy - Release a body pool + * @pool: The body pool + * + * Release all components of a pool allocation. */ -static void vsp1_dl_body_cleanup(struct vsp1_dl_body *dlb) +void vsp1_dl_body_pool_destroy(struct vsp1_dl_body_pool *pool) { - dma_free_wc(dlb->vsp1->bus_master, dlb->size, dlb->entries, dlb->dma); + if (!pool) + return; + + if (pool->mem) + dma_free_wc(pool->vsp1->bus_master, pool->size, pool->mem, + pool->dma); + + kfree(pool->bodies); + kfree(pool); } /** - * vsp1_dl_fragment_alloc - Allocate a display list fragment - * @vsp1: The VSP1 device - * @num_entries: The maximum number of entries that the fragment can contain + * vsp1_dl_body_get - Obtain a body from a pool + * @pool: The body pool * - * Allocate a display list fragment with enough memory to contain the requested - * number of entries. + * Obtain a body from the pool without blocking. * - * Return a pointer to a fragment on success or NULL if memory can't be - * allocated. + * Returns a display list body or NULL if there are none available. */ -struct vsp1_dl_body *vsp1_dl_fragment_alloc(struct vsp1_device *vsp1, - unsigned int num_entries) +struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool) { - struct vsp1_dl_body *dlb; - int ret; + struct vsp1_dl_body *dlb = NULL; + unsigned long flags; - dlb = kzalloc(sizeof(*dlb), GFP_KERNEL); - if (!dlb) - return NULL; + spin_lock_irqsave(&pool->lock, flags); - ret = vsp1_dl_body_init(vsp1, dlb, num_entries, 0); - if (ret < 0) { - kfree(dlb); - return NULL; + if (!list_empty(&pool->free)) { + dlb = list_first_entry(&pool->free, struct vsp1_dl_body, free); + list_del(&dlb->free); + refcount_set(&dlb->refcnt, 1); } + spin_unlock_irqrestore(&pool->lock, flags); + return dlb; } /** - * vsp1_dl_fragment_free - Free a display list fragment - * @dlb: The fragment - * - * Free the given display list fragment and the associated DMA memory. - * - * Fragments must only be freed explicitly if they are not added to a display - * list, as the display list will take ownership of them and free them - * otherwise. Manual free typically happens at cleanup time for fragments that - * have been allocated but not used. + * vsp1_dl_body_put - Return a body back to its pool + * @dlb: The display list body * - * Passing a NULL pointer to this function is safe, in that case no operation - * will be performed. + * Return a body back to the pool, and reset the num_entries to clear the list. */ -void vsp1_dl_fragment_free(struct vsp1_dl_body *dlb) +void vsp1_dl_body_put(struct vsp1_dl_body *dlb) { + unsigned long flags; + if (!dlb) return; - vsp1_dl_body_cleanup(dlb); - kfree(dlb); + if (!refcount_dec_and_test(&dlb->refcnt)) + return; + + dlb->num_entries = 0; + + spin_lock_irqsave(&dlb->pool->lock, flags); + list_add_tail(&dlb->free, &dlb->pool->free); + spin_unlock_irqrestore(&dlb->pool->lock, flags); } /** - * vsp1_dl_fragment_write - Write a register to a display list fragment - * @dlb: The fragment + * vsp1_dl_body_write - Write a register to a display list body + * @dlb: The body * @reg: The register address * @data: The register value * - * Write the given register and value to the display list fragment. The maximum - * number of entries that can be written in a fragment is specified when the - * fragment is allocated by vsp1_dl_fragment_alloc(). + * Write the given register and value to the display list body. The maximum + * number of entries that can be written in a body is specified when the body is + * allocated by vsp1_dl_body_alloc(). */ -void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data) +void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data) { + if (WARN_ONCE(dlb->num_entries >= dlb->max_entries, + "DLB size exceeded (max %u)", dlb->max_entries)) + return; + dlb->entries[dlb->num_entries].addr = reg; dlb->entries[dlb->num_entries].data = data; dlb->num_entries++; @@ -233,51 +320,47 @@ void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data) static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm) { struct vsp1_dl_list *dl; - size_t header_size; - int ret; dl = kzalloc(sizeof(*dl), GFP_KERNEL); if (!dl) return NULL; - INIT_LIST_HEAD(&dl->fragments); + INIT_LIST_HEAD(&dl->bodies); dl->dlm = dlm; - /* - * Initialize the display list body and allocate DMA memory for the body - * and the optional header. Both are allocated together to avoid memory - * fragmentation, with the header located right after the body in - * memory. - */ - header_size = dlm->mode == VSP1_DL_MODE_HEADER - ? ALIGN(sizeof(struct vsp1_dl_header), 8) - : 0; - - ret = vsp1_dl_body_init(dlm->vsp1, &dl->body0, VSP1_DL_NUM_ENTRIES, - header_size); - if (ret < 0) { - kfree(dl); + /* Get a default body for our list. */ + dl->body0 = vsp1_dl_body_get(dlm->pool); + if (!dl->body0) return NULL; - } - if (dlm->mode == VSP1_DL_MODE_HEADER) { - size_t header_offset = VSP1_DL_NUM_ENTRIES - * sizeof(*dl->body0.entries); + size_t header_offset = dl->body0->max_entries + * sizeof(*dl->body0->entries); - dl->header = ((void *)dl->body0.entries) + header_offset; - dl->dma = dl->body0.dma + header_offset; + dl->header = ((void *)dl->body0->entries) + header_offset; + dl->dma = dl->body0->dma + header_offset; memset(dl->header, 0, sizeof(*dl->header)); - dl->header->lists[0].addr = dl->body0.dma; + dl->header->lists[0].addr = dl->body0->dma; } return dl; } +static void vsp1_dl_list_bodies_put(struct vsp1_dl_list *dl) +{ + struct vsp1_dl_body *dlb, *tmp; + + list_for_each_entry_safe(dlb, tmp, &dl->bodies, list) { + list_del(&dlb->list); + vsp1_dl_body_put(dlb); + } +} + static void vsp1_dl_list_free(struct vsp1_dl_list *dl) { - vsp1_dl_body_cleanup(&dl->body0); - list_splice_init(&dl->fragments, &dl->dlm->gc_fragments); + vsp1_dl_body_put(dl->body0); + vsp1_dl_list_bodies_put(dl); + kfree(dl); } @@ -331,18 +414,13 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) dl->has_chain = false; + vsp1_dl_list_bodies_put(dl); + /* - * We can't free fragments here as DMA memory can only be freed in - * interruptible context. Move all fragments to the display list - * manager's list of fragments to be freed, they will be - * garbage-collected by the work queue. + * body0 is reused as as an optimisation as presently every display list + * has at least one body, thus we reinitialise the entries list. */ - if (!list_empty(&dl->fragments)) { - list_splice_init(&dl->fragments, &dl->dlm->gc_fragments); - schedule_work(&dl->dlm->gc_work); - } - - dl->body0.num_entries = 0; + dl->body0->num_entries = 0; list_add_tail(&dl->list, &dl->dlm->free); } @@ -369,43 +447,46 @@ void vsp1_dl_list_put(struct vsp1_dl_list *dl) } /** - * vsp1_dl_list_write - Write a register to the display list + * vsp1_dl_list_get_body0 - Obtain the default body for the display list * @dl: The display list - * @reg: The register address - * @data: The register value * - * Write the given register and value to the display list. Up to 256 registers - * can be written per display list. + * Obtain a pointer to the internal display list body allowing this to be passed + * directly to configure operations. */ -void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data) +struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl) { - vsp1_dl_fragment_write(&dl->body0, reg, data); + return dl->body0; } /** - * vsp1_dl_list_add_fragment - Add a fragment to the display list + * vsp1_dl_list_add_body - Add a body to the display list * @dl: The display list - * @dlb: The fragment + * @dlb: The body + * + * Add a display list body to a display list. Registers contained in bodies are + * processed after registers contained in the main display list, in the order in + * which bodies are added. * - * Add a display list body as a fragment to a display list. Registers contained - * in fragments are processed after registers contained in the main display - * list, in the order in which fragments are added. + * Adding a body to a display list passes ownership of the body to the list. The + * caller retains its reference to the fragment when adding it to the display + * list, but is not allowed to add new entries to the body. * - * Adding a fragment to a display list passes ownership of the fragment to the - * list. The caller must not touch the fragment after this call, and must not - * free it explicitly with vsp1_dl_fragment_free(). + * The reference must be explicitly released by a call to vsp1_dl_body_put() + * when the body isn't needed anymore. * - * Fragments are only usable for display lists in header mode. Attempt to - * add a fragment to a header-less display list will return an error. + * Additional bodies are only usable for display lists in header mode. + * Attempting to add a body to a header-less display list will return an error. */ -int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl, - struct vsp1_dl_body *dlb) +int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { /* Multi-body lists are only available in header mode. */ if (dl->dlm->mode != VSP1_DL_MODE_HEADER) return -EINVAL; - list_add_tail(&dlb->list, &dl->fragments); + refcount_inc(&dlb->refcnt); + + list_add_tail(&dlb->list, &dl->bodies); + return 0; } @@ -451,10 +532,10 @@ static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last) * list was allocated. */ - hdr->num_bytes = dl->body0.num_entries + hdr->num_bytes = dl->body0->num_entries * sizeof(*dl->header->lists); - list_for_each_entry(dlb, &dl->fragments, list) { + list_for_each_entry(dlb, &dl->bodies, list) { num_lists++; hdr++; @@ -525,9 +606,9 @@ static void vsp1_dl_list_hw_enqueue(struct vsp1_dl_list *dl) * bit will be cleared by the hardware when the display list * processing starts. */ - vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->body0.dma); + vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->body0->dma); vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD | - (dl->body0.num_entries * sizeof(*dl->header->lists))); + (dl->body0->num_entries * sizeof(*dl->header->lists))); } else { /* * In header mode, program the display list header address. If @@ -550,8 +631,16 @@ static void vsp1_dl_list_commit_continuous(struct vsp1_dl_list *dl) * case we can't replace the queued list by the new one, as we could * race with the hardware. We thus mark the update as pending, it will * be queued up to the hardware by the frame end interrupt handler. + * + * If a display list is already pending we simply drop it as the new + * display list is assumed to contain a more recent configuration. It is + * an error if the already pending list has the internal flag set, as + * there is then a process waiting for that list to complete. This + * shouldn't happen as the waiting process should perform proper + * locking, but warn just in case. */ if (vsp1_dl_list_hw_update_pending(dlm)) { + WARN_ON(dlm->pending && dlm->pending->internal); __vsp1_dl_list_put(dlm->pending); dlm->pending = dl; return; @@ -581,7 +670,7 @@ static void vsp1_dl_list_commit_singleshot(struct vsp1_dl_list *dl) dlm->active = dl; } -void vsp1_dl_list_commit(struct vsp1_dl_list *dl) +void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal) { struct vsp1_dl_manager *dlm = dl->dlm; struct vsp1_dl_list *dl_child; @@ -598,6 +687,8 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl) } } + dl->internal = internal; + spin_lock_irqsave(&dlm->lock, flags); if (dlm->singleshot) @@ -616,14 +707,22 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl) * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt * @dlm: the display list manager * - * Return true if the previous display list has completed at frame end, or false - * if it has been delayed by one frame because the display list commit raced - * with the frame end interrupt. The function always returns true in header mode - * as display list processing is then not continuous and races never occur. + * Return a set of flags that indicates display list completion status. + * + * The VSP1_DL_FRAME_END_COMPLETED flag indicates that the previous display list + * has completed at frame end. If the flag is not returned display list + * completion has been delayed by one frame because the display list commit + * raced with the frame end interrupt. The function always returns with the flag + * set in header mode as display list processing is then not continuous and + * races never occur. + * + * The VSP1_DL_FRAME_END_INTERNAL flag indicates that the previous display list + * has completed and had been queued with the internal notification flag. + * Internal notification is only supported for continuous mode. */ -bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) +unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) { - bool completed = false; + unsigned int flags = 0; spin_lock(&dlm->lock); @@ -634,7 +733,7 @@ bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) if (dlm->singleshot) { __vsp1_dl_list_put(dlm->active); dlm->active = NULL; - completed = true; + flags |= VSP1_DL_FRAME_END_COMPLETED; goto done; } @@ -652,10 +751,14 @@ bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) * frame end interrupt. The display list thus becomes active. */ if (dlm->queued) { + if (dlm->queued->internal) + flags |= VSP1_DL_FRAME_END_INTERNAL; + dlm->queued->internal = false; + __vsp1_dl_list_put(dlm->active); dlm->active = dlm->queued; dlm->queued = NULL; - completed = true; + flags |= VSP1_DL_FRAME_END_COMPLETED; } /* @@ -672,7 +775,7 @@ bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) done: spin_unlock(&dlm->lock); - return completed; + return flags; } /* Hardware Setup */ @@ -710,38 +813,9 @@ void vsp1_dlm_reset(struct vsp1_dl_manager *dlm) dlm->pending = NULL; } -/* - * Free all fragments awaiting to be garbage-collected. - * - * This function must be called without the display list manager lock held. - */ -static void vsp1_dlm_fragments_free(struct vsp1_dl_manager *dlm) +struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm) { - unsigned long flags; - - spin_lock_irqsave(&dlm->lock, flags); - - while (!list_empty(&dlm->gc_fragments)) { - struct vsp1_dl_body *dlb; - - dlb = list_first_entry(&dlm->gc_fragments, struct vsp1_dl_body, - list); - list_del(&dlb->list); - - spin_unlock_irqrestore(&dlm->lock, flags); - vsp1_dl_fragment_free(dlb); - spin_lock_irqsave(&dlm->lock, flags); - } - - spin_unlock_irqrestore(&dlm->lock, flags); -} - -static void vsp1_dlm_garbage_collect(struct work_struct *work) -{ - struct vsp1_dl_manager *dlm = - container_of(work, struct vsp1_dl_manager, gc_work); - - vsp1_dlm_fragments_free(dlm); + return vsp1_dl_body_get(dlm->pool); } struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, @@ -749,6 +823,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, unsigned int prealloc) { struct vsp1_dl_manager *dlm; + size_t header_size; unsigned int i; dlm = devm_kzalloc(vsp1->dev, sizeof(*dlm), GFP_KERNEL); @@ -763,8 +838,22 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, spin_lock_init(&dlm->lock); INIT_LIST_HEAD(&dlm->free); - INIT_LIST_HEAD(&dlm->gc_fragments); - INIT_WORK(&dlm->gc_work, vsp1_dlm_garbage_collect); + + /* + * Initialize the display list body and allocate DMA memory for the body + * and the optional header. Both are allocated together to avoid memory + * fragmentation, with the header located right after the body in + * memory. An extra body is allocated on top of the prealloc to account + * for the cached body used by the vsp1_pipeline object. + */ + header_size = dlm->mode == VSP1_DL_MODE_HEADER + ? ALIGN(sizeof(struct vsp1_dl_header), 8) + : 0; + + dlm->pool = vsp1_dl_body_pool_create(vsp1, prealloc + 1, + VSP1_DL_NUM_ENTRIES, header_size); + if (!dlm->pool) + return NULL; for (i = 0; i < prealloc; ++i) { struct vsp1_dl_list *dl; @@ -786,12 +875,10 @@ void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm) if (!dlm) return; - cancel_work_sync(&dlm->gc_work); - list_for_each_entry_safe(dl, next, &dlm->free, list) { list_del(&dl->list); vsp1_dl_list_free(dl); } - vsp1_dlm_fragments_free(dlm); + vsp1_dl_body_pool_destroy(dlm->pool); } diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h index ee3508172f0a..7dba0469c92e 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.h +++ b/drivers/media/platform/vsp1/vsp1_dl.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_dl.h -- R-Car VSP1 Display List * * Copyright (C) 2015 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_DL_H__ #define __VSP1_DL_H__ @@ -16,10 +12,14 @@ #include <linux/types.h> struct vsp1_device; -struct vsp1_dl_fragment; +struct vsp1_dl_body; +struct vsp1_dl_body_pool; struct vsp1_dl_list; struct vsp1_dl_manager; +#define VSP1_DL_FRAME_END_COMPLETED BIT(0) +#define VSP1_DL_FRAME_END_INTERNAL BIT(1) + void vsp1_dlm_setup(struct vsp1_device *vsp1); struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, @@ -27,19 +27,23 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, unsigned int prealloc); void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm); void vsp1_dlm_reset(struct vsp1_dl_manager *dlm); -bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm); +unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm); +struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm); struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm); void vsp1_dl_list_put(struct vsp1_dl_list *dl); -void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data); -void vsp1_dl_list_commit(struct vsp1_dl_list *dl); - -struct vsp1_dl_body *vsp1_dl_fragment_alloc(struct vsp1_device *vsp1, - unsigned int num_entries); -void vsp1_dl_fragment_free(struct vsp1_dl_body *dlb); -void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data); -int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl, - struct vsp1_dl_body *dlb); +struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl); +void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal); + +struct vsp1_dl_body_pool * +vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies, + unsigned int num_entries, size_t extra_size); +void vsp1_dl_body_pool_destroy(struct vsp1_dl_body_pool *pool); +struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool); +void vsp1_dl_body_put(struct vsp1_dl_body *dlb); + +void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data); +int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl); #endif /* __VSP1_DL_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index b8fee1834253..edb35a5c57ea 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* - * vsp1_drm.c -- R-Car VSP1 DRM API + * vsp1_drm.c -- R-Car VSP1 DRM/KMS Interface * * Copyright (C) 2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -20,26 +16,550 @@ #include <media/vsp1.h> #include "vsp1.h" -#include "vsp1_bru.h" +#include "vsp1_brx.h" #include "vsp1_dl.h" #include "vsp1_drm.h" #include "vsp1_lif.h" #include "vsp1_pipe.h" #include "vsp1_rwpf.h" +#include "vsp1_uif.h" -#define BRU_NAME(e) (e)->type == VSP1_ENTITY_BRU ? "BRU" : "BRS" +#define BRX_NAME(e) (e)->type == VSP1_ENTITY_BRU ? "BRU" : "BRS" /* ----------------------------------------------------------------------------- * Interrupt Handling */ static void vsp1_du_pipeline_frame_end(struct vsp1_pipeline *pipe, - bool completed) + unsigned int completion) { struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); + bool complete = completion == VSP1_DL_FRAME_END_COMPLETED; + + if (drm_pipe->du_complete) { + struct vsp1_entity *uif = drm_pipe->uif; + u32 crc; - if (drm_pipe->du_complete) - drm_pipe->du_complete(drm_pipe->du_private, completed); + crc = uif ? vsp1_uif_get_crc(to_uif(&uif->subdev)) : 0; + drm_pipe->du_complete(drm_pipe->du_private, complete, crc); + } + + if (completion & VSP1_DL_FRAME_END_INTERNAL) { + drm_pipe->force_brx_release = false; + wake_up(&drm_pipe->wait_queue); + } +} + +/* ----------------------------------------------------------------------------- + * Pipeline Configuration + */ + +/* + * Insert the UIF in the pipeline between the prev and next entities. If no UIF + * is available connect the two entities directly. + */ +static int vsp1_du_insert_uif(struct vsp1_device *vsp1, + struct vsp1_pipeline *pipe, + struct vsp1_entity *uif, + struct vsp1_entity *prev, unsigned int prev_pad, + struct vsp1_entity *next, unsigned int next_pad) +{ + struct v4l2_subdev_format format; + int ret; + + if (!uif) { + /* + * If there's no UIF to be inserted, connect the previous and + * next entities directly. + */ + prev->sink = next; + prev->sink_pad = next_pad; + return 0; + } + + prev->sink = uif; + prev->sink_pad = UIF_PAD_SINK; + + memset(&format, 0, sizeof(format)); + format.which = V4L2_SUBDEV_FORMAT_ACTIVE; + format.pad = prev_pad; + + ret = v4l2_subdev_call(&prev->subdev, pad, get_fmt, NULL, &format); + if (ret < 0) + return ret; + + format.pad = UIF_PAD_SINK; + + ret = v4l2_subdev_call(&uif->subdev, pad, set_fmt, NULL, &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on UIF sink\n", + __func__, format.format.width, format.format.height, + format.format.code); + + /* + * The UIF doesn't mangle the format between its sink and source pads, + * so there is no need to retrieve the format on its source pad. + */ + + uif->sink = next; + uif->sink_pad = next_pad; + + return 0; +} + +/* Setup one RPF and the connected BRx sink pad. */ +static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1, + struct vsp1_pipeline *pipe, + struct vsp1_rwpf *rpf, + struct vsp1_entity *uif, + unsigned int brx_input) +{ + struct v4l2_subdev_selection sel; + struct v4l2_subdev_format format; + const struct v4l2_rect *crop; + int ret; + + /* + * Configure the format on the RPF sink pad and propagate it up to the + * BRx sink pad. + */ + crop = &vsp1->drm->inputs[rpf->entity.index].crop; + + memset(&format, 0, sizeof(format)); + format.which = V4L2_SUBDEV_FORMAT_ACTIVE; + format.pad = RWPF_PAD_SINK; + format.format.width = crop->width + crop->left; + format.format.height = crop->height + crop->top; + format.format.code = rpf->fmtinfo->mbus; + format.format.field = V4L2_FIELD_NONE; + + ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, + "%s: set format %ux%u (%x) on RPF%u sink\n", + __func__, format.format.width, format.format.height, + format.format.code, rpf->entity.index); + + memset(&sel, 0, sizeof(sel)); + sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sel.pad = RWPF_PAD_SINK; + sel.target = V4L2_SEL_TGT_CROP; + sel.r = *crop; + + ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL, + &sel); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, + "%s: set selection (%u,%u)/%ux%u on RPF%u sink\n", + __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height, + rpf->entity.index); + + /* + * RPF source, hardcode the format to ARGB8888 to turn on format + * conversion if needed. + */ + format.pad = RWPF_PAD_SOURCE; + + ret = v4l2_subdev_call(&rpf->entity.subdev, pad, get_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, + "%s: got format %ux%u (%x) on RPF%u source\n", + __func__, format.format.width, format.format.height, + format.format.code, rpf->entity.index); + + format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32; + + ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + /* Insert and configure the UIF if available. */ + ret = vsp1_du_insert_uif(vsp1, pipe, uif, &rpf->entity, RWPF_PAD_SOURCE, + pipe->brx, brx_input); + if (ret < 0) + return ret; + + /* BRx sink, propagate the format from the RPF source. */ + format.pad = brx_input; + + ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n", + __func__, format.format.width, format.format.height, + format.format.code, BRX_NAME(pipe->brx), format.pad); + + sel.pad = brx_input; + sel.target = V4L2_SEL_TGT_COMPOSE; + sel.r = vsp1->drm->inputs[rpf->entity.index].compose; + + ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_selection, NULL, + &sel); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set selection (%u,%u)/%ux%u on %s pad %u\n", + __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height, + BRX_NAME(pipe->brx), sel.pad); + + return 0; +} + +/* Setup the BRx source pad. */ +static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1, + struct vsp1_pipeline *pipe); +static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe); + +static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, + struct vsp1_pipeline *pipe) +{ + struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct vsp1_entity *brx; + int ret; + + /* + * Pick a BRx: + * - If we need more than two inputs, use the BRU. + * - Otherwise, if we are not forced to release our BRx, keep it. + * - Else, use any free BRx (randomly starting with the BRU). + */ + if (pipe->num_inputs > 2) + brx = &vsp1->bru->entity; + else if (pipe->brx && !drm_pipe->force_brx_release) + brx = pipe->brx; + else if (!vsp1->bru->entity.pipe) + brx = &vsp1->bru->entity; + else + brx = &vsp1->brs->entity; + + /* Switch BRx if needed. */ + if (brx != pipe->brx) { + struct vsp1_entity *released_brx = NULL; + + /* Release our BRx if we have one. */ + if (pipe->brx) { + dev_dbg(vsp1->dev, "%s: pipe %u: releasing %s\n", + __func__, pipe->lif->index, + BRX_NAME(pipe->brx)); + + /* + * The BRx might be acquired by the other pipeline in + * the next step. We must thus remove it from the list + * of entities for this pipeline. The other pipeline's + * hardware configuration will reconfigure the BRx + * routing. + * + * However, if the other pipeline doesn't acquire our + * BRx, we need to keep it in the list, otherwise the + * hardware configuration step won't disconnect it from + * the pipeline. To solve this, store the released BRx + * pointer to add it back to the list of entities later + * if it isn't acquired by the other pipeline. + */ + released_brx = pipe->brx; + + list_del(&pipe->brx->list_pipe); + pipe->brx->sink = NULL; + pipe->brx->pipe = NULL; + pipe->brx = NULL; + } + + /* + * If the BRx we need is in use, force the owner pipeline to + * switch to the other BRx and wait until the switch completes. + */ + if (brx->pipe) { + struct vsp1_drm_pipeline *owner_pipe; + + dev_dbg(vsp1->dev, "%s: pipe %u: waiting for %s\n", + __func__, pipe->lif->index, BRX_NAME(brx)); + + owner_pipe = to_vsp1_drm_pipeline(brx->pipe); + owner_pipe->force_brx_release = true; + + vsp1_du_pipeline_setup_inputs(vsp1, &owner_pipe->pipe); + vsp1_du_pipeline_configure(&owner_pipe->pipe); + + ret = wait_event_timeout(owner_pipe->wait_queue, + !owner_pipe->force_brx_release, + msecs_to_jiffies(500)); + if (ret == 0) + dev_warn(vsp1->dev, + "DRM pipeline %u reconfiguration timeout\n", + owner_pipe->pipe.lif->index); + } + + /* + * If the BRx we have released previously hasn't been acquired + * by the other pipeline, add it back to the entities list (with + * the pipe pointer NULL) to let vsp1_du_pipeline_configure() + * disconnect it from the hardware pipeline. + */ + if (released_brx && !released_brx->pipe) + list_add_tail(&released_brx->list_pipe, + &pipe->entities); + + /* Add the BRx to the pipeline. */ + dev_dbg(vsp1->dev, "%s: pipe %u: acquired %s\n", + __func__, pipe->lif->index, BRX_NAME(brx)); + + pipe->brx = brx; + pipe->brx->pipe = pipe; + pipe->brx->sink = &pipe->output->entity; + pipe->brx->sink_pad = 0; + + list_add_tail(&pipe->brx->list_pipe, &pipe->entities); + } + + /* + * Configure the format on the BRx source and verify that it matches the + * requested format. We don't set the media bus code as it is configured + * on the BRx sink pad 0 and propagated inside the entity, not on the + * source pad. + */ + format.pad = pipe->brx->source_pad; + format.format.width = drm_pipe->width; + format.format.height = drm_pipe->height; + format.format.field = V4L2_FIELD_NONE; + + ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n", + __func__, format.format.width, format.format.height, + format.format.code, BRX_NAME(pipe->brx), pipe->brx->source_pad); + + if (format.format.width != drm_pipe->width || + format.format.height != drm_pipe->height) { + dev_dbg(vsp1->dev, "%s: format mismatch\n", __func__); + return -EPIPE; + } + + return 0; +} + +static unsigned int rpf_zpos(struct vsp1_device *vsp1, struct vsp1_rwpf *rpf) +{ + return vsp1->drm->inputs[rpf->entity.index].zpos; +} + +/* Setup the input side of the pipeline (RPFs and BRx). */ +static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1, + struct vsp1_pipeline *pipe) +{ + struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); + struct vsp1_rwpf *inputs[VSP1_MAX_RPF] = { NULL, }; + struct vsp1_entity *uif; + bool use_uif = false; + struct vsp1_brx *brx; + unsigned int i; + int ret; + + /* Count the number of enabled inputs and sort them by Z-order. */ + pipe->num_inputs = 0; + + for (i = 0; i < vsp1->info->rpf_count; ++i) { + struct vsp1_rwpf *rpf = vsp1->rpf[i]; + unsigned int j; + + if (!pipe->inputs[i]) + continue; + + /* Insert the RPF in the sorted RPFs array. */ + for (j = pipe->num_inputs++; j > 0; --j) { + if (rpf_zpos(vsp1, inputs[j-1]) <= rpf_zpos(vsp1, rpf)) + break; + inputs[j] = inputs[j-1]; + } + + inputs[j] = rpf; + } + + /* + * Setup the BRx. This must be done before setting up the RPF input + * pipelines as the BRx sink compose rectangles depend on the BRx source + * format. + */ + ret = vsp1_du_pipeline_setup_brx(vsp1, pipe); + if (ret < 0) { + dev_err(vsp1->dev, "%s: failed to setup %s source\n", __func__, + BRX_NAME(pipe->brx)); + return ret; + } + + brx = to_brx(&pipe->brx->subdev); + + /* Setup the RPF input pipeline for every enabled input. */ + for (i = 0; i < pipe->brx->source_pad; ++i) { + struct vsp1_rwpf *rpf = inputs[i]; + + if (!rpf) { + brx->inputs[i].rpf = NULL; + continue; + } + + if (!rpf->entity.pipe) { + rpf->entity.pipe = pipe; + list_add_tail(&rpf->entity.list_pipe, &pipe->entities); + } + + brx->inputs[i].rpf = rpf; + rpf->brx_input = i; + rpf->entity.sink = pipe->brx; + rpf->entity.sink_pad = i; + + dev_dbg(vsp1->dev, "%s: connecting RPF.%u to %s:%u\n", + __func__, rpf->entity.index, BRX_NAME(pipe->brx), i); + + uif = drm_pipe->crc.source == VSP1_DU_CRC_PLANE && + drm_pipe->crc.index == i ? drm_pipe->uif : NULL; + if (uif) + use_uif = true; + ret = vsp1_du_pipeline_setup_rpf(vsp1, pipe, rpf, uif, i); + if (ret < 0) { + dev_err(vsp1->dev, + "%s: failed to setup RPF.%u\n", + __func__, rpf->entity.index); + return ret; + } + } + + /* Insert and configure the UIF at the BRx output if available. */ + uif = drm_pipe->crc.source == VSP1_DU_CRC_OUTPUT ? drm_pipe->uif : NULL; + if (uif) + use_uif = true; + ret = vsp1_du_insert_uif(vsp1, pipe, uif, + pipe->brx, pipe->brx->source_pad, + &pipe->output->entity, 0); + if (ret < 0) + dev_err(vsp1->dev, "%s: failed to setup UIF after %s\n", + __func__, BRX_NAME(pipe->brx)); + + /* + * If the UIF is not in use schedule it for removal by setting its pipe + * pointer to NULL, vsp1_du_pipeline_configure() will remove it from the + * hardware pipeline and from the pipeline's list of entities. Otherwise + * make sure it is present in the pipeline's list of entities if it + * wasn't already. + */ + if (!use_uif) { + drm_pipe->uif->pipe = NULL; + } else if (!drm_pipe->uif->pipe) { + drm_pipe->uif->pipe = pipe; + list_add_tail(&drm_pipe->uif->list_pipe, &pipe->entities); + } + + return 0; +} + +/* Setup the output side of the pipeline (WPF and LIF). */ +static int vsp1_du_pipeline_setup_output(struct vsp1_device *vsp1, + struct vsp1_pipeline *pipe) +{ + struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); + struct v4l2_subdev_format format = { 0, }; + int ret; + + format.which = V4L2_SUBDEV_FORMAT_ACTIVE; + format.pad = RWPF_PAD_SINK; + format.format.width = drm_pipe->width; + format.format.height = drm_pipe->height; + format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32; + format.format.field = V4L2_FIELD_NONE; + + ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on WPF%u sink\n", + __func__, format.format.width, format.format.height, + format.format.code, pipe->output->entity.index); + + format.pad = RWPF_PAD_SOURCE; + ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, get_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: got format %ux%u (%x) on WPF%u source\n", + __func__, format.format.width, format.format.height, + format.format.code, pipe->output->entity.index); + + format.pad = LIF_PAD_SINK; + ret = v4l2_subdev_call(&pipe->lif->subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on LIF%u sink\n", + __func__, format.format.width, format.format.height, + format.format.code, pipe->lif->index); + + /* + * Verify that the format at the output of the pipeline matches the + * requested frame size and media bus code. + */ + if (format.format.width != drm_pipe->width || + format.format.height != drm_pipe->height || + format.format.code != MEDIA_BUS_FMT_ARGB8888_1X32) { + dev_dbg(vsp1->dev, "%s: format mismatch on LIF%u\n", __func__, + pipe->lif->index); + return -EPIPE; + } + + return 0; +} + +/* Configure all entities in the pipeline. */ +static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) +{ + struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); + struct vsp1_entity *entity; + struct vsp1_entity *next; + struct vsp1_dl_list *dl; + struct vsp1_dl_body *dlb; + + dl = vsp1_dl_list_get(pipe->output->dlm); + dlb = vsp1_dl_list_get_body0(dl); + + list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) { + /* Disconnect unused entities from the pipeline. */ + if (!entity->pipe) { + vsp1_dl_body_write(dlb, entity->route->reg, + VI6_DPR_NODE_UNUSED); + + entity->sink = NULL; + list_del(&entity->list_pipe); + + continue; + } + + vsp1_entity_route_setup(entity, pipe, dlb); + vsp1_entity_configure_stream(entity, pipe, dlb); + vsp1_entity_configure_frame(entity, pipe, dl, dlb); + vsp1_entity_configure_partition(entity, pipe, dl, dlb); + } + + vsp1_dl_list_commit(dl, drm_pipe->force_brx_release); } /* ----------------------------------------------------------------------------- @@ -64,8 +584,8 @@ EXPORT_SYMBOL_GPL(vsp1_du_init); * @cfg: the LIF configuration * * Configure the output part of VSP DRM pipeline for the given frame @cfg.width - * and @cfg.height. This sets up formats on the blend unit (BRU or BRS) source - * pad, the WPF sink and source pads, and the LIF sink pad. + * and @cfg.height. This sets up formats on the BRx source pad, the WPF sink and + * source pads, and the LIF sink pad. * * The @pipe_index argument selects which DRM pipeline to setup. The number of * available pipelines depend on the VSP instance. @@ -84,11 +604,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_drm_pipeline *drm_pipe; struct vsp1_pipeline *pipe; - struct vsp1_bru *bru; - struct vsp1_entity *entity; - struct vsp1_entity *next; - struct vsp1_dl_list *dl; - struct v4l2_subdev_format format; unsigned long flags; unsigned int i; int ret; @@ -98,9 +613,14 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, drm_pipe = &vsp1->drm->pipe[pipe_index]; pipe = &drm_pipe->pipe; - bru = to_bru(&pipe->bru->subdev); if (!cfg) { + struct vsp1_brx *brx; + + mutex_lock(&vsp1->drm->lock); + + brx = to_brx(&pipe->brx->subdev); + /* * NULL configuration means the CRTC is being disabled, stop * the pipeline and turn the light off. @@ -109,8 +629,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, if (ret == -ETIMEDOUT) dev_err(vsp1->dev, "DRM pipeline stop timeout\n"); - media_pipeline_stop(&pipe->output->entity.subdev.entity); - for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { struct vsp1_rwpf *rpf = pipe->inputs[i]; @@ -118,19 +636,30 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, continue; /* - * Remove the RPF from the pipe and the list of BRU + * Remove the RPF from the pipe and the list of BRx * inputs. */ - WARN_ON(list_empty(&rpf->entity.list_pipe)); - list_del_init(&rpf->entity.list_pipe); + WARN_ON(!rpf->entity.pipe); + rpf->entity.pipe = NULL; + list_del(&rpf->entity.list_pipe); pipe->inputs[i] = NULL; - bru->inputs[rpf->bru_input].rpf = NULL; + brx->inputs[rpf->brx_input].rpf = NULL; } drm_pipe->du_complete = NULL; pipe->num_inputs = 0; + dev_dbg(vsp1->dev, "%s: pipe %u: releasing %s\n", + __func__, pipe->lif->index, + BRX_NAME(pipe->brx)); + + list_del(&pipe->brx->list_pipe); + pipe->brx->pipe = NULL; + pipe->brx = NULL; + + mutex_unlock(&vsp1->drm->lock); + vsp1_dlm_reset(pipe->output->dlm); vsp1_device_put(vsp1); @@ -139,100 +668,27 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, return 0; } + drm_pipe->width = cfg->width; + drm_pipe->height = cfg->height; + dev_dbg(vsp1->dev, "%s: configuring LIF%u with format %ux%u\n", __func__, pipe_index, cfg->width, cfg->height); - /* - * Configure the format at the BRU sinks and propagate it through the - * pipeline. - */ - memset(&format, 0, sizeof(format)); - format.which = V4L2_SUBDEV_FORMAT_ACTIVE; - - for (i = 0; i < pipe->bru->source_pad; ++i) { - format.pad = i; - - format.format.width = cfg->width; - format.format.height = cfg->height; - format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32; - format.format.field = V4L2_FIELD_NONE; - - ret = v4l2_subdev_call(&pipe->bru->subdev, pad, - set_fmt, NULL, &format); - if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n", - __func__, format.format.width, format.format.height, - format.format.code, BRU_NAME(pipe->bru), i); - } - - format.pad = pipe->bru->source_pad; - format.format.width = cfg->width; - format.format.height = cfg->height; - format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32; - format.format.field = V4L2_FIELD_NONE; - - ret = v4l2_subdev_call(&pipe->bru->subdev, pad, set_fmt, NULL, - &format); - if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n", - __func__, format.format.width, format.format.height, - format.format.code, BRU_NAME(pipe->bru), i); - - format.pad = RWPF_PAD_SINK; - ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, set_fmt, NULL, - &format); - if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on WPF%u sink\n", - __func__, format.format.width, format.format.height, - format.format.code, pipe->output->entity.index); + mutex_lock(&vsp1->drm->lock); - format.pad = RWPF_PAD_SOURCE; - ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, get_fmt, NULL, - &format); + /* Setup formats through the pipeline. */ + ret = vsp1_du_pipeline_setup_inputs(vsp1, pipe); if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, "%s: got format %ux%u (%x) on WPF%u source\n", - __func__, format.format.width, format.format.height, - format.format.code, pipe->output->entity.index); + goto unlock; - format.pad = LIF_PAD_SINK; - ret = v4l2_subdev_call(&pipe->lif->subdev, pad, set_fmt, NULL, - &format); + ret = vsp1_du_pipeline_setup_output(vsp1, pipe); if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on LIF%u sink\n", - __func__, format.format.width, format.format.height, - format.format.code, pipe_index); + goto unlock; - /* - * Verify that the format at the output of the pipeline matches the - * requested frame size and media bus code. - */ - if (format.format.width != cfg->width || - format.format.height != cfg->height || - format.format.code != MEDIA_BUS_FMT_ARGB8888_1X32) { - dev_dbg(vsp1->dev, "%s: format mismatch\n", __func__); - return -EPIPE; - } - - /* - * Mark the pipeline as streaming and enable the VSP1. This will store - * the pipeline pointer in all entities, which the s_stream handlers - * will need. We don't start the entities themselves right at this point - * as there's no plane configured yet, so we can't start processing - * buffers. - */ + /* Enable the VSP1. */ ret = vsp1_device_get(vsp1); if (ret < 0) - return ret; + goto unlock; /* * Register a callback to allow us to notify the DRM driver of frame @@ -241,35 +697,18 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, drm_pipe->du_complete = cfg->callback; drm_pipe->du_private = cfg->callback_data; - ret = media_pipeline_start(&pipe->output->entity.subdev.entity, - &pipe->pipe); - if (ret < 0) { - dev_dbg(vsp1->dev, "%s: pipeline start failed\n", __func__); - vsp1_device_put(vsp1); - return ret; - } - /* Disable the display interrupts. */ vsp1_write(vsp1, VI6_DISP_IRQ_STA, 0); vsp1_write(vsp1, VI6_DISP_IRQ_ENB, 0); /* Configure all entities in the pipeline. */ - dl = vsp1_dl_list_get(pipe->output->dlm); + vsp1_du_pipeline_configure(pipe); - list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) { - vsp1_entity_route_setup(entity, pipe, dl); - - if (entity->ops->configure) { - entity->ops->configure(entity, pipe, dl, - VSP1_ENTITY_PARAMS_INIT); - entity->ops->configure(entity, pipe, dl, - VSP1_ENTITY_PARAMS_RUNTIME); - entity->ops->configure(entity, pipe, dl, - VSP1_ENTITY_PARAMS_PARTITION); - } - } +unlock: + mutex_unlock(&vsp1->drm->lock); - vsp1_dl_list_commit(dl); + if (ret < 0) + return ret; /* Start the pipeline. */ spin_lock_irqsave(&pipe->irqlock, flags); @@ -290,9 +729,8 @@ EXPORT_SYMBOL_GPL(vsp1_du_setup_lif); void vsp1_du_atomic_begin(struct device *dev, unsigned int pipe_index) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); - struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index]; - drm_pipe->enabled = drm_pipe->pipe.num_inputs != 0; + mutex_lock(&vsp1->drm->lock); } EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin); @@ -345,10 +783,11 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index, rpf_index); /* - * Remove the RPF from the pipe's inputs. The atomic flush - * handler will disable the input and remove the entity from the - * pipe's entities list. + * Remove the RPF from the pipeline's inputs. Keep it in the + * pipeline's entity list to let vsp1_du_pipeline_configure() + * remove it from the hardware pipeline. */ + rpf->entity.pipe = NULL; drm_pipe->pipe.inputs[rpf_index] = NULL; return 0; } @@ -392,214 +831,24 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index, } EXPORT_SYMBOL_GPL(vsp1_du_atomic_update); -static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1, - struct vsp1_pipeline *pipe, - struct vsp1_rwpf *rpf, unsigned int bru_input) -{ - struct v4l2_subdev_selection sel; - struct v4l2_subdev_format format; - const struct v4l2_rect *crop; - int ret; - - /* - * Configure the format on the RPF sink pad and propagate it up to the - * BRU sink pad. - */ - crop = &vsp1->drm->inputs[rpf->entity.index].crop; - - memset(&format, 0, sizeof(format)); - format.which = V4L2_SUBDEV_FORMAT_ACTIVE; - format.pad = RWPF_PAD_SINK; - format.format.width = crop->width + crop->left; - format.format.height = crop->height + crop->top; - format.format.code = rpf->fmtinfo->mbus; - format.format.field = V4L2_FIELD_NONE; - - ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL, - &format); - if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, - "%s: set format %ux%u (%x) on RPF%u sink\n", - __func__, format.format.width, format.format.height, - format.format.code, rpf->entity.index); - - memset(&sel, 0, sizeof(sel)); - sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sel.pad = RWPF_PAD_SINK; - sel.target = V4L2_SEL_TGT_CROP; - sel.r = *crop; - - ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL, - &sel); - if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, - "%s: set selection (%u,%u)/%ux%u on RPF%u sink\n", - __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height, - rpf->entity.index); - - /* - * RPF source, hardcode the format to ARGB8888 to turn on format - * conversion if needed. - */ - format.pad = RWPF_PAD_SOURCE; - - ret = v4l2_subdev_call(&rpf->entity.subdev, pad, get_fmt, NULL, - &format); - if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, - "%s: got format %ux%u (%x) on RPF%u source\n", - __func__, format.format.width, format.format.height, - format.format.code, rpf->entity.index); - - format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32; - - ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL, - &format); - if (ret < 0) - return ret; - - /* BRU sink, propagate the format from the RPF source. */ - format.pad = bru_input; - - ret = v4l2_subdev_call(&pipe->bru->subdev, pad, set_fmt, NULL, - &format); - if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n", - __func__, format.format.width, format.format.height, - format.format.code, BRU_NAME(pipe->bru), format.pad); - - sel.pad = bru_input; - sel.target = V4L2_SEL_TGT_COMPOSE; - sel.r = vsp1->drm->inputs[rpf->entity.index].compose; - - ret = v4l2_subdev_call(&pipe->bru->subdev, pad, set_selection, NULL, - &sel); - if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, "%s: set selection (%u,%u)/%ux%u on %s pad %u\n", - __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height, - BRU_NAME(pipe->bru), sel.pad); - - return 0; -} - -static unsigned int rpf_zpos(struct vsp1_device *vsp1, struct vsp1_rwpf *rpf) -{ - return vsp1->drm->inputs[rpf->entity.index].zpos; -} - /** * vsp1_du_atomic_flush - Commit an atomic update * @dev: the VSP device * @pipe_index: the DRM pipeline index + * @cfg: atomic pipe configuration */ -void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index) +void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, + const struct vsp1_du_atomic_pipe_config *cfg) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index]; struct vsp1_pipeline *pipe = &drm_pipe->pipe; - struct vsp1_rwpf *inputs[VSP1_MAX_RPF] = { NULL, }; - struct vsp1_bru *bru = to_bru(&pipe->bru->subdev); - struct vsp1_entity *entity; - struct vsp1_entity *next; - struct vsp1_dl_list *dl; - unsigned int i; - int ret; - - /* Prepare the display list. */ - dl = vsp1_dl_list_get(pipe->output->dlm); - - /* Count the number of enabled inputs and sort them by Z-order. */ - pipe->num_inputs = 0; - - for (i = 0; i < vsp1->info->rpf_count; ++i) { - struct vsp1_rwpf *rpf = vsp1->rpf[i]; - unsigned int j; - /* - * Make sure we don't accept more inputs than the hardware can - * handle. This is a temporary fix to avoid display stall, we - * need to instead allocate the BRU or BRS to display pipelines - * dynamically based on the number of planes they each use. - */ - if (pipe->num_inputs >= pipe->bru->source_pad) - pipe->inputs[i] = NULL; + drm_pipe->crc = cfg->crc; - if (!pipe->inputs[i]) - continue; - - /* Insert the RPF in the sorted RPFs array. */ - for (j = pipe->num_inputs++; j > 0; --j) { - if (rpf_zpos(vsp1, inputs[j-1]) <= rpf_zpos(vsp1, rpf)) - break; - inputs[j] = inputs[j-1]; - } - - inputs[j] = rpf; - } - - /* Setup the RPF input pipeline for every enabled input. */ - for (i = 0; i < pipe->bru->source_pad; ++i) { - struct vsp1_rwpf *rpf = inputs[i]; - - if (!rpf) { - bru->inputs[i].rpf = NULL; - continue; - } - - if (list_empty(&rpf->entity.list_pipe)) - list_add_tail(&rpf->entity.list_pipe, &pipe->entities); - - bru->inputs[i].rpf = rpf; - rpf->bru_input = i; - rpf->entity.sink = pipe->bru; - rpf->entity.sink_pad = i; - - dev_dbg(vsp1->dev, "%s: connecting RPF.%u to %s:%u\n", - __func__, rpf->entity.index, BRU_NAME(pipe->bru), i); - - ret = vsp1_du_setup_rpf_pipe(vsp1, pipe, rpf, i); - if (ret < 0) - dev_err(vsp1->dev, - "%s: failed to setup RPF.%u\n", - __func__, rpf->entity.index); - } - - /* Configure all entities in the pipeline. */ - list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) { - /* Disconnect unused RPFs from the pipeline. */ - if (entity->type == VSP1_ENTITY_RPF && - !pipe->inputs[entity->index]) { - vsp1_dl_list_write(dl, entity->route->reg, - VI6_DPR_NODE_UNUSED); - - list_del_init(&entity->list_pipe); - - continue; - } - - vsp1_entity_route_setup(entity, pipe, dl); - - if (entity->ops->configure) { - entity->ops->configure(entity, pipe, dl, - VSP1_ENTITY_PARAMS_INIT); - entity->ops->configure(entity, pipe, dl, - VSP1_ENTITY_PARAMS_RUNTIME); - entity->ops->configure(entity, pipe, dl, - VSP1_ENTITY_PARAMS_PARTITION); - } - } - - vsp1_dl_list_commit(dl); + vsp1_du_pipeline_setup_inputs(vsp1, pipe); + vsp1_du_pipeline_configure(pipe); + mutex_unlock(&vsp1->drm->lock); } EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush); @@ -638,31 +887,40 @@ int vsp1_drm_init(struct vsp1_device *vsp1) if (!vsp1->drm) return -ENOMEM; + mutex_init(&vsp1->drm->lock); + /* Create one DRM pipeline per LIF. */ for (i = 0; i < vsp1->info->lif_count; ++i) { struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[i]; struct vsp1_pipeline *pipe = &drm_pipe->pipe; + init_waitqueue_head(&drm_pipe->wait_queue); + vsp1_pipeline_init(pipe); + pipe->frame_end = vsp1_du_pipeline_frame_end; + /* - * The DRM pipeline is static, add entities manually. The first - * pipeline uses the BRU and the second pipeline the BRS. + * The output side of the DRM pipeline is static, add the + * corresponding entities manually. */ - pipe->bru = i == 0 ? &vsp1->bru->entity : &vsp1->brs->entity; - pipe->lif = &vsp1->lif[i]->entity; pipe->output = vsp1->wpf[i]; - pipe->output->pipe = pipe; - pipe->frame_end = vsp1_du_pipeline_frame_end; + pipe->lif = &vsp1->lif[i]->entity; - pipe->bru->sink = &pipe->output->entity; - pipe->bru->sink_pad = 0; + pipe->output->entity.pipe = pipe; pipe->output->entity.sink = pipe->lif; pipe->output->entity.sink_pad = 0; + list_add_tail(&pipe->output->entity.list_pipe, &pipe->entities); - list_add_tail(&pipe->bru->list_pipe, &pipe->entities); + pipe->lif->pipe = pipe; list_add_tail(&pipe->lif->list_pipe, &pipe->entities); - list_add_tail(&pipe->output->entity.list_pipe, &pipe->entities); + + /* + * CRC computation is initially disabled, don't add the UIF to + * the pipeline. + */ + if (i < vsp1->info->uif_count) + drm_pipe->uif = &vsp1->uif[i]->entity; } /* Disable all RPFs initially. */ @@ -677,4 +935,5 @@ int vsp1_drm_init(struct vsp1_device *vsp1) void vsp1_drm_cleanup(struct vsp1_device *vsp1) { + mutex_destroy(&vsp1->drm->lock); } diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/vsp1/vsp1_drm.h index 1cd9db785bf7..8dfd274a59e2 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.h +++ b/drivers/media/platform/vsp1/vsp1_drm.h @@ -1,46 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_drm.h -- R-Car VSP1 DRM/KMS Interface * * Copyright (C) 2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_DRM_H__ #define __VSP1_DRM_H__ +#include <linux/mutex.h> #include <linux/videodev2.h> +#include <linux/wait.h> + +#include <media/vsp1.h> #include "vsp1_pipe.h" /** * vsp1_drm_pipeline - State for the API exposed to the DRM driver * @pipe: the VSP1 pipeline used for display - * @enabled: pipeline state at the beginning of an update + * @width: output display width + * @height: output display height + * @force_brx_release: when set, release the BRx during the next reconfiguration + * @wait_queue: wait queue to wait for BRx release completion + * @uif: UIF entity if available for the pipeline + * @crc: CRC computation configuration * @du_complete: frame completion callback for the DU driver (optional) * @du_private: data to be passed to the du_complete callback */ struct vsp1_drm_pipeline { struct vsp1_pipeline pipe; - bool enabled; + + unsigned int width; + unsigned int height; + + bool force_brx_release; + wait_queue_head_t wait_queue; + + struct vsp1_entity *uif; + struct vsp1_du_crc_config crc; /* Frame synchronisation */ - void (*du_complete)(void *, bool); + void (*du_complete)(void *data, bool completed, u32 crc); void *du_private; }; /** * vsp1_drm - State for the API exposed to the DRM driver * @pipe: the VSP1 DRM pipeline used for display + * @lock: protects the BRU and BRS allocation * @inputs: source crop rectangle, destination compose rectangle and z-order * position for every input (indexed by RPF index) */ struct vsp1_drm { struct vsp1_drm_pipeline pipe[VSP1_MAX_LIF]; + struct mutex lock; struct { struct v4l2_rect crop; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index eed9516e25e1..5d82f6ee56ea 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_drv.c -- R-Car VSP1 Driver * * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/clk.h> @@ -26,7 +22,7 @@ #include <media/v4l2-subdev.h> #include "vsp1.h" -#include "vsp1_bru.h" +#include "vsp1_brx.h" #include "vsp1_clu.h" #include "vsp1_dl.h" #include "vsp1_drm.h" @@ -39,6 +35,7 @@ #include "vsp1_rwpf.h" #include "vsp1_sru.h" #include "vsp1_uds.h" +#include "vsp1_uif.h" #include "vsp1_video.h" /* ----------------------------------------------------------------------------- @@ -63,7 +60,7 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data) vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask); if (status & VI6_WFP_IRQ_STA_DFE) { - vsp1_pipeline_frame_end(wpf->pipe); + vsp1_pipeline_frame_end(wpf->entity.pipe); ret = IRQ_HANDLED; } } @@ -269,7 +266,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) /* Instantiate all the entities. */ if (vsp1->info->features & VSP1_HAS_BRS) { - vsp1->brs = vsp1_bru_create(vsp1, VSP1_ENTITY_BRS); + vsp1->brs = vsp1_brx_create(vsp1, VSP1_ENTITY_BRS); if (IS_ERR(vsp1->brs)) { ret = PTR_ERR(vsp1->brs); goto done; @@ -279,7 +276,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) } if (vsp1->info->features & VSP1_HAS_BRU) { - vsp1->bru = vsp1_bru_create(vsp1, VSP1_ENTITY_BRU); + vsp1->bru = vsp1_brx_create(vsp1, VSP1_ENTITY_BRU); if (IS_ERR(vsp1->bru)) { ret = PTR_ERR(vsp1->bru); goto done; @@ -413,6 +410,19 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&uds->entity.list_dev, &vsp1->entities); } + for (i = 0; i < vsp1->info->uif_count; ++i) { + struct vsp1_uif *uif; + + uif = vsp1_uif_create(vsp1, i); + if (IS_ERR(uif)) { + ret = PTR_ERR(uif); + goto done; + } + + vsp1->uif[i] = uif; + list_add_tail(&uif->entity.list_dev, &vsp1->entities); + } + for (i = 0; i < vsp1->info->wpf_count; ++i) { struct vsp1_rwpf *wpf; @@ -517,6 +527,9 @@ static int vsp1_device_init(struct vsp1_device *vsp1) for (i = 0; i < vsp1->info->uds_count; ++i) vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED); + for (i = 0; i < vsp1->info->uif_count; ++i) + vsp1_write(vsp1, VI6_DPR_UIF_ROUTE(i), VI6_DPR_NODE_UNUSED); + vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED); vsp1_write(vsp1, VI6_DPR_LUT_ROUTE, VI6_DPR_NODE_UNUSED); vsp1_write(vsp1, VI6_DPR_CLU_ROUTE, VI6_DPR_NODE_UNUSED); @@ -576,7 +589,7 @@ static int __maybe_unused vsp1_pm_suspend(struct device *dev) * restarted explicitly by the DU. */ if (!vsp1->drm) - vsp1_pipelines_suspend(vsp1); + vsp1_video_suspend(vsp1); pm_runtime_force_suspend(vsp1->dev); @@ -594,7 +607,7 @@ static int __maybe_unused vsp1_pm_resume(struct device *dev) * restarted explicitly by the DU. */ if (!vsp1->drm) - vsp1_pipelines_resume(vsp1); + vsp1_video_resume(vsp1); return 0; } @@ -744,6 +757,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP, .lif_count = 1, .rpf_count = 5, + .uif_count = 1, .wpf_count = 2, .num_bru_inputs = 5, }, { @@ -753,6 +767,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .features = VSP1_HAS_BRS | VSP1_HAS_BRU, .lif_count = 1, .rpf_count = 5, + .uif_count = 1, .wpf_count = 1, .num_bru_inputs = 5, }, { @@ -762,6 +777,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .features = VSP1_HAS_BRS | VSP1_HAS_BRU, .lif_count = 2, .rpf_count = 5, + .uif_count = 2, .wpf_count = 2, .num_bru_inputs = 5, }, diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 54de15095709..da276a85aa95 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_entity.c -- R-Car VSP1 Base Entity * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -26,7 +22,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_body *dlb) { struct vsp1_entity *source; u32 route; @@ -42,7 +38,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, smppt = (pipe->output->entity.index << VI6_DPR_SMPPT_TGW_SHIFT) | (source->route->output << VI6_DPR_SMPPT_PT_SHIFT); - vsp1_dl_list_write(dl, VI6_DPR_HGO_SMPPT, smppt); + vsp1_dl_body_write(dlb, VI6_DPR_HGO_SMPPT, smppt); return; } else if (entity->type == VSP1_ENTITY_HGT) { u32 smppt; @@ -55,7 +51,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, smppt = (pipe->output->entity.index << VI6_DPR_SMPPT_TGW_SHIFT) | (source->route->output << VI6_DPR_SMPPT_PT_SHIFT); - vsp1_dl_list_write(dl, VI6_DPR_HGT_SMPPT, smppt); + vsp1_dl_body_write(dlb, VI6_DPR_HGT_SMPPT, smppt); return; } @@ -70,7 +66,33 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, */ if (source->type == VSP1_ENTITY_BRS) route |= VI6_DPR_ROUTE_BRSSEL; - vsp1_dl_list_write(dl, source->route->reg, route); + vsp1_dl_body_write(dlb, source->route->reg, route); +} + +void vsp1_entity_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) +{ + if (entity->ops->configure_stream) + entity->ops->configure_stream(entity, pipe, dlb); +} + +void vsp1_entity_configure_frame(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + if (entity->ops->configure_frame) + entity->ops->configure_frame(entity, pipe, dl, dlb); +} + +void vsp1_entity_configure_partition(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + if (entity->ops->configure_partition) + entity->ops->configure_partition(entity, pipe, dl, dlb); } /* ----------------------------------------------------------------------------- @@ -311,6 +333,97 @@ done: return ret; } +/* + * vsp1_subdev_set_pad_format - Subdev pad set_fmt handler + * @subdev: V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fmt: V4L2 subdev format + * @codes: Array of supported media bus codes + * @ncodes: Number of supported media bus codes + * @min_width: Minimum image width + * @min_height: Minimum image height + * @max_width: Maximum image width + * @max_height: Maximum image height + * + * This function implements the subdev set_fmt pad operation for entities that + * do not support scaling or cropping. It defaults to the first supplied media + * bus code if the requested code isn't supported, clamps the size to the + * supplied minimum and maximum, and propagates the sink pad format to the + * source pad. + */ +int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt, + const unsigned int *codes, unsigned int ncodes, + unsigned int min_width, unsigned int min_height, + unsigned int max_width, unsigned int max_height) +{ + struct vsp1_entity *entity = to_vsp1_entity(subdev); + struct v4l2_subdev_pad_config *config; + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *selection; + unsigned int i; + int ret = 0; + + mutex_lock(&entity->lock); + + config = vsp1_entity_get_pad_config(entity, cfg, fmt->which); + if (!config) { + ret = -EINVAL; + goto done; + } + + format = vsp1_entity_get_pad_format(entity, config, fmt->pad); + + if (fmt->pad == entity->source_pad) { + /* The output format can't be modified. */ + fmt->format = *format; + goto done; + } + + /* + * Default to the first media bus code if the requested format is not + * supported. + */ + for (i = 0; i < ncodes; ++i) { + if (fmt->format.code == codes[i]) + break; + } + + format->code = i < ncodes ? codes[i] : codes[0]; + format->width = clamp_t(unsigned int, fmt->format.width, + min_width, max_width); + format->height = clamp_t(unsigned int, fmt->format.height, + min_height, max_height); + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + fmt->format = *format; + + /* Propagate the format to the source pad. */ + format = vsp1_entity_get_pad_format(entity, config, entity->source_pad); + *format = fmt->format; + + /* Reset the crop and compose rectangles */ + selection = vsp1_entity_get_pad_selection(entity, config, fmt->pad, + V4L2_SEL_TGT_CROP); + selection->left = 0; + selection->top = 0; + selection->width = format->width; + selection->height = format->height; + + selection = vsp1_entity_get_pad_selection(entity, config, fmt->pad, + V4L2_SEL_TGT_COMPOSE); + selection->left = 0; + selection->top = 0; + selection->width = format->width; + selection->height = format->height; + +done: + mutex_unlock(&entity->lock); + return ret; +} + /* ----------------------------------------------------------------------------- * Media Operations */ @@ -452,6 +565,10 @@ struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad) { VSP1_ENTITY_UDS, idx, VI6_DPR_UDS_ROUTE(idx), \ { VI6_DPR_NODE_UDS(idx) }, VI6_DPR_NODE_UDS(idx) } +#define VSP1_ENTITY_ROUTE_UIF(idx) \ + { VSP1_ENTITY_UIF, idx, VI6_DPR_UIF_ROUTE(idx), \ + { VI6_DPR_NODE_UIF(idx) }, VI6_DPR_NODE_UIF(idx) } + #define VSP1_ENTITY_ROUTE_WPF(idx) \ { VSP1_ENTITY_WPF, idx, 0, \ { VI6_DPR_NODE_WPF(idx) }, VI6_DPR_NODE_WPF(idx) } @@ -480,6 +597,8 @@ static const struct vsp1_route vsp1_routes[] = { VSP1_ENTITY_ROUTE_UDS(0), VSP1_ENTITY_ROUTE_UDS(1), VSP1_ENTITY_ROUTE_UDS(2), + VSP1_ENTITY_ROUTE_UIF(0), /* Named UIF4 in the documentation */ + VSP1_ENTITY_ROUTE_UIF(1), /* Named UIF5 in the documentation */ VSP1_ENTITY_ROUTE_WPF(0), VSP1_ENTITY_ROUTE_WPF(1), VSP1_ENTITY_ROUTE_WPF(2), diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 408602ebeb97..97acb7795cf1 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_entity.h -- R-Car VSP1 Base Entity * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_ENTITY_H__ #define __VSP1_ENTITY_H__ @@ -19,6 +15,7 @@ #include <media/v4l2-subdev.h> struct vsp1_device; +struct vsp1_dl_body; struct vsp1_dl_list; struct vsp1_pipeline; struct vsp1_partition; @@ -37,21 +34,10 @@ enum vsp1_entity_type { VSP1_ENTITY_RPF, VSP1_ENTITY_SRU, VSP1_ENTITY_UDS, + VSP1_ENTITY_UIF, VSP1_ENTITY_WPF, }; -/** - * enum vsp1_entity_params - Entity configuration parameters class - * @VSP1_ENTITY_PARAMS_INIT - Initial parameters - * @VSP1_ENTITY_PARAMS_PARTITION - Per-image partition parameters - * @VSP1_ENTITY_PARAMS_RUNTIME - Runtime-configurable parameters - */ -enum vsp1_entity_params { - VSP1_ENTITY_PARAMS_INIT, - VSP1_ENTITY_PARAMS_PARTITION, - VSP1_ENTITY_PARAMS_RUNTIME, -}; - #define VSP1_ENTITY_MAX_INPUTS 5 /* For the BRU */ /* @@ -80,8 +66,10 @@ struct vsp1_route { /** * struct vsp1_entity_operations - Entity operations * @destroy: Destroy the entity. - * @configure: Setup the hardware based on the entity state (pipeline, formats, - * selection rectangles, ...) + * @configure_stream: Setup the hardware parameters for the stream which do + * not vary between frames (pipeline, formats). + * @configure_frame: Configure the runtime parameters for each frame. + * @configure_partition: Configure partition specific parameters. * @max_width: Return the max supported width of data that the entity can * process in a single operation. * @partition: Process the partition construction based on this entity's @@ -89,8 +77,14 @@ struct vsp1_route { */ struct vsp1_entity_operations { void (*destroy)(struct vsp1_entity *); - void (*configure)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_dl_list *, enum vsp1_entity_params); + void (*configure_stream)(struct vsp1_entity *, struct vsp1_pipeline *, + struct vsp1_dl_body *); + void (*configure_frame)(struct vsp1_entity *, struct vsp1_pipeline *, + struct vsp1_dl_list *, struct vsp1_dl_body *); + void (*configure_partition)(struct vsp1_entity *, + struct vsp1_pipeline *, + struct vsp1_dl_list *, + struct vsp1_dl_body *); unsigned int (*max_width)(struct vsp1_entity *, struct vsp1_pipeline *); void (*partition)(struct vsp1_entity *, struct vsp1_pipeline *, struct vsp1_partition *, unsigned int, @@ -106,6 +100,8 @@ struct vsp1_entity { unsigned int index; const struct vsp1_route *route; + struct vsp1_pipeline *pipe; + struct list_head list_dev; struct list_head list_pipe; @@ -155,13 +151,33 @@ int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, void vsp1_entity_route_setup(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl); + struct vsp1_dl_body *dlb); + +void vsp1_entity_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb); + +void vsp1_entity_configure_frame(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + +void vsp1_entity_configure_partition(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad); int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt); +int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt, + const unsigned int *codes, unsigned int ncodes, + unsigned int min_width, unsigned int min_height, + unsigned int max_width, unsigned int max_height); int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code, diff --git a/drivers/media/platform/vsp1/vsp1_hgo.c b/drivers/media/platform/vsp1/vsp1_hgo.c index 50309c053b78..827373c25351 100644 --- a/drivers/media/platform/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/vsp1/vsp1_hgo.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_hgo.c -- R-Car VSP1 Histogram Generator 1D * * Copyright (C) 2016 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -32,10 +28,10 @@ static inline u32 vsp1_hgo_read(struct vsp1_hgo *hgo, u32 reg) return vsp1_read(hgo->histo.entity.vsp1, reg); } -static inline void vsp1_hgo_write(struct vsp1_hgo *hgo, struct vsp1_dl_list *dl, - u32 reg, u32 data) +static inline void vsp1_hgo_write(struct vsp1_hgo *hgo, + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg, data); + vsp1_dl_body_write(dlb, reg, data); } /* ----------------------------------------------------------------------------- @@ -133,10 +129,9 @@ static const struct v4l2_ctrl_config hgo_num_bins_control = { * VSP1 Entity Operations */ -static void hgo_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void hgo_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { struct vsp1_hgo *hgo = to_hgo(&entity->subdev); struct v4l2_rect *compose; @@ -144,21 +139,18 @@ static void hgo_configure(struct vsp1_entity *entity, unsigned int hratio; unsigned int vratio; - if (params != VSP1_ENTITY_PARAMS_INIT) - return; - crop = vsp1_entity_get_pad_selection(entity, entity->config, HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); compose = vsp1_entity_get_pad_selection(entity, entity->config, HISTO_PAD_SINK, V4L2_SEL_TGT_COMPOSE); - vsp1_hgo_write(hgo, dl, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA); + vsp1_hgo_write(hgo, dlb, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA); - vsp1_hgo_write(hgo, dl, VI6_HGO_OFFSET, + vsp1_hgo_write(hgo, dlb, VI6_HGO_OFFSET, (crop->left << VI6_HGO_OFFSET_HOFFSET_SHIFT) | (crop->top << VI6_HGO_OFFSET_VOFFSET_SHIFT)); - vsp1_hgo_write(hgo, dl, VI6_HGO_SIZE, + vsp1_hgo_write(hgo, dlb, VI6_HGO_SIZE, (crop->width << VI6_HGO_SIZE_HSIZE_SHIFT) | (crop->height << VI6_HGO_SIZE_VSIZE_SHIFT)); @@ -170,7 +162,7 @@ static void hgo_configure(struct vsp1_entity *entity, hratio = crop->width * 2 / compose->width / 3; vratio = crop->height * 2 / compose->height / 3; - vsp1_hgo_write(hgo, dl, VI6_HGO_MODE, + vsp1_hgo_write(hgo, dlb, VI6_HGO_MODE, (hgo->num_bins == 256 ? VI6_HGO_MODE_STEP : 0) | (hgo->max_rgb ? VI6_HGO_MODE_MAXRGB : 0) | (hratio << VI6_HGO_MODE_HRATIO_SHIFT) | @@ -178,7 +170,7 @@ static void hgo_configure(struct vsp1_entity *entity, } static const struct vsp1_entity_operations hgo_entity_ops = { - .configure = hgo_configure, + .configure_stream = hgo_configure_stream, .destroy = vsp1_histogram_destroy, }; diff --git a/drivers/media/platform/vsp1/vsp1_hgo.h b/drivers/media/platform/vsp1/vsp1_hgo.h index c6c0b7a80e0c..6b0c8580e1bf 100644 --- a/drivers/media/platform/vsp1/vsp1_hgo.h +++ b/drivers/media/platform/vsp1/vsp1_hgo.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_hgo.h -- R-Car VSP1 Histogram Generator 1D * * Copyright (C) 2016 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_HGO_H__ #define __VSP1_HGO_H__ diff --git a/drivers/media/platform/vsp1/vsp1_hgt.c b/drivers/media/platform/vsp1/vsp1_hgt.c index b5ce305e3e6f..bb6ce6fdd5f4 100644 --- a/drivers/media/platform/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/vsp1/vsp1_hgt.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_hgt.c -- R-Car VSP1 Histogram Generator 2D * * Copyright (C) 2016 Renesas Electronics Corporation * * Contact: Niklas Söderlund (niklas.soderlund@ragnatech.se) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -32,10 +28,10 @@ static inline u32 vsp1_hgt_read(struct vsp1_hgt *hgt, u32 reg) return vsp1_read(hgt->histo.entity.vsp1, reg); } -static inline void vsp1_hgt_write(struct vsp1_hgt *hgt, struct vsp1_dl_list *dl, - u32 reg, u32 data) +static inline void vsp1_hgt_write(struct vsp1_hgt *hgt, + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg, data); + vsp1_dl_body_write(dlb, reg, data); } /* ----------------------------------------------------------------------------- @@ -129,10 +125,9 @@ static const struct v4l2_ctrl_config hgt_hue_areas = { * VSP1 Entity Operations */ -static void hgt_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void hgt_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { struct vsp1_hgt *hgt = to_hgt(&entity->subdev); struct v4l2_rect *compose; @@ -143,21 +138,18 @@ static void hgt_configure(struct vsp1_entity *entity, u8 upper; unsigned int i; - if (params != VSP1_ENTITY_PARAMS_INIT) - return; - crop = vsp1_entity_get_pad_selection(entity, entity->config, HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); compose = vsp1_entity_get_pad_selection(entity, entity->config, HISTO_PAD_SINK, V4L2_SEL_TGT_COMPOSE); - vsp1_hgt_write(hgt, dl, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA); + vsp1_hgt_write(hgt, dlb, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA); - vsp1_hgt_write(hgt, dl, VI6_HGT_OFFSET, + vsp1_hgt_write(hgt, dlb, VI6_HGT_OFFSET, (crop->left << VI6_HGT_OFFSET_HOFFSET_SHIFT) | (crop->top << VI6_HGT_OFFSET_VOFFSET_SHIFT)); - vsp1_hgt_write(hgt, dl, VI6_HGT_SIZE, + vsp1_hgt_write(hgt, dlb, VI6_HGT_SIZE, (crop->width << VI6_HGT_SIZE_HSIZE_SHIFT) | (crop->height << VI6_HGT_SIZE_VSIZE_SHIFT)); @@ -165,7 +157,7 @@ static void hgt_configure(struct vsp1_entity *entity, for (i = 0; i < HGT_NUM_HUE_AREAS; ++i) { lower = hgt->hue_areas[i*2 + 0]; upper = hgt->hue_areas[i*2 + 1]; - vsp1_hgt_write(hgt, dl, VI6_HGT_HUE_AREA(i), + vsp1_hgt_write(hgt, dlb, VI6_HGT_HUE_AREA(i), (lower << VI6_HGT_HUE_AREA_LOWER_SHIFT) | (upper << VI6_HGT_HUE_AREA_UPPER_SHIFT)); } @@ -173,13 +165,13 @@ static void hgt_configure(struct vsp1_entity *entity, hratio = crop->width * 2 / compose->width / 3; vratio = crop->height * 2 / compose->height / 3; - vsp1_hgt_write(hgt, dl, VI6_HGT_MODE, + vsp1_hgt_write(hgt, dlb, VI6_HGT_MODE, (hratio << VI6_HGT_MODE_HRATIO_SHIFT) | (vratio << VI6_HGT_MODE_VRATIO_SHIFT)); } static const struct vsp1_entity_operations hgt_entity_ops = { - .configure = hgt_configure, + .configure_stream = hgt_configure_stream, .destroy = vsp1_histogram_destroy, }; diff --git a/drivers/media/platform/vsp1/vsp1_hgt.h b/drivers/media/platform/vsp1/vsp1_hgt.h index 83f2e130942a..38ec237bdd2d 100644 --- a/drivers/media/platform/vsp1/vsp1_hgt.h +++ b/drivers/media/platform/vsp1/vsp1_hgt.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_hgt.h -- R-Car VSP1 Histogram Generator 2D * * Copyright (C) 2016 Renesas Electronics Corporation * * Contact: Niklas Söderlund (niklas.soderlund@ragnatech.se) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_HGT_H__ #define __VSP1_HGT_H__ diff --git a/drivers/media/platform/vsp1/vsp1_histo.c b/drivers/media/platform/vsp1/vsp1_histo.c index afab77cf4fa5..5e15c8ff88d9 100644 --- a/drivers/media/platform/vsp1/vsp1_histo.c +++ b/drivers/media/platform/vsp1/vsp1_histo.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_histo.c -- R-Car VSP1 Histogram API * @@ -5,11 +6,6 @@ * Copyright (C) 2016 Laurent Pinchart * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -61,7 +57,7 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo, struct vsp1_histogram_buffer *buf, size_t size) { - struct vsp1_pipeline *pipe = histo->pipe; + struct vsp1_pipeline *pipe = histo->entity.pipe; unsigned long flags; /* @@ -393,65 +389,14 @@ static int histo_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_histogram *histo = subdev_to_histo(subdev); - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *selection; - unsigned int i; - int ret = 0; if (fmt->pad != HISTO_PAD_SINK) return histo_get_format(subdev, cfg, fmt); - mutex_lock(&histo->entity.lock); - - config = vsp1_entity_get_pad_config(&histo->entity, cfg, fmt->which); - if (!config) { - ret = -EINVAL; - goto done; - } - - /* - * Default to the first format if the requested format is not - * supported. - */ - for (i = 0; i < histo->num_formats; ++i) { - if (fmt->format.code == histo->formats[i]) - break; - } - if (i == histo->num_formats) - fmt->format.code = histo->formats[0]; - - format = vsp1_entity_get_pad_format(&histo->entity, config, fmt->pad); - - format->code = fmt->format.code; - format->width = clamp_t(unsigned int, fmt->format.width, - HISTO_MIN_SIZE, HISTO_MAX_SIZE); - format->height = clamp_t(unsigned int, fmt->format.height, - HISTO_MIN_SIZE, HISTO_MAX_SIZE); - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; - - fmt->format = *format; - - /* Reset the crop and compose rectangles */ - selection = vsp1_entity_get_pad_selection(&histo->entity, config, - fmt->pad, V4L2_SEL_TGT_CROP); - selection->left = 0; - selection->top = 0; - selection->width = format->width; - selection->height = format->height; - - selection = vsp1_entity_get_pad_selection(&histo->entity, config, - fmt->pad, - V4L2_SEL_TGT_COMPOSE); - selection->left = 0; - selection->top = 0; - selection->width = format->width; - selection->height = format->height; - -done: - mutex_unlock(&histo->entity.lock); - return ret; + return vsp1_subdev_set_pad_format(subdev, cfg, fmt, + histo->formats, histo->num_formats, + HISTO_MIN_SIZE, HISTO_MIN_SIZE, + HISTO_MAX_SIZE, HISTO_MAX_SIZE); } static const struct v4l2_subdev_pad_ops histo_pad_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_histo.h b/drivers/media/platform/vsp1/vsp1_histo.h index af2874f6031d..06f029846244 100644 --- a/drivers/media/platform/vsp1/vsp1_histo.h +++ b/drivers/media/platform/vsp1/vsp1_histo.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_histo.h -- R-Car VSP1 Histogram API * @@ -5,11 +6,6 @@ * Copyright (C) 2016 Laurent Pinchart * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_HISTO_H__ #define __VSP1_HISTO_H__ @@ -25,7 +21,6 @@ #include "vsp1_entity.h" struct vsp1_device; -struct vsp1_pipeline; #define HISTO_PAD_SINK 0 #define HISTO_PAD_SOURCE 1 @@ -37,8 +32,6 @@ struct vsp1_histogram_buffer { }; struct vsp1_histogram { - struct vsp1_pipeline *pipe; - struct vsp1_entity entity; struct video_device video; struct media_pad pad; diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 764d405345ee..39ab2e0c7c18 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_hsit.c -- R-Car VSP1 Hue Saturation value (Inverse) Transform * * Copyright (C) 2013 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -28,9 +24,9 @@ */ static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, - struct vsp1_dl_list *dl, u32 reg, u32 data) + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg, data); + vsp1_dl_body_write(dlb, reg, data); } /* ----------------------------------------------------------------------------- @@ -131,24 +127,20 @@ static const struct v4l2_subdev_ops hsit_ops = { * VSP1 Entity Operations */ -static void hsit_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void hsit_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { struct vsp1_hsit *hsit = to_hsit(&entity->subdev); - if (params != VSP1_ENTITY_PARAMS_INIT) - return; - if (hsit->inverse) - vsp1_hsit_write(hsit, dl, VI6_HSI_CTRL, VI6_HSI_CTRL_EN); + vsp1_hsit_write(hsit, dlb, VI6_HSI_CTRL, VI6_HSI_CTRL_EN); else - vsp1_hsit_write(hsit, dl, VI6_HST_CTRL, VI6_HST_CTRL_EN); + vsp1_hsit_write(hsit, dlb, VI6_HST_CTRL, VI6_HST_CTRL_EN); } static const struct vsp1_entity_operations hsit_entity_ops = { - .configure = hsit_configure, + .configure_stream = hsit_configure_stream, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/vsp1/vsp1_hsit.h b/drivers/media/platform/vsp1/vsp1_hsit.h index 82f1c8426900..a658b1aa49e7 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.h +++ b/drivers/media/platform/vsp1/vsp1_hsit.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_hsit.h -- R-Car VSP1 Hue Saturation value (Inverse) Transform * * Copyright (C) 2013 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_HSIT_H__ #define __VSP1_HSIT_H__ diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 704920753998..0cb63244b21a 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_lif.c -- R-Car VSP1 LCD Controller Interface * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -27,27 +23,28 @@ * Device Access */ -static inline void vsp1_lif_write(struct vsp1_lif *lif, struct vsp1_dl_list *dl, - u32 reg, u32 data) +static inline void vsp1_lif_write(struct vsp1_lif *lif, + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg + lif->entity.index * VI6_LIF_OFFSET, data); + vsp1_dl_body_write(dlb, reg + lif->entity.index * VI6_LIF_OFFSET, + data); } /* ----------------------------------------------------------------------------- * V4L2 Subdevice Operations */ +static const unsigned int lif_codes[] = { + MEDIA_BUS_FMT_ARGB8888_1X32, + MEDIA_BUS_FMT_AYUV8_1X32, +}; + static int lif_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { - static const unsigned int codes[] = { - MEDIA_BUS_FMT_ARGB8888_1X32, - MEDIA_BUS_FMT_AYUV8_1X32, - }; - - return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, - ARRAY_SIZE(codes)); + return vsp1_subdev_enum_mbus_code(subdev, cfg, code, lif_codes, + ARRAY_SIZE(lif_codes)); } static int lif_enum_frame_size(struct v4l2_subdev *subdev, @@ -63,53 +60,10 @@ static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { - struct vsp1_lif *lif = to_lif(subdev); - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - int ret = 0; - - mutex_lock(&lif->entity.lock); - - config = vsp1_entity_get_pad_config(&lif->entity, cfg, fmt->which); - if (!config) { - ret = -EINVAL; - goto done; - } - - /* Default to YUV if the requested format is not supported. */ - if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && - fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) - fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - - format = vsp1_entity_get_pad_format(&lif->entity, config, fmt->pad); - - if (fmt->pad == LIF_PAD_SOURCE) { - /* - * The LIF source format is always identical to its sink - * format. - */ - fmt->format = *format; - goto done; - } - - format->code = fmt->format.code; - format->width = clamp_t(unsigned int, fmt->format.width, - LIF_MIN_SIZE, LIF_MAX_SIZE); - format->height = clamp_t(unsigned int, fmt->format.height, - LIF_MIN_SIZE, LIF_MAX_SIZE); - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; - - fmt->format = *format; - - /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&lif->entity, config, - LIF_PAD_SOURCE); - *format = fmt->format; - -done: - mutex_unlock(&lif->entity.lock); - return ret; + return vsp1_subdev_set_pad_format(subdev, cfg, fmt, lif_codes, + ARRAY_SIZE(lif_codes), + LIF_MIN_SIZE, LIF_MIN_SIZE, + LIF_MAX_SIZE, LIF_MAX_SIZE); } static const struct v4l2_subdev_pad_ops lif_pad_ops = { @@ -128,10 +82,9 @@ static const struct v4l2_subdev_ops lif_ops = { * VSP1 Entity Operations */ -static void lif_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void lif_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { const struct v4l2_mbus_framefmt *format; struct vsp1_lif *lif = to_lif(&entity->subdev); @@ -139,19 +92,16 @@ static void lif_configure(struct vsp1_entity *entity, unsigned int obth = 400; unsigned int lbth = 200; - if (params != VSP1_ENTITY_PARAMS_INIT) - return; - format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config, LIF_PAD_SOURCE); obth = min(obth, (format->width + 1) / 2 * format->height - 4); - vsp1_lif_write(lif, dl, VI6_LIF_CSBTH, + vsp1_lif_write(lif, dlb, VI6_LIF_CSBTH, (hbth << VI6_LIF_CSBTH_HBTH_SHIFT) | (lbth << VI6_LIF_CSBTH_LBTH_SHIFT)); - vsp1_lif_write(lif, dl, VI6_LIF_CTRL, + vsp1_lif_write(lif, dlb, VI6_LIF_CTRL, (obth << VI6_LIF_CTRL_OBTH_SHIFT) | (format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) | VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN); @@ -164,13 +114,13 @@ static void lif_configure(struct vsp1_entity *entity, */ if ((entity->vsp1->version & VI6_IP_VERSION_MASK) == (VI6_IP_VERSION_MODEL_VSPD_V3 | VI6_IP_VERSION_SOC_V3M)) - vsp1_lif_write(lif, dl, VI6_LIF_LBA, + vsp1_lif_write(lif, dlb, VI6_LIF_LBA, VI6_LIF_LBA_LBA0 | (1536 << VI6_LIF_LBA_LBA1_SHIFT)); } static const struct vsp1_entity_operations lif_entity_ops = { - .configure = lif_configure, + .configure_stream = lif_configure_stream, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/vsp1/vsp1_lif.h b/drivers/media/platform/vsp1/vsp1_lif.h index 3417339379b1..71a4eda9c2b2 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.h +++ b/drivers/media/platform/vsp1/vsp1_lif.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_lif.h -- R-Car VSP1 LCD Controller Interface * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_LIF_H__ #define __VSP1_LIF_H__ diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index c67cc60db0db..64c48d9459b0 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_lut.c -- R-Car VSP1 Look-Up Table * * Copyright (C) 2013 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -23,14 +19,16 @@ #define LUT_MIN_SIZE 4U #define LUT_MAX_SIZE 8190U +#define LUT_SIZE 256 + /* ----------------------------------------------------------------------------- * Device Access */ -static inline void vsp1_lut_write(struct vsp1_lut *lut, struct vsp1_dl_list *dl, - u32 reg, u32 data) +static inline void vsp1_lut_write(struct vsp1_lut *lut, + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg, data); + vsp1_dl_body_write(dlb, reg, data); } /* ----------------------------------------------------------------------------- @@ -44,19 +42,19 @@ static int lut_set_table(struct vsp1_lut *lut, struct v4l2_ctrl *ctrl) struct vsp1_dl_body *dlb; unsigned int i; - dlb = vsp1_dl_fragment_alloc(lut->entity.vsp1, 256); + dlb = vsp1_dl_body_get(lut->pool); if (!dlb) return -ENOMEM; - for (i = 0; i < 256; ++i) - vsp1_dl_fragment_write(dlb, VI6_LUT_TABLE + 4 * i, + for (i = 0; i < LUT_SIZE; ++i) + vsp1_dl_body_write(dlb, VI6_LUT_TABLE + 4 * i, ctrl->p_new.p_u32[i]); spin_lock_irq(&lut->lock); swap(lut->lut, dlb); spin_unlock_irq(&lut->lock); - vsp1_dl_fragment_free(dlb); + vsp1_dl_body_put(dlb); return 0; } @@ -87,25 +85,25 @@ static const struct v4l2_ctrl_config lut_table_control = { .max = 0x00ffffff, .step = 1, .def = 0, - .dims = { 256}, + .dims = { LUT_SIZE }, }; /* ----------------------------------------------------------------------------- * V4L2 Subdevice Pad Operations */ +static const unsigned int lut_codes[] = { + MEDIA_BUS_FMT_ARGB8888_1X32, + MEDIA_BUS_FMT_AHSV8888_1X32, + MEDIA_BUS_FMT_AYUV8_1X32, +}; + static int lut_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { - static const unsigned int codes[] = { - MEDIA_BUS_FMT_ARGB8888_1X32, - MEDIA_BUS_FMT_AHSV8888_1X32, - MEDIA_BUS_FMT_AYUV8_1X32, - }; - - return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, - ARRAY_SIZE(codes)); + return vsp1_subdev_enum_mbus_code(subdev, cfg, code, lut_codes, + ARRAY_SIZE(lut_codes)); } static int lut_enum_frame_size(struct v4l2_subdev *subdev, @@ -121,51 +119,10 @@ static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { - struct vsp1_lut *lut = to_lut(subdev); - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - int ret = 0; - - mutex_lock(&lut->entity.lock); - - config = vsp1_entity_get_pad_config(&lut->entity, cfg, fmt->which); - if (!config) { - ret = -EINVAL; - goto done; - } - - /* Default to YUV if the requested format is not supported. */ - if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && - fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 && - fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) - fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - - format = vsp1_entity_get_pad_format(&lut->entity, config, fmt->pad); - - if (fmt->pad == LUT_PAD_SOURCE) { - /* The LUT output format can't be modified. */ - fmt->format = *format; - goto done; - } - - format->code = fmt->format.code; - format->width = clamp_t(unsigned int, fmt->format.width, - LUT_MIN_SIZE, LUT_MAX_SIZE); - format->height = clamp_t(unsigned int, fmt->format.height, - LUT_MIN_SIZE, LUT_MAX_SIZE); - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; - - fmt->format = *format; - - /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&lut->entity, config, - LUT_PAD_SOURCE); - *format = fmt->format; - -done: - mutex_unlock(&lut->entity.lock); - return ret; + return vsp1_subdev_set_pad_format(subdev, cfg, fmt, lut_codes, + ARRAY_SIZE(lut_codes), + LUT_MIN_SIZE, LUT_MIN_SIZE, + LUT_MAX_SIZE, LUT_MAX_SIZE); } /* ----------------------------------------------------------------------------- @@ -188,37 +145,48 @@ static const struct v4l2_subdev_ops lut_ops = { * VSP1 Entity Operations */ -static void lut_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void lut_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { struct vsp1_lut *lut = to_lut(&entity->subdev); - struct vsp1_dl_body *dlb; - unsigned long flags; - switch (params) { - case VSP1_ENTITY_PARAMS_INIT: - vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); - break; + vsp1_lut_write(lut, dlb, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); +} - case VSP1_ENTITY_PARAMS_PARTITION: - break; +static void lut_configure_frame(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + struct vsp1_lut *lut = to_lut(&entity->subdev); + struct vsp1_dl_body *lut_dlb; + unsigned long flags; - case VSP1_ENTITY_PARAMS_RUNTIME: - spin_lock_irqsave(&lut->lock, flags); - dlb = lut->lut; - lut->lut = NULL; - spin_unlock_irqrestore(&lut->lock, flags); + spin_lock_irqsave(&lut->lock, flags); + lut_dlb = lut->lut; + lut->lut = NULL; + spin_unlock_irqrestore(&lut->lock, flags); - if (dlb) - vsp1_dl_list_add_fragment(dl, dlb); - break; + if (lut_dlb) { + vsp1_dl_list_add_body(dl, lut_dlb); + + /* Release our local reference. */ + vsp1_dl_body_put(lut_dlb); } } +static void lut_destroy(struct vsp1_entity *entity) +{ + struct vsp1_lut *lut = to_lut(&entity->subdev); + + vsp1_dl_body_pool_destroy(lut->pool); +} + static const struct vsp1_entity_operations lut_entity_ops = { - .configure = lut_configure, + .configure_stream = lut_configure_stream, + .configure_frame = lut_configure_frame, + .destroy = lut_destroy, }; /* ----------------------------------------------------------------------------- @@ -244,6 +212,15 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) if (ret < 0) return ERR_PTR(ret); + /* + * Pre-allocate a body pool, with 3 bodies allowing a userspace update + * before the hardware has committed a previous set of tables, handling + * both the queued and pending dl entries. + */ + lut->pool = vsp1_dl_body_pool_create(vsp1, 3, LUT_SIZE, 0); + if (!lut->pool) + return ERR_PTR(-ENOMEM); + /* Initialize the control handler. */ v4l2_ctrl_handler_init(&lut->ctrls, 1); v4l2_ctrl_new_custom(&lut->ctrls, &lut_table_control, NULL); diff --git a/drivers/media/platform/vsp1/vsp1_lut.h b/drivers/media/platform/vsp1/vsp1_lut.h index f8c4e8f0a79d..8cb0df1b7e27 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.h +++ b/drivers/media/platform/vsp1/vsp1_lut.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_lut.h -- R-Car VSP1 Look-Up Table * * Copyright (C) 2013 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_LUT_H__ #define __VSP1_LUT_H__ @@ -33,6 +29,7 @@ struct vsp1_lut { spinlock_t lock; struct vsp1_dl_body *lut; + struct vsp1_dl_body_pool *pool; }; static inline struct vsp1_lut *to_lut(struct v4l2_subdev *subdev) diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index 44944ac86d9b..54ff539ffea0 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_pipe.c -- R-Car VSP1 Pipeline * * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/delay.h> @@ -20,7 +16,7 @@ #include <media/v4l2-subdev.h> #include "vsp1.h" -#include "vsp1_bru.h" +#include "vsp1_brx.h" #include "vsp1_dl.h" #include "vsp1_entity.h" #include "vsp1_hgo.h" @@ -185,44 +181,29 @@ const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, void vsp1_pipeline_reset(struct vsp1_pipeline *pipe) { + struct vsp1_entity *entity; unsigned int i; - if (pipe->bru) { - struct vsp1_bru *bru = to_bru(&pipe->bru->subdev); - - for (i = 0; i < ARRAY_SIZE(bru->inputs); ++i) - bru->inputs[i].rpf = NULL; - } - - for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { - if (pipe->inputs[i]) { - pipe->inputs[i]->pipe = NULL; - pipe->inputs[i] = NULL; - } - } + if (pipe->brx) { + struct vsp1_brx *brx = to_brx(&pipe->brx->subdev); - if (pipe->output) { - pipe->output->pipe = NULL; - pipe->output = NULL; + for (i = 0; i < ARRAY_SIZE(brx->inputs); ++i) + brx->inputs[i].rpf = NULL; } - if (pipe->hgo) { - struct vsp1_hgo *hgo = to_hgo(&pipe->hgo->subdev); - - hgo->histo.pipe = NULL; - } + for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) + pipe->inputs[i] = NULL; - if (pipe->hgt) { - struct vsp1_hgt *hgt = to_hgt(&pipe->hgt->subdev); + pipe->output = NULL; - hgt->histo.pipe = NULL; - } + list_for_each_entry(entity, &pipe->entities, list_pipe) + entity->pipe = NULL; INIT_LIST_HEAD(&pipe->entities); pipe->state = VSP1_PIPELINE_STOPPED; pipe->buffers_ready = 0; pipe->num_inputs = 0; - pipe->bru = NULL; + pipe->brx = NULL; pipe->hgo = NULL; pipe->hgt = NULL; pipe->lif = NULL; @@ -330,17 +311,17 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe) void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) { - bool completed; + unsigned int flags; if (pipe == NULL) return; /* * If the DL commit raced with the frame end interrupt, the commit ends - * up being postponed by one frame. @completed represents whether the + * up being postponed by one frame. The returned flags tell whether the * active frame was finished or postponed. */ - completed = vsp1_dlm_irq_frame_end(pipe->output->dlm); + flags = vsp1_dlm_irq_frame_end(pipe->output->dlm); if (pipe->hgo) vsp1_hgo_frame_end(pipe->hgo); @@ -353,7 +334,7 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) * frame_end to account for vblank events. */ if (pipe->frame_end) - pipe->frame_end(pipe, completed); + pipe->frame_end(pipe, flags); pipe->sequence++; } @@ -367,7 +348,7 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) * from the input RPF alpha. */ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, unsigned int alpha) + struct vsp1_dl_body *dlb, unsigned int alpha) { if (!pipe->uds) return; @@ -380,7 +361,7 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, pipe->uds_input->type == VSP1_ENTITY_BRS) alpha = 255; - vsp1_uds_set_alpha(pipe->uds, dl, alpha); + vsp1_uds_set_alpha(pipe->uds, dlb, alpha); } /* @@ -405,73 +386,3 @@ void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, } } -void vsp1_pipelines_suspend(struct vsp1_device *vsp1) -{ - unsigned long flags; - unsigned int i; - int ret; - - /* - * To avoid increasing the system suspend time needlessly, loop over the - * pipelines twice, first to set them all to the stopping state, and - * then to wait for the stop to complete. - */ - for (i = 0; i < vsp1->info->wpf_count; ++i) { - struct vsp1_rwpf *wpf = vsp1->wpf[i]; - struct vsp1_pipeline *pipe; - - if (wpf == NULL) - continue; - - pipe = wpf->pipe; - if (pipe == NULL) - continue; - - spin_lock_irqsave(&pipe->irqlock, flags); - if (pipe->state == VSP1_PIPELINE_RUNNING) - pipe->state = VSP1_PIPELINE_STOPPING; - spin_unlock_irqrestore(&pipe->irqlock, flags); - } - - for (i = 0; i < vsp1->info->wpf_count; ++i) { - struct vsp1_rwpf *wpf = vsp1->wpf[i]; - struct vsp1_pipeline *pipe; - - if (wpf == NULL) - continue; - - pipe = wpf->pipe; - if (pipe == NULL) - continue; - - ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), - msecs_to_jiffies(500)); - if (ret == 0) - dev_warn(vsp1->dev, "pipeline %u stop timeout\n", - wpf->entity.index); - } -} - -void vsp1_pipelines_resume(struct vsp1_device *vsp1) -{ - unsigned long flags; - unsigned int i; - - /* Resume all running pipelines. */ - for (i = 0; i < vsp1->info->wpf_count; ++i) { - struct vsp1_rwpf *wpf = vsp1->wpf[i]; - struct vsp1_pipeline *pipe; - - if (wpf == NULL) - continue; - - pipe = wpf->pipe; - if (pipe == NULL) - continue; - - spin_lock_irqsave(&pipe->irqlock, flags); - if (vsp1_pipeline_ready(pipe)) - vsp1_pipeline_run(pipe); - spin_unlock_irqrestore(&pipe->irqlock, flags); - } -} diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h index dfff9b5685fe..743d8f0db45c 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/vsp1/vsp1_pipe.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_pipe.h -- R-Car VSP1 Pipeline * * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_PIPE_H__ #define __VSP1_PIPE_H__ @@ -99,14 +95,15 @@ struct vsp1_partition { * @num_inputs: number of RPFs * @inputs: array of RPFs in the pipeline (indexed by RPF index) * @output: WPF at the output of the pipeline - * @bru: BRU entity, if present + * @brx: BRx entity, if present * @hgo: HGO entity, if present * @hgt: HGT entity, if present * @lif: LIF entity, if present * @uds: UDS entity, if present * @uds_input: entity at the input of the UDS, if the UDS is present * @entities: list of entities in the pipeline - * @dl: display list associated with the pipeline + * @stream_config: cached stream configuration for video pipelines + * @configured: when false the @stream_config shall be written to the hardware * @partitions: The number of partitions used to process one frame * @partition: The current partition for configuration to process * @part_table: The pre-calculated partitions used by the pipeline @@ -118,7 +115,7 @@ struct vsp1_pipeline { enum vsp1_pipeline_state state; wait_queue_head_t wq; - void (*frame_end)(struct vsp1_pipeline *pipe, bool completed); + void (*frame_end)(struct vsp1_pipeline *pipe, unsigned int completion); struct mutex lock; struct kref kref; @@ -129,7 +126,7 @@ struct vsp1_pipeline { unsigned int num_inputs; struct vsp1_rwpf *inputs[VSP1_MAX_RPF]; struct vsp1_rwpf *output; - struct vsp1_entity *bru; + struct vsp1_entity *brx; struct vsp1_entity *hgo; struct vsp1_entity *hgt; struct vsp1_entity *lif; @@ -143,7 +140,8 @@ struct vsp1_pipeline { */ struct list_head entities; - struct vsp1_dl_list *dl; + struct vsp1_dl_body *stream_config; + bool configured; unsigned int partitions; struct vsp1_partition *partition; @@ -161,16 +159,14 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe); void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe); void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, unsigned int alpha); + struct vsp1_dl_body *dlb, + unsigned int alpha); void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int index, struct vsp1_partition_window *window); -void vsp1_pipelines_suspend(struct vsp1_device *vsp1); -void vsp1_pipelines_resume(struct vsp1_device *vsp1); - const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, u32 fourcc); diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index dae0c1901297..0d249ff9f564 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * vsp1_regs.h -- R-Car VSP1 Registers Definitions * * Copyright (C) 2013 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. */ #ifndef __VSP1_REGS_H__ @@ -311,6 +308,44 @@ #define VI6_WPF_WRBCK_CTRL_WBMD (1 << 0) /* ----------------------------------------------------------------------------- + * UIF Control Registers + */ + +#define VI6_UIF_OFFSET 0x100 + +#define VI6_UIF_DISCOM_DOCMCR 0x1c00 +#define VI6_UIF_DISCOM_DOCMCR_CMPRU (1 << 16) +#define VI6_UIF_DISCOM_DOCMCR_CMPR (1 << 0) + +#define VI6_UIF_DISCOM_DOCMSTR 0x1c04 +#define VI6_UIF_DISCOM_DOCMSTR_CMPPRE (1 << 1) +#define VI6_UIF_DISCOM_DOCMSTR_CMPST (1 << 0) + +#define VI6_UIF_DISCOM_DOCMCLSTR 0x1c08 +#define VI6_UIF_DISCOM_DOCMCLSTR_CMPCLPRE (1 << 1) +#define VI6_UIF_DISCOM_DOCMCLSTR_CMPCLST (1 << 0) + +#define VI6_UIF_DISCOM_DOCMIENR 0x1c0c +#define VI6_UIF_DISCOM_DOCMIENR_CMPPREIEN (1 << 1) +#define VI6_UIF_DISCOM_DOCMIENR_CMPIEN (1 << 0) + +#define VI6_UIF_DISCOM_DOCMMDR 0x1c10 +#define VI6_UIF_DISCOM_DOCMMDR_INTHRH(n) ((n) << 16) + +#define VI6_UIF_DISCOM_DOCMPMR 0x1c14 +#define VI6_UIF_DISCOM_DOCMPMR_CMPDFF(n) ((n) << 17) +#define VI6_UIF_DISCOM_DOCMPMR_CMPDFA(n) ((n) << 8) +#define VI6_UIF_DISCOM_DOCMPMR_CMPDAUF (1 << 7) +#define VI6_UIF_DISCOM_DOCMPMR_SEL(n) ((n) << 0) + +#define VI6_UIF_DISCOM_DOCMECRCR 0x1c18 +#define VI6_UIF_DISCOM_DOCMCCRCR 0x1c1c +#define VI6_UIF_DISCOM_DOCMSPXR 0x1c20 +#define VI6_UIF_DISCOM_DOCMSPYR 0x1c24 +#define VI6_UIF_DISCOM_DOCMSZXR 0x1c28 +#define VI6_UIF_DISCOM_DOCMSZYR 0x1c2c + +/* ----------------------------------------------------------------------------- * DPR Control Registers */ @@ -342,7 +377,10 @@ #define VI6_DPR_SMPPT_PT_MASK (0x3f << 0) #define VI6_DPR_SMPPT_PT_SHIFT 0 +#define VI6_DPR_UIF_ROUTE(n) (0x2074 + (n) * 4) + #define VI6_DPR_NODE_RPF(n) (n) +#define VI6_DPR_NODE_UIF(n) (12 + (n)) #define VI6_DPR_NODE_SRU 16 #define VI6_DPR_NODE_UDS(n) (17 + (n)) #define VI6_DPR_NODE_LUT 22 diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index fe0633da5a5f..69e5fe6e6b50 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -29,9 +25,10 @@ */ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, - struct vsp1_dl_list *dl, u32 reg, u32 data) + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg + rpf->entity.index * VI6_RPF_OFFSET, data); + vsp1_dl_body_write(dlb, reg + rpf->entity.index * VI6_RPF_OFFSET, + data); } /* ----------------------------------------------------------------------------- @@ -46,10 +43,9 @@ static const struct v4l2_subdev_ops rpf_ops = { * VSP1 Entity Operations */ -static void rpf_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void rpf_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; @@ -61,80 +57,6 @@ static void rpf_configure(struct vsp1_entity *entity, u32 pstride; u32 infmt; - if (params == VSP1_ENTITY_PARAMS_RUNTIME) { - vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET, - rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); - vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, rpf->mult_alpha | - (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT)); - - vsp1_pipeline_propagate_alpha(pipe, dl, rpf->alpha); - return; - } - - if (params == VSP1_ENTITY_PARAMS_PARTITION) { - struct vsp1_device *vsp1 = rpf->entity.vsp1; - struct vsp1_rwpf_memory mem = rpf->mem; - struct v4l2_rect crop; - - /* - * Source size and crop offsets. - * - * The crop offsets correspond to the location of the crop - * rectangle top left corner in the plane buffer. Only two - * offsets are needed, as planes 2 and 3 always have identical - * strides. - */ - crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.config); - - /* - * Partition Algorithm Control - * - * The partition algorithm can split this frame into multiple - * slices. We must scale our partition window based on the pipe - * configuration to match the destination partition window. - * To achieve this, we adjust our crop to provide a 'sub-crop' - * matching the expected partition window. Only 'left' and - * 'width' need to be adjusted. - */ - if (pipe->partitions > 1) { - crop.width = pipe->partition->rpf.width; - crop.left += pipe->partition->rpf.left; - } - - vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE, - (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | - (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); - vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE, - (crop.width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | - (crop.height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); - - mem.addr[0] += crop.top * format->plane_fmt[0].bytesperline - + crop.left * fmtinfo->bpp[0] / 8; - - if (format->num_planes > 1) { - unsigned int offset; - - offset = crop.top * format->plane_fmt[1].bytesperline - + crop.left / fmtinfo->hsub - * fmtinfo->bpp[1] / 8; - mem.addr[1] += offset; - mem.addr[2] += offset; - } - - /* - * On Gen3 hardware the SPUVS bit has no effect on 3-planar - * formats. Swap the U and V planes manually in that case. - */ - if (vsp1->info->gen == 3 && format->num_planes == 3 && - fmtinfo->swap_uv) - swap(mem.addr[1], mem.addr[2]); - - vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]); - vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]); - vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]); - return; - } - /* Stride */ pstride = format->plane_fmt[0].bytesperline << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT; @@ -142,7 +64,7 @@ static void rpf_configure(struct vsp1_entity *entity, pstride |= format->plane_fmt[1].bytesperline << VI6_RPF_SRCM_PSTRIDE_C_SHIFT; - vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_PSTRIDE, pstride); + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride); /* Format */ sink_format = vsp1_entity_get_pad_format(&rpf->entity, @@ -163,22 +85,22 @@ static void rpf_configure(struct vsp1_entity *entity, if (sink_format->code != source_format->code) infmt |= VI6_RPF_INFMT_CSC; - vsp1_rpf_write(rpf, dl, VI6_RPF_INFMT, infmt); - vsp1_rpf_write(rpf, dl, VI6_RPF_DSWAP, fmtinfo->swap); + vsp1_rpf_write(rpf, dlb, VI6_RPF_INFMT, infmt); + vsp1_rpf_write(rpf, dlb, VI6_RPF_DSWAP, fmtinfo->swap); /* Output location */ - if (pipe->bru) { + if (pipe->brx) { const struct v4l2_rect *compose; - compose = vsp1_entity_get_pad_selection(pipe->bru, - pipe->bru->config, - rpf->bru_input, + compose = vsp1_entity_get_pad_selection(pipe->brx, + pipe->brx->config, + rpf->brx_input, V4L2_SEL_TGT_COMPOSE); left = compose->left; top = compose->top; } - vsp1_rpf_write(rpf, dl, VI6_RPF_LOC, + vsp1_rpf_write(rpf, dlb, VI6_RPF_LOC, (left << VI6_RPF_LOC_HCOORD_SHIFT) | (top << VI6_RPF_LOC_VCOORD_SHIFT)); @@ -191,10 +113,10 @@ static void rpf_configure(struct vsp1_entity *entity, * alpha channel by a fixed global alpha value, and multiply the pixel * components to convert the input to premultiplied alpha. * - * As alpha premultiplication is available in the BRU for both Gen2 and + * As alpha premultiplication is available in the BRx for both Gen2 and * Gen3 we handle it there and use the Gen3 alpha multiplier for global * alpha multiplication only. This however prevents conversion to - * premultiplied alpha if no BRU is present in the pipeline. If that use + * premultiplied alpha if no BRx is present in the pipeline. If that use * case turns out to be useful we will revisit the implementation (for * Gen3 only). * @@ -205,7 +127,7 @@ static void rpf_configure(struct vsp1_entity *entity, * * In all cases, disable color keying. */ - vsp1_rpf_write(rpf, dl, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT | + vsp1_rpf_write(rpf, dlb, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT | (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED : VI6_RPF_ALPH_SEL_ASEL_FIXED)); @@ -242,9 +164,94 @@ static void rpf_configure(struct vsp1_entity *entity, rpf->mult_alpha = mult; } - vsp1_rpf_write(rpf, dl, VI6_RPF_MSK_CTRL, 0); - vsp1_rpf_write(rpf, dl, VI6_RPF_CKEY_CTRL, 0); + vsp1_rpf_write(rpf, dlb, VI6_RPF_MSK_CTRL, 0); + vsp1_rpf_write(rpf, dlb, VI6_RPF_CKEY_CTRL, 0); + +} + +static void rpf_configure_frame(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); + + vsp1_rpf_write(rpf, dlb, VI6_RPF_VRTCOL_SET, + rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); + vsp1_rpf_write(rpf, dlb, VI6_RPF_MULT_ALPHA, rpf->mult_alpha | + (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT)); + + vsp1_pipeline_propagate_alpha(pipe, dlb, rpf->alpha); +} + +static void rpf_configure_partition(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); + struct vsp1_rwpf_memory mem = rpf->mem; + struct vsp1_device *vsp1 = rpf->entity.vsp1; + const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; + const struct v4l2_pix_format_mplane *format = &rpf->format; + struct v4l2_rect crop; + + /* + * Source size and crop offsets. + * + * The crop offsets correspond to the location of the crop + * rectangle top left corner in the plane buffer. Only two + * offsets are needed, as planes 2 and 3 always have identical + * strides. + */ + crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.config); + + /* + * Partition Algorithm Control + * + * The partition algorithm can split this frame into multiple + * slices. We must scale our partition window based on the pipe + * configuration to match the destination partition window. + * To achieve this, we adjust our crop to provide a 'sub-crop' + * matching the expected partition window. Only 'left' and + * 'width' need to be adjusted. + */ + if (pipe->partitions > 1) { + crop.width = pipe->partition->rpf.width; + crop.left += pipe->partition->rpf.left; + } + + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_BSIZE, + (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | + (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_ESIZE, + (crop.width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | + (crop.height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); + + mem.addr[0] += crop.top * format->plane_fmt[0].bytesperline + + crop.left * fmtinfo->bpp[0] / 8; + + if (format->num_planes > 1) { + unsigned int offset; + + offset = crop.top * format->plane_fmt[1].bytesperline + + crop.left / fmtinfo->hsub + * fmtinfo->bpp[1] / 8; + mem.addr[1] += offset; + mem.addr[2] += offset; + } + + /* + * On Gen3 hardware the SPUVS bit has no effect on 3-planar + * formats. Swap the U and V planes manually in that case. + */ + if (vsp1->info->gen == 3 && format->num_planes == 3 && + fmtinfo->swap_uv) + swap(mem.addr[1], mem.addr[2]); + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]); + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]); + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]); } static void rpf_partition(struct vsp1_entity *entity, @@ -257,7 +264,9 @@ static void rpf_partition(struct vsp1_entity *entity, } static const struct vsp1_entity_operations rpf_entity_ops = { - .configure = rpf_configure, + .configure_stream = rpf_configure_stream, + .configure_frame = rpf_configure_frame, + .configure_partition = rpf_configure_partition, .partition = rpf_partition, }; diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index cfd8f1904fa6..049bdd958e56 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_rwpf.c -- R-Car VSP1 Read and Write Pixel Formatters * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <media/v4l2-subdev.h> diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 58215a7ab631..70742ecf766f 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_rwpf.h -- R-Car VSP1 Read and Write Pixel Formatters * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_RWPF_H__ #define __VSP1_RWPF_H__ @@ -27,7 +23,6 @@ struct v4l2_ctrl; struct vsp1_dl_manager; -struct vsp1_pipeline; struct vsp1_rwpf; struct vsp1_video; @@ -39,7 +34,6 @@ struct vsp1_rwpf { struct vsp1_entity entity; struct v4l2_ctrl_handler ctrls; - struct vsp1_pipeline *pipe; struct vsp1_video *video; unsigned int max_width; @@ -47,7 +41,7 @@ struct vsp1_rwpf { struct v4l2_pix_format_mplane format; const struct vsp1_format_info *fmtinfo; - unsigned int bru_input; + unsigned int brx_input; unsigned int alpha; diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 51e5691187c3..04e4e05af6ae 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_sru.c -- R-Car VSP1 Super Resolution Unit * * Copyright (C) 2013 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -28,10 +24,10 @@ * Device Access */ -static inline void vsp1_sru_write(struct vsp1_sru *sru, struct vsp1_dl_list *dl, - u32 reg, u32 data) +static inline void vsp1_sru_write(struct vsp1_sru *sru, + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg, data); + vsp1_dl_body_write(dlb, reg, data); } /* ----------------------------------------------------------------------------- @@ -271,10 +267,9 @@ static const struct v4l2_subdev_ops sru_ops = { * VSP1 Entity Operations */ -static void sru_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void sru_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { const struct vsp1_sru_param *param; struct vsp1_sru *sru = to_sru(&entity->subdev); @@ -282,9 +277,6 @@ static void sru_configure(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *output; u32 ctrl0; - if (params != VSP1_ENTITY_PARAMS_INIT) - return; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, SRU_PAD_SINK); output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, @@ -303,9 +295,9 @@ static void sru_configure(struct vsp1_entity *entity, ctrl0 |= param->ctrl0; - vsp1_sru_write(sru, dl, VI6_SRU_CTRL0, ctrl0); - vsp1_sru_write(sru, dl, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5); - vsp1_sru_write(sru, dl, VI6_SRU_CTRL2, param->ctrl2); + vsp1_sru_write(sru, dlb, VI6_SRU_CTRL0, ctrl0); + vsp1_sru_write(sru, dlb, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5); + vsp1_sru_write(sru, dlb, VI6_SRU_CTRL2, param->ctrl2); } static unsigned int sru_max_width(struct vsp1_entity *entity, @@ -351,7 +343,7 @@ static void sru_partition(struct vsp1_entity *entity, } static const struct vsp1_entity_operations sru_entity_ops = { - .configure = sru_configure, + .configure_stream = sru_configure_stream, .max_width = sru_max_width, .partition = sru_partition, }; diff --git a/drivers/media/platform/vsp1/vsp1_sru.h b/drivers/media/platform/vsp1/vsp1_sru.h index 85e241457af2..ddb00eadd1ea 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.h +++ b/drivers/media/platform/vsp1/vsp1_sru.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_sru.h -- R-Car VSP1 Super Resolution Unit * * Copyright (C) 2013 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_SRU_H__ #define __VSP1_SRU_H__ diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 72f72a9d2152..c20c84b54936 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_uds.c -- R-Car VSP1 Up and Down Scaler * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -31,22 +27,22 @@ * Device Access */ -static inline void vsp1_uds_write(struct vsp1_uds *uds, struct vsp1_dl_list *dl, - u32 reg, u32 data) +static inline void vsp1_uds_write(struct vsp1_uds *uds, + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg + uds->entity.index * VI6_UDS_OFFSET, data); + vsp1_dl_body_write(dlb, reg + uds->entity.index * VI6_UDS_OFFSET, data); } /* ----------------------------------------------------------------------------- * Scaling Computation */ -void vsp1_uds_set_alpha(struct vsp1_entity *entity, struct vsp1_dl_list *dl, +void vsp1_uds_set_alpha(struct vsp1_entity *entity, struct vsp1_dl_body *dlb, unsigned int alpha) { struct vsp1_uds *uds = to_uds(&entity->subdev); - vsp1_uds_write(uds, dl, VI6_UDS_ALPVAL, + vsp1_uds_write(uds, dlb, VI6_UDS_ALPVAL, alpha << VI6_UDS_ALPVAL_VAL0_SHIFT); } @@ -259,10 +255,9 @@ static const struct v4l2_subdev_ops uds_ops = { * VSP1 Entity Operations */ -static void uds_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void uds_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; @@ -276,27 +271,6 @@ static void uds_configure(struct vsp1_entity *entity, output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, UDS_PAD_SOURCE); - if (params == VSP1_ENTITY_PARAMS_PARTITION) { - struct vsp1_partition *partition = pipe->partition; - - /* Input size clipping */ - vsp1_uds_write(uds, dl, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN | - (0 << VI6_UDS_HSZCLIP_HCL_OFST_SHIFT) | - (partition->uds_sink.width - << VI6_UDS_HSZCLIP_HCL_SIZE_SHIFT)); - - /* Output size clipping */ - vsp1_uds_write(uds, dl, VI6_UDS_CLIP_SIZE, - (partition->uds_source.width - << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | - (output->height - << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); - return; - } - - if (params != VSP1_ENTITY_PARAMS_INIT) - return; - hscale = uds_compute_ratio(input->width, output->width); vscale = uds_compute_ratio(input->height, output->height); @@ -312,22 +286,48 @@ static void uds_configure(struct vsp1_entity *entity, else multitap = true; - vsp1_uds_write(uds, dl, VI6_UDS_CTRL, + vsp1_uds_write(uds, dlb, VI6_UDS_CTRL, (uds->scale_alpha ? VI6_UDS_CTRL_AON : 0) | (multitap ? VI6_UDS_CTRL_BC : 0)); - vsp1_uds_write(uds, dl, VI6_UDS_PASS_BWIDTH, + vsp1_uds_write(uds, dlb, VI6_UDS_PASS_BWIDTH, (uds_passband_width(hscale) << VI6_UDS_PASS_BWIDTH_H_SHIFT) | (uds_passband_width(vscale) << VI6_UDS_PASS_BWIDTH_V_SHIFT)); /* Set the scaling ratios. */ - vsp1_uds_write(uds, dl, VI6_UDS_SCALE, + vsp1_uds_write(uds, dlb, VI6_UDS_SCALE, (hscale << VI6_UDS_SCALE_HFRAC_SHIFT) | (vscale << VI6_UDS_SCALE_VFRAC_SHIFT)); } +static void uds_configure_partition(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + struct vsp1_uds *uds = to_uds(&entity->subdev); + struct vsp1_partition *partition = pipe->partition; + const struct v4l2_mbus_framefmt *output; + + output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, + UDS_PAD_SOURCE); + + /* Input size clipping */ + vsp1_uds_write(uds, dlb, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN | + (0 << VI6_UDS_HSZCLIP_HCL_OFST_SHIFT) | + (partition->uds_sink.width + << VI6_UDS_HSZCLIP_HCL_SIZE_SHIFT)); + + /* Output size clipping */ + vsp1_uds_write(uds, dlb, VI6_UDS_CLIP_SIZE, + (partition->uds_source.width + << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | + (output->height + << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); +} + static unsigned int uds_max_width(struct vsp1_entity *entity, struct vsp1_pipeline *pipe) { @@ -384,7 +384,8 @@ static void uds_partition(struct vsp1_entity *entity, } static const struct vsp1_entity_operations uds_entity_ops = { - .configure = uds_configure, + .configure_stream = uds_configure_stream, + .configure_partition = uds_configure_partition, .max_width = uds_max_width, .partition = uds_partition, }; diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/vsp1/vsp1_uds.h index 7bf3cdcffc65..c34f95a666d2 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.h +++ b/drivers/media/platform/vsp1/vsp1_uds.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_uds.h -- R-Car VSP1 Up and Down Scaler * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_UDS_H__ #define __VSP1_UDS_H__ @@ -35,7 +31,7 @@ static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev) struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index); -void vsp1_uds_set_alpha(struct vsp1_entity *uds, struct vsp1_dl_list *dl, +void vsp1_uds_set_alpha(struct vsp1_entity *uds, struct vsp1_dl_body *dlb, unsigned int alpha); #endif /* __VSP1_UDS_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_uif.c b/drivers/media/platform/vsp1/vsp1_uif.c new file mode 100644 index 000000000000..4b58d51df231 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_uif.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * vsp1_uif.c -- R-Car VSP1 User Logic Interface + * + * Copyright (C) 2017-2018 Laurent Pinchart + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + */ + +#include <linux/device.h> +#include <linux/gfp.h> +#include <linux/sys_soc.h> + +#include <media/media-entity.h> +#include <media/v4l2-subdev.h> + +#include "vsp1.h" +#include "vsp1_dl.h" +#include "vsp1_entity.h" +#include "vsp1_uif.h" + +#define UIF_MIN_SIZE 4U +#define UIF_MAX_SIZE 8190U + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_uif_read(struct vsp1_uif *uif, u32 reg) +{ + return vsp1_read(uif->entity.vsp1, + uif->entity.index * VI6_UIF_OFFSET + reg); +} + +static inline void vsp1_uif_write(struct vsp1_uif *uif, + struct vsp1_dl_body *dlb, u32 reg, u32 data) +{ + vsp1_dl_body_write(dlb, reg + uif->entity.index * VI6_UIF_OFFSET, data); +} + +u32 vsp1_uif_get_crc(struct vsp1_uif *uif) +{ + return vsp1_uif_read(uif, VI6_UIF_DISCOM_DOCMCCRCR); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static const unsigned int uif_codes[] = { + MEDIA_BUS_FMT_ARGB8888_1X32, + MEDIA_BUS_FMT_AHSV8888_1X32, + MEDIA_BUS_FMT_AYUV8_1X32, +}; + +static int uif_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + return vsp1_subdev_enum_mbus_code(subdev, cfg, code, uif_codes, + ARRAY_SIZE(uif_codes)); +} + +static int uif_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + return vsp1_subdev_enum_frame_size(subdev, cfg, fse, UIF_MIN_SIZE, + UIF_MIN_SIZE, UIF_MAX_SIZE, + UIF_MAX_SIZE); +} + +static int uif_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + return vsp1_subdev_set_pad_format(subdev, cfg, fmt, uif_codes, + ARRAY_SIZE(uif_codes), + UIF_MIN_SIZE, UIF_MIN_SIZE, + UIF_MAX_SIZE, UIF_MAX_SIZE); +} + +static int uif_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct vsp1_uif *uif = to_uif(subdev); + struct v4l2_subdev_pad_config *config; + struct v4l2_mbus_framefmt *format; + int ret = 0; + + if (sel->pad != UIF_PAD_SINK) + return -EINVAL; + + mutex_lock(&uif->entity.lock); + + config = vsp1_entity_get_pad_config(&uif->entity, cfg, sel->which); + if (!config) { + ret = -EINVAL; + goto done; + } + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + format = vsp1_entity_get_pad_format(&uif->entity, config, + UIF_PAD_SINK); + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = format->width; + sel->r.height = format->height; + break; + + case V4L2_SEL_TGT_CROP: + sel->r = *vsp1_entity_get_pad_selection(&uif->entity, config, + sel->pad, sel->target); + break; + + default: + ret = -EINVAL; + break; + } + +done: + mutex_unlock(&uif->entity.lock); + return ret; +} + +static int uif_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct vsp1_uif *uif = to_uif(subdev); + struct v4l2_subdev_pad_config *config; + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *selection; + int ret = 0; + + if (sel->pad != UIF_PAD_SINK || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + mutex_lock(&uif->entity.lock); + + config = vsp1_entity_get_pad_config(&uif->entity, cfg, sel->which); + if (!config) { + ret = -EINVAL; + goto done; + } + + /* The crop rectangle must be inside the input frame. */ + format = vsp1_entity_get_pad_format(&uif->entity, config, UIF_PAD_SINK); + + sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); + sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); + sel->r.width = clamp_t(unsigned int, sel->r.width, UIF_MIN_SIZE, + format->width - sel->r.left); + sel->r.height = clamp_t(unsigned int, sel->r.height, UIF_MIN_SIZE, + format->height - sel->r.top); + + /* Store the crop rectangle. */ + selection = vsp1_entity_get_pad_selection(&uif->entity, config, + sel->pad, V4L2_SEL_TGT_CROP); + *selection = sel->r; + +done: + mutex_unlock(&uif->entity.lock); + return ret; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static const struct v4l2_subdev_pad_ops uif_pad_ops = { + .init_cfg = vsp1_entity_init_cfg, + .enum_mbus_code = uif_enum_mbus_code, + .enum_frame_size = uif_enum_frame_size, + .get_fmt = vsp1_subdev_get_pad_format, + .set_fmt = uif_set_format, + .get_selection = uif_get_selection, + .set_selection = uif_set_selection, +}; + +static const struct v4l2_subdev_ops uif_ops = { + .pad = &uif_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * VSP1 Entity Operations + */ + +static void uif_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) +{ + struct vsp1_uif *uif = to_uif(&entity->subdev); + const struct v4l2_rect *crop; + unsigned int left; + unsigned int width; + + vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMPMR, + VI6_UIF_DISCOM_DOCMPMR_SEL(9)); + + crop = vsp1_entity_get_pad_selection(entity, entity->config, + UIF_PAD_SINK, V4L2_SEL_TGT_CROP); + + left = crop->left; + width = crop->width; + + /* On M3-W the horizontal coordinates are twice the register value. */ + if (uif->m3w_quirk) { + left /= 2; + width /= 2; + } + + vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMSPXR, left); + vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMSPYR, crop->top); + vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMSZXR, width); + vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMSZYR, crop->height); + + vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMCR, + VI6_UIF_DISCOM_DOCMCR_CMPR); +} + +static const struct vsp1_entity_operations uif_entity_ops = { + .configure_stream = uif_configure_stream, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +static const struct soc_device_attribute vsp1_r8a7796[] = { + { .soc_id = "r8a7796" }, + { /* sentinel */ } +}; + +struct vsp1_uif *vsp1_uif_create(struct vsp1_device *vsp1, unsigned int index) +{ + struct vsp1_uif *uif; + char name[6]; + int ret; + + uif = devm_kzalloc(vsp1->dev, sizeof(*uif), GFP_KERNEL); + if (!uif) + return ERR_PTR(-ENOMEM); + + if (soc_device_match(vsp1_r8a7796)) + uif->m3w_quirk = true; + + uif->entity.ops = &uif_entity_ops; + uif->entity.type = VSP1_ENTITY_UIF; + uif->entity.index = index; + + /* The datasheet names the two UIF instances UIF4 and UIF5. */ + sprintf(name, "uif.%u", index + 4); + ret = vsp1_entity_init(vsp1, &uif->entity, name, 2, &uif_ops, + MEDIA_ENT_F_PROC_VIDEO_STATISTICS); + if (ret < 0) + return ERR_PTR(ret); + + return uif; +} diff --git a/drivers/media/platform/vsp1/vsp1_uif.h b/drivers/media/platform/vsp1/vsp1_uif.h new file mode 100644 index 000000000000..c71ab5f6a6f8 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_uif.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * vsp1_uif.h -- R-Car VSP1 User Logic Interface + * + * Copyright (C) 2017-2018 Laurent Pinchart + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + */ +#ifndef __VSP1_UIF_H__ +#define __VSP1_UIF_H__ + +#include "vsp1_entity.h" + +struct vsp1_device; + +#define UIF_PAD_SINK 0 +#define UIF_PAD_SOURCE 1 + +struct vsp1_uif { + struct vsp1_entity entity; + bool m3w_quirk; +}; + +static inline struct vsp1_uif *to_uif(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_uif, entity.subdev); +} + +struct vsp1_uif *vsp1_uif_create(struct vsp1_device *vsp1, unsigned int index); +u32 vsp1_uif_get_crc(struct vsp1_uif *uif); + +#endif /* __VSP1_UIF_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index c2d3b8f0f487..81d47a09d7bc 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_video.c -- R-Car VSP1 Video Node * * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/list.h> @@ -28,7 +24,7 @@ #include <media/videobuf2-dma-contig.h> #include "vsp1.h" -#include "vsp1_bru.h" +#include "vsp1_brx.h" #include "vsp1_dl.h" #include "vsp1_entity.h" #include "vsp1_hgo.h" @@ -324,7 +320,7 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) static struct vsp1_vb2_buffer * vsp1_video_complete_buffer(struct vsp1_video *video) { - struct vsp1_pipeline *pipe = video->rwpf->pipe; + struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; struct vsp1_vb2_buffer *next = NULL; struct vsp1_vb2_buffer *done; unsigned long flags; @@ -382,69 +378,71 @@ static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, unsigned int partition) { + struct vsp1_dl_body *dlb = vsp1_dl_list_get_body0(dl); struct vsp1_entity *entity; pipe->partition = &pipe->part_table[partition]; - list_for_each_entry(entity, &pipe->entities, list_pipe) { - if (entity->ops->configure) - entity->ops->configure(entity, pipe, dl, - VSP1_ENTITY_PARAMS_PARTITION); - } + list_for_each_entry(entity, &pipe->entities, list_pipe) + vsp1_entity_configure_partition(entity, pipe, dl, dlb); } static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; struct vsp1_entity *entity; + struct vsp1_dl_body *dlb; + struct vsp1_dl_list *dl; unsigned int partition; - if (!pipe->dl) - pipe->dl = vsp1_dl_list_get(pipe->output->dlm); + dl = vsp1_dl_list_get(pipe->output->dlm); /* - * Start with the runtime parameters as the configure operation can - * compute/cache information needed when configuring partitions. This - * is the case with flipping in the WPF. + * If the VSP hardware isn't configured yet (which occurs either when + * processing the first frame or after a system suspend/resume), add the + * cached stream configuration to the display list to perform a full + * initialisation. */ - list_for_each_entry(entity, &pipe->entities, list_pipe) { - if (entity->ops->configure) - entity->ops->configure(entity, pipe, pipe->dl, - VSP1_ENTITY_PARAMS_RUNTIME); - } + if (!pipe->configured) + vsp1_dl_list_add_body(dl, pipe->stream_config); + + dlb = vsp1_dl_list_get_body0(dl); - /* Run the first partition */ - vsp1_video_pipeline_run_partition(pipe, pipe->dl, 0); + list_for_each_entry(entity, &pipe->entities, list_pipe) + vsp1_entity_configure_frame(entity, pipe, dl, dlb); - /* Process consecutive partitions as necessary */ + /* Run the first partition. */ + vsp1_video_pipeline_run_partition(pipe, dl, 0); + + /* Process consecutive partitions as necessary. */ for (partition = 1; partition < pipe->partitions; ++partition) { - struct vsp1_dl_list *dl; + struct vsp1_dl_list *dl_next; - dl = vsp1_dl_list_get(pipe->output->dlm); + dl_next = vsp1_dl_list_get(pipe->output->dlm); /* * An incomplete chain will still function, but output only * the partitions that had a dl available. The frame end * interrupt will be marked on the last dl in the chain. */ - if (!dl) { + if (!dl_next) { dev_err(vsp1->dev, "Failed to obtain a dl list. Frame will be incomplete\n"); break; } - vsp1_video_pipeline_run_partition(pipe, dl, partition); - vsp1_dl_list_add_chain(pipe->dl, dl); + vsp1_video_pipeline_run_partition(pipe, dl_next, partition); + vsp1_dl_list_add_chain(dl, dl_next); } /* Complete, and commit the head display list. */ - vsp1_dl_list_commit(pipe->dl); - pipe->dl = NULL; + vsp1_dl_list_commit(dl, false); + pipe->configured = true; vsp1_pipeline_run(pipe); } static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe, - bool completed) + unsigned int completion) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; enum vsp1_pipeline_state state; @@ -452,7 +450,7 @@ static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe, unsigned int i; /* M2M Pipelines should never call here with an incomplete frame. */ - WARN_ON_ONCE(!completed); + WARN_ON_ONCE(!(completion & VSP1_DL_FRAME_END_COMPLETED)); spin_lock_irqsave(&pipe->irqlock, flags); @@ -488,7 +486,7 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe, struct media_entity_enum ent_enum; struct vsp1_entity *entity; struct media_pad *pad; - struct vsp1_bru *bru = NULL; + struct vsp1_brx *brx = NULL; int ret; ret = media_entity_enum_init(&ent_enum, &input->entity.vsp1->media_dev); @@ -524,14 +522,14 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe, if (entity->type == VSP1_ENTITY_BRU || entity->type == VSP1_ENTITY_BRS) { /* BRU and BRS can't be chained. */ - if (bru) { + if (brx) { ret = -EPIPE; goto out; } - bru = to_bru(&entity->subdev); - bru->inputs[pad->index].rpf = input; - input->bru_input = pad->index; + brx = to_brx(&entity->subdev); + brx->inputs[pad->index].rpf = input; + input->brx_input = pad->index; } /* We've reached the WPF, we're done. */ @@ -553,7 +551,7 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe, } pipe->uds = entity; - pipe->uds_input = bru ? &bru->entity : &input->entity; + pipe->uds_input = brx ? &brx->entity : &input->entity; } /* Follow the source link, ignoring any HGO or HGT. */ @@ -598,20 +596,19 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, subdev = media_entity_to_v4l2_subdev(entity); e = to_vsp1_entity(subdev); list_add_tail(&e->list_pipe, &pipe->entities); + e->pipe = pipe; switch (e->type) { case VSP1_ENTITY_RPF: rwpf = to_rwpf(subdev); pipe->inputs[rwpf->entity.index] = rwpf; rwpf->video->pipe_index = ++pipe->num_inputs; - rwpf->pipe = pipe; break; case VSP1_ENTITY_WPF: rwpf = to_rwpf(subdev); pipe->output = rwpf; rwpf->video->pipe_index = 0; - rwpf->pipe = pipe; break; case VSP1_ENTITY_LIF: @@ -620,17 +617,15 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, case VSP1_ENTITY_BRU: case VSP1_ENTITY_BRS: - pipe->bru = e; + pipe->brx = e; break; case VSP1_ENTITY_HGO: pipe->hgo = e; - to_hgo(subdev)->histo.pipe = pipe; break; case VSP1_ENTITY_HGT: pipe->hgt = e; - to_hgt(subdev)->histo.pipe = pipe; break; default: @@ -682,7 +677,7 @@ static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video) * Otherwise allocate a new pipeline and initialize it, it will be freed * when the last reference is released. */ - if (!video->rwpf->pipe) { + if (!video->rwpf->entity.pipe) { pipe = kzalloc(sizeof(*pipe), GFP_KERNEL); if (!pipe) return ERR_PTR(-ENOMEM); @@ -694,7 +689,7 @@ static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video) return ERR_PTR(ret); } } else { - pipe = video->rwpf->pipe; + pipe = video->rwpf->entity.pipe; kref_get(&pipe->kref); } @@ -777,7 +772,7 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); - struct vsp1_pipeline *pipe = video->rwpf->pipe; + struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf); unsigned long flags; bool empty; @@ -812,11 +807,6 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) if (ret < 0) return ret; - /* Prepare the display list. */ - pipe->dl = vsp1_dl_list_get(pipe->output->dlm); - if (!pipe->dl) - return -ENOMEM; - if (pipe->uds) { struct vsp1_uds *uds = to_uds(&pipe->uds->subdev); @@ -838,20 +828,25 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) } } - list_for_each_entry(entity, &pipe->entities, list_pipe) { - vsp1_entity_route_setup(entity, pipe, pipe->dl); + /* + * Compute and cache the stream configuration into a body. The cached + * body will be added to the display list by vsp1_video_pipeline_run() + * whenever the pipeline needs to be fully reconfigured. + */ + pipe->stream_config = vsp1_dlm_dl_body_get(pipe->output->dlm); + if (!pipe->stream_config) + return -ENOMEM; - if (entity->ops->configure) - entity->ops->configure(entity, pipe, pipe->dl, - VSP1_ENTITY_PARAMS_INIT); + list_for_each_entry(entity, &pipe->entities, list_pipe) { + vsp1_entity_route_setup(entity, pipe, pipe->stream_config); + vsp1_entity_configure_stream(entity, pipe, pipe->stream_config); } return 0; } -static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe) +static void vsp1_video_release_buffers(struct vsp1_video *video) { - struct vsp1_video *video = pipe->output->video; struct vsp1_vb2_buffer *buffer; unsigned long flags; @@ -861,18 +856,26 @@ static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe) vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR); INIT_LIST_HEAD(&video->irqqueue); spin_unlock_irqrestore(&video->irqlock, flags); +} + +static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe) +{ + lockdep_assert_held(&pipe->lock); + + /* Release any cached configuration from our output video. */ + vsp1_dl_body_put(pipe->stream_config); + pipe->stream_config = NULL; + pipe->configured = false; /* Release our partition table allocation */ - mutex_lock(&pipe->lock); kfree(pipe->part_table); pipe->part_table = NULL; - mutex_unlock(&pipe->lock); } static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) { struct vsp1_video *video = vb2_get_drv_priv(vq); - struct vsp1_pipeline *pipe = video->rwpf->pipe; + struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; bool start_pipeline = false; unsigned long flags; int ret; @@ -881,8 +884,9 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) if (pipe->stream_count == pipe->num_inputs) { ret = vsp1_video_setup_pipeline(pipe); if (ret < 0) { - mutex_unlock(&pipe->lock); + vsp1_video_release_buffers(video); vsp1_video_cleanup_pipeline(pipe); + mutex_unlock(&pipe->lock); return ret; } @@ -913,7 +917,7 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) static void vsp1_video_stop_streaming(struct vb2_queue *vq) { struct vsp1_video *video = vb2_get_drv_priv(vq); - struct vsp1_pipeline *pipe = video->rwpf->pipe; + struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; unsigned long flags; int ret; @@ -932,13 +936,12 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) if (ret == -ETIMEDOUT) dev_err(video->vsp1->dev, "pipeline stop timeout\n"); - vsp1_dl_list_put(pipe->dl); - pipe->dl = NULL; + vsp1_video_cleanup_pipeline(pipe); } mutex_unlock(&pipe->lock); media_pipeline_stop(&video->video.entity); - vsp1_video_cleanup_pipeline(pipe); + vsp1_video_release_buffers(video); vsp1_video_pipeline_put(pipe); } @@ -1173,6 +1176,87 @@ static const struct v4l2_file_operations vsp1_video_fops = { }; /* ----------------------------------------------------------------------------- + * Suspend and Resume + */ + +void vsp1_video_suspend(struct vsp1_device *vsp1) +{ + unsigned long flags; + unsigned int i; + int ret; + + /* + * To avoid increasing the system suspend time needlessly, loop over the + * pipelines twice, first to set them all to the stopping state, and + * then to wait for the stop to complete. + */ + for (i = 0; i < vsp1->info->wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = wpf->entity.pipe; + if (pipe == NULL) + continue; + + spin_lock_irqsave(&pipe->irqlock, flags); + if (pipe->state == VSP1_PIPELINE_RUNNING) + pipe->state = VSP1_PIPELINE_STOPPING; + spin_unlock_irqrestore(&pipe->irqlock, flags); + } + + for (i = 0; i < vsp1->info->wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = wpf->entity.pipe; + if (pipe == NULL) + continue; + + ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), + msecs_to_jiffies(500)); + if (ret == 0) + dev_warn(vsp1->dev, "pipeline %u stop timeout\n", + wpf->entity.index); + } +} + +void vsp1_video_resume(struct vsp1_device *vsp1) +{ + unsigned long flags; + unsigned int i; + + /* Resume all running pipelines. */ + for (i = 0; i < vsp1->info->wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = wpf->entity.pipe; + if (pipe == NULL) + continue; + + /* + * The hardware may have been reset during a suspend and will + * need a full reconfiguration. + */ + pipe->configured = false; + + spin_lock_irqsave(&pipe->irqlock, flags); + if (vsp1_pipeline_ready(pipe)) + vsp1_video_pipeline_run(pipe); + spin_unlock_irqrestore(&pipe->irqlock, flags); + } +} + +/* ----------------------------------------------------------------------------- * Initialization and Cleanup */ diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h index 50ea7f02205f..f3cf5e2fdf5a 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_video.h -- R-Car VSP1 Video Node * * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_VIDEO_H__ #define __VSP1_VIDEO_H__ @@ -55,6 +51,9 @@ static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev) return container_of(vdev, struct vsp1_video, video); } +void vsp1_video_suspend(struct vsp1_device *vsp1); +void vsp1_video_resume(struct vsp1_device *vsp1); + struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, struct vsp1_rwpf *rwpf); void vsp1_video_cleanup(struct vsp1_video *video); diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 8bd6b2f1af15..23c8f706b3f2 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_wpf.c -- R-Car VSP1 Write Pixel Formatter * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -31,9 +27,9 @@ */ static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, - struct vsp1_dl_list *dl, u32 reg, u32 data) + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg + wpf->entity.index * VI6_WPF_OFFSET, data); + vsp1_dl_body_write(dlb, reg + wpf->entity.index * VI6_WPF_OFFSET, data); } /* ----------------------------------------------------------------------------- @@ -236,10 +232,9 @@ static void vsp1_wpf_destroy(struct vsp1_entity *entity) vsp1_dlm_destroy(wpf->dlm); } -static void wpf_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void wpf_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); struct vsp1_device *vsp1 = wpf->entity.vsp1; @@ -249,149 +244,12 @@ static void wpf_configure(struct vsp1_entity *entity, u32 outfmt = 0; u32 srcrpf = 0; - if (params == VSP1_ENTITY_PARAMS_RUNTIME) { - const unsigned int mask = BIT(WPF_CTRL_VFLIP) - | BIT(WPF_CTRL_HFLIP); - unsigned long flags; - - spin_lock_irqsave(&wpf->flip.lock, flags); - wpf->flip.active = (wpf->flip.active & ~mask) - | (wpf->flip.pending & mask); - spin_unlock_irqrestore(&wpf->flip.lock, flags); - - outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt; - - if (wpf->flip.active & BIT(WPF_CTRL_VFLIP)) - outfmt |= VI6_WPF_OUTFMT_FLP; - if (wpf->flip.active & BIT(WPF_CTRL_HFLIP)) - outfmt |= VI6_WPF_OUTFMT_HFLP; - - vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, outfmt); - return; - } - sink_format = vsp1_entity_get_pad_format(&wpf->entity, wpf->entity.config, RWPF_PAD_SINK); source_format = vsp1_entity_get_pad_format(&wpf->entity, wpf->entity.config, RWPF_PAD_SOURCE); - - if (params == VSP1_ENTITY_PARAMS_PARTITION) { - const struct v4l2_pix_format_mplane *format = &wpf->format; - const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; - struct vsp1_rwpf_memory mem = wpf->mem; - unsigned int flip = wpf->flip.active; - unsigned int width = sink_format->width; - unsigned int height = sink_format->height; - unsigned int offset; - - /* - * Cropping. The partition algorithm can split the image into - * multiple slices. - */ - if (pipe->partitions > 1) - width = pipe->partition->wpf.width; - - vsp1_wpf_write(wpf, dl, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | - (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | - (width << VI6_WPF_SZCLIP_SIZE_SHIFT)); - vsp1_wpf_write(wpf, dl, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | - (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | - (height << VI6_WPF_SZCLIP_SIZE_SHIFT)); - - if (pipe->lif) - return; - - /* - * Update the memory offsets based on flipping configuration. - * The destination addresses point to the locations where the - * VSP starts writing to memory, which can be any corner of the - * image depending on the combination of flipping and rotation. - */ - - /* - * First take the partition left coordinate into account. - * Compute the offset to order the partitions correctly on the - * output based on whether flipping is enabled. Consider - * horizontal flipping when rotation is disabled but vertical - * flipping when rotation is enabled, as rotating the image - * switches the horizontal and vertical directions. The offset - * is applied horizontally or vertically accordingly. - */ - if (flip & BIT(WPF_CTRL_HFLIP) && !wpf->flip.rotate) - offset = format->width - pipe->partition->wpf.left - - pipe->partition->wpf.width; - else if (flip & BIT(WPF_CTRL_VFLIP) && wpf->flip.rotate) - offset = format->height - pipe->partition->wpf.left - - pipe->partition->wpf.width; - else - offset = pipe->partition->wpf.left; - - for (i = 0; i < format->num_planes; ++i) { - unsigned int hsub = i > 0 ? fmtinfo->hsub : 1; - unsigned int vsub = i > 0 ? fmtinfo->vsub : 1; - - if (wpf->flip.rotate) - mem.addr[i] += offset / vsub - * format->plane_fmt[i].bytesperline; - else - mem.addr[i] += offset / hsub - * fmtinfo->bpp[i] / 8; - } - - if (flip & BIT(WPF_CTRL_VFLIP)) { - /* - * When rotating the output (after rotation) image - * height is equal to the partition width (before - * rotation). Otherwise it is equal to the output - * image height. - */ - if (wpf->flip.rotate) - height = pipe->partition->wpf.width; - else - height = format->height; - - mem.addr[0] += (height - 1) - * format->plane_fmt[0].bytesperline; - - if (format->num_planes > 1) { - offset = (height / fmtinfo->vsub - 1) - * format->plane_fmt[1].bytesperline; - mem.addr[1] += offset; - mem.addr[2] += offset; - } - } - - if (wpf->flip.rotate && !(flip & BIT(WPF_CTRL_HFLIP))) { - unsigned int hoffset = max(0, (int)format->width - 16); - - /* - * Compute the output coordinate. The partition - * horizontal (left) offset becomes a vertical offset. - */ - for (i = 0; i < format->num_planes; ++i) { - unsigned int hsub = i > 0 ? fmtinfo->hsub : 1; - - mem.addr[i] += hoffset / hsub - * fmtinfo->bpp[i] / 8; - } - } - - /* - * On Gen3 hardware the SPUVS bit has no effect on 3-planar - * formats. Swap the U and V planes manually in that case. - */ - if (vsp1->info->gen == 3 && format->num_planes == 3 && - fmtinfo->swap_uv) - swap(mem.addr[1], mem.addr[2]); - - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]); - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]); - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]); - return; - } - /* Format */ if (!pipe->lif) { const struct v4l2_pix_format_mplane *format = &wpf->format; @@ -410,17 +268,17 @@ static void wpf_configure(struct vsp1_entity *entity, outfmt |= VI6_WPF_OUTFMT_SPUVS; /* Destination stride and byte swapping. */ - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_STRIDE_Y, + vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_STRIDE_Y, format->plane_fmt[0].bytesperline); if (format->num_planes > 1) - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_STRIDE_C, + vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_STRIDE_C, format->plane_fmt[1].bytesperline); - vsp1_wpf_write(wpf, dl, VI6_WPF_DSWAP, fmtinfo->swap); + vsp1_wpf_write(wpf, dlb, VI6_WPF_DSWAP, fmtinfo->swap); if (vsp1->info->features & VSP1_HAS_WPF_HFLIP && wpf->entity.index == 0) - vsp1_wpf_write(wpf, dl, VI6_WPF_ROT_CTRL, + vsp1_wpf_write(wpf, dlb, VI6_WPF_ROT_CTRL, VI6_WPF_ROT_CTRL_LN16 | (256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT)); } @@ -430,13 +288,13 @@ static void wpf_configure(struct vsp1_entity *entity, wpf->outfmt = outfmt; - vsp1_dl_list_write(dl, VI6_DPR_WPF_FPORCH(wpf->entity.index), + vsp1_dl_body_write(dlb, VI6_DPR_WPF_FPORCH(wpf->entity.index), VI6_DPR_WPF_FPORCH_FP_WPFN); - vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL, 0); + vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL, 0); /* - * Sources. If the pipeline has a single input and BRU is not used, + * Sources. If the pipeline has a single input and BRx is not used, * configure it as the master layer. Otherwise configure all * inputs as sub-layers and select the virtual RPF as the master * layer. @@ -447,24 +305,180 @@ static void wpf_configure(struct vsp1_entity *entity, if (!input) continue; - srcrpf |= (!pipe->bru && pipe->num_inputs == 1) + srcrpf |= (!pipe->brx && pipe->num_inputs == 1) ? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index) : VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index); } - if (pipe->bru) - srcrpf |= pipe->bru->type == VSP1_ENTITY_BRU + if (pipe->brx) + srcrpf |= pipe->brx->type == VSP1_ENTITY_BRU ? VI6_WPF_SRCRPF_VIRACT_MST : VI6_WPF_SRCRPF_VIRACT2_MST; - vsp1_wpf_write(wpf, dl, VI6_WPF_SRCRPF, srcrpf); + vsp1_wpf_write(wpf, dlb, VI6_WPF_SRCRPF, srcrpf); /* Enable interrupts */ - vsp1_dl_list_write(dl, VI6_WPF_IRQ_STA(wpf->entity.index), 0); - vsp1_dl_list_write(dl, VI6_WPF_IRQ_ENB(wpf->entity.index), + vsp1_dl_body_write(dlb, VI6_WPF_IRQ_STA(wpf->entity.index), 0); + vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(wpf->entity.index), VI6_WFP_IRQ_ENB_DFEE); } +static void wpf_configure_frame(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + const unsigned int mask = BIT(WPF_CTRL_VFLIP) + | BIT(WPF_CTRL_HFLIP); + struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); + unsigned long flags; + u32 outfmt; + + spin_lock_irqsave(&wpf->flip.lock, flags); + wpf->flip.active = (wpf->flip.active & ~mask) + | (wpf->flip.pending & mask); + spin_unlock_irqrestore(&wpf->flip.lock, flags); + + outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt; + + if (wpf->flip.active & BIT(WPF_CTRL_VFLIP)) + outfmt |= VI6_WPF_OUTFMT_FLP; + if (wpf->flip.active & BIT(WPF_CTRL_HFLIP)) + outfmt |= VI6_WPF_OUTFMT_HFLP; + + vsp1_wpf_write(wpf, dlb, VI6_WPF_OUTFMT, outfmt); +} + +static void wpf_configure_partition(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); + struct vsp1_device *vsp1 = wpf->entity.vsp1; + struct vsp1_rwpf_memory mem = wpf->mem; + const struct v4l2_mbus_framefmt *sink_format; + const struct v4l2_pix_format_mplane *format = &wpf->format; + const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; + unsigned int width; + unsigned int height; + unsigned int offset; + unsigned int flip; + unsigned int i; + + sink_format = vsp1_entity_get_pad_format(&wpf->entity, + wpf->entity.config, + RWPF_PAD_SINK); + width = sink_format->width; + height = sink_format->height; + + /* + * Cropping. The partition algorithm can split the image into + * multiple slices. + */ + if (pipe->partitions > 1) + width = pipe->partition->wpf.width; + + vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | + (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | + (width << VI6_WPF_SZCLIP_SIZE_SHIFT)); + vsp1_wpf_write(wpf, dlb, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | + (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | + (height << VI6_WPF_SZCLIP_SIZE_SHIFT)); + + if (pipe->lif) + return; + + /* + * Update the memory offsets based on flipping configuration. + * The destination addresses point to the locations where the + * VSP starts writing to memory, which can be any corner of the + * image depending on the combination of flipping and rotation. + */ + + /* + * First take the partition left coordinate into account. + * Compute the offset to order the partitions correctly on the + * output based on whether flipping is enabled. Consider + * horizontal flipping when rotation is disabled but vertical + * flipping when rotation is enabled, as rotating the image + * switches the horizontal and vertical directions. The offset + * is applied horizontally or vertically accordingly. + */ + flip = wpf->flip.active; + + if (flip & BIT(WPF_CTRL_HFLIP) && !wpf->flip.rotate) + offset = format->width - pipe->partition->wpf.left + - pipe->partition->wpf.width; + else if (flip & BIT(WPF_CTRL_VFLIP) && wpf->flip.rotate) + offset = format->height - pipe->partition->wpf.left + - pipe->partition->wpf.width; + else + offset = pipe->partition->wpf.left; + + for (i = 0; i < format->num_planes; ++i) { + unsigned int hsub = i > 0 ? fmtinfo->hsub : 1; + unsigned int vsub = i > 0 ? fmtinfo->vsub : 1; + + if (wpf->flip.rotate) + mem.addr[i] += offset / vsub + * format->plane_fmt[i].bytesperline; + else + mem.addr[i] += offset / hsub + * fmtinfo->bpp[i] / 8; + } + + if (flip & BIT(WPF_CTRL_VFLIP)) { + /* + * When rotating the output (after rotation) image + * height is equal to the partition width (before + * rotation). Otherwise it is equal to the output + * image height. + */ + if (wpf->flip.rotate) + height = pipe->partition->wpf.width; + else + height = format->height; + + mem.addr[0] += (height - 1) + * format->plane_fmt[0].bytesperline; + + if (format->num_planes > 1) { + offset = (height / fmtinfo->vsub - 1) + * format->plane_fmt[1].bytesperline; + mem.addr[1] += offset; + mem.addr[2] += offset; + } + } + + if (wpf->flip.rotate && !(flip & BIT(WPF_CTRL_HFLIP))) { + unsigned int hoffset = max(0, (int)format->width - 16); + + /* + * Compute the output coordinate. The partition + * horizontal (left) offset becomes a vertical offset. + */ + for (i = 0; i < format->num_planes; ++i) { + unsigned int hsub = i > 0 ? fmtinfo->hsub : 1; + + mem.addr[i] += hoffset / hsub + * fmtinfo->bpp[i] / 8; + } + } + + /* + * On Gen3 hardware the SPUVS bit has no effect on 3-planar + * formats. Swap the U and V planes manually in that case. + */ + if (vsp1->info->gen == 3 && format->num_planes == 3 && + fmtinfo->swap_uv) + swap(mem.addr[1], mem.addr[2]); + + vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]); + vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]); + vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]); +} + static unsigned int wpf_max_width(struct vsp1_entity *entity, struct vsp1_pipeline *pipe) { @@ -484,7 +498,9 @@ static void wpf_partition(struct vsp1_entity *entity, static const struct vsp1_entity_operations wpf_entity_ops = { .destroy = vsp1_wpf_destroy, - .configure = wpf_configure, + .configure_stream = wpf_configure_stream, + .configure_frame = wpf_configure_frame, + .configure_partition = wpf_configure_partition, .max_width = wpf_max_width, .partition = wpf_partition, }; diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c index 522cdfdd3345..d041f94be832 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.c +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -494,13 +494,15 @@ xvip_dma_querycap(struct file *file, void *fh, struct v4l2_capability *cap) struct v4l2_fh *vfh = file->private_data; struct xvip_dma *dma = to_xvip_dma(vfh->vdev); - cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING - | dma->xdev->v4l2_caps; + cap->device_caps = V4L2_CAP_STREAMING; if (dma->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE; else - cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + cap->device_caps |= V4L2_CAP_VIDEO_OUTPUT; + + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS + | dma->xdev->v4l2_caps; strlcpy(cap->driver, "xilinx-vipp", sizeof(cap->driver)); strlcpy(cap->card, dma->video.name, sizeof(cap->card)); diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 192f36f2f4aa..39b04ad924c0 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -15,10 +15,6 @@ if RADIO_ADAPTERS && VIDEO_V4L2 config RADIO_TEA575X tristate -config RADIO_SI470X - bool "Silicon Labs Si470x FM Radio Receiver support" - depends on VIDEO_V4L2 - source "drivers/media/radio/si470x/Kconfig" config RADIO_SI4713 @@ -235,7 +231,7 @@ source "drivers/media/radio/wl128x/Kconfig" menuconfig V4L_RADIO_ISA_DRIVERS bool "ISA radio devices" - depends on ISA + depends on ISA || COMPILE_TEST default n ---help--- Say Y here to enable support for these ISA drivers. @@ -243,12 +239,13 @@ menuconfig V4L_RADIO_ISA_DRIVERS if V4L_RADIO_ISA_DRIVERS config RADIO_ISA - depends on ISA + depends on ISA || COMPILE_TEST tristate config RADIO_CADET tristate "ADS Cadet AM/FM Tuner" - depends on ISA && VIDEO_V4L2 + depends on ISA || COMPILE_TEST + depends on VIDEO_V4L2 ---help--- Choose Y here if you have one of these AM/FM radio cards, and then fill in the port address below. @@ -258,7 +255,8 @@ config RADIO_CADET config RADIO_RTRACK tristate "AIMSlab RadioTrack (aka RadioReveal) support" - depends on ISA && VIDEO_V4L2 + depends on ISA || COMPILE_TEST + depends on VIDEO_V4L2 select RADIO_ISA ---help--- Choose Y here if you have one of these FM radio cards, and then fill @@ -289,7 +287,8 @@ config RADIO_RTRACK_PORT config RADIO_RTRACK2 tristate "AIMSlab RadioTrack II support" - depends on ISA && VIDEO_V4L2 + depends on ISA || COMPILE_TEST + depends on VIDEO_V4L2 select RADIO_ISA ---help--- Choose Y here if you have this FM radio card, and then fill in the @@ -312,7 +311,8 @@ config RADIO_RTRACK2_PORT config RADIO_AZTECH tristate "Aztech/Packard Bell Radio" - depends on ISA && VIDEO_V4L2 + depends on ISA || COMPILE_TEST + depends on VIDEO_V4L2 select RADIO_ISA ---help--- Choose Y here if you have one of these FM radio cards, and then fill @@ -332,7 +332,8 @@ config RADIO_AZTECH_PORT config RADIO_GEMTEK tristate "GemTek Radio card (or compatible) support" - depends on ISA && VIDEO_V4L2 + depends on ISA || COMPILE_TEST + depends on VIDEO_V4L2 select RADIO_ISA ---help--- Choose Y here if you have this FM radio card, and then fill in the @@ -372,7 +373,8 @@ config RADIO_GEMTEK_PROBE config RADIO_MIROPCM20 tristate "miroSOUND PCM20 radio" - depends on ISA && ISA_DMA_API && VIDEO_V4L2 && SND + depends on ISA || COMPILE_TEST + depends on ISA_DMA_API && VIDEO_V4L2 && SND select SND_ISA select SND_MIRO ---help--- @@ -386,7 +388,8 @@ config RADIO_MIROPCM20 config RADIO_SF16FMI tristate "SF16-FMI/SF16-FMP/SF16-FMD Radio" - depends on ISA && VIDEO_V4L2 + depends on ISA || COMPILE_TEST + depends on VIDEO_V4L2 ---help--- Choose Y here if you have one of these FM radio cards. @@ -395,7 +398,8 @@ config RADIO_SF16FMI config RADIO_SF16FMR2 tristate "SF16-FMR2/SF16-FMD2 Radio" - depends on ISA && VIDEO_V4L2 + depends on ISA || COMPILE_TEST + depends on VIDEO_V4L2 select RADIO_TEA575X ---help--- Choose Y here if you have one of these FM radio cards. @@ -405,7 +409,8 @@ config RADIO_SF16FMR2 config RADIO_TERRATEC tristate "TerraTec ActiveRadio ISA Standalone" - depends on ISA && VIDEO_V4L2 + depends on ISA || COMPILE_TEST + depends on VIDEO_V4L2 select RADIO_ISA ---help--- Choose Y here if you have this FM radio card. @@ -419,7 +424,8 @@ config RADIO_TERRATEC config RADIO_TRUST tristate "Trust FM radio card" - depends on ISA && VIDEO_V4L2 + depends on ISA || COMPILE_TEST + depends on VIDEO_V4L2 select RADIO_ISA help This is a driver for the Trust FM radio cards. Say Y if you have @@ -442,7 +448,8 @@ config RADIO_TRUST_PORT config RADIO_TYPHOON tristate "Typhoon Radio (a.k.a. EcoRadio)" - depends on ISA && VIDEO_V4L2 + depends on ISA || COMPILE_TEST + depends on VIDEO_V4L2 select RADIO_ISA ---help--- Choose Y here if you have one of these FM radio cards, and then fill @@ -476,7 +483,8 @@ config RADIO_TYPHOON_MUTEFREQ config RADIO_ZOLTRIX tristate "Zoltrix Radio" - depends on ISA && VIDEO_V4L2 + depends on ISA || COMPILE_TEST + depends on VIDEO_V4L2 select RADIO_ISA ---help--- Choose Y here if you have one of these FM radio cards, and then fill diff --git a/drivers/media/radio/si470x/Kconfig b/drivers/media/radio/si470x/Kconfig index a466654ee5c9..a21172e413a9 100644 --- a/drivers/media/radio/si470x/Kconfig +++ b/drivers/media/radio/si470x/Kconfig @@ -1,3 +1,17 @@ +config RADIO_SI470X + tristate "Silicon Labs Si470x FM Radio Receiver support" + depends on VIDEO_V4L2 + ---help--- + This is a driver for devices with the Silicon Labs SI470x + chip (either via USB or I2C buses). + + Say Y here if you want to connect this type of radio to your + computer's USB port or if it is used by some other driver + via I2C bus. + + To compile this driver as a module, choose M here: the + module will be called radio-si470x-common. + config USB_SI470X tristate "Silicon Labs Si470x FM Radio Receiver support with USB" depends on USB && RADIO_SI470X @@ -25,7 +39,7 @@ config USB_SI470X config I2C_SI470X tristate "Silicon Labs Si470x FM Radio Receiver support with I2C" - depends on I2C && RADIO_SI470X && !USB_SI470X + depends on I2C && RADIO_SI470X ---help--- This is a driver for I2C devices with the Silicon Labs SI470x chip. diff --git a/drivers/media/radio/si470x/Makefile b/drivers/media/radio/si470x/Makefile index 06964816cfd6..682b3146397e 100644 --- a/drivers/media/radio/si470x/Makefile +++ b/drivers/media/radio/si470x/Makefile @@ -2,8 +2,6 @@ # Makefile for radios with Silicon Labs Si470x FM Radio Receivers # -radio-usb-si470x-objs := radio-si470x-usb.o radio-si470x-common.o -radio-i2c-si470x-objs := radio-si470x-i2c.o radio-si470x-common.o - -obj-$(CONFIG_USB_SI470X) += radio-usb-si470x.o -obj-$(CONFIG_I2C_SI470X) += radio-i2c-si470x.o +obj-$(CONFIG_RADIO_SI470X) += radio-si470x-common.o +obj-$(CONFIG_USB_SI470X) += radio-si470x-usb.o +obj-$(CONFIG_I2C_SI470X) += radio-si470x-i2c.o diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index b94d66e53d4e..c40e1753f34b 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -110,8 +110,6 @@ /* kernel includes */ #include "radio-si470x.h" - - /************************************************************************** * Module Parameters **************************************************************************/ @@ -195,7 +193,7 @@ static int si470x_set_band(struct si470x_device *radio, int band) radio->band = band; radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_BAND; radio->registers[SYSCONFIG2] |= radio->band << 6; - return si470x_set_register(radio, SYSCONFIG2); + return radio->set_register(radio, SYSCONFIG2); } /* @@ -207,7 +205,7 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) unsigned long time_left; bool timed_out = false; - retval = si470x_get_register(radio, POWERCFG); + retval = radio->get_register(radio, POWERCFG); if (retval) return retval; @@ -219,7 +217,7 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) /* start tuning */ radio->registers[CHANNEL] &= ~CHANNEL_CHAN; radio->registers[CHANNEL] |= CHANNEL_TUNE | chan; - retval = si470x_set_register(radio, CHANNEL); + retval = radio->set_register(radio, CHANNEL); if (retval < 0) goto done; @@ -238,7 +236,7 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) /* stop tuning */ radio->registers[CHANNEL] &= ~CHANNEL_TUNE; - retval = si470x_set_register(radio, CHANNEL); + retval = radio->set_register(radio, CHANNEL); done: return retval; @@ -272,7 +270,7 @@ static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq) int chan, retval; /* read channel */ - retval = si470x_get_register(radio, READCHAN); + retval = radio->get_register(radio, READCHAN); chan = radio->registers[READCHAN] & READCHAN_READCHAN; /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */ @@ -296,6 +294,7 @@ int si470x_set_freq(struct si470x_device *radio, unsigned int freq) return si470x_set_chan(radio, chan); } +EXPORT_SYMBOL_GPL(si470x_set_freq); /* @@ -343,7 +342,7 @@ static int si470x_set_seek(struct si470x_device *radio, radio->registers[POWERCFG] |= POWERCFG_SEEKUP; else radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP; - retval = si470x_set_register(radio, POWERCFG); + retval = radio->set_register(radio, POWERCFG); if (retval < 0) return retval; @@ -362,7 +361,7 @@ static int si470x_set_seek(struct si470x_device *radio, /* stop seeking */ radio->registers[POWERCFG] &= ~POWERCFG_SEEK; - retval = si470x_set_register(radio, POWERCFG); + retval = radio->set_register(radio, POWERCFG); /* try again, if timed out */ if (retval == 0 && timed_out) @@ -381,7 +380,7 @@ int si470x_start(struct si470x_device *radio) /* powercfg */ radio->registers[POWERCFG] = POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM; - retval = si470x_set_register(radio, POWERCFG); + retval = radio->set_register(radio, POWERCFG); if (retval < 0) goto done; @@ -392,7 +391,7 @@ int si470x_start(struct si470x_device *radio) radio->registers[SYSCONFIG1] |= SYSCONFIG1_GPIO2_INT; if (de) radio->registers[SYSCONFIG1] |= SYSCONFIG1_DE; - retval = si470x_set_register(radio, SYSCONFIG1); + retval = radio->set_register(radio, SYSCONFIG1); if (retval < 0) goto done; @@ -402,7 +401,7 @@ int si470x_start(struct si470x_device *radio) ((radio->band << 6) & SYSCONFIG2_BAND) |/* BAND */ ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */ 15; /* VOLUME (max) */ - retval = si470x_set_register(radio, SYSCONFIG2); + retval = radio->set_register(radio, SYSCONFIG2); if (retval < 0) goto done; @@ -413,6 +412,7 @@ int si470x_start(struct si470x_device *radio) done: return retval; } +EXPORT_SYMBOL_GPL(si470x_start); /* @@ -424,7 +424,7 @@ int si470x_stop(struct si470x_device *radio) /* sysconfig 1 */ radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; - retval = si470x_set_register(radio, SYSCONFIG1); + retval = radio->set_register(radio, SYSCONFIG1); if (retval < 0) goto done; @@ -432,11 +432,12 @@ int si470x_stop(struct si470x_device *radio) radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; /* POWERCFG_ENABLE has to automatically go low */ radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE; - retval = si470x_set_register(radio, POWERCFG); + retval = radio->set_register(radio, POWERCFG); done: return retval; } +EXPORT_SYMBOL_GPL(si470x_stop); /* @@ -448,7 +449,7 @@ static int si470x_rds_on(struct si470x_device *radio) /* sysconfig 1 */ radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS; - retval = si470x_set_register(radio, SYSCONFIG1); + retval = radio->set_register(radio, SYSCONFIG1); if (retval < 0) radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; @@ -542,6 +543,25 @@ static __poll_t si470x_fops_poll(struct file *file, } +static int si470x_fops_open(struct file *file) +{ + struct si470x_device *radio = video_drvdata(file); + + return radio->fops_open(file); +} + + +/* + * si470x_fops_release - file release + */ +static int si470x_fops_release(struct file *file) +{ + struct si470x_device *radio = video_drvdata(file); + + return radio->fops_release(file); +} + + /* * si470x_fops - file operations interface */ @@ -570,13 +590,13 @@ static int si470x_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_AUDIO_VOLUME: radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME; radio->registers[SYSCONFIG2] |= ctrl->val; - return si470x_set_register(radio, SYSCONFIG2); + return radio->set_register(radio, SYSCONFIG2); case V4L2_CID_AUDIO_MUTE: if (ctrl->val) radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; else radio->registers[POWERCFG] |= POWERCFG_DMUTE; - return si470x_set_register(radio, POWERCFG); + return radio->set_register(radio, POWERCFG); default: return -EINVAL; } @@ -596,7 +616,7 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, return -EINVAL; if (!radio->status_rssi_auto_update) { - retval = si470x_get_register(radio, STATUSRSSI); + retval = radio->get_register(radio, STATUSRSSI); if (retval < 0) return retval; } @@ -665,7 +685,7 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, break; } - return si470x_set_register(radio, POWERCFG); + return radio->set_register(radio, POWERCFG); } @@ -742,6 +762,15 @@ static int si470x_vidioc_enum_freq_bands(struct file *file, void *priv, const struct v4l2_ctrl_ops si470x_ctrl_ops = { .s_ctrl = si470x_s_ctrl, }; +EXPORT_SYMBOL_GPL(si470x_ctrl_ops); + +static int si470x_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *capability) +{ + struct si470x_device *radio = video_drvdata(file); + + return radio->vidioc_querycap(file, priv, capability); +}; /* * si470x_ioctl_ops - video device ioctl operations @@ -768,3 +797,6 @@ const struct video_device si470x_viddev_template = { .release = video_device_release_empty, .ioctl_ops = &si470x_ioctl_ops, }; +EXPORT_SYMBOL_GPL(si470x_viddev_template); + +MODULE_LICENSE("GPL"); diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index 41709b24b28f..e3b3ecd14a4d 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -89,9 +89,9 @@ MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); /* * si470x_get_register - read register */ -int si470x_get_register(struct si470x_device *radio, int regnr) +static int si470x_get_register(struct si470x_device *radio, int regnr) { - u16 buf[READ_REG_NUM]; + __be16 buf[READ_REG_NUM]; struct i2c_msg msgs[1] = { { .addr = radio->client->addr, @@ -113,10 +113,10 @@ int si470x_get_register(struct si470x_device *radio, int regnr) /* * si470x_set_register - write register */ -int si470x_set_register(struct si470x_device *radio, int regnr) +static int si470x_set_register(struct si470x_device *radio, int regnr) { int i; - u16 buf[WRITE_REG_NUM]; + __be16 buf[WRITE_REG_NUM]; struct i2c_msg msgs[1] = { { .addr = radio->client->addr, @@ -146,7 +146,7 @@ int si470x_set_register(struct si470x_device *radio, int regnr) static int si470x_get_all_registers(struct si470x_device *radio) { int i; - u16 buf[READ_REG_NUM]; + __be16 buf[READ_REG_NUM]; struct i2c_msg msgs[1] = { { .addr = radio->client->addr, @@ -174,7 +174,7 @@ static int si470x_get_all_registers(struct si470x_device *radio) /* * si470x_fops_open - file open */ -int si470x_fops_open(struct file *file) +static int si470x_fops_open(struct file *file) { struct si470x_device *radio = video_drvdata(file); int retval = v4l2_fh_open(file); @@ -206,7 +206,7 @@ done: /* * si470x_fops_release - file release */ -int si470x_fops_release(struct file *file) +static int si470x_fops_release(struct file *file) { struct si470x_device *radio = video_drvdata(file); @@ -226,8 +226,8 @@ int si470x_fops_release(struct file *file) /* * si470x_vidioc_querycap - query device capabilities */ -int si470x_vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *capability) +static int si470x_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *capability) { strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); @@ -361,6 +361,12 @@ static int si470x_i2c_probe(struct i2c_client *client, mutex_init(&radio->lock); init_completion(&radio->completion); + radio->get_register = si470x_get_register; + radio->set_register = si470x_set_register; + radio->fops_open = si470x_fops_open; + radio->fops_release = si470x_fops_release; + radio->vidioc_querycap = si470x_vidioc_querycap; + retval = v4l2_device_register(&client->dev, &radio->v4l2_dev); if (retval < 0) { dev_err(&client->dev, "couldn't register v4l2_device\n"); diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index 2277e850bb5e..313a95f195a2 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -250,7 +250,7 @@ static int si470x_set_report(struct si470x_device *radio, void *buf, int size) /* * si470x_get_register - read register */ -int si470x_get_register(struct si470x_device *radio, int regnr) +static int si470x_get_register(struct si470x_device *radio, int regnr) { int retval; @@ -268,7 +268,7 @@ int si470x_get_register(struct si470x_device *radio, int regnr) /* * si470x_set_register - write register */ -int si470x_set_register(struct si470x_device *radio, int regnr) +static int si470x_set_register(struct si470x_device *radio, int regnr) { int retval; @@ -482,12 +482,12 @@ resubmit: } -int si470x_fops_open(struct file *file) +static int si470x_fops_open(struct file *file) { return v4l2_fh_open(file); } -int si470x_fops_release(struct file *file) +static int si470x_fops_release(struct file *file) { return v4l2_fh_release(file); } @@ -514,8 +514,8 @@ static void si470x_usb_release(struct v4l2_device *v4l2_dev) /* * si470x_vidioc_querycap - query device capabilities */ -int si470x_vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *capability) +static int si470x_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *capability) { struct si470x_device *radio = video_drvdata(file); @@ -598,6 +598,12 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, mutex_init(&radio->lock); init_completion(&radio->completion); + radio->get_register = si470x_get_register; + radio->set_register = si470x_set_register; + radio->fops_open = si470x_fops_open; + radio->fops_release = si470x_fops_release; + radio->vidioc_querycap = si470x_vidioc_querycap; + iface_desc = intf->cur_altsetting; /* Set up interrupt endpoint information. */ diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h index 0202f8eb90c4..35fa0f3bbdd2 100644 --- a/drivers/media/radio/si470x/radio-si470x.h +++ b/drivers/media/radio/si470x/radio-si470x.h @@ -161,6 +161,15 @@ struct si470x_device { struct completion completion; bool status_rssi_auto_update; /* Does RSSI get updated automatic? */ + /* si470x ops */ + + int (*get_register)(struct si470x_device *radio, int regnr); + int (*set_register)(struct si470x_device *radio, int regnr); + int (*fops_open)(struct file *file); + int (*fops_release)(struct file *file); + int (*vidioc_querycap)(struct file *file, void *priv, + struct v4l2_capability *capability); + #if IS_ENABLED(CONFIG_USB_SI470X) /* reference to USB and video device */ struct usb_device *usbdev; @@ -213,13 +222,7 @@ struct si470x_device { **************************************************************************/ extern const struct video_device si470x_viddev_template; extern const struct v4l2_ctrl_ops si470x_ctrl_ops; -int si470x_get_register(struct si470x_device *radio, int regnr); -int si470x_set_register(struct si470x_device *radio, int regnr); int si470x_disconnect_check(struct si470x_device *radio); int si470x_set_freq(struct si470x_device *radio, unsigned int freq); int si470x_start(struct si470x_device *radio); int si470x_stop(struct si470x_device *radio); -int si470x_fops_open(struct file *file); -int si470x_fops_release(struct file *file); -int si470x_vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *capability); diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index d5b35a6ba899..1021c08a9ba4 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -162,7 +162,7 @@ config RC_ATI_REMOTE config IR_ENE tristate "ENE eHome Receiver/Transceiver (pnp id: ENE0100/ENE02xxx)" - depends on PNP + depends on PNP || COMPILE_TEST depends on RC_CORE ---help--- Say Y here to enable support for integrated infrared receiver @@ -223,7 +223,7 @@ config IR_MCEUSB config IR_ITE_CIR tristate "ITE Tech Inc. IT8712/IT8512 Consumer Infrared Transceiver" - depends on PNP + depends on PNP || COMPILE_TEST depends on RC_CORE ---help--- Say Y here to enable support for integrated infrared receivers @@ -236,7 +236,7 @@ config IR_ITE_CIR config IR_FINTEK tristate "Fintek Consumer Infrared Transceiver" - depends on PNP + depends on PNP || COMPILE_TEST depends on RC_CORE ---help--- Say Y here to enable support for integrated infrared receiver @@ -270,7 +270,7 @@ config IR_MTK config IR_NUVOTON tristate "Nuvoton w836x7hg Consumer Infrared Transceiver" - depends on PNP + depends on PNP || COMPILE_TEST depends on RC_CORE ---help--- Say Y here to enable support for integrated infrared receiver @@ -318,7 +318,7 @@ config IR_STREAMZAP config IR_WINBOND_CIR tristate "Winbond IR remote control" - depends on X86 && PNP + depends on (X86 && PNP) || COMPILE_TEST depends on RC_CORE select NEW_LEDS select LEDS_CLASS diff --git a/drivers/media/rc/ir-imon-decoder.c b/drivers/media/rc/ir-imon-decoder.c index a1ff06a26542..67c1b0c15aae 100644 --- a/drivers/media/rc/ir-imon-decoder.c +++ b/drivers/media/rc/ir-imon-decoder.c @@ -31,9 +31,69 @@ enum imon_state { STATE_INACTIVE, STATE_BIT_CHK, STATE_BIT_START, - STATE_FINISHED + STATE_FINISHED, + STATE_ERROR, }; +static void ir_imon_decode_scancode(struct rc_dev *dev) +{ + struct imon_dec *imon = &dev->raw->imon; + + /* Keyboard/Mouse toggle */ + if (imon->bits == 0x299115b7) + imon->stick_keyboard = !imon->stick_keyboard; + + if ((imon->bits & 0xfc0000ff) == 0x680000b7) { + int rel_x, rel_y; + u8 buf; + + buf = imon->bits >> 16; + rel_x = (buf & 0x08) | (buf & 0x10) >> 2 | + (buf & 0x20) >> 4 | (buf & 0x40) >> 6; + if (imon->bits & 0x02000000) + rel_x |= ~0x0f; + buf = imon->bits >> 8; + rel_y = (buf & 0x08) | (buf & 0x10) >> 2 | + (buf & 0x20) >> 4 | (buf & 0x40) >> 6; + if (imon->bits & 0x01000000) + rel_y |= ~0x0f; + + if (rel_x && rel_y && imon->stick_keyboard) { + if (abs(rel_y) > abs(rel_x)) + imon->bits = rel_y > 0 ? + 0x289515b7 : /* KEY_DOWN */ + 0x2aa515b7; /* KEY_UP */ + else + imon->bits = rel_x > 0 ? + 0x2ba515b7 : /* KEY_RIGHT */ + 0x29a515b7; /* KEY_LEFT */ + } + + if (!imon->stick_keyboard) { + struct lirc_scancode lsc = { + .scancode = imon->bits, + .rc_proto = RC_PROTO_IMON, + }; + + ir_lirc_scancode_event(dev, &lsc); + + input_event(imon->idev, EV_MSC, MSC_SCAN, imon->bits); + + input_report_rel(imon->idev, REL_X, rel_x); + input_report_rel(imon->idev, REL_Y, rel_y); + + input_report_key(imon->idev, BTN_LEFT, + (imon->bits & 0x00010000) != 0); + input_report_key(imon->idev, BTN_RIGHT, + (imon->bits & 0x00040000) != 0); + input_sync(imon->idev); + return; + } + } + + rc_keydown(dev, RC_PROTO_IMON, imon->bits, 0); +} + /** * ir_imon_decode() - Decode one iMON pulse or space * @dev: the struct rc_dev descriptor of the device @@ -56,6 +116,22 @@ static int ir_imon_decode(struct rc_dev *dev, struct ir_raw_event ev) data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse)); + /* + * Since iMON protocol is a series of bits, if at any point + * we encounter an error, make sure that any remaining bits + * aren't parsed as a scancode made up of less bits. + * + * Note that if the stick is held, then the remote repeats + * the scancode with about 12ms between them. So, make sure + * we have at least 10ms of space after an error. That way, + * we're at a new scancode. + */ + if (data->state == STATE_ERROR) { + if (!ev.pulse && ev.duration > MS_TO_NS(10)) + data->state = STATE_INACTIVE; + return 0; + } + for (;;) { if (!geq_margin(ev.duration, IMON_UNIT, IMON_UNIT / 2)) return 0; @@ -95,7 +171,7 @@ static int ir_imon_decode(struct rc_dev *dev, struct ir_raw_event ev) case STATE_FINISHED: if (ev.pulse) goto err_out; - rc_keydown(dev, RC_PROTO_IMON, data->bits, 0); + ir_imon_decode_scancode(dev); data->state = STATE_INACTIVE; break; } @@ -107,7 +183,7 @@ err_out: data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse)); - data->state = STATE_INACTIVE; + data->state = STATE_ERROR; return -EINVAL; } @@ -165,11 +241,65 @@ static int ir_imon_encode(enum rc_proto protocol, u32 scancode, return e - events; } +static int ir_imon_register(struct rc_dev *dev) +{ + struct input_dev *idev; + struct imon_dec *imon = &dev->raw->imon; + int ret; + + idev = input_allocate_device(); + if (!idev) + return -ENOMEM; + + snprintf(imon->name, sizeof(imon->name), + "iMON PAD Stick (%s)", dev->device_name); + idev->name = imon->name; + idev->phys = dev->input_phys; + + /* Mouse bits */ + set_bit(EV_REL, idev->evbit); + set_bit(EV_KEY, idev->evbit); + set_bit(REL_X, idev->relbit); + set_bit(REL_Y, idev->relbit); + set_bit(BTN_LEFT, idev->keybit); + set_bit(BTN_RIGHT, idev->keybit); + + /* Report scancodes too */ + set_bit(EV_MSC, idev->evbit); + set_bit(MSC_SCAN, idev->mscbit); + + input_set_drvdata(idev, imon); + + ret = input_register_device(idev); + if (ret < 0) { + input_free_device(idev); + return -EIO; + } + + imon->idev = idev; + imon->stick_keyboard = false; + + return 0; +} + +static int ir_imon_unregister(struct rc_dev *dev) +{ + struct imon_dec *imon = &dev->raw->imon; + + input_unregister_device(imon->idev); + imon->idev = NULL; + + return 0; +} + static struct ir_raw_handler imon_handler = { .protocols = RC_PROTO_BIT_IMON, .decode = ir_imon_decode, .encode = ir_imon_encode, .carrier = 38000, + .raw_register = ir_imon_register, + .raw_unregister = ir_imon_unregister, + .min_timeout = IMON_UNIT * IMON_BITS * 2, }; static int __init ir_imon_decode_init(void) diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c index 8cb68ae43282..5706cfe60027 100644 --- a/drivers/media/rc/ir-jvc-decoder.c +++ b/drivers/media/rc/ir-jvc-decoder.c @@ -213,6 +213,7 @@ static struct ir_raw_handler jvc_handler = { .decode = ir_jvc_decode, .encode = ir_jvc_encode, .carrier = 38000, + .min_timeout = JVC_TRAILER_SPACE, }; static int __init ir_jvc_decode_init(void) diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c index c110984ca671..64ea42927669 100644 --- a/drivers/media/rc/ir-mce_kbd-decoder.c +++ b/drivers/media/rc/ir-mce_kbd-decoder.c @@ -119,17 +119,25 @@ static void mce_kbd_rx_timeout(struct timer_list *t) { struct ir_raw_event_ctrl *raw = from_timer(raw, t, mce_kbd.rx_timeout); unsigned char maskcode; + unsigned long flags; int i; dev_dbg(&raw->dev->dev, "timer callback clearing all keys\n"); - for (i = 0; i < 7; i++) { - maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i]; - input_report_key(raw->mce_kbd.idev, maskcode, 0); - } + spin_lock_irqsave(&raw->mce_kbd.keylock, flags); + + if (time_is_before_eq_jiffies(raw->mce_kbd.rx_timeout.expires)) { + for (i = 0; i < 7; i++) { + maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i]; + input_report_key(raw->mce_kbd.idev, maskcode, 0); + } + + for (i = 0; i < MCIR2_MASK_KEYS_START; i++) + input_report_key(raw->mce_kbd.idev, kbd_keycodes[i], 0); - for (i = 0; i < MCIR2_MASK_KEYS_START; i++) - input_report_key(raw->mce_kbd.idev, kbd_keycodes[i], 0); + input_sync(raw->mce_kbd.idev); + } + spin_unlock_irqrestore(&raw->mce_kbd.keylock, flags); } static enum mce_kbd_mode mce_kbd_mode(struct mce_kbd_dec *data) @@ -147,13 +155,14 @@ static enum mce_kbd_mode mce_kbd_mode(struct mce_kbd_dec *data) static void ir_mce_kbd_process_keyboard_data(struct rc_dev *dev, u32 scancode) { struct mce_kbd_dec *data = &dev->raw->mce_kbd; - u8 keydata = (scancode >> 8) & 0xff; + u8 keydata1 = (scancode >> 8) & 0xff; + u8 keydata2 = (scancode >> 16) & 0xff; u8 shiftmask = scancode & 0xff; - unsigned char keycode, maskcode; + unsigned char maskcode; int i, keystate; - dev_dbg(&dev->dev, "keyboard: keydata = 0x%02x, shiftmask = 0x%02x\n", - keydata, shiftmask); + dev_dbg(&dev->dev, "keyboard: keydata2 = 0x%02x, keydata1 = 0x%02x, shiftmask = 0x%02x\n", + keydata2, keydata1, shiftmask); for (i = 0; i < 7; i++) { maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i]; @@ -164,10 +173,12 @@ static void ir_mce_kbd_process_keyboard_data(struct rc_dev *dev, u32 scancode) input_report_key(data->idev, maskcode, keystate); } - if (keydata) { - keycode = kbd_keycodes[keydata]; - input_report_key(data->idev, keycode, 1); - } else { + if (keydata1) + input_report_key(data->idev, kbd_keycodes[keydata1], 1); + if (keydata2) + input_report_key(data->idev, kbd_keycodes[keydata2], 1); + + if (!keydata1 && !keydata2) { for (i = 0; i < MCIR2_MASK_KEYS_START; i++) input_report_key(data->idev, kbd_keycodes[i], 0); } @@ -263,9 +274,6 @@ again: return 0; case STATE_HEADER_BIT_END: - if (!is_transition(&ev, &dev->raw->prev_ev)) - break; - decrease_duration(&ev, MCIR2_BIT_END); if (data->count != MCIR2_HEADER_NBITS) { @@ -302,9 +310,6 @@ again: return 0; case STATE_BODY_BIT_END: - if (!is_transition(&ev, &dev->raw->prev_ev)) - break; - if (data->count == data->wanted_bits) data->state = STATE_FINISHED; else @@ -319,16 +324,20 @@ again: switch (data->wanted_bits) { case MCIR2_KEYBOARD_NBITS: - scancode = data->body & 0xffff; + scancode = data->body & 0xffffff; dev_dbg(&dev->dev, "keyboard data 0x%08x\n", data->body); - if (dev->timeout) - delay = usecs_to_jiffies(dev->timeout / 1000); - else - delay = msecs_to_jiffies(100); - mod_timer(&data->rx_timeout, jiffies + delay); + spin_lock(&data->keylock); + if (scancode) { + delay = nsecs_to_jiffies(dev->timeout) + + msecs_to_jiffies(100); + mod_timer(&data->rx_timeout, jiffies + delay); + } else { + del_timer(&data->rx_timeout); + } /* Pass data to keyboard buffer parser */ ir_mce_kbd_process_keyboard_data(dev, scancode); + spin_unlock(&data->keylock); lsc.rc_proto = RC_PROTO_MCIR2_KBD; break; case MCIR2_MOUSE_NBITS: @@ -355,7 +364,6 @@ out: dev_dbg(&dev->dev, "failed at state %i (%uus %s)\n", data->state, TO_US(ev.duration), TO_STR(ev.pulse)); data->state = STATE_INACTIVE; - input_sync(data->idev); return -EINVAL; } @@ -394,6 +402,7 @@ static int ir_mce_kbd_register(struct rc_dev *dev) set_bit(MSC_SCAN, idev->mscbit); timer_setup(&mce_kbd->rx_timeout, mce_kbd_rx_timeout, 0); + spin_lock_init(&mce_kbd->keylock); input_set_drvdata(idev, mce_kbd); @@ -475,6 +484,7 @@ static struct ir_raw_handler mce_kbd_handler = { .raw_register = ir_mce_kbd_register, .raw_unregister = ir_mce_kbd_unregister, .carrier = 36000, + .min_timeout = MCIR2_MAX_LEN + MCIR2_UNIT / 2, }; static int __init ir_mce_kbd_decode_init(void) diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c index 21647b809e6f..6a8973ae3684 100644 --- a/drivers/media/rc/ir-nec-decoder.c +++ b/drivers/media/rc/ir-nec-decoder.c @@ -253,6 +253,7 @@ static struct ir_raw_handler nec_handler = { .decode = ir_nec_decode, .encode = ir_nec_encode, .carrier = 38000, + .min_timeout = NEC_TRAILER_SPACE, }; static int __init ir_nec_decode_init(void) diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c index 74d3b859c3a2..63624654a71e 100644 --- a/drivers/media/rc/ir-rc5-decoder.c +++ b/drivers/media/rc/ir-rc5-decoder.c @@ -88,9 +88,6 @@ again: return 0; case STATE_BIT_END: - if (!is_transition(&ev, &dev->raw->prev_ev)) - break; - if (data->count == CHECK_RC5X_NBITS) data->state = STATE_CHECK_RC5X; else @@ -274,6 +271,7 @@ static struct ir_raw_handler rc5_handler = { .decode = ir_rc5_decode, .encode = ir_rc5_encode, .carrier = 36000, + .min_timeout = RC5_TRAILER, }; static int __init ir_rc5_decode_init(void) diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index 8314da32453f..68487ce9f79b 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -145,9 +145,6 @@ again: return 0; case STATE_HEADER_BIT_END: - if (!is_transition(&ev, &dev->raw->prev_ev)) - break; - if (data->count == RC6_HEADER_NBITS) data->state = STATE_TOGGLE_START; else @@ -165,10 +162,6 @@ again: return 0; case STATE_TOGGLE_END: - if (!is_transition(&ev, &dev->raw->prev_ev) || - !geq_margin(ev.duration, RC6_TOGGLE_END, RC6_UNIT / 2)) - break; - if (!(data->header & RC6_STARTBIT_MASK)) { dev_dbg(&dev->dev, "RC6 invalid start bit\n"); break; @@ -210,9 +203,6 @@ again: break; case STATE_BODY_BIT_END: - if (!is_transition(&ev, &dev->raw->prev_ev)) - break; - if (data->count == data->wanted_bits) data->state = STATE_FINISHED; else @@ -394,6 +384,7 @@ static struct ir_raw_handler rc6_handler = { .decode = ir_rc6_decode, .encode = ir_rc6_encode, .carrier = 36000, + .min_timeout = RC6_SUFFIX_SPACE, }; static int __init ir_rc6_decode_init(void) diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c index 4efe6db5376a..dd6ee1e339d6 100644 --- a/drivers/media/rc/ir-sanyo-decoder.c +++ b/drivers/media/rc/ir-sanyo-decoder.c @@ -210,6 +210,7 @@ static struct ir_raw_handler sanyo_handler = { .decode = ir_sanyo_decode, .encode = ir_sanyo_encode, .carrier = 38000, + .min_timeout = SANYO_TRAILER_SPACE, }; static int __init ir_sanyo_decode_init(void) diff --git a/drivers/media/rc/ir-sharp-decoder.c b/drivers/media/rc/ir-sharp-decoder.c index 6a38c50566a4..f96e0c992eed 100644 --- a/drivers/media/rc/ir-sharp-decoder.c +++ b/drivers/media/rc/ir-sharp-decoder.c @@ -226,6 +226,7 @@ static struct ir_raw_handler sharp_handler = { .decode = ir_sharp_decode, .encode = ir_sharp_encode, .carrier = 38000, + .min_timeout = SHARP_ECHO_SPACE + SHARP_ECHO_SPACE / 4, }; static int __init ir_sharp_decode_init(void) diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c index 6764ec9de646..5065c081238d 100644 --- a/drivers/media/rc/ir-sony-decoder.c +++ b/drivers/media/rc/ir-sony-decoder.c @@ -224,6 +224,7 @@ static struct ir_raw_handler sony_handler = { .decode = ir_sony_decode, .encode = ir_sony_encode, .carrier = 40000, + .min_timeout = SONY_TRAILER_SPACE, }; static int __init ir_sony_decode_init(void) diff --git a/drivers/media/rc/ir-spi.c b/drivers/media/rc/ir-spi.c index 7163d5ce2e64..66334e8d63ba 100644 --- a/drivers/media/rc/ir-spi.c +++ b/drivers/media/rc/ir-spi.c @@ -2,7 +2,7 @@ // SPI driven IR LED device driver // // Copyright (c) 2016 Samsung Electronics Co., Ltd. -// Copyright (c) Andi Shyti <andi.shyti@samsung.com> +// Copyright (c) Andi Shyti <andi@etezian.org> #include <linux/delay.h> #include <linux/fs.h> @@ -173,6 +173,6 @@ static struct spi_driver ir_spi_driver = { module_spi_driver(ir_spi_driver); -MODULE_AUTHOR("Andi Shyti <andi.shyti@samsung.com>"); +MODULE_AUTHOR("Andi Shyti <andi@etezian.org>"); MODULE_DESCRIPTION("SPI IR LED"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/rc/ir-xmp-decoder.c b/drivers/media/rc/ir-xmp-decoder.c index 58b47af1a763..c965f51df1c1 100644 --- a/drivers/media/rc/ir-xmp-decoder.c +++ b/drivers/media/rc/ir-xmp-decoder.c @@ -199,6 +199,7 @@ static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) static struct ir_raw_handler xmp_handler = { .protocols = RC_PROTO_BIT_XMP, .decode = ir_xmp_decode, + .min_timeout = XMP_TRAILER_SPACE, }; static int __init ir_xmp_decode_init(void) diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 65e104c7ddfc..de77d22c30a7 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1561,9 +1561,11 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id rdev->close = ite_close; rdev->s_idle = ite_s_idle; rdev->s_rx_carrier_range = ite_set_rx_carrier_range; - rdev->min_timeout = ITE_MIN_IDLE_TIMEOUT; - rdev->max_timeout = ITE_MAX_IDLE_TIMEOUT; - rdev->timeout = ITE_IDLE_TIMEOUT; + /* FIFO threshold is 17 bytes, so 17 * 8 samples minimum */ + rdev->min_timeout = 17 * 8 * ITE_BAUDRATE_DIVISOR * + itdev->params.sample_period; + rdev->timeout = IR_DEFAULT_TIMEOUT; + rdev->max_timeout = 10 * IR_DEFAULT_TIMEOUT; rdev->rx_resolution = ITE_BAUDRATE_DIVISOR * itdev->params.sample_period; rdev->tx_resolution = ITE_BAUDRATE_DIVISOR * diff --git a/drivers/media/rc/ite-cir.h b/drivers/media/rc/ite-cir.h index 0e8ebc880d1f..9cb24ac01350 100644 --- a/drivers/media/rc/ite-cir.h +++ b/drivers/media/rc/ite-cir.h @@ -154,13 +154,6 @@ struct ite_dev { /* default carrier freq for when demodulator is off (Hz) */ #define ITE_DEFAULT_CARRIER_FREQ 38000 -/* default idling timeout in ns (0.2 seconds) */ -#define ITE_IDLE_TIMEOUT 200000000UL - -/* limit timeout values */ -#define ITE_MIN_IDLE_TIMEOUT 100000000UL -#define ITE_MAX_IDLE_TIMEOUT 1000000000UL - /* convert bits to us */ #define ITE_BITS_TO_NS(bits, sample_period) \ ((u32) ((bits) * ITE_BAUDRATE_DIVISOR * sample_period)) diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index da7013a12a58..f862f1b7f996 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -582,10 +582,17 @@ static long ir_lirc_ioctl(struct file *file, unsigned int cmd, } break; - case LIRC_SET_REC_TIMEOUT_REPORTS: + case LIRC_GET_REC_TIMEOUT: if (!dev->timeout) ret = -ENOTTY; else + val = DIV_ROUND_UP(dev->timeout, 1000); + break; + + case LIRC_SET_REC_TIMEOUT_REPORTS: + if (dev->driver_type != RC_DRIVER_IR_RAW) + ret = -ENOTTY; + else fh->send_timeout_reports = !!val; break; @@ -742,6 +749,7 @@ static void lirc_release_device(struct device *ld) int ir_lirc_register(struct rc_dev *dev) { + const char *rx_type, *tx_type; int err, minor; minor = ida_simple_get(&lirc_ida, 0, RC_DEV_MAX, GFP_KERNEL); @@ -766,8 +774,25 @@ int ir_lirc_register(struct rc_dev *dev) get_device(&dev->dev); - dev_info(&dev->dev, "lirc_dev: driver %s registered at minor = %d", - dev->driver_name, minor); + switch (dev->driver_type) { + case RC_DRIVER_SCANCODE: + rx_type = "scancode"; + break; + case RC_DRIVER_IR_RAW: + rx_type = "raw IR"; + break; + default: + rx_type = "no"; + break; + } + + if (dev->tx_ir) + tx_type = "raw IR"; + else + tx_type = "no"; + + dev_info(&dev->dev, "lirc_dev: driver %s registered at minor = %d, %s receiver, %s transmitter", + dev->driver_name, minor, rx_type, tx_type); return 0; diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 69ba57372c05..4c0c8008872a 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -181,6 +181,7 @@ enum mceusb_model_type { MCE_GEN2 = 0, /* Most boards */ MCE_GEN1, MCE_GEN3, + MCE_GEN3_BROKEN_IRTIMEOUT, MCE_GEN2_TX_INV, MCE_GEN2_TX_INV_RX_GOOD, POLARIS_EVK, @@ -199,6 +200,7 @@ struct mceusb_model { u32 mce_gen3:1; u32 tx_mask_normal:1; u32 no_tx:1; + u32 broken_irtimeout:1; /* * 2nd IR receiver (short-range, wideband) for learning mode: * 0, absent 2nd receiver (rx2) @@ -242,6 +244,12 @@ static const struct mceusb_model mceusb_model[] = { .tx_mask_normal = 1, .rx2 = 2, }, + [MCE_GEN3_BROKEN_IRTIMEOUT] = { + .mce_gen3 = 1, + .tx_mask_normal = 1, + .rx2 = 2, + .broken_irtimeout = 1 + }, [POLARIS_EVK] = { /* * In fact, the EVK is shipped without @@ -352,7 +360,7 @@ static const struct usb_device_id mceusb_dev_table[] = { .driver_info = MCE_GEN2_TX_INV }, /* Topseed eHome Infrared Transceiver */ { USB_DEVICE(VENDOR_TOPSEED, 0x0011), - .driver_info = MCE_GEN3 }, + .driver_info = MCE_GEN3_BROKEN_IRTIMEOUT }, /* Ricavision internal Infrared Transceiver */ { USB_DEVICE(VENDOR_RICAVISION, 0x0010) }, /* Itron ione Libra Q-11 */ @@ -564,6 +572,7 @@ static int mceusb_cmd_datasize(u8 cmd, u8 subcmd) datasize = 1; break; } + break; case MCE_CMD_PORT_IR: switch (subcmd) { case MCE_CMD_UNKNOWN: @@ -982,6 +991,25 @@ static int mceusb_set_tx_carrier(struct rc_dev *dev, u32 carrier) return 0; } +static int mceusb_set_timeout(struct rc_dev *dev, unsigned int timeout) +{ + u8 cmdbuf[4] = { MCE_CMD_PORT_IR, MCE_CMD_SETIRTIMEOUT, 0, 0 }; + struct mceusb_dev *ir = dev->priv; + unsigned int units; + + units = DIV_ROUND_CLOSEST(timeout, US_TO_NS(MCE_TIME_UNIT)); + + cmdbuf[2] = units >> 8; + cmdbuf[3] = units; + + mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); + + /* get receiver timeout value */ + mce_async_out(ir, GET_RX_TIMEOUT, sizeof(GET_RX_TIMEOUT)); + + return 0; +} + /* * Select or deselect the 2nd receiver port. * Second receiver is learning mode, wide-band, short-range receiver. @@ -1150,6 +1178,11 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) init_ir_raw_event(&rawir); rawir.pulse = ((ir->buf_in[i] & MCE_PULSE_BIT) != 0); rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK); + if (unlikely(!rawir.duration)) { + dev_warn(ir->dev, "nonsensical irdata %02x with duration 0", + ir->buf_in[i]); + break; + } if (rawir.pulse) { ir->pulse_tunit += rawir.duration; ir->pulse_count++; @@ -1182,7 +1215,12 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) if (ir->rem) { ir->parser_state = PARSE_IRDATA; } else { - ir_raw_event_reset(ir->rc); + init_ir_raw_event(&rawir); + rawir.timeout = 1; + rawir.duration = ir->rc->timeout; + if (ir_raw_event_store_with_filter(ir->rc, + &rawir)) + event = true; ir->pulse_tunit = 0; ir->pulse_count = 0; } @@ -1415,7 +1453,18 @@ 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); + if (!mceusb_model[ir->model].broken_irtimeout) { + rc->s_timeout = mceusb_set_timeout; + rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT; + } else { + /* + * If we can't set the timeout using CMD_SETIRTIMEOUT, we can + * rely on software timeouts for timeouts < 100ms. + */ + rc->max_timeout = rc->timeout; + } if (!ir->flags.no_tx) { rc->s_tx_mask = mceusb_set_tx_mask; rc->s_tx_carrier = mceusb_set_tx_carrier; diff --git a/drivers/media/rc/mtk-cir.c b/drivers/media/rc/mtk-cir.c index e88eb64e8e69..e42efd9d382e 100644 --- a/drivers/media/rc/mtk-cir.c +++ b/drivers/media/rc/mtk-cir.c @@ -299,8 +299,6 @@ static int mtk_ir_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *dn = dev->of_node; - const struct of_device_id *of_id = - of_match_device(mtk_ir_match, &pdev->dev); struct resource *res; struct mtk_ir *ir; u32 val; @@ -312,7 +310,7 @@ static int mtk_ir_probe(struct platform_device *pdev) return -ENOMEM; ir->dev = dev; - ir->data = of_id->data; + ir->data = of_device_get_match_data(dev); ir->clk = devm_clk_get(dev, "clk"); if (IS_ERR(ir->clk)) { diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 5e1d866a61a5..b8299c9a9744 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -535,6 +535,8 @@ static void nvt_set_cir_iren(struct nvt_dev *nvt) static void nvt_cir_regs_init(struct nvt_dev *nvt) { + nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR); + /* set sample limit count (PE interrupt raised when reached) */ nvt_cir_reg_write(nvt, CIR_RX_LIMIT_COUNT >> 8, CIR_SLCH); nvt_cir_reg_write(nvt, CIR_RX_LIMIT_COUNT & 0xff, CIR_SLCL); @@ -543,31 +545,17 @@ static void nvt_cir_regs_init(struct nvt_dev *nvt) nvt_cir_reg_write(nvt, CIR_FIFOCON_TX_TRIGGER_LEV | CIR_FIFOCON_RX_TRIGGER_LEV, CIR_FIFOCON); - /* - * Enable TX and RX, specify carrier on = low, off = high, and set - * sample period (currently 50us) - */ - nvt_cir_reg_write(nvt, - CIR_IRCON_TXEN | CIR_IRCON_RXEN | - CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL, - CIR_IRCON); - /* clear hardware rx and tx fifos */ nvt_clear_cir_fifo(nvt); nvt_clear_tx_fifo(nvt); - /* clear any and all stray interrupts */ - nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS); - - /* and finally, enable interrupts */ - nvt_set_cir_iren(nvt); - - /* enable the CIR logical device */ - nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR); + nvt_disable_logical_dev(nvt, LOGICAL_DEV_CIR); } static void nvt_cir_wake_regs_init(struct nvt_dev *nvt) { + nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE); + /* * Disable RX, set specific carrier on = low, off = high, * and sample period (currently 50us) @@ -579,9 +567,6 @@ static void nvt_cir_wake_regs_init(struct nvt_dev *nvt) /* clear any and all stray interrupts */ nvt_cir_wake_reg_write(nvt, 0xff, CIR_WAKE_IRSTS); - - /* enable the CIR WAKE logical device */ - nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE); } static void nvt_enable_wake(struct nvt_dev *nvt) @@ -892,6 +877,32 @@ static irqreturn_t nvt_cir_isr(int irq, void *data) return IRQ_HANDLED; } +static void nvt_enable_cir(struct nvt_dev *nvt) +{ + unsigned long flags; + + /* enable the CIR logical device */ + nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR); + + spin_lock_irqsave(&nvt->lock, flags); + + /* + * Enable TX and RX, specify carrier on = low, off = high, and set + * sample period (currently 50us) + */ + nvt_cir_reg_write(nvt, CIR_IRCON_TXEN | CIR_IRCON_RXEN | + CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL, + CIR_IRCON); + + /* clear all pending interrupts */ + nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS); + + /* enable interrupts */ + nvt_set_cir_iren(nvt); + + spin_unlock_irqrestore(&nvt->lock, flags); +} + static void nvt_disable_cir(struct nvt_dev *nvt) { unsigned long flags; @@ -920,25 +931,8 @@ static void nvt_disable_cir(struct nvt_dev *nvt) static int nvt_open(struct rc_dev *dev) { struct nvt_dev *nvt = dev->priv; - unsigned long flags; - - spin_lock_irqsave(&nvt->lock, flags); - - /* set function enable flags */ - nvt_cir_reg_write(nvt, CIR_IRCON_TXEN | CIR_IRCON_RXEN | - CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL, - CIR_IRCON); - - /* clear all pending interrupts */ - nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS); - - /* enable interrupts */ - nvt_set_cir_iren(nvt); - spin_unlock_irqrestore(&nvt->lock, flags); - - /* enable the CIR logical device */ - nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR); + nvt_enable_cir(nvt); return 0; } @@ -1093,19 +1087,13 @@ static void nvt_remove(struct pnp_dev *pdev) static int nvt_suspend(struct pnp_dev *pdev, pm_message_t state) { struct nvt_dev *nvt = pnp_get_drvdata(pdev); - unsigned long flags; nvt_dbg("%s called", __func__); - spin_lock_irqsave(&nvt->lock, flags); - - /* disable all CIR interrupts */ - nvt_cir_reg_write(nvt, 0, CIR_IREN); - - spin_unlock_irqrestore(&nvt->lock, flags); - - /* disable cir logical dev */ - nvt_disable_logical_dev(nvt, LOGICAL_DEV_CIR); + mutex_lock(&nvt->rdev->lock); + if (nvt->rdev->users) + nvt_disable_cir(nvt); + mutex_unlock(&nvt->rdev->lock); /* make sure wake is enabled */ nvt_enable_wake(nvt); @@ -1122,6 +1110,11 @@ static int nvt_resume(struct pnp_dev *pdev) nvt_cir_regs_init(nvt); nvt_cir_wake_regs_init(nvt); + mutex_lock(&nvt->rdev->lock); + if (nvt->rdev->users) + nvt_enable_cir(nvt); + mutex_unlock(&nvt->rdev->lock); + return 0; } diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index eb004757038b..e847bdad5c51 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -38,6 +38,7 @@ struct ir_raw_handler { int (*encode)(enum rc_proto protocol, u32 scancode, struct ir_raw_event *events, unsigned int max); u32 carrier; + u32 min_timeout; /* These two should only be used by the mce kbd decoder */ int (*raw_register)(struct rc_dev *dev); @@ -110,6 +111,8 @@ struct ir_raw_event_ctrl { } sharp; struct mce_kbd_dec { struct input_dev *idev; + /* locks key up timer */ + spinlock_t keylock; struct timer_list rx_timeout; char name[64]; char phys[64]; @@ -129,6 +132,9 @@ struct ir_raw_event_ctrl { int count; int last_chk; unsigned int bits; + bool stick_keyboard; + struct input_dev *idev; + char name[64]; } imon; }; diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c index 7675b7ee5bc7..2e0066b1a31c 100644 --- a/drivers/media/rc/rc-ir-raw.c +++ b/drivers/media/rc/rc-ir-raw.c @@ -22,16 +22,27 @@ static int ir_raw_event_thread(void *data) { struct ir_raw_event ev; struct ir_raw_handler *handler; - struct ir_raw_event_ctrl *raw = (struct ir_raw_event_ctrl *)data; + struct ir_raw_event_ctrl *raw = data; + struct rc_dev *dev = raw->dev; while (1) { mutex_lock(&ir_raw_handler_lock); while (kfifo_out(&raw->kfifo, &ev, 1)) { + if (is_timing_event(ev)) { + if (ev.duration == 0) + dev_err(&dev->dev, "nonsensical timing event of duration 0"); + if (is_timing_event(raw->prev_ev) && + !is_transition(&ev, &raw->prev_ev)) + dev_err(&dev->dev, "two consecutive events of type %s", + TO_STR(ev.pulse)); + if (raw->prev_ev.reset && ev.pulse == 0) + dev_err(&dev->dev, "timing event after reset should be pulse"); + } list_for_each_entry(handler, &ir_raw_handler_list, list) - if (raw->dev->enabled_protocols & + if (dev->enabled_protocols & handler->protocols || !handler->protocols) - handler->decode(raw->dev, ev); - ir_lirc_raw_event(raw->dev, ev); + handler->decode(dev, ev); + ir_lirc_raw_event(dev, ev); raw->prev_ev = ev; } mutex_unlock(&ir_raw_handler_lock); @@ -233,7 +244,49 @@ ir_raw_get_allowed_protocols(void) static int change_protocol(struct rc_dev *dev, u64 *rc_proto) { - /* the caller will update dev->enabled_protocols */ + struct ir_raw_handler *handler; + u32 timeout = 0; + + mutex_lock(&ir_raw_handler_lock); + list_for_each_entry(handler, &ir_raw_handler_list, list) { + if (!(dev->enabled_protocols & handler->protocols) && + (*rc_proto & handler->protocols) && handler->raw_register) + handler->raw_register(dev); + + if ((dev->enabled_protocols & handler->protocols) && + !(*rc_proto & handler->protocols) && + handler->raw_unregister) + handler->raw_unregister(dev); + } + mutex_unlock(&ir_raw_handler_lock); + + if (!dev->max_timeout) + return 0; + + mutex_lock(&ir_raw_handler_lock); + list_for_each_entry(handler, &ir_raw_handler_list, list) { + if (handler->protocols & *rc_proto) { + if (timeout < handler->min_timeout) + timeout = handler->min_timeout; + } + } + mutex_unlock(&ir_raw_handler_lock); + + if (timeout == 0) + timeout = IR_DEFAULT_TIMEOUT; + else + timeout += MS_TO_NS(10); + + if (timeout < dev->min_timeout) + timeout = dev->min_timeout; + else if (timeout > dev->max_timeout) + timeout = dev->max_timeout; + + if (dev->s_timeout) + dev->s_timeout(dev, timeout); + else + dev->timeout = timeout; + return 0; } @@ -569,6 +622,7 @@ int ir_raw_event_prepare(struct rc_dev *dev) dev->raw->dev = dev; dev->change_protocol = change_protocol; + dev->idle = true; spin_lock_init(&dev->raw->edge_spinlock); timer_setup(&dev->raw->edge_handle, ir_raw_edge_handle, 0); INIT_KFIFO(dev->raw->kfifo); @@ -578,7 +632,6 @@ int ir_raw_event_prepare(struct rc_dev *dev) int ir_raw_event_register(struct rc_dev *dev) { - struct ir_raw_handler *handler; struct task_struct *thread; thread = kthread_run(ir_raw_event_thread, dev->raw, "rc%u", dev->minor); @@ -589,9 +642,6 @@ int ir_raw_event_register(struct rc_dev *dev) mutex_lock(&ir_raw_handler_lock); list_add_tail(&dev->raw->list, &ir_raw_client_list); - list_for_each_entry(handler, &ir_raw_handler_list, list) - if (handler->raw_register) - handler->raw_register(dev); mutex_unlock(&ir_raw_handler_lock); return 0; @@ -619,7 +669,8 @@ void ir_raw_event_unregister(struct rc_dev *dev) mutex_lock(&ir_raw_handler_lock); list_del(&dev->raw->list); list_for_each_entry(handler, &ir_raw_handler_list, list) - if (handler->raw_unregister) + if (handler->raw_unregister && + (handler->protocols & dev->enabled_protocols)) handler->raw_unregister(dev); lirc_bpf_free(dev); @@ -640,13 +691,8 @@ void ir_raw_event_unregister(struct rc_dev *dev) int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler) { - struct ir_raw_event_ctrl *raw; - mutex_lock(&ir_raw_handler_lock); list_add_tail(&ir_raw_handler->list, &ir_raw_handler_list); - if (ir_raw_handler->raw_register) - list_for_each_entry(raw, &ir_raw_client_list, list) - ir_raw_handler->raw_register(raw->dev); atomic64_or(ir_raw_handler->protocols, &available_protocols); mutex_unlock(&ir_raw_handler_lock); @@ -662,9 +708,10 @@ void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler) mutex_lock(&ir_raw_handler_lock); list_del(&ir_raw_handler->list); list_for_each_entry(raw, &ir_raw_client_list, list) { - ir_raw_disable_protocols(raw->dev, protocols); - if (ir_raw_handler->raw_unregister) + if (ir_raw_handler->raw_unregister && + (raw->dev->enabled_protocols & protocols)) ir_raw_handler->raw_unregister(raw->dev); + ir_raw_disable_protocols(raw->dev, protocols); } atomic64_andnot(protocols, &available_protocols); mutex_unlock(&ir_raw_handler_lock); diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index b67be33bd62f..2e222d9ee01f 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -26,50 +26,50 @@ static const struct { unsigned int repeat_period; unsigned int scancode_bits; } protocols[] = { - [RC_PROTO_UNKNOWN] = { .name = "unknown", .repeat_period = 250 }, - [RC_PROTO_OTHER] = { .name = "other", .repeat_period = 250 }, + [RC_PROTO_UNKNOWN] = { .name = "unknown", .repeat_period = 125 }, + [RC_PROTO_OTHER] = { .name = "other", .repeat_period = 125 }, [RC_PROTO_RC5] = { .name = "rc-5", - .scancode_bits = 0x1f7f, .repeat_period = 250 }, + .scancode_bits = 0x1f7f, .repeat_period = 114 }, [RC_PROTO_RC5X_20] = { .name = "rc-5x-20", - .scancode_bits = 0x1f7f3f, .repeat_period = 250 }, + .scancode_bits = 0x1f7f3f, .repeat_period = 114 }, [RC_PROTO_RC5_SZ] = { .name = "rc-5-sz", - .scancode_bits = 0x2fff, .repeat_period = 250 }, + .scancode_bits = 0x2fff, .repeat_period = 114 }, [RC_PROTO_JVC] = { .name = "jvc", - .scancode_bits = 0xffff, .repeat_period = 250 }, + .scancode_bits = 0xffff, .repeat_period = 125 }, [RC_PROTO_SONY12] = { .name = "sony-12", - .scancode_bits = 0x1f007f, .repeat_period = 250 }, + .scancode_bits = 0x1f007f, .repeat_period = 100 }, [RC_PROTO_SONY15] = { .name = "sony-15", - .scancode_bits = 0xff007f, .repeat_period = 250 }, + .scancode_bits = 0xff007f, .repeat_period = 100 }, [RC_PROTO_SONY20] = { .name = "sony-20", - .scancode_bits = 0x1fff7f, .repeat_period = 250 }, + .scancode_bits = 0x1fff7f, .repeat_period = 100 }, [RC_PROTO_NEC] = { .name = "nec", - .scancode_bits = 0xffff, .repeat_period = 250 }, + .scancode_bits = 0xffff, .repeat_period = 110 }, [RC_PROTO_NECX] = { .name = "nec-x", - .scancode_bits = 0xffffff, .repeat_period = 250 }, + .scancode_bits = 0xffffff, .repeat_period = 110 }, [RC_PROTO_NEC32] = { .name = "nec-32", - .scancode_bits = 0xffffffff, .repeat_period = 250 }, + .scancode_bits = 0xffffffff, .repeat_period = 110 }, [RC_PROTO_SANYO] = { .name = "sanyo", - .scancode_bits = 0x1fffff, .repeat_period = 250 }, + .scancode_bits = 0x1fffff, .repeat_period = 125 }, [RC_PROTO_MCIR2_KBD] = { .name = "mcir2-kbd", - .scancode_bits = 0xffff, .repeat_period = 250 }, + .scancode_bits = 0xffffff, .repeat_period = 100 }, [RC_PROTO_MCIR2_MSE] = { .name = "mcir2-mse", - .scancode_bits = 0x1fffff, .repeat_period = 250 }, + .scancode_bits = 0x1fffff, .repeat_period = 100 }, [RC_PROTO_RC6_0] = { .name = "rc-6-0", - .scancode_bits = 0xffff, .repeat_period = 250 }, + .scancode_bits = 0xffff, .repeat_period = 114 }, [RC_PROTO_RC6_6A_20] = { .name = "rc-6-6a-20", - .scancode_bits = 0xfffff, .repeat_period = 250 }, + .scancode_bits = 0xfffff, .repeat_period = 114 }, [RC_PROTO_RC6_6A_24] = { .name = "rc-6-6a-24", - .scancode_bits = 0xffffff, .repeat_period = 250 }, + .scancode_bits = 0xffffff, .repeat_period = 114 }, [RC_PROTO_RC6_6A_32] = { .name = "rc-6-6a-32", - .scancode_bits = 0xffffffff, .repeat_period = 250 }, + .scancode_bits = 0xffffffff, .repeat_period = 114 }, [RC_PROTO_RC6_MCE] = { .name = "rc-6-mce", - .scancode_bits = 0xffff7fff, .repeat_period = 250 }, + .scancode_bits = 0xffff7fff, .repeat_period = 114 }, [RC_PROTO_SHARP] = { .name = "sharp", - .scancode_bits = 0x1fff, .repeat_period = 250 }, - [RC_PROTO_XMP] = { .name = "xmp", .repeat_period = 250 }, - [RC_PROTO_CEC] = { .name = "cec", .repeat_period = 550 }, + .scancode_bits = 0x1fff, .repeat_period = 125 }, + [RC_PROTO_XMP] = { .name = "xmp", .repeat_period = 125 }, + [RC_PROTO_CEC] = { .name = "cec", .repeat_period = 0 }, [RC_PROTO_IMON] = { .name = "imon", - .scancode_bits = 0x7fffffff, .repeat_period = 250 }, + .scancode_bits = 0x7fffffff, .repeat_period = 114 }, }; /* Used to keep track of known keymaps */ @@ -690,7 +690,8 @@ static void ir_timer_repeat(struct timer_list *t) void rc_repeat(struct rc_dev *dev) { unsigned long flags; - unsigned int timeout = protocols[dev->last_protocol].repeat_period; + unsigned int timeout = nsecs_to_jiffies(dev->timeout) + + msecs_to_jiffies(protocols[dev->last_protocol].repeat_period); struct lirc_scancode sc = { .scancode = dev->last_scancode, .rc_proto = dev->last_protocol, .keycode = dev->keypressed ? dev->last_keycode : KEY_RESERVED, @@ -706,7 +707,7 @@ void rc_repeat(struct rc_dev *dev) input_sync(dev->input_dev); if (dev->keypressed) { - dev->keyup_jiffies = jiffies + msecs_to_jiffies(timeout); + dev->keyup_jiffies = jiffies + timeout; mod_timer(&dev->timer_keyup, dev->keyup_jiffies); } @@ -801,7 +802,7 @@ void rc_keydown(struct rc_dev *dev, enum rc_proto protocol, u32 scancode, ir_do_keydown(dev, protocol, scancode, keycode, toggle); if (dev->keypressed) { - dev->keyup_jiffies = jiffies + + dev->keyup_jiffies = jiffies + nsecs_to_jiffies(dev->timeout) + msecs_to_jiffies(protocols[protocol].repeat_period); mod_timer(&dev->timer_keyup, dev->keyup_jiffies); } @@ -1241,6 +1242,9 @@ static ssize_t store_protocols(struct device *device, if (rc < 0) goto out; + if (dev->driver_type == RC_DRIVER_IR_RAW) + ir_raw_load_modules(&new_protocols); + rc = dev->change_protocol(dev, &new_protocols); if (rc < 0) { dev_dbg(&dev->dev, "Error setting protocols to 0x%llx\n", @@ -1248,9 +1252,6 @@ static ssize_t store_protocols(struct device *device, goto out; } - if (dev->driver_type == RC_DRIVER_IR_RAW) - ir_raw_load_modules(&new_protocols); - if (new_protocols != old_protocols) { *current_protocols = new_protocols; dev_dbg(&dev->dev, "Protocols changed to 0x%llx\n", @@ -1647,6 +1648,7 @@ struct rc_dev *rc_allocate_device(enum rc_driver_type type) dev->input_dev->setkeycode = ir_setkeycode; input_set_drvdata(dev->input_dev, dev); + dev->timeout = IR_DEFAULT_TIMEOUT; timer_setup(&dev->timer_keyup, ir_timer_keyup, 0); timer_setup(&dev->timer_repeat, ir_timer_repeat, 0); @@ -1735,6 +1737,9 @@ static int rc_prepare_rx_device(struct rc_dev *dev) if (dev->driver_type == RC_DRIVER_SCANCODE && !dev->change_protocol) dev->enabled_protocols = dev->allowed_protocols; + if (dev->driver_type == RC_DRIVER_IR_RAW) + ir_raw_load_modules(&rc_proto); + if (dev->change_protocol) { rc = dev->change_protocol(dev, &rc_proto); if (rc < 0) @@ -1742,9 +1747,6 @@ static int rc_prepare_rx_device(struct rc_dev *dev) dev->enabled_protocols = rc_proto; } - if (dev->driver_type == RC_DRIVER_IR_RAW) - ir_raw_load_modules(&rc_proto); - set_bit(EV_KEY, dev->input_dev->evbit); set_bit(EV_REP, dev->input_dev->evbit); set_bit(EV_MSC, dev->input_dev->evbit); @@ -1860,6 +1862,8 @@ int rc_register_device(struct rc_dev *dev) dev->device_name ?: "Unspecified device", path ?: "N/A"); kfree(path); + dev->registered = true; + if (dev->driver_type != RC_DRIVER_IR_RAW_TX) { rc = rc_setup_rx_device(dev); if (rc) @@ -1879,8 +1883,6 @@ int rc_register_device(struct rc_dev *dev) goto out_lirc; } - dev->registered = true; - dev_dbg(&dev->dev, "Registered rc%u (driver: %s)\n", dev->minor, dev->driver_name ? dev->driver_name : "unknown"); diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c index d2efd7b2c3bc..c855b177103c 100644 --- a/drivers/media/rc/st_rc.c +++ b/drivers/media/rc/st_rc.c @@ -96,19 +96,24 @@ static void st_rc_send_lirc_timeout(struct rc_dev *rdev) static irqreturn_t st_rc_rx_interrupt(int irq, void *data) { + unsigned long timeout; unsigned int symbol, mark = 0; struct st_rc_device *dev = data; int last_symbol = 0; - u32 status; + u32 status, int_status; DEFINE_IR_RAW_EVENT(ev); if (dev->irq_wake) pm_wakeup_event(dev->dev, 0); - status = readl(dev->rx_base + IRB_RX_STATUS); + /* FIXME: is 10ms good enough ? */ + timeout = jiffies + msecs_to_jiffies(10); + do { + status = readl(dev->rx_base + IRB_RX_STATUS); + if (!(status & (IRB_FIFO_NOT_EMPTY | IRB_OVERFLOW))) + break; - while (status & (IRB_FIFO_NOT_EMPTY | IRB_OVERFLOW)) { - u32 int_status = readl(dev->rx_base + IRB_RX_INT_STATUS); + int_status = readl(dev->rx_base + IRB_RX_INT_STATUS); if (unlikely(int_status & IRB_RX_OVERRUN_INT)) { /* discard the entire collection in case of errors! */ ir_raw_event_reset(dev->rdev); @@ -148,8 +153,7 @@ static irqreturn_t st_rc_rx_interrupt(int irq, void *data) } last_symbol = 0; - status = readl(dev->rx_base + IRB_RX_STATUS); - } + } while (time_is_after_jiffies(timeout)); writel(IRB_RX_INTS, dev->rx_base + IRB_RX_INT_CLEAR); diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 0adf0991f5ab..851acba9b436 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -989,8 +989,7 @@ wbcir_init_hw(struct wbcir_data *data) /* Clear RX state */ data->rxstate = WBCIR_RXSTATE_INACTIVE; - ir_raw_event_reset(data->dev); - ir_raw_event_set_idle(data->dev, true); + wbcir_idle_rx(data->dev, true); /* Clear TX state */ if (data->txstate == WBCIR_TXSTATE_ACTIVE) { @@ -1009,6 +1008,7 @@ wbcir_resume(struct pnp_dev *device) struct wbcir_data *data = pnp_get_drvdata(device); wbcir_init_hw(data); + ir_raw_event_reset(data->dev); enable_irq(data->irq); led_classdev_resume(&data->led); diff --git a/drivers/media/spi/cxd2880-spi.c b/drivers/media/spi/cxd2880-spi.c index 4df3bd312f48..11ce5101e19f 100644 --- a/drivers/media/spi/cxd2880-spi.c +++ b/drivers/media/spi/cxd2880-spi.c @@ -60,14 +60,13 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static int cxd2880_write_spi(struct spi_device *spi, u8 *data, u32 size) { struct spi_message msg; - struct spi_transfer tx; + struct spi_transfer tx = {}; if (!spi || !data) { pr_err("invalid arg\n"); return -EINVAL; } - memset(&tx, 0, sizeof(tx)); tx.tx_buf = data; tx.len = size; @@ -88,7 +87,7 @@ static int cxd2880_write_reg(struct spi_device *spi, pr_err("invalid arg\n"); return -EINVAL; } - if (size > BURST_WRITE_MAX) { + if (size > BURST_WRITE_MAX || size > U8_MAX) { pr_err("data size > WRITE_MAX\n"); return -EINVAL; } @@ -101,24 +100,14 @@ static int cxd2880_write_reg(struct spi_device *spi, send_data[0] = 0x0e; write_data_top = data; - while (size > 0) { - send_data[1] = sub_address; - if (size > 255) - send_data[2] = 255; - else - send_data[2] = (u8)size; + send_data[1] = sub_address; + send_data[2] = (u8)size; - memcpy(&send_data[3], write_data_top, send_data[2]); + memcpy(&send_data[3], write_data_top, send_data[2]); - ret = cxd2880_write_spi(spi, send_data, send_data[2] + 3); - if (ret) { - pr_err("write spi failed %d\n", ret); - break; - } - sub_address += send_data[2]; - write_data_top += send_data[2]; - size -= send_data[2]; - } + ret = cxd2880_write_spi(spi, send_data, send_data[2] + 3); + if (ret) + pr_err("write spi failed %d\n", ret); return ret; } @@ -130,7 +119,7 @@ static int cxd2880_spi_read_ts(struct spi_device *spi, int ret; u8 data[3]; struct spi_message message; - struct spi_transfer transfer[2]; + struct spi_transfer transfer[2] = {}; if (!spi || !read_data || !packet_num) { pr_err("invalid arg\n"); @@ -146,7 +135,6 @@ static int cxd2880_spi_read_ts(struct spi_device *spi, data[2] = packet_num; spi_message_init(&message); - memset(transfer, 0, sizeof(transfer)); transfer[0].len = 3; transfer[0].tx_buf = data; @@ -383,7 +371,7 @@ static int cxd2880_start_feed(struct dvb_demux_feed *feed) } } if (i == CXD2880_MAX_FILTER_SIZE) { - pr_err("PID filter is full. Assumed bug.\n"); + pr_err("PID filter is full.\n"); return -EINVAL; } if (!dvb_spi->all_pid_feed_count) diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index 6687514df97f..147f3cd0bb95 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -284,4 +284,11 @@ config MEDIA_TUNER_QM1D1C0042 default m if !MEDIA_SUBDRV_AUTOSELECT help Sharp QM1D1C0042 trellis coded 8PSK tuner driver. + +config MEDIA_TUNER_QM1D1B0004 + tristate "Sharp QM1D1B0004 tuner" + depends on MEDIA_SUPPORT && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Sharp QM1D1B0004 ISDB-S tuner driver. endmenu diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile index 0ff21f1c7eed..7b4f8423501e 100644 --- a/drivers/media/tuners/Makefile +++ b/drivers/media/tuners/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_MEDIA_TUNER_IT913X) += it913x.o obj-$(CONFIG_MEDIA_TUNER_R820T) += r820t.o obj-$(CONFIG_MEDIA_TUNER_MXL301RF) += mxl301rf.o obj-$(CONFIG_MEDIA_TUNER_QM1D1C0042) += qm1d1c0042.o +obj-$(CONFIG_MEDIA_TUNER_QM1D1B0004) += qm1d1b0004.o obj-$(CONFIG_MEDIA_TUNER_M88RS6000T) += m88rs6000t.o obj-$(CONFIG_MEDIA_TUNER_TDA18250) += tda18250.o diff --git a/drivers/media/tuners/mxl301rf.c b/drivers/media/tuners/mxl301rf.c index 1575a5db776a..57b0e4862aaf 100644 --- a/drivers/media/tuners/mxl301rf.c +++ b/drivers/media/tuners/mxl301rf.c @@ -1,17 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MaxLinear MxL301RF OFDM tuner driver * * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ /* diff --git a/drivers/media/tuners/mxl301rf.h b/drivers/media/tuners/mxl301rf.h index d32d4e8dc448..1d5bb10ba07a 100644 --- a/drivers/media/tuners/mxl301rf.h +++ b/drivers/media/tuners/mxl301rf.h @@ -1,17 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * MaxLinear MxL301RF OFDM tuner driver * * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef MXL301RF_H diff --git a/drivers/media/tuners/qm1d1b0004.c b/drivers/media/tuners/qm1d1b0004.c new file mode 100644 index 000000000000..b4495cc1626b --- /dev/null +++ b/drivers/media/tuners/qm1d1b0004.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sharp QM1D1B0004 satellite tuner + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> + * + * based on (former) drivers/media/pci/pt1/va1j5jf8007s.c. + */ + +/* + * Note: + * Since the data-sheet of this tuner chip is not available, + * this driver lacks some tuner_ops and config options. + * In addition, the implementation might be dependent on the specific use + * in the FE module: VA1J5JF8007S and/or in the product: Earthsoft PT1/PT2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <media/dvb_frontend.h> +#include "qm1d1b0004.h" + +/* + * Tuner I/F (copied from the former va1j5jf8007s.c) + * b[0] I2C addr + * b[1] "0":1, BG:2, divider_quotient[7:3]:5 + * b[2] divider_quotient[2:0]:3, divider_remainder:5 + * b[3] "111":3, LPF[3:2]:2, TM:1, "0":1, REF:1 + * b[4] BANDX, PSC:1, LPF[1:0]:2, DIV:1, "0":1 + * + * PLL frequency step := + * REF == 0 -> PLL XTL frequency(4MHz) / 8 + * REF == 1 -> PLL XTL frequency(4MHz) / 4 + * + * PreScaler := + * PSC == 0 -> x32 + * PSC == 1 -> x16 + * + * divider_quotient := (frequency / PLL frequency step) / PreScaler + * divider_remainder := (frequency / PLL frequency step) % PreScaler + * + * LPF := LPF Frequency / 1000 / 2 - 2 + * LPF Frequency @ baudrate=28.86Mbps = 30000 + * + * band (1..9) + * band 1 (freq < 986000) -> DIV:1, BANDX:5, PSC:1 + * band 2 (freq < 1072000) -> DIV:1, BANDX:6, PSC:1 + * band 3 (freq < 1154000) -> DIV:1, BANDX:7, PSC:0 + * band 4 (freq < 1291000) -> DIV:0, BANDX:1, PSC:0 + * band 5 (freq < 1447000) -> DIV:0, BANDX:2, PSC:0 + * band 6 (freq < 1615000) -> DIV:0, BANDX:3, PSC:0 + * band 7 (freq < 1791000) -> DIV:0, BANDX:4, PSC:0 + * band 8 (freq < 1972000) -> DIV:0, BANDX:5, PSC:0 + * band 9 (freq < 2150000) -> DIV:0, BANDX:6, PSC:0 + */ + +#define QM1D1B0004_PSC_MASK (1 << 4) + +#define QM1D1B0004_XTL_FREQ 4000 +#define QM1D1B0004_LPF_FALLBACK 30000 + +#if 0 /* Currently unused */ +static const struct qm1d1b0004_config default_cfg = { + .lpf_freq = QM1D1B0004_CFG_LPF_DFLT, + .half_step = false, +}; +#endif + +struct qm1d1b0004_state { + struct qm1d1b0004_config cfg; + struct i2c_client *i2c; +}; + + +struct qm1d1b0004_cb_map { + u32 frequency; + u8 cb; +}; + +static const struct qm1d1b0004_cb_map cb_maps[] = { + { 986000, 0xb2 }, + { 1072000, 0xd2 }, + { 1154000, 0xe2 }, + { 1291000, 0x20 }, + { 1447000, 0x40 }, + { 1615000, 0x60 }, + { 1791000, 0x80 }, + { 1972000, 0xa0 }, +}; + +static u8 lookup_cb(u32 frequency) +{ + int i; + const struct qm1d1b0004_cb_map *map; + + for (i = 0; i < ARRAY_SIZE(cb_maps); i++) { + map = &cb_maps[i]; + if (frequency < map->frequency) + return map->cb; + } + return 0xc0; +} + +static int qm1d1b0004_set_params(struct dvb_frontend *fe) +{ + struct qm1d1b0004_state *state; + u32 frequency, pll, lpf_freq; + u16 word; + u8 buf[4], cb, lpf; + int ret; + + state = fe->tuner_priv; + frequency = fe->dtv_property_cache.frequency; + + pll = QM1D1B0004_XTL_FREQ / 4; + if (state->cfg.half_step) + pll /= 2; + word = DIV_ROUND_CLOSEST(frequency, pll); + cb = lookup_cb(frequency); + if (cb & QM1D1B0004_PSC_MASK) + word = (word << 1 & ~0x1f) | (word & 0x0f); + + /* step.1: set frequency with BG:2, TM:0(4MHZ), LPF:4MHz */ + buf[0] = 0x40 | word >> 8; + buf[1] = word; + /* inconsisnten with the above I/F doc. maybe the doc is wrong */ + buf[2] = 0xe0 | state->cfg.half_step; + buf[3] = cb; + ret = i2c_master_send(state->i2c, buf, 4); + if (ret < 0) + return ret; + + /* step.2: set TM:1 */ + buf[0] = 0xe4 | state->cfg.half_step; + ret = i2c_master_send(state->i2c, buf, 1); + if (ret < 0) + return ret; + msleep(20); + + /* step.3: set LPF */ + lpf_freq = state->cfg.lpf_freq; + if (lpf_freq == QM1D1B0004_CFG_LPF_DFLT) + lpf_freq = fe->dtv_property_cache.symbol_rate / 1000; + if (lpf_freq == 0) + lpf_freq = QM1D1B0004_LPF_FALLBACK; + lpf = DIV_ROUND_UP(lpf_freq, 2000) - 2; + buf[0] = 0xe4 | ((lpf & 0x0c) << 1) | state->cfg.half_step; + buf[1] = cb | ((lpf & 0x03) << 2); + ret = i2c_master_send(state->i2c, buf, 2); + if (ret < 0) + return ret; + + /* step.4: read PLL lock? */ + buf[0] = 0; + ret = i2c_master_recv(state->i2c, buf, 1); + if (ret < 0) + return ret; + return 0; +} + + +static int qm1d1b0004_set_config(struct dvb_frontend *fe, void *priv_cfg) +{ + struct qm1d1b0004_state *state; + + state = fe->tuner_priv; + memcpy(&state->cfg, priv_cfg, sizeof(state->cfg)); + return 0; +} + + +static int qm1d1b0004_init(struct dvb_frontend *fe) +{ + struct qm1d1b0004_state *state; + u8 buf[2] = {0xf8, 0x04}; + + state = fe->tuner_priv; + if (state->cfg.half_step) + buf[0] |= 0x01; + + return i2c_master_send(state->i2c, buf, 2); +} + + +static const struct dvb_tuner_ops qm1d1b0004_ops = { + .info = { + .name = "Sharp qm1d1b0004", + + .frequency_min = 950000, + .frequency_max = 2150000, + }, + + .init = qm1d1b0004_init, + + .set_params = qm1d1b0004_set_params, + .set_config = qm1d1b0004_set_config, +}; + +static int +qm1d1b0004_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct dvb_frontend *fe; + struct qm1d1b0004_config *cfg; + struct qm1d1b0004_state *state; + int ret; + + cfg = client->dev.platform_data; + fe = cfg->fe; + i2c_set_clientdata(client, fe); + + fe->tuner_priv = kzalloc(sizeof(struct qm1d1b0004_state), GFP_KERNEL); + if (!fe->tuner_priv) { + ret = -ENOMEM; + goto err_mem; + } + + memcpy(&fe->ops.tuner_ops, &qm1d1b0004_ops, sizeof(fe->ops.tuner_ops)); + + state = fe->tuner_priv; + state->i2c = client; + ret = qm1d1b0004_set_config(fe, cfg); + if (ret != 0) + goto err_priv; + + dev_info(&client->dev, "Sharp QM1D1B0004 attached.\n"); + return 0; + +err_priv: + kfree(fe->tuner_priv); +err_mem: + fe->tuner_priv = NULL; + return ret; +} + +static int qm1d1b0004_remove(struct i2c_client *client) +{ + struct dvb_frontend *fe; + + fe = i2c_get_clientdata(client); + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + + +static const struct i2c_device_id qm1d1b0004_id[] = { + {"qm1d1b0004", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, qm1d1b0004_id); + +static struct i2c_driver qm1d1b0004_driver = { + .driver = { + .name = "qm1d1b0004", + }, + .probe = qm1d1b0004_probe, + .remove = qm1d1b0004_remove, + .id_table = qm1d1b0004_id, +}; + +module_i2c_driver(qm1d1b0004_driver); + +MODULE_DESCRIPTION("Sharp QM1D1B0004"); +MODULE_AUTHOR("Akihiro Tsukada"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/qm1d1b0004.h b/drivers/media/tuners/qm1d1b0004.h new file mode 100644 index 000000000000..7734ed109a22 --- /dev/null +++ b/drivers/media/tuners/qm1d1b0004.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Sharp QM1D1B0004 satellite tuner + * + * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> + */ + +#ifndef QM1D1B0004_H +#define QM1D1B0004_H + +#include <media/dvb_frontend.h> + +struct qm1d1b0004_config { + struct dvb_frontend *fe; + + u32 lpf_freq; /* LPF frequency[kHz]. Default: symbol rate */ + bool half_step; /* use PLL frequency step of 500Hz istead of 1000Hz */ +}; + +/* special values indicating to use the default in qm1d1b0004_config */ +#define QM1D1B0004_CFG_PLL_DFLT 0 +#define QM1D1B0004_CFG_LPF_DFLT 0 + +#endif diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c index 9af2a155cfca..642a065b9a07 100644 --- a/drivers/media/tuners/qm1d1c0042.c +++ b/drivers/media/tuners/qm1d1c0042.c @@ -1,17 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Sharp QM1D1C0042 8PSK tuner driver * * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ /* diff --git a/drivers/media/tuners/qm1d1c0042.h b/drivers/media/tuners/qm1d1c0042.h index 8331f8baa094..b22fa98bf56e 100644 --- a/drivers/media/tuners/qm1d1c0042.h +++ b/drivers/media/tuners/qm1d1c0042.h @@ -1,17 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Sharp QM1D1C0042 8PSK tuner driver * * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef QM1D1C0042_H diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig index 6276d9b2198b..0f13192634c7 100644 --- a/drivers/media/usb/cx231xx/Kconfig +++ b/drivers/media/usb/cx231xx/Kconfig @@ -1,13 +1,11 @@ config VIDEO_CX231XX tristate "Conexant cx231xx USB video capture support" - depends on VIDEO_DEV && I2C + depends on VIDEO_DEV && I2C && I2C_MUX select VIDEO_TUNER select VIDEO_TVEEPROM - depends on RC_CORE select VIDEOBUF_VMALLOC select VIDEO_CX25840 select VIDEO_CX2341X - select I2C_MUX ---help--- This is a video4linux driver for Conexant 231xx USB based TV cards. @@ -42,7 +40,6 @@ config VIDEO_CX231XX_ALSA config VIDEO_CX231XX_DVB tristate "DVB/ATSC Support for Cx231xx based TV cards" depends on VIDEO_CX231XX && DVB_CORE - select VIDEOBUF_DVB select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index b80e6857e2eb..2f3b0564d676 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -29,6 +29,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/firmware.h> +#include <linux/slab.h> #include <linux/vmalloc.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index c76b2101193c..a431a998d58f 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -715,7 +715,7 @@ struct cx231xx_board cx231xx_boards[] = { .tuner_i2c_master = I2C_1_MUX_3, .demod_i2c_master = I2C_1_MUX_3, .has_dvb = 1, - .demod_addr = 0x0e, + .demod_addr = 0x64, /* 0xc8 >> 1 */ .norm = V4L2_STD_PAL, .input = {{ @@ -754,7 +754,7 @@ struct cx231xx_board cx231xx_boards[] = { .tuner_i2c_master = I2C_1_MUX_3, .demod_i2c_master = I2C_1_MUX_3, .has_dvb = 1, - .demod_addr = 0x0e, + .demod_addr = 0x64, /* 0xc8 >> 1 */ .norm = V4L2_STD_PAL, .input = {{ @@ -793,7 +793,7 @@ struct cx231xx_board cx231xx_boards[] = { .tuner_i2c_master = I2C_1_MUX_3, .demod_i2c_master = I2C_1_MUX_3, .has_dvb = 1, - .demod_addr = 0x0e, + .demod_addr = 0x59, /* 0xb2 >> 1 */ .norm = V4L2_STD_NTSC, .input = {{ @@ -1024,6 +1024,9 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_CNXT_RDE_250}, {USB_DEVICE(0x0572, 0x58A0), .driver_info = CX231XX_BOARD_CNXT_RDU_250}, + /* AverMedia DVD EZMaker 7 */ + {USB_DEVICE(0x07ca, 0xc039), + .driver_info = CX231XX_BOARD_CNXT_VIDEO_GRABBER}, {USB_DEVICE(0x2040, 0xb110), .driver_info = CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL}, {USB_DEVICE(0x2040, 0xb111), diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c index 67ed66712d05..89357cb08b1a 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -23,8 +23,12 @@ #include <linux/kernel.h> #include <linux/slab.h> +#include <media/dvbdev.h> +#include <media/dmxdev.h> +#include <media/dvb_demux.h> +#include <media/dvb_net.h> +#include <media/dvb_frontend.h> #include <media/v4l2-common.h> -#include <media/videobuf-vmalloc.h> #include <media/tuner.h> #include "xc5000.h" @@ -156,10 +160,8 @@ static struct tda18271_config pv_tda18271_config = { }; static struct lgdt3306a_config hauppauge_955q_lgdt3306a_config = { - .i2c_addr = 0x59, .qam_if_khz = 4000, .vsb_if_khz = 3250, - .deny_i2c_rptr = 1, .spectral_inversion = 1, .mpeg_mode = LGDT3306A_MPEG_SERIAL, .tpclk_edge = LGDT3306A_TPCLK_RISING_EDGE, @@ -600,7 +602,6 @@ fail_adapter: static void unregister_dvb(struct cx231xx_dvb *dvb) { - struct i2c_client *client; dvb_net_release(&dvb->net); dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); @@ -613,23 +614,15 @@ static void unregister_dvb(struct cx231xx_dvb *dvb) dvb_frontend_detach(dvb->frontend[1]); dvb_frontend_detach(dvb->frontend[0]); dvb_unregister_adapter(&dvb->adapter); + /* remove I2C tuner */ - client = dvb->i2c_client_tuner; - if (client) { - module_put(client->dev.driver->owner); - i2c_unregister_device(client); - } - /* remove I2C demod */ - client = dvb->i2c_client_demod[1]; - if (client) { - module_put(client->dev.driver->owner); - i2c_unregister_device(client); - } - client = dvb->i2c_client_demod[0]; - if (client) { - module_put(client->dev.driver->owner); - i2c_unregister_device(client); - } + dvb_module_release(dvb->i2c_client_tuner); + dvb->i2c_client_tuner = NULL; + /* remove I2C demod(s) */ + dvb_module_release(dvb->i2c_client_demod[1]); + dvb->i2c_client_demod[1] = NULL; + dvb_module_release(dvb->i2c_client_demod[0]); + dvb->i2c_client_demod[0] = NULL; } static int dvb_init(struct cx231xx *dev) @@ -638,6 +631,8 @@ static int dvb_init(struct cx231xx *dev) struct cx231xx_dvb *dvb; struct i2c_adapter *tuner_i2c; struct i2c_adapter *demod_i2c; + struct i2c_client *client; + struct i2c_adapter *adapter; if (!dev->board.has_dvb) { /* This device does not support the extension */ @@ -728,7 +723,7 @@ static int dvb_init(struct cx231xx *dev) dvb->frontend[0]->callback = cx231xx_tuner_callback; if (!dvb_attach(tda18271_attach, dev->dvb->frontend[0], - 0x60, tuner_i2c, + dev->board.tuner_addr, tuner_i2c, &cnxt_rde253s_tunerconfig)) { result = -EINVAL; goto out_free; @@ -752,7 +747,7 @@ static int dvb_init(struct cx231xx *dev) dvb->frontend[0]->callback = cx231xx_tuner_callback; if (!dvb_attach(tda18271_attach, dev->dvb->frontend[0], - 0x60, tuner_i2c, + dev->board.tuner_addr, tuner_i2c, &cnxt_rde253s_tunerconfig)) { result = -EINVAL; goto out_free; @@ -779,41 +774,27 @@ static int dvb_init(struct cx231xx *dev) dvb->frontend[0]->callback = cx231xx_tuner_callback; dvb_attach(tda18271_attach, dev->dvb->frontend[0], - 0x60, tuner_i2c, + dev->board.tuner_addr, tuner_i2c, &hcw_tda18271_config); break; case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx: { - struct i2c_client *client; - struct i2c_board_info info; - struct si2165_platform_data si2165_pdata; + struct si2165_platform_data si2165_pdata = {}; /* attach demod */ - memset(&si2165_pdata, 0, sizeof(si2165_pdata)); si2165_pdata.fe = &dev->dvb->frontend[0]; si2165_pdata.chip_mode = SI2165_MODE_PLL_XTAL; si2165_pdata.ref_freq_hz = 16000000; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "si2165", I2C_NAME_SIZE); - info.addr = 0x64; - info.platform_data = &si2165_pdata; - request_module(info.type); - client = i2c_new_device(demod_i2c, &info); - if (!client || !client->dev.driver || !dev->dvb->frontend[0]) { - dev_err(dev->dev, - "Failed to attach SI2165 front end\n"); - result = -EINVAL; - goto out_free; - } - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); + /* perform probe/init/attach */ + client = dvb_module_probe("si2165", NULL, demod_i2c, + dev->board.demod_addr, + &si2165_pdata); + if (!client) { result = -ENODEV; goto out_free; } - dvb->i2c_client_demod[0] = client; dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL; @@ -822,8 +803,7 @@ static int dvb_init(struct cx231xx *dev) dvb->frontend[0]->callback = cx231xx_tuner_callback; dvb_attach(tda18271_attach, dev->dvb->frontend[0], - 0x60, - tuner_i2c, + dev->board.tuner_addr, tuner_i2c, &hcw_tda18271_config); dev->cx231xx_reset_analog_tuner = NULL; @@ -831,74 +811,45 @@ static int dvb_init(struct cx231xx *dev) } case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx: { - struct i2c_client *client; - struct i2c_board_info info; - struct si2165_platform_data si2165_pdata; - struct si2157_config si2157_config; + struct si2165_platform_data si2165_pdata = {}; + struct si2157_config si2157_config = {}; /* attach demod */ - memset(&si2165_pdata, 0, sizeof(si2165_pdata)); si2165_pdata.fe = &dev->dvb->frontend[0]; si2165_pdata.chip_mode = SI2165_MODE_PLL_EXT; si2165_pdata.ref_freq_hz = 24000000; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "si2165", I2C_NAME_SIZE); - info.addr = 0x64; - info.platform_data = &si2165_pdata; - request_module(info.type); - client = i2c_new_device(demod_i2c, &info); - if (!client || !client->dev.driver || !dev->dvb->frontend[0]) { - dev_err(dev->dev, - "Failed to attach SI2165 front end\n"); - result = -EINVAL; - goto out_free; - } - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); + /* perform probe/init/attach */ + client = dvb_module_probe("si2165", NULL, demod_i2c, + dev->board.demod_addr, + &si2165_pdata); + if (!client) { result = -ENODEV; goto out_free; } - dvb->i2c_client_demod[0] = client; - memset(&info, 0, sizeof(struct i2c_board_info)); - dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL; /* define general-purpose callback pointer */ dvb->frontend[0]->callback = cx231xx_tuner_callback; /* attach tuner */ - memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = dev->dvb->frontend[0]; #ifdef CONFIG_MEDIA_CONTROLLER_DVB si2157_config.mdev = dev->media_dev; #endif si2157_config.if_port = 1; si2157_config.inversion = true; - strlcpy(info.type, "si2157", I2C_NAME_SIZE); - info.addr = 0x60; - info.platform_data = &si2157_config; - request_module("si2157"); - - client = i2c_new_device( - tuner_i2c, - &info); - if (client == NULL || client->dev.driver == NULL) { - dvb_frontend_detach(dev->dvb->frontend[0]); - result = -ENODEV; - goto out_free; - } - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - dvb_frontend_detach(dev->dvb->frontend[0]); + /* perform probe/init/attach */ + client = dvb_module_probe("si2157", NULL, tuner_i2c, + dev->board.tuner_addr, + &si2157_config); + if (!client) { result = -ENODEV; goto out_free; } - dev->cx231xx_reset_analog_tuner = NULL; dev->dvb->i2c_client_tuner = client; @@ -906,23 +857,22 @@ static int dvb_init(struct cx231xx *dev) } case CX231XX_BOARD_HAUPPAUGE_955Q: { - struct i2c_client *client; - struct i2c_board_info info; - struct si2157_config si2157_config; - - memset(&info, 0, sizeof(struct i2c_board_info)); + struct si2157_config si2157_config = {}; + struct lgdt3306a_config lgdt3306a_config = {}; - dev->dvb->frontend[0] = dvb_attach(lgdt3306a_attach, - &hauppauge_955q_lgdt3306a_config, - demod_i2c - ); + lgdt3306a_config = hauppauge_955q_lgdt3306a_config; + lgdt3306a_config.fe = &dev->dvb->frontend[0]; + lgdt3306a_config.i2c_adapter = &adapter; - if (!dev->dvb->frontend[0]) { - dev_err(dev->dev, - "Failed to attach LGDT3306A frontend.\n"); - result = -EINVAL; + /* perform probe/init/attach */ + client = dvb_module_probe("lgdt3306a", NULL, demod_i2c, + dev->board.demod_addr, + &lgdt3306a_config); + if (!client) { + result = -ENODEV; goto out_free; } + dvb->i2c_client_demod[0] = client; dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL; @@ -930,34 +880,21 @@ static int dvb_init(struct cx231xx *dev) dvb->frontend[0]->callback = cx231xx_tuner_callback; /* attach tuner */ - memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = dev->dvb->frontend[0]; #ifdef CONFIG_MEDIA_CONTROLLER_DVB si2157_config.mdev = dev->media_dev; #endif si2157_config.if_port = 1; si2157_config.inversion = true; - strlcpy(info.type, "si2157", I2C_NAME_SIZE); - info.addr = 0x60; - info.platform_data = &si2157_config; - request_module("si2157"); - - client = i2c_new_device( - tuner_i2c, - &info); - if (client == NULL || client->dev.driver == NULL) { - dvb_frontend_detach(dev->dvb->frontend[0]); - result = -ENODEV; - goto out_free; - } - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - dvb_frontend_detach(dev->dvb->frontend[0]); + /* perform probe/init/attach */ + client = dvb_module_probe("si2157", NULL, tuner_i2c, + dev->board.tuner_addr, + &si2157_config); + if (!client) { result = -ENODEV; goto out_free; } - dev->cx231xx_reset_analog_tuner = NULL; dev->dvb->i2c_client_tuner = client; @@ -985,7 +922,7 @@ static int dvb_init(struct cx231xx *dev) dvb->frontend[0]->callback = cx231xx_tuner_callback; dvb_attach(tda18271_attach, dev->dvb->frontend[0], - 0x60, tuner_i2c, + dev->board.tuner_addr, tuner_i2c, &pv_tda18271_config); break; @@ -993,9 +930,6 @@ static int dvb_init(struct cx231xx *dev) { struct si2157_config si2157_config = {}; struct si2168_config si2168_config = {}; - struct i2c_board_info info = {}; - struct i2c_client *client; - struct i2c_adapter *adapter; /* attach demodulator chip */ si2168_config.ts_mode = SI2168_TS_SERIAL; /* from *.inf file */ @@ -1003,24 +937,14 @@ static int dvb_init(struct cx231xx *dev) si2168_config.i2c_adapter = &adapter; si2168_config.ts_clock_inv = true; - strlcpy(info.type, "si2168", sizeof(info.type)); - info.addr = dev->board.demod_addr; - info.platform_data = &si2168_config; - - request_module(info.type); - client = i2c_new_device(demod_i2c, &info); - - if (client == NULL || client->dev.driver == NULL) { + /* perform probe/init/attach */ + client = dvb_module_probe("si2168", NULL, demod_i2c, + dev->board.demod_addr, + &si2168_config); + if (!client) { result = -ENODEV; goto out_free; } - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - result = -ENODEV; - goto out_free; - } - dvb->i2c_client_demod[0] = client; /* attach tuner chip */ @@ -1031,37 +955,20 @@ static int dvb_init(struct cx231xx *dev) si2157_config.if_port = 1; si2157_config.inversion = false; - memset(&info, 0, sizeof(info)); - strlcpy(info.type, "si2157", sizeof(info.type)); - info.addr = dev->board.tuner_addr; - info.platform_data = &si2157_config; - - request_module(info.type); - client = i2c_new_device(tuner_i2c, &info); - - if (client == NULL || client->dev.driver == NULL) { - module_put(dvb->i2c_client_demod[0]->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod[0]); - result = -ENODEV; - goto out_free; - } - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - module_put(dvb->i2c_client_demod[0]->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod[0]); + /* perform probe/init/attach */ + client = dvb_module_probe("si2157", NULL, tuner_i2c, + dev->board.tuner_addr, + &si2157_config); + if (!client) { result = -ENODEV; goto out_free; } - dev->cx231xx_reset_analog_tuner = NULL; dev->dvb->i2c_client_tuner = client; break; } case CX231XX_BOARD_ASTROMETA_T2HYBRID: { - struct i2c_client *client; - struct i2c_board_info info = {}; struct mn88473_config mn88473_config = {}; /* attach demodulator chip */ @@ -1069,24 +976,14 @@ static int dvb_init(struct cx231xx *dev) mn88473_config.xtal = 25000000; mn88473_config.fe = &dev->dvb->frontend[0]; - strlcpy(info.type, "mn88473", sizeof(info.type)); - info.addr = dev->board.demod_addr; - info.platform_data = &mn88473_config; - - request_module(info.type); - client = i2c_new_device(demod_i2c, &info); - - if (client == NULL || client->dev.driver == NULL) { - result = -ENODEV; - goto out_free; - } - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); + /* perform probe/init/attach */ + client = dvb_module_probe("mn88473", NULL, demod_i2c, + dev->board.demod_addr, + &mn88473_config); + if (!client) { result = -ENODEV; goto out_free; } - dvb->i2c_client_demod[0] = client; /* define general-purpose callback pointer */ @@ -1100,9 +997,6 @@ static int dvb_init(struct cx231xx *dev) } case CX231XX_BOARD_HAUPPAUGE_935C: { - struct i2c_client *client; - struct i2c_adapter *adapter; - struct i2c_board_info info = {}; struct si2157_config si2157_config = {}; struct si2168_config si2168_config = {}; @@ -1112,25 +1006,14 @@ static int dvb_init(struct cx231xx *dev) si2168_config.i2c_adapter = &adapter; si2168_config.ts_clock_inv = true; - strlcpy(info.type, "si2168", sizeof(info.type)); - info.addr = dev->board.demod_addr; - info.platform_data = &si2168_config; - - request_module(info.type); - client = i2c_new_device(demod_i2c, &info); - if (client == NULL || client->dev.driver == NULL) { - result = -ENODEV; - goto out_free; - } - - if (!try_module_get(client->dev.driver->owner)) { - dev_err(dev->dev, - "Failed to attach %s frontend.\n", info.type); - i2c_unregister_device(client); + /* perform probe/init/attach */ + client = dvb_module_probe("si2168", NULL, demod_i2c, + dev->board.demod_addr, + &si2168_config); + if (!client) { result = -ENODEV; goto out_free; } - dvb->i2c_client_demod[0] = client; dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL; @@ -1145,40 +1028,21 @@ static int dvb_init(struct cx231xx *dev) si2157_config.if_port = 1; si2157_config.inversion = true; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "si2157", I2C_NAME_SIZE); - info.addr = dev->board.tuner_addr; - info.platform_data = &si2157_config; - request_module("si2157"); - - client = i2c_new_device(adapter, &info); - if (client == NULL || client->dev.driver == NULL) { - module_put(dvb->i2c_client_demod[0]->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod[0]); - result = -ENODEV; - goto out_free; - } - - if (!try_module_get(client->dev.driver->owner)) { - dev_err(dev->dev, - "Failed to obtain %s tuner.\n", info.type); - i2c_unregister_device(client); - module_put(dvb->i2c_client_demod[0]->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod[0]); + /* perform probe/init/attach */ + client = dvb_module_probe("si2157", NULL, tuner_i2c, + dev->board.tuner_addr, + &si2157_config); + if (!client) { result = -ENODEV; goto out_free; } - dev->cx231xx_reset_analog_tuner = NULL; dev->dvb->i2c_client_tuner = client; break; } case CX231XX_BOARD_HAUPPAUGE_975: { - struct i2c_client *client; - struct i2c_adapter *adapter; struct i2c_adapter *adapter2; - struct i2c_board_info info = {}; struct si2157_config si2157_config = {}; struct lgdt3306a_config lgdt3306a_config = {}; struct si2168_config si2168_config = {}; @@ -1187,27 +1051,15 @@ static int dvb_init(struct cx231xx *dev) lgdt3306a_config = hauppauge_955q_lgdt3306a_config; lgdt3306a_config.fe = &dev->dvb->frontend[0]; lgdt3306a_config.i2c_adapter = &adapter; - lgdt3306a_config.deny_i2c_rptr = 0; - - strlcpy(info.type, "lgdt3306a", sizeof(info.type)); - info.addr = dev->board.demod_addr; - info.platform_data = &lgdt3306a_config; - - request_module(info.type); - client = i2c_new_device(demod_i2c, &info); - if (client == NULL || client->dev.driver == NULL) { - result = -ENODEV; - goto out_free; - } - if (!try_module_get(client->dev.driver->owner)) { - dev_err(dev->dev, - "Failed to attach %s frontend.\n", info.type); - i2c_unregister_device(client); + /* perform probe/init/attach */ + client = dvb_module_probe("lgdt3306a", NULL, demod_i2c, + dev->board.demod_addr, + &lgdt3306a_config); + if (!client) { result = -ENODEV; goto out_free; } - dvb->i2c_client_demod[0] = client; /* attach second demodulator chip */ @@ -1216,30 +1068,14 @@ static int dvb_init(struct cx231xx *dev) si2168_config.i2c_adapter = &adapter2; si2168_config.ts_clock_inv = true; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "si2168", sizeof(info.type)); - info.addr = dev->board.demod_addr2; - info.platform_data = &si2168_config; - - request_module(info.type); - client = i2c_new_device(adapter, &info); - if (client == NULL || client->dev.driver == NULL) { - dev_err(dev->dev, - "Failed to attach %s frontend.\n", info.type); - module_put(dvb->i2c_client_demod[0]->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod[0]); - result = -ENODEV; - goto out_free; - } - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - module_put(dvb->i2c_client_demod[0]->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod[0]); + /* perform probe/init/attach */ + client = dvb_module_probe("si2168", NULL, adapter, + dev->board.demod_addr2, + &si2168_config); + if (!client) { result = -ENODEV; goto out_free; } - dvb->i2c_client_demod[1] = client; dvb->frontend[1]->id = 1; @@ -1255,34 +1091,14 @@ static int dvb_init(struct cx231xx *dev) si2157_config.if_port = 1; si2157_config.inversion = true; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "si2157", I2C_NAME_SIZE); - info.addr = dev->board.tuner_addr; - info.platform_data = &si2157_config; - request_module("si2157"); - - client = i2c_new_device(adapter, &info); - if (client == NULL || client->dev.driver == NULL) { - module_put(dvb->i2c_client_demod[1]->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod[1]); - module_put(dvb->i2c_client_demod[0]->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod[0]); - result = -ENODEV; - goto out_free; - } - - if (!try_module_get(client->dev.driver->owner)) { - dev_err(dev->dev, - "Failed to obtain %s tuner.\n", info.type); - i2c_unregister_device(client); - module_put(dvb->i2c_client_demod[1]->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod[1]); - module_put(dvb->i2c_client_demod[0]->dev.driver->owner); - i2c_unregister_device(dvb->i2c_client_demod[0]); + /* perform probe/init/attach */ + client = dvb_module_probe("si2157", NULL, adapter, + dev->board.tuner_addr, + &si2157_config); + if (!client) { result = -ENODEV; goto out_free; } - dev->cx231xx_reset_analog_tuner = NULL; dvb->i2c_client_tuner = client; @@ -1321,6 +1137,14 @@ ret: return result; out_free: + /* remove I2C tuner */ + dvb_module_release(dvb->i2c_client_tuner); + dvb->i2c_client_tuner = NULL; + /* remove I2C demod(s) */ + dvb_module_release(dvb->i2c_client_demod[1]); + dvb->i2c_client_demod[1] = NULL; + dvb_module_release(dvb->i2c_client_demod[0]); + dvb->i2c_client_demod[0] = NULL; kfree(dvb); dev->dvb = NULL; goto ret; diff --git a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c index 5bc74149fcb9..746c34ab0ec8 100644 --- a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c +++ b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c @@ -769,7 +769,7 @@ int initialize_cx231xx(struct cx231xx *dev) break; default: dev_err(dev->dev, - "bad senario!!!!!\nconfig_info=%x\n", + "bad scenario!!!!!\nconfig_info=%x\n", config_info & SELFPOWER_MASK); return -ENODEV; } diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index 6ffa4bd96484..fa640bf20111 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -38,7 +38,6 @@ #include <media/v4l2-fh.h> #include <media/rc-core.h> #include <media/i2c/ir-kbd-i2c.h> -#include <media/videobuf-dvb.h> #include "cx231xx-reg.h" #include "cx231xx-pcb-cfg.h" @@ -546,8 +545,6 @@ struct cx231xx_tsport { int nr; int sram_chno; - struct videobuf_dvb_frontends frontends; - /* dma queues */ u32 ts_packet_size; diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c index 43eb82884555..1aa88d94e57f 100644 --- a/drivers/media/usb/dvb-usb-v2/dvbsky.c +++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -31,7 +31,6 @@ MODULE_PARM_DESC(disable_rc, "Disable inbuilt IR receiver."); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); struct dvbsky_state { - struct mutex stream_mutex; u8 ibuf[DVBSKY_BUF_LEN]; u8 obuf[DVBSKY_BUF_LEN]; u8 last_lock; @@ -68,18 +67,17 @@ static int dvbsky_usb_generic_rw(struct dvb_usb_device *d, static int dvbsky_stream_ctrl(struct dvb_usb_device *d, u8 onoff) { - struct dvbsky_state *state = d_to_priv(d); int ret; - u8 obuf_pre[3] = { 0x37, 0, 0 }; - u8 obuf_post[3] = { 0x36, 3, 0 }; + static u8 obuf_pre[3] = { 0x37, 0, 0 }; + static u8 obuf_post[3] = { 0x36, 3, 0 }; - mutex_lock(&state->stream_mutex); - ret = dvbsky_usb_generic_rw(d, obuf_pre, 3, NULL, 0); + mutex_lock(&d->usb_mutex); + ret = dvb_usbv2_generic_rw_locked(d, obuf_pre, 3, NULL, 0); if (!ret && onoff) { msleep(20); - ret = dvbsky_usb_generic_rw(d, obuf_post, 3, NULL, 0); + ret = dvb_usbv2_generic_rw_locked(d, obuf_post, 3, NULL, 0); } - mutex_unlock(&state->stream_mutex); + mutex_unlock(&d->usb_mutex); return ret; } @@ -290,61 +288,44 @@ static int dvbsky_usb_read_status(struct dvb_frontend *fe, return ret; } -static const struct m88ds3103_config dvbsky_s960_m88ds3103_config = { - .i2c_addr = 0x68, - .clock = 27000000, - .i2c_wr_max = 33, - .clock_out = 0, - .ts_mode = M88DS3103_TS_CI, - .ts_clk = 16000, - .ts_clk_pol = 0, - .agc = 0x99, - .lnb_hv_pol = 1, - .lnb_en_pol = 1, -}; - static int dvbsky_s960_attach(struct dvb_usb_adapter *adap) { struct dvbsky_state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); - int ret = 0; - /* demod I2C adapter */ - struct i2c_adapter *i2c_adapter = NULL; - struct i2c_client *client; - struct i2c_board_info info; + struct i2c_adapter *i2c_adapter; + struct m88ds3103_platform_data m88ds3103_pdata = {}; struct ts2020_config ts2020_config = {}; - memset(&info, 0, sizeof(struct i2c_board_info)); /* attach demod */ - adap->fe[0] = dvb_attach(m88ds3103_attach, - &dvbsky_s960_m88ds3103_config, - &d->i2c_adap, - &i2c_adapter); - if (!adap->fe[0]) { - dev_err(&d->udev->dev, "dvbsky_s960_attach fail.\n"); - ret = -ENODEV; - goto fail_attach; - } + m88ds3103_pdata.clk = 27000000; + m88ds3103_pdata.i2c_wr_max = 33; + m88ds3103_pdata.clk_out = 0; + m88ds3103_pdata.ts_mode = M88DS3103_TS_CI; + m88ds3103_pdata.ts_clk = 16000; + m88ds3103_pdata.ts_clk_pol = 0; + m88ds3103_pdata.agc = 0x99; + m88ds3103_pdata.lnb_hv_pol = 1, + m88ds3103_pdata.lnb_en_pol = 1, + + state->i2c_client_demod = dvb_module_probe("m88ds3103", NULL, + &d->i2c_adap, + 0x68, &m88ds3103_pdata); + if (!state->i2c_client_demod) + return -ENODEV; + + adap->fe[0] = m88ds3103_pdata.get_dvb_frontend(state->i2c_client_demod); + i2c_adapter = m88ds3103_pdata.get_i2c_adapter(state->i2c_client_demod); /* attach tuner */ ts2020_config.fe = adap->fe[0]; ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm; - strlcpy(info.type, "ts2020", I2C_NAME_SIZE); - info.addr = 0x60; - info.platform_data = &ts2020_config; - request_module("ts2020"); - client = i2c_new_device(i2c_adapter, &info); - if (client == NULL || client->dev.driver == NULL) { - dvb_frontend_detach(adap->fe[0]); - ret = -ENODEV; - goto fail_attach; - } - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - dvb_frontend_detach(adap->fe[0]); - ret = -ENODEV; - goto fail_attach; + state->i2c_client_tuner = dvb_module_probe("ts2020", NULL, + i2c_adapter, + 0x60, &ts2020_config); + if (!state->i2c_client_tuner) { + dvb_module_release(state->i2c_client_demod); + return -ENODEV; } /* delegate signal strength measurement to tuner */ @@ -359,10 +340,7 @@ static int dvbsky_s960_attach(struct dvb_usb_adapter *adap) state->fe_set_voltage = adap->fe[0]->ops.set_voltage; adap->fe[0]->ops.set_voltage = dvbsky_usb_set_voltage; - state->i2c_client_tuner = client; - -fail_attach: - return ret; + return 0; } static int dvbsky_usb_ci_set_voltage(struct dvb_frontend *fe, @@ -412,80 +390,60 @@ err: return ret; } -static const struct m88ds3103_config dvbsky_s960c_m88ds3103_config = { - .i2c_addr = 0x68, - .clock = 27000000, - .i2c_wr_max = 33, - .clock_out = 0, - .ts_mode = M88DS3103_TS_CI, - .ts_clk = 10000, - .ts_clk_pol = 1, - .agc = 0x99, - .lnb_hv_pol = 0, - .lnb_en_pol = 1, -}; - static int dvbsky_s960c_attach(struct dvb_usb_adapter *adap) { struct dvbsky_state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); - int ret = 0; - /* demod I2C adapter */ - struct i2c_adapter *i2c_adapter = NULL; - struct i2c_client *client_tuner, *client_ci; - struct i2c_board_info info; - struct sp2_config sp2_config; + struct i2c_adapter *i2c_adapter; + struct m88ds3103_platform_data m88ds3103_pdata = {}; struct ts2020_config ts2020_config = {}; - memset(&info, 0, sizeof(struct i2c_board_info)); + struct sp2_config sp2_config = {}; /* attach demod */ - adap->fe[0] = dvb_attach(m88ds3103_attach, - &dvbsky_s960c_m88ds3103_config, - &d->i2c_adap, - &i2c_adapter); - if (!adap->fe[0]) { - dev_err(&d->udev->dev, "dvbsky_s960ci_attach fail.\n"); - ret = -ENODEV; - goto fail_attach; - } + m88ds3103_pdata.clk = 27000000, + m88ds3103_pdata.i2c_wr_max = 33, + m88ds3103_pdata.clk_out = 0, + m88ds3103_pdata.ts_mode = M88DS3103_TS_CI, + m88ds3103_pdata.ts_clk = 10000, + m88ds3103_pdata.ts_clk_pol = 1, + m88ds3103_pdata.agc = 0x99, + m88ds3103_pdata.lnb_hv_pol = 0, + m88ds3103_pdata.lnb_en_pol = 1, + + state->i2c_client_demod = dvb_module_probe("m88ds3103", NULL, + &d->i2c_adap, + 0x68, &m88ds3103_pdata); + if (!state->i2c_client_demod) + return -ENODEV; + + adap->fe[0] = m88ds3103_pdata.get_dvb_frontend(state->i2c_client_demod); + i2c_adapter = m88ds3103_pdata.get_i2c_adapter(state->i2c_client_demod); /* attach tuner */ ts2020_config.fe = adap->fe[0]; ts2020_config.get_agc_pwm = m88ds3103_get_agc_pwm; - strlcpy(info.type, "ts2020", I2C_NAME_SIZE); - info.addr = 0x60; - info.platform_data = &ts2020_config; - request_module("ts2020"); - client_tuner = i2c_new_device(i2c_adapter, &info); - if (client_tuner == NULL || client_tuner->dev.driver == NULL) { - ret = -ENODEV; - goto fail_tuner_device; - } - if (!try_module_get(client_tuner->dev.driver->owner)) { - ret = -ENODEV; - goto fail_tuner_module; + state->i2c_client_tuner = dvb_module_probe("ts2020", NULL, + i2c_adapter, + 0x60, &ts2020_config); + if (!state->i2c_client_tuner) { + dvb_module_release(state->i2c_client_demod); + return -ENODEV; } /* attach ci controller */ - memset(&sp2_config, 0, sizeof(sp2_config)); sp2_config.dvb_adap = &adap->dvb_adap; sp2_config.priv = d; sp2_config.ci_control = dvbsky_ci_ctrl; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "sp2", I2C_NAME_SIZE); - info.addr = 0x40; - info.platform_data = &sp2_config; - request_module("sp2"); - client_ci = i2c_new_device(&d->i2c_adap, &info); - if (client_ci == NULL || client_ci->dev.driver == NULL) { - ret = -ENODEV; - goto fail_ci_device; - } - if (!try_module_get(client_ci->dev.driver->owner)) { - ret = -ENODEV; - goto fail_ci_module; + state->i2c_client_ci = dvb_module_probe("sp2", NULL, + &d->i2c_adap, + 0x40, &sp2_config); + + if (!state->i2c_client_ci) { + dvb_module_release(state->i2c_client_tuner); + dvb_module_release(state->i2c_client_demod); + return -ENODEV; } /* delegate signal strength measurement to tuner */ @@ -500,165 +458,92 @@ static int dvbsky_s960c_attach(struct dvb_usb_adapter *adap) state->fe_set_voltage = adap->fe[0]->ops.set_voltage; adap->fe[0]->ops.set_voltage = dvbsky_usb_ci_set_voltage; - state->i2c_client_tuner = client_tuner; - state->i2c_client_ci = client_ci; - return ret; -fail_ci_module: - i2c_unregister_device(client_ci); -fail_ci_device: - module_put(client_tuner->dev.driver->owner); -fail_tuner_module: - i2c_unregister_device(client_tuner); -fail_tuner_device: - dvb_frontend_detach(adap->fe[0]); -fail_attach: - return ret; + return 0; } static int dvbsky_t680c_attach(struct dvb_usb_adapter *adap) { struct dvbsky_state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); - int ret = 0; struct i2c_adapter *i2c_adapter; - struct i2c_client *client_demod, *client_tuner, *client_ci; - struct i2c_board_info info; - struct si2168_config si2168_config; - struct si2157_config si2157_config; - struct sp2_config sp2_config; + struct si2168_config si2168_config = {}; + struct si2157_config si2157_config = {}; + struct sp2_config sp2_config = {}; /* attach demod */ - memset(&si2168_config, 0, sizeof(si2168_config)); si2168_config.i2c_adapter = &i2c_adapter; si2168_config.fe = &adap->fe[0]; si2168_config.ts_mode = SI2168_TS_PARALLEL; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "si2168", I2C_NAME_SIZE); - info.addr = 0x64; - info.platform_data = &si2168_config; - - request_module(info.type); - client_demod = i2c_new_device(&d->i2c_adap, &info); - if (client_demod == NULL || - client_demod->dev.driver == NULL) - goto fail_demod_device; - if (!try_module_get(client_demod->dev.driver->owner)) - goto fail_demod_module; + + state->i2c_client_demod = dvb_module_probe("si2168", NULL, + &d->i2c_adap, + 0x64, &si2168_config); + if (!state->i2c_client_demod) + return -ENODEV; /* attach tuner */ - memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = adap->fe[0]; si2157_config.if_port = 1; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "si2157", I2C_NAME_SIZE); - info.addr = 0x60; - info.platform_data = &si2157_config; - - request_module(info.type); - client_tuner = i2c_new_device(i2c_adapter, &info); - if (client_tuner == NULL || - client_tuner->dev.driver == NULL) - goto fail_tuner_device; - if (!try_module_get(client_tuner->dev.driver->owner)) - goto fail_tuner_module; + + state->i2c_client_tuner = dvb_module_probe("si2157", NULL, + i2c_adapter, + 0x60, &si2157_config); + if (!state->i2c_client_tuner) { + dvb_module_release(state->i2c_client_demod); + return -ENODEV; + } /* attach ci controller */ - memset(&sp2_config, 0, sizeof(sp2_config)); sp2_config.dvb_adap = &adap->dvb_adap; sp2_config.priv = d; sp2_config.ci_control = dvbsky_ci_ctrl; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "sp2", I2C_NAME_SIZE); - info.addr = 0x40; - info.platform_data = &sp2_config; - request_module(info.type); - client_ci = i2c_new_device(&d->i2c_adap, &info); + state->i2c_client_ci = dvb_module_probe("sp2", NULL, + &d->i2c_adap, + 0x40, &sp2_config); - if (client_ci == NULL || client_ci->dev.driver == NULL) - goto fail_ci_device; - - if (!try_module_get(client_ci->dev.driver->owner)) - goto fail_ci_module; + if (!state->i2c_client_ci) { + dvb_module_release(state->i2c_client_tuner); + dvb_module_release(state->i2c_client_demod); + return -ENODEV; + } - state->i2c_client_demod = client_demod; - state->i2c_client_tuner = client_tuner; - state->i2c_client_ci = client_ci; - return ret; -fail_ci_module: - i2c_unregister_device(client_ci); -fail_ci_device: - module_put(client_tuner->dev.driver->owner); -fail_tuner_module: - i2c_unregister_device(client_tuner); -fail_tuner_device: - module_put(client_demod->dev.driver->owner); -fail_demod_module: - i2c_unregister_device(client_demod); -fail_demod_device: - ret = -ENODEV; - return ret; + return 0; } static int dvbsky_t330_attach(struct dvb_usb_adapter *adap) { struct dvbsky_state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); - int ret = 0; struct i2c_adapter *i2c_adapter; - struct i2c_client *client_demod, *client_tuner; - struct i2c_board_info info; - struct si2168_config si2168_config; - struct si2157_config si2157_config; + struct si2168_config si2168_config = {}; + struct si2157_config si2157_config = {}; /* attach demod */ - memset(&si2168_config, 0, sizeof(si2168_config)); si2168_config.i2c_adapter = &i2c_adapter; si2168_config.fe = &adap->fe[0]; si2168_config.ts_mode = SI2168_TS_PARALLEL; si2168_config.ts_clock_gapped = true; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "si2168", I2C_NAME_SIZE); - info.addr = 0x64; - info.platform_data = &si2168_config; - - request_module(info.type); - client_demod = i2c_new_device(&d->i2c_adap, &info); - if (client_demod == NULL || - client_demod->dev.driver == NULL) - goto fail_demod_device; - if (!try_module_get(client_demod->dev.driver->owner)) - goto fail_demod_module; + + state->i2c_client_demod = dvb_module_probe("si2168", NULL, + &d->i2c_adap, + 0x64, &si2168_config); + if (!state->i2c_client_demod) + return -ENODEV; /* attach tuner */ - memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = adap->fe[0]; si2157_config.if_port = 1; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "si2157", I2C_NAME_SIZE); - info.addr = 0x60; - info.platform_data = &si2157_config; - - request_module(info.type); - client_tuner = i2c_new_device(i2c_adapter, &info); - if (client_tuner == NULL || - client_tuner->dev.driver == NULL) - goto fail_tuner_device; - if (!try_module_get(client_tuner->dev.driver->owner)) - goto fail_tuner_module; - - state->i2c_client_demod = client_demod; - state->i2c_client_tuner = client_tuner; - return ret; -fail_tuner_module: - i2c_unregister_device(client_tuner); -fail_tuner_device: - module_put(client_demod->dev.driver->owner); -fail_demod_module: - i2c_unregister_device(client_demod); -fail_demod_device: - ret = -ENODEV; - return ret; + + state->i2c_client_tuner = dvb_module_probe("si2157", NULL, + i2c_adapter, + 0x60, &si2157_config); + if (!state->i2c_client_tuner) { + dvb_module_release(state->i2c_client_demod); + return -ENODEV; + } + + return 0; } static int dvbsky_mygica_t230c_attach(struct dvb_usb_adapter *adap) @@ -666,57 +551,34 @@ static int dvbsky_mygica_t230c_attach(struct dvb_usb_adapter *adap) struct dvbsky_state *state = adap_to_priv(adap); struct dvb_usb_device *d = adap_to_d(adap); struct i2c_adapter *i2c_adapter; - struct i2c_client *client_demod, *client_tuner; - struct i2c_board_info info; - struct si2168_config si2168_config; - struct si2157_config si2157_config; + struct si2168_config si2168_config = {}; + struct si2157_config si2157_config = {}; /* attach demod */ - memset(&si2168_config, 0, sizeof(si2168_config)); si2168_config.i2c_adapter = &i2c_adapter; si2168_config.fe = &adap->fe[0]; si2168_config.ts_mode = SI2168_TS_PARALLEL; si2168_config.ts_clock_inv = 1; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "si2168", sizeof(info.type)); - info.addr = 0x64; - info.platform_data = &si2168_config; - - request_module("si2168"); - client_demod = i2c_new_device(&d->i2c_adap, &info); - if (!client_demod || !client_demod->dev.driver) - goto fail_demod_device; - if (!try_module_get(client_demod->dev.driver->owner)) - goto fail_demod_module; + + state->i2c_client_demod = dvb_module_probe("si2168", NULL, + &d->i2c_adap, + 0x64, &si2168_config); + if (!state->i2c_client_demod) + return -ENODEV; /* attach tuner */ - memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = adap->fe[0]; si2157_config.if_port = 0; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "si2141", sizeof(info.type)); - info.addr = 0x60; - info.platform_data = &si2157_config; - - request_module("si2157"); - client_tuner = i2c_new_device(i2c_adapter, &info); - if (!client_tuner || !client_tuner->dev.driver) - goto fail_tuner_device; - if (!try_module_get(client_tuner->dev.driver->owner)) - goto fail_tuner_module; - - state->i2c_client_demod = client_demod; - state->i2c_client_tuner = client_tuner; - return 0; -fail_tuner_module: - i2c_unregister_device(client_tuner); -fail_tuner_device: - module_put(client_demod->dev.driver->owner); -fail_demod_module: - i2c_unregister_device(client_demod); -fail_demod_device: - return -ENODEV; + state->i2c_client_tuner = dvb_module_probe("si2157", "si2141", + i2c_adapter, + 0x60, &si2157_config); + if (!state->i2c_client_tuner) { + dvb_module_release(state->i2c_client_demod); + return -ENODEV; + } + + return 0; } @@ -744,8 +606,6 @@ static int dvbsky_init(struct dvb_usb_device *d) if (ret) return ret; */ - mutex_init(&state->stream_mutex); - state->last_lock = 0; return 0; @@ -754,26 +614,13 @@ static int dvbsky_init(struct dvb_usb_device *d) static void dvbsky_exit(struct dvb_usb_device *d) { struct dvbsky_state *state = d_to_priv(d); - struct i2c_client *client; + struct dvb_usb_adapter *adap = &d->adapter[0]; - client = state->i2c_client_tuner; - /* remove I2C tuner */ - if (client) { - module_put(client->dev.driver->owner); - i2c_unregister_device(client); - } - client = state->i2c_client_demod; - /* remove I2C demod */ - if (client) { - module_put(client->dev.driver->owner); - i2c_unregister_device(client); - } - client = state->i2c_client_ci; - /* remove I2C ci */ - if (client) { - module_put(client->dev.driver->owner); - i2c_unregister_device(client); - } + dvb_module_release(state->i2c_client_tuner); + dvb_module_release(state->i2c_client_demod); + dvb_module_release(state->i2c_client_ci); + + adap->fe[0] = NULL; } /* DVB USB Driver stuff */ diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c index b1b09c547861..4817dfd3e659 100644 --- a/drivers/media/usb/dvb-usb-v2/gl861.c +++ b/drivers/media/usb/dvb-usb-v2/gl861.c @@ -20,6 +20,8 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, u16 value = addr << (8 + 1); int wo = (rbuf == NULL || rlen == 0); /* write-only */ u8 req, type; + u8 *buf; + int ret; if (wo) { req = GL861_REQ_I2C_WRITE; @@ -42,11 +44,23 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, KBUILD_MODNAME, wlen); return -EINVAL; } + buf = NULL; + if (rlen > 0) { + buf = kmalloc(rlen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + } + usleep_range(1000, 2000); /* avoid I2C errors */ + + ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type, + value, index, buf, rlen, 2000); + if (rlen > 0) { + if (ret > 0) + memcpy(rbuf, buf, rlen); + kfree(buf); + } - msleep(1); /* avoid I2C errors */ - - return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type, - value, index, rbuf, rlen, 2000); + return ret; } /* I2C */ diff --git a/drivers/media/usb/dvb-usb-v2/usb_urb.c b/drivers/media/usb/dvb-usb-v2/usb_urb.c index dce2b97efce4..b0499f95ec45 100644 --- a/drivers/media/usb/dvb-usb-v2/usb_urb.c +++ b/drivers/media/usb/dvb-usb-v2/usb_urb.c @@ -155,8 +155,7 @@ static int usb_urb_alloc_bulk_urbs(struct usb_data_stream *stream) stream->props.u.bulk.buffersize, usb_urb_complete, stream); - stream->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; - stream->urb_list[i]->transfer_dma = stream->dma_addr[i]; + stream->urb_list[i]->transfer_flags = URB_FREE_BUFFER; stream->urbs_initialized++; } return 0; @@ -187,13 +186,12 @@ static int usb_urb_alloc_isoc_urbs(struct usb_data_stream *stream) urb->complete = usb_urb_complete; urb->pipe = usb_rcvisocpipe(stream->udev, stream->props.endpoint); - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + urb->transfer_flags = URB_ISO_ASAP | URB_FREE_BUFFER; urb->interval = stream->props.u.isoc.interval; urb->number_of_packets = stream->props.u.isoc.framesperurb; urb->transfer_buffer_length = stream->props.u.isoc.framesize * stream->props.u.isoc.framesperurb; urb->transfer_buffer = stream->buf_list[i]; - urb->transfer_dma = stream->dma_addr[i]; for (j = 0; j < stream->props.u.isoc.framesperurb; j++) { urb->iso_frame_desc[j].offset = frame_offset; @@ -212,11 +210,7 @@ static int usb_free_stream_buffers(struct usb_data_stream *stream) if (stream->state & USB_STATE_URB_BUF) { while (stream->buf_num) { stream->buf_num--; - dev_dbg(&stream->udev->dev, "%s: free buf=%d\n", - __func__, stream->buf_num); - usb_free_coherent(stream->udev, stream->buf_size, - stream->buf_list[stream->buf_num], - stream->dma_addr[stream->buf_num]); + stream->buf_list[stream->buf_num] = NULL; } } @@ -236,9 +230,7 @@ static int usb_alloc_stream_buffers(struct usb_data_stream *stream, int num, __func__, num * size); for (stream->buf_num = 0; stream->buf_num < num; stream->buf_num++) { - stream->buf_list[stream->buf_num] = usb_alloc_coherent( - stream->udev, size, GFP_ATOMIC, - &stream->dma_addr[stream->buf_num]); + stream->buf_list[stream->buf_num] = kzalloc(size, GFP_ATOMIC); if (!stream->buf_list[stream->buf_num]) { dev_dbg(&stream->udev->dev, "%s: alloc buf=%d failed\n", __func__, stream->buf_num); @@ -250,7 +242,6 @@ static int usb_alloc_stream_buffers(struct usb_data_stream *stream, int num, __func__, stream->buf_num, stream->buf_list[stream->buf_num], (long long)stream->dma_addr[stream->buf_num]); - memset(stream->buf_list[stream->buf_num], 0, size); stream->state |= USB_STATE_URB_BUF; } diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index 387a074ea6ec..b70d289dc738 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -540,12 +540,10 @@ static struct cx22702_config cxusb_cx22702_config = { }; static struct lgdt330x_config cxusb_lgdt3303_config = { - .demod_address = 0x0e, .demod_chip = LGDT3303, }; static struct lgdt330x_config cxusb_aver_lgdt3303_config = { - .demod_address = 0x0e, .demod_chip = LGDT3303, .clock_polarity_flip = 2, }; @@ -763,6 +761,7 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) adap->fe_adap[0].fe = dvb_attach(lgdt330x_attach, &cxusb_lgdt3303_config, + 0x0e, &adap->dev->i2c_adap); if ((adap->fe_adap[0].fe) != NULL) return 0; @@ -772,8 +771,10 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) static int cxusb_aver_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) { - adap->fe_adap[0].fe = dvb_attach(lgdt330x_attach, &cxusb_aver_lgdt3303_config, - &adap->dev->i2c_adap); + adap->fe_adap[0].fe = dvb_attach(lgdt330x_attach, + &cxusb_aver_lgdt3303_config, + 0x0e, + &adap->dev->i2c_adap); if (adap->fe_adap[0].fe != NULL) return 0; diff --git a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c index 3a66e732e0d8..8056053c9ab0 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c @@ -132,10 +132,14 @@ static void dvb_usb_media_device_unregister(struct dvb_usb_adapter *adap) if (!adap->dvb_adap.mdev) return; + mutex_lock(&adap->dvb_adap.mdev_lock); + media_device_unregister(adap->dvb_adap.mdev); media_device_cleanup(adap->dvb_adap.mdev); kfree(adap->dvb_adap.mdev); adap->dvb_adap.mdev = NULL; + + mutex_unlock(&adap->dvb_adap.mdev_lock); #endif } diff --git a/drivers/media/usb/dvb-usb/gp8psk.c b/drivers/media/usb/dvb-usb/gp8psk.c index 37f062225ed2..334b9fb98112 100644 --- a/drivers/media/usb/dvb-usb/gp8psk.c +++ b/drivers/media/usb/dvb-usb/gp8psk.c @@ -148,7 +148,7 @@ static int gp8psk_load_bcm4500fw(struct dvb_usb_device *d) info("downloading bcm4500 firmware from file '%s'",bcm4500_firmware); ptr = fw->data; - buf = kmalloc(64, GFP_KERNEL | GFP_DMA); + buf = kmalloc(64, GFP_KERNEL); if (!buf) { ret = -ENOMEM; goto out_rel_fw; diff --git a/drivers/media/usb/dvb-usb/usb-urb.c b/drivers/media/usb/dvb-usb/usb-urb.c index 2804d2d0e83a..5e05963f4220 100644 --- a/drivers/media/usb/dvb-usb/usb-urb.c +++ b/drivers/media/usb/dvb-usb/usb-urb.c @@ -118,7 +118,7 @@ static int usb_allocate_stream_buffers(struct usb_data_stream *stream, int num, for (stream->buf_num = 0; stream->buf_num < num; stream->buf_num++) { deb_mem("allocating buffer %d\n",stream->buf_num); if (( stream->buf_list[stream->buf_num] = - usb_alloc_coherent(stream->udev, size, GFP_ATOMIC, + usb_alloc_coherent(stream->udev, size, GFP_KERNEL, &stream->dma_addr[stream->buf_num]) ) == NULL) { deb_mem("not enough memory for urb-buffer allocation.\n"); usb_free_stream_buffers(stream); @@ -145,7 +145,7 @@ static int usb_bulk_urb_init(struct usb_data_stream *stream) /* allocate the URBs */ for (i = 0; i < stream->props.count; i++) { - stream->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); + stream->urb_list[i] = usb_alloc_urb(0, GFP_KERNEL); if (!stream->urb_list[i]) { deb_mem("not enough memory for urb_alloc_urb!.\n"); for (j = 0; j < i; j++) @@ -178,7 +178,7 @@ static int usb_isoc_urb_init(struct usb_data_stream *stream) struct urb *urb; int frame_offset = 0; - stream->urb_list[i] = usb_alloc_urb(stream->props.u.isoc.framesperurb, GFP_ATOMIC); + stream->urb_list[i] = usb_alloc_urb(stream->props.u.isoc.framesperurb, GFP_KERNEL); if (!stream->urb_list[i]) { deb_mem("not enough memory for urb_alloc_urb!\n"); for (j = 0; j < i; j++) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 7c3203d7044b..6c8438311d3b 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -87,6 +87,21 @@ static const struct em28xx_reg_seq default_digital[] = { { -1, -1, -1, -1}, }; +/* Board :Zolid Hybrid Tv Stick */ +static struct em28xx_reg_seq zolid_tuner[] = { + {EM2820_R08_GPIO_CTRL, 0xfd, 0xff, 100}, + {EM2820_R08_GPIO_CTRL, 0xfe, 0xff, 100}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq zolid_digital[] = { + {EM2820_R08_GPIO_CTRL, 0x6a, 0xff, 100}, + {EM2820_R08_GPIO_CTRL, 0x7a, 0xff, 100}, + {EM2880_R04_GPO, 0x04, 0xff, 100}, + {EM2880_R04_GPO, 0x0c, 0xff, 100}, + { -1, -1, -1, -1}, +}; + /* Board Hauppauge WinTV HVR 900 analog */ static const struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = { {EM2820_R08_GPIO_CTRL, 0x2d, ~EM_GPIO_4, 10}, @@ -666,6 +681,16 @@ const struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_ABSENT, .is_webcam = 1, /* To enable sensor probe */ }, + [EM2882_BOARD_ZOLID_HYBRID_TV_STICK] = { + .name = ":ZOLID HYBRID TV STICK", + .tuner_type = TUNER_XC2028, + .tuner_gpio = zolid_tuner, + .decoder = EM28XX_TVP5150, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = zolid_digital, + }, [EM2750_BOARD_DLCW_130] = { /* Beijing Huaqi Information Digital Technology Co., Ltd */ .name = "Huaqi DLCW-130", @@ -2493,7 +2518,7 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2881), .driver_info = EM2820_BOARD_UNKNOWN }, - { USB_DEVICE(0xeb1a, 0x2883), + { USB_DEVICE(0xeb1a, 0x2883), /* used by :Zolid Hybrid Tv Stick */ .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2868), .driver_info = EM2820_BOARD_UNKNOWN }, @@ -2663,6 +2688,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM28178_BOARD_PCTV_292E }, { USB_DEVICE(0x2040, 0x8268), /* Hauppauge Retail WinTV-soloHD Bulk */ .driver_info = EM28178_BOARD_PCTV_292E }, + { USB_DEVICE(0x2040, 0x8268), /* Hauppauge WinTV-soloHD alt. PID */ + .driver_info = EM28178_BOARD_PCTV_292E }, { USB_DEVICE(0x0413, 0x6f07), .driver_info = EM2861_BOARD_LEADTEK_VC100 }, { USB_DEVICE(0xeb1a, 0x8179), @@ -2688,6 +2715,7 @@ static const struct em28xx_hash_table em28xx_eeprom_hash[] = { {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028}, {0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT}, {0x4e913442, EM2882_BOARD_DIKOM_DK300, TUNER_XC2028}, + {0x85dd871e, EM2882_BOARD_ZOLID_HYBRID_TV_STICK, TUNER_XC2028}, }; /* I2C devicelist hash table for devices with generic USB IDs */ @@ -2699,6 +2727,7 @@ static const struct em28xx_hash_table em28xx_i2c_hash[] = { {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC}, {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF}, {0x6b800080, EM2874_BOARD_LEADERSHIP_ISDBT, TUNER_ABSENT}, + {0x27e10080, EM2882_BOARD_ZOLID_HYBRID_TV_STICK, TUNER_XC2028}, }; /* NOTE: introduce a separate hash table for devices with 16 bit eeproms */ @@ -3182,11 +3211,10 @@ void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) case EM2880_BOARD_EMPIRE_DUAL_TV: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: case EM2882_BOARD_TERRATEC_HYBRID_XS: - ctl->demod = XC3028_FE_ZARLINK456; - break; case EM2880_BOARD_TERRATEC_HYBRID_XS: case EM2880_BOARD_TERRATEC_HYBRID_XS_FR: case EM2881_BOARD_PINNACLE_HYBRID_PRO: + case EM2882_BOARD_ZOLID_HYBRID_TV_STICK: ctl->demod = XC3028_FE_ZARLINK456; break; case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: @@ -3674,7 +3702,7 @@ static int em28xx_usb_probe(struct usb_interface *intf, /* Don't register audio interfaces */ if (intf->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { - dev_err(&intf->dev, + dev_info(&intf->dev, "audio device (%04x:%04x): interface %i, class %i\n", le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct), @@ -3736,7 +3764,7 @@ static int em28xx_usb_probe(struct usb_interface *intf, speed = "unknown"; } - dev_err(&intf->dev, + dev_info(&intf->dev, "New device %s %s @ %s Mbps (%04x:%04x, interface %d, class %d)\n", udev->manufacturer ? udev->manufacturer : "", udev->product ? udev->product : "", @@ -3771,7 +3799,7 @@ static int em28xx_usb_probe(struct usb_interface *intf, dev->dev_next = NULL; if (has_vendor_audio) { - dev_err(&intf->dev, + dev_info(&intf->dev, "Audio interface %i found (Vendor Class)\n", ifnum); dev->usb_audio_type = EM28XX_USB_AUDIO_VENDOR; } @@ -3790,12 +3818,12 @@ static int em28xx_usb_probe(struct usb_interface *intf, } if (has_video) - dev_err(&intf->dev, "Video interface %i found:%s%s\n", + dev_info(&intf->dev, "Video interface %i found:%s%s\n", ifnum, dev->analog_ep_bulk ? " bulk" : "", dev->analog_ep_isoc ? " isoc" : ""); if (has_dvb) - dev_err(&intf->dev, "DVB interface %i found:%s%s\n", + dev_info(&intf->dev, "DVB interface %i found:%s%s\n", ifnum, dev->dvb_ep_bulk ? " bulk" : "", dev->dvb_ep_isoc ? " isoc" : ""); @@ -3837,13 +3865,13 @@ static int em28xx_usb_probe(struct usb_interface *intf, if (has_video) { if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk)) dev->analog_xfer_bulk = 1; - dev_err(&intf->dev, "analog set to %s mode.\n", + dev_info(&intf->dev, "analog set to %s mode.\n", dev->analog_xfer_bulk ? "bulk" : "isoc"); } if (has_dvb) { if (!dev->dvb_ep_isoc || (try_bulk && dev->dvb_ep_bulk)) dev->dvb_xfer_bulk = 1; - dev_err(&intf->dev, "dvb set to %s mode.\n", + dev_info(&intf->dev, "dvb set to %s mode.\n", dev->dvb_xfer_bulk ? "bulk" : "isoc"); } @@ -3957,7 +3985,7 @@ static void em28xx_usb_disconnect(struct usb_interface *intf) dev->disconnected = 1; - dev_err(&dev->intf->dev, "Disconnecting %s\n", dev->name); + dev_info(&dev->intf->dev, "Disconnecting %s\n", dev->name); flush_request_modules(dev); diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index f28995383090..f70845e7d8c6 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1171,8 +1171,9 @@ int em28xx_resume_extension(struct em28xx *dev) dev_info(&dev->intf->dev, "Resuming extensions\n"); mutex_lock(&em28xx_devlist_mutex); list_for_each_entry(ops, &em28xx_extension_devlist, next) { - if (ops->resume) - ops->resume(dev); + if (!ops->resume) + continue; + ops->resume(dev); if (dev->dev_next) ops->resume(dev->dev_next); } diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 3f493e0b0716..b778d8a1983e 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -199,6 +199,7 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) int rc; struct em28xx_i2c_bus *i2c_bus = dvb->adapter.priv; struct em28xx *dev = i2c_bus->dev; + struct usb_device *udev = interface_to_usbdev(dev->intf); int dvb_max_packet_size, packet_multiplier, dvb_alt; if (dev->dvb_xfer_bulk) { @@ -217,6 +218,7 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) dvb_alt = dev->dvb_alt_isoc; } + usb_set_interface(udev, dev->ifnum, dvb_alt); rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); if (rc < 0) return rc; @@ -298,7 +300,6 @@ static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) /* ------------------------------------------------------------------ */ static struct lgdt330x_config em2880_lgdt3303_dev = { - .demod_address = 0x0e, .demod_chip = LGDT3303, }; @@ -1392,7 +1393,7 @@ static int em28174_dvb_init_hauppauge_wintv_dualhd_01595(struct em28xx *dev) dvb->i2c_client_tuner = dvb_module_probe("si2157", NULL, adapter, - 0x60, &si2157_config); + addr, &si2157_config); if (!dvb->i2c_client_tuner) { dvb_module_release(dvb->i2c_client_demod); return -ENODEV; @@ -1470,6 +1471,7 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: dvb->fe[0] = dvb_attach(lgdt330x_attach, &em2880_lgdt3303_dev, + 0x0e, &dev->i2c_adap[dev->def_i2c_bus]); if (em28xx_attach_xc3028(0x61, dev) < 0) { result = -EINVAL; @@ -1488,6 +1490,7 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: case EM2882_BOARD_TERRATEC_HYBRID_XS: case EM2880_BOARD_EMPIRE_DUAL_TV: + case EM2882_BOARD_ZOLID_HYBRID_TV_STICK: dvb->fe[0] = dvb_attach(zl10353_attach, &em28xx_zl10353_xc3028_no_i2c_gate, &dev->i2c_adap[dev->def_i2c_bus]); @@ -1550,6 +1553,7 @@ static int em28xx_dvb_init(struct em28xx *dev) case EM2882_BOARD_KWORLD_ATSC_315U: dvb->fe[0] = dvb_attach(lgdt330x_attach, &em2880_lgdt3303_dev, + 0x0e, &dev->i2c_adap[dev->def_i2c_bus]); if (dvb->fe[0]) { if (!dvb_attach(simple_tuner_attach, dvb->fe[0], diff --git a/drivers/media/usb/em28xx/em28xx-v4l.h b/drivers/media/usb/em28xx/em28xx-v4l.h index 1788dbf9024a..6216cdd182f3 100644 --- a/drivers/media/usb/em28xx/em28xx-v4l.h +++ b/drivers/media/usb/em28xx/em28xx-v4l.h @@ -3,7 +3,7 @@ * em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB * video capture devices * - * Copyright (C) 2013-2014 Mauro Carvalho Chehab <m.chehab@samsung.com> + * Copyright (C) 2013-2014 Mauro Carvalho Chehab <mchehab+samsung@kernel.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index b0378e77ddff..953caac025f2 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -148,6 +148,7 @@ #define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB 99 #define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 100 #define EM2884_BOARD_TERRATEC_H6 101 +#define EM2882_BOARD_ZOLID_HYBRID_TV_STICK 102 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 diff --git a/drivers/media/usb/go7007/go7007-fw.c b/drivers/media/usb/go7007/go7007-fw.c index 60bf5f0644d1..87b4fc48ef09 100644 --- a/drivers/media/usb/go7007/go7007-fw.c +++ b/drivers/media/usb/go7007/go7007-fw.c @@ -1514,7 +1514,10 @@ static int do_special(struct go7007 *go, u16 type, __le16 *code, int space, case V4L2_PIX_FMT_MPEG4: return gen_mpeg4hdr_to_package(go, code, space, framelen); + default: + break; } + break; case SPECIAL_BRC_CTRL: return brctrl_to_package(go, code, space, framelen); case SPECIAL_CONFIG: diff --git a/drivers/media/usb/go7007/go7007-v4l2.c b/drivers/media/usb/go7007/go7007-v4l2.c index 98cd57eaf36a..c55c82f70e54 100644 --- a/drivers/media/usb/go7007/go7007-v4l2.c +++ b/drivers/media/usb/go7007/go7007-v4l2.c @@ -634,7 +634,7 @@ static int vidioc_enum_input(struct file *file, void *priv, if (inp->index >= go->board_info->num_inputs) return -EINVAL; - strncpy(inp->name, go->board_info->inputs[inp->index].name, + strlcpy(inp->name, go->board_info->inputs[inp->index].name, sizeof(inp->name)); /* If this board has a tuner, it will be the first input */ diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig index bc9a439745aa..d3b6665c342d 100644 --- a/drivers/media/usb/gspca/Kconfig +++ b/drivers/media/usb/gspca/Kconfig @@ -2,6 +2,7 @@ menuconfig USB_GSPCA tristate "GSPCA based webcams" depends on VIDEO_V4L2 depends on INPUT || INPUT=n + select VIDEOBUF2_VMALLOC default m ---help--- Say Y here if you want to enable selecting webcams based diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index d29773b8f696..57aa521e16b1 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -82,32 +82,6 @@ static void PDEBUG_MODE(struct gspca_dev *gspca_dev, int debug, char *txt, #define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */ #define GSPCA_MEMORY_READ 7 -#define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE) - -/* - * VMA operations. - */ -static void gspca_vm_open(struct vm_area_struct *vma) -{ - struct gspca_frame *frame = vma->vm_private_data; - - frame->vma_use_count++; - frame->v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED; -} - -static void gspca_vm_close(struct vm_area_struct *vma) -{ - struct gspca_frame *frame = vma->vm_private_data; - - if (--frame->vma_use_count <= 0) - frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_MAPPED; -} - -static const struct vm_operations_struct gspca_vm_ops = { - .open = gspca_vm_open, - .close = gspca_vm_close, -}; - /* * Input and interrupt endpoint handling functions */ @@ -356,7 +330,7 @@ static void isoc_irq(struct urb *urb) struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; gspca_dbg(gspca_dev, D_PACK, "isoc irq\n"); - if (!gspca_dev->streaming) + if (!vb2_start_streaming_called(&gspca_dev->queue)) return; fill_frame(gspca_dev, urb); } @@ -370,7 +344,7 @@ static void bulk_irq(struct urb *urb) int st; gspca_dbg(gspca_dev, D_PACK, "bulk irq\n"); - if (!gspca_dev->streaming) + if (!vb2_start_streaming_called(&gspca_dev->queue)) return; switch (urb->status) { case 0: @@ -417,25 +391,24 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, const u8 *data, int len) { - struct gspca_frame *frame; - int i, j; + struct gspca_buffer *buf; + unsigned long flags; gspca_dbg(gspca_dev, D_PACK, "add t:%d l:%d\n", packet_type, len); - if (packet_type == FIRST_PACKET) { - i = atomic_read(&gspca_dev->fr_i); + spin_lock_irqsave(&gspca_dev->qlock, flags); + buf = list_first_entry_or_null(&gspca_dev->buf_list, + typeof(*buf), list); + spin_unlock_irqrestore(&gspca_dev->qlock, flags); - /* if there are no queued buffer, discard the whole frame */ - if (i == atomic_read(&gspca_dev->fr_q)) { + if (packet_type == FIRST_PACKET) { + /* if there is no queued buffer, discard the whole frame */ + if (!buf) { gspca_dev->last_packet_type = DISCARD_PACKET; gspca_dev->sequence++; return; } - j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; - v4l2_get_timestamp(&frame->v4l2_buf.timestamp); - frame->v4l2_buf.sequence = gspca_dev->sequence++; - gspca_dev->image = frame->data; + gspca_dev->image = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); gspca_dev->image_len = 0; } else { switch (gspca_dev->last_packet_type) { @@ -453,10 +426,10 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, /* append the packet to the frame buffer */ if (len > 0) { - if (gspca_dev->image_len + len > gspca_dev->frsz) { + if (gspca_dev->image_len + len > gspca_dev->pixfmt.sizeimage) { gspca_err(gspca_dev, "frame overflow %d > %d\n", gspca_dev->image_len + len, - gspca_dev->frsz); + gspca_dev->pixfmt.sizeimage); packet_type = DISCARD_PACKET; } else { /* !! image is NULL only when last pkt is LAST or DISCARD @@ -476,93 +449,43 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, * next first packet, wake up the application and advance * in the queue */ if (packet_type == LAST_PACKET) { - i = atomic_read(&gspca_dev->fr_i); - j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; - frame->v4l2_buf.bytesused = gspca_dev->image_len; - frame->v4l2_buf.flags = (frame->v4l2_buf.flags - | V4L2_BUF_FLAG_DONE) - & ~V4L2_BUF_FLAG_QUEUED; - i = (i + 1) % GSPCA_MAX_FRAMES; - atomic_set(&gspca_dev->fr_i, i); - wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ + spin_lock_irqsave(&gspca_dev->qlock, flags); + list_del(&buf->list); + spin_unlock_irqrestore(&gspca_dev->qlock, flags); + buf->vb.vb2_buf.timestamp = ktime_get_ns(); + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, + gspca_dev->image_len); + buf->vb.sequence = gspca_dev->sequence++; + buf->vb.field = V4L2_FIELD_NONE; gspca_dbg(gspca_dev, D_FRAM, "frame complete len:%d\n", - frame->v4l2_buf.bytesused); + gspca_dev->image_len); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); gspca_dev->image = NULL; gspca_dev->image_len = 0; } } EXPORT_SYMBOL(gspca_frame_add); -static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file, - enum v4l2_memory memory, unsigned int count) -{ - struct gspca_frame *frame; - unsigned int frsz; - int i; - - frsz = gspca_dev->pixfmt.sizeimage; - gspca_dbg(gspca_dev, D_STREAM, "frame alloc frsz: %d\n", frsz); - frsz = PAGE_ALIGN(frsz); - if (count >= GSPCA_MAX_FRAMES) - count = GSPCA_MAX_FRAMES - 1; - gspca_dev->frbuf = vmalloc_32(frsz * count); - if (!gspca_dev->frbuf) { - pr_err("frame alloc failed\n"); - return -ENOMEM; - } - gspca_dev->capt_file = file; - gspca_dev->memory = memory; - gspca_dev->frsz = frsz; - gspca_dev->nframes = count; - for (i = 0; i < count; i++) { - frame = &gspca_dev->frame[i]; - frame->v4l2_buf.index = i; - frame->v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - frame->v4l2_buf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - frame->v4l2_buf.field = V4L2_FIELD_NONE; - frame->v4l2_buf.length = frsz; - frame->v4l2_buf.memory = memory; - frame->v4l2_buf.sequence = 0; - frame->data = gspca_dev->frbuf + i * frsz; - frame->v4l2_buf.m.offset = i * frsz; - } - atomic_set(&gspca_dev->fr_q, 0); - atomic_set(&gspca_dev->fr_i, 0); - gspca_dev->fr_o = 0; - return 0; -} - -static void frame_free(struct gspca_dev *gspca_dev) -{ - int i; - - gspca_dbg(gspca_dev, D_STREAM, "frame free\n"); - if (gspca_dev->frbuf != NULL) { - vfree(gspca_dev->frbuf); - gspca_dev->frbuf = NULL; - for (i = 0; i < gspca_dev->nframes; i++) - gspca_dev->frame[i].data = NULL; - } - gspca_dev->nframes = 0; - gspca_dev->frsz = 0; - gspca_dev->capt_file = NULL; - gspca_dev->memory = GSPCA_MEMORY_NO; -} - static void destroy_urbs(struct gspca_dev *gspca_dev) { struct urb *urb; unsigned int i; gspca_dbg(gspca_dev, D_STREAM, "kill transfer\n"); + + /* Killing all URBs guarantee that no URB completion + * handler is running. Therefore, there shouldn't + * be anyone trying to access gspca_dev->urb[i] + */ + for (i = 0; i < MAX_NURBS; i++) + usb_kill_urb(gspca_dev->urb[i]); + + gspca_dbg(gspca_dev, D_STREAM, "releasing urbs\n"); for (i = 0; i < MAX_NURBS; i++) { urb = gspca_dev->urb[i]; - if (urb == NULL) - break; - + if (!urb) + continue; gspca_dev->urb[i] = NULL; - usb_kill_urb(urb); usb_free_coherent(gspca_dev->dev, urb->transfer_buffer_length, urb->transfer_buffer, @@ -583,22 +506,6 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev) return ret; } -/* Note: both the queue and the usb locks should be held when calling this */ -static void gspca_stream_off(struct gspca_dev *gspca_dev) -{ - gspca_dev->streaming = 0; - gspca_dev->usb_err = 0; - if (gspca_dev->sd_desc->stopN) - gspca_dev->sd_desc->stopN(gspca_dev); - destroy_urbs(gspca_dev); - gspca_input_destroy_urb(gspca_dev); - gspca_set_alt0(gspca_dev); - gspca_input_create_urb(gspca_dev); - if (gspca_dev->sd_desc->stop0) - gspca_dev->sd_desc->stop0(gspca_dev); - gspca_dbg(gspca_dev, D_STREAM, "stream off OK\n"); -} - /* * look for an input transfer endpoint in an alternate setting. * @@ -829,6 +736,23 @@ static int create_urbs(struct gspca_dev *gspca_dev, return 0; } +/* Note: both the queue and the usb locks should be held when calling this */ +static void gspca_stream_off(struct gspca_dev *gspca_dev) +{ + gspca_dev->streaming = false; + gspca_dev->usb_err = 0; + if (gspca_dev->sd_desc->stopN) + gspca_dev->sd_desc->stopN(gspca_dev); + destroy_urbs(gspca_dev); + gspca_input_destroy_urb(gspca_dev); + gspca_set_alt0(gspca_dev); + if (gspca_dev->present) + gspca_input_create_urb(gspca_dev); + if (gspca_dev->sd_desc->stop0) + gspca_dev->sd_desc->stop0(gspca_dev); + gspca_dbg(gspca_dev, D_STREAM, "stream off OK\n"); +} + /* * start the USB transfer */ @@ -844,7 +768,6 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) gspca_dev->image = NULL; gspca_dev->image_len = 0; gspca_dev->last_packet_type = DISCARD_PACKET; - gspca_dev->sequence = 0; gspca_dev->usb_err = 0; @@ -924,8 +847,8 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) destroy_urbs(gspca_dev); goto out; } - gspca_dev->streaming = 1; v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); + gspca_dev->streaming = true; /* some bulk transfers are started by the subdriver */ if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0) @@ -1165,11 +1088,9 @@ static int vidioc_try_fmt_vid_cap(struct file *file, struct v4l2_format *fmt) { struct gspca_dev *gspca_dev = video_drvdata(file); - int ret; - ret = try_fmt_vid_cap(gspca_dev, fmt); - if (ret < 0) - return ret; + if (try_fmt_vid_cap(gspca_dev, fmt) < 0) + return -EINVAL; return 0; } @@ -1177,36 +1098,22 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { struct gspca_dev *gspca_dev = video_drvdata(file); - int ret; - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; + int mode; - ret = try_fmt_vid_cap(gspca_dev, fmt); - if (ret < 0) - goto out; + if (vb2_is_busy(&gspca_dev->queue)) + return -EBUSY; - if (gspca_dev->nframes != 0 - && fmt->fmt.pix.sizeimage > gspca_dev->frsz) { - ret = -EINVAL; - goto out; - } + mode = try_fmt_vid_cap(gspca_dev, fmt); + if (mode < 0) + return -EINVAL; - if (gspca_dev->streaming) { - ret = -EBUSY; - goto out; - } - gspca_dev->curr_mode = ret; + gspca_dev->curr_mode = mode; if (gspca_dev->sd_desc->try_fmt) /* subdriver try_fmt can modify format parameters */ gspca_dev->pixfmt = fmt->fmt.pix; else - gspca_dev->pixfmt = gspca_dev->cam.cam_mode[ret]; - - ret = 0; -out: - mutex_unlock(&gspca_dev->queue_lock); - return ret; + gspca_dev->pixfmt = gspca_dev->cam.cam_mode[mode]; + return 0; } static int vidioc_enum_framesizes(struct file *file, void *priv, @@ -1281,53 +1188,6 @@ static void gspca_release(struct v4l2_device *v4l2_device) kfree(gspca_dev); } -static int dev_open(struct file *file) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - int ret; - - gspca_dbg(gspca_dev, D_STREAM, "[%s] open\n", current->comm); - - /* protect the subdriver against rmmod */ - if (!try_module_get(gspca_dev->module)) - return -ENODEV; - - ret = v4l2_fh_open(file); - if (ret) - module_put(gspca_dev->module); - return ret; -} - -static int dev_close(struct file *file) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - - gspca_dbg(gspca_dev, D_STREAM, "[%s] close\n", current->comm); - - /* Needed for gspca_stream_off, always lock before queue_lock! */ - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) { - mutex_unlock(&gspca_dev->usb_lock); - return -ERESTARTSYS; - } - - /* if the file did the capture, free the streaming resources */ - if (gspca_dev->capt_file == file) { - if (gspca_dev->streaming) - gspca_stream_off(gspca_dev); - frame_free(gspca_dev); - } - module_put(gspca_dev->module); - mutex_unlock(&gspca_dev->queue_lock); - mutex_unlock(&gspca_dev->usb_lock); - - gspca_dbg(gspca_dev, D_STREAM, "close done\n"); - - return v4l2_fh_release(file); -} - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -1377,167 +1237,9 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { if (i > 0) return -EINVAL; - return (0); -} - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - int i, ret = 0, streaming; - - i = rb->memory; /* (avoid compilation warning) */ - switch (i) { - case GSPCA_MEMORY_READ: /* (internal call) */ - case V4L2_MEMORY_MMAP: - case V4L2_MEMORY_USERPTR: - break; - default: - return -EINVAL; - } - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - - if (gspca_dev->memory != GSPCA_MEMORY_NO - && gspca_dev->memory != GSPCA_MEMORY_READ - && gspca_dev->memory != rb->memory) { - ret = -EBUSY; - goto out; - } - - /* only one file may do the capture */ - if (gspca_dev->capt_file != NULL - && gspca_dev->capt_file != file) { - ret = -EBUSY; - goto out; - } - - /* if allocated, the buffers must not be mapped */ - for (i = 0; i < gspca_dev->nframes; i++) { - if (gspca_dev->frame[i].vma_use_count) { - ret = -EBUSY; - goto out; - } - } - - /* stop streaming */ - streaming = gspca_dev->streaming; - if (streaming) { - gspca_stream_off(gspca_dev); - - /* Don't restart the stream when switching from read - * to mmap mode */ - if (gspca_dev->memory == GSPCA_MEMORY_READ) - streaming = 0; - } - - /* free the previous allocated buffers, if any */ - if (gspca_dev->nframes != 0) - frame_free(gspca_dev); - if (rb->count == 0) /* unrequest */ - goto out; - ret = frame_alloc(gspca_dev, file, rb->memory, rb->count); - if (ret == 0) { - rb->count = gspca_dev->nframes; - if (streaming) - ret = gspca_init_transfer(gspca_dev); - } -out: - mutex_unlock(&gspca_dev->queue_lock); - gspca_dbg(gspca_dev, D_STREAM, "reqbufs st:%d c:%d\n", ret, rb->count); - return ret; -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *v4l2_buf) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - struct gspca_frame *frame; - - if (v4l2_buf->index >= gspca_dev->nframes) - return -EINVAL; - - frame = &gspca_dev->frame[v4l2_buf->index]; - memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); return 0; } -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type buf_type) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - int ret; - - if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - - /* check the capture file */ - if (gspca_dev->capt_file != file) { - ret = -EBUSY; - goto out; - } - - if (gspca_dev->nframes == 0 - || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) { - ret = -EINVAL; - goto out; - } - if (!gspca_dev->streaming) { - ret = gspca_init_transfer(gspca_dev); - if (ret < 0) - goto out; - } - PDEBUG_MODE(gspca_dev, D_STREAM, "stream on OK", - gspca_dev->pixfmt.pixelformat, - gspca_dev->pixfmt.width, gspca_dev->pixfmt.height); - ret = 0; -out: - mutex_unlock(&gspca_dev->queue_lock); - return ret; -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type buf_type) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - int i, ret; - - if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - - if (!gspca_dev->streaming) { - ret = 0; - goto out; - } - - /* check the capture file */ - if (gspca_dev->capt_file != file) { - ret = -EBUSY; - goto out; - } - - /* stop streaming */ - gspca_stream_off(gspca_dev); - /* In case another thread is waiting in dqbuf */ - wake_up_interruptible(&gspca_dev->wq); - - /* empty the transfer queues */ - for (i = 0; i < gspca_dev->nframes; i++) - gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS; - atomic_set(&gspca_dev->fr_q, 0); - atomic_set(&gspca_dev->fr_i, 0); - gspca_dev->fr_o = 0; - ret = 0; -out: - mutex_unlock(&gspca_dev->queue_lock); - return ret; -} - static int vidioc_g_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jpegcomp) { @@ -1561,449 +1263,167 @@ static int vidioc_g_parm(struct file *filp, void *priv, { struct gspca_dev *gspca_dev = video_drvdata(filp); - parm->parm.capture.readbuffers = gspca_dev->nbufread; + parm->parm.capture.readbuffers = gspca_dev->queue.min_buffers_needed; - if (gspca_dev->sd_desc->get_streamparm) { - gspca_dev->usb_err = 0; - gspca_dev->sd_desc->get_streamparm(gspca_dev, parm); - return gspca_dev->usb_err; - } - return 0; + if (!gspca_dev->sd_desc->get_streamparm) + return 0; + + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + gspca_dev->usb_err = 0; + gspca_dev->sd_desc->get_streamparm(gspca_dev, parm); + return gspca_dev->usb_err; } static int vidioc_s_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm) { struct gspca_dev *gspca_dev = video_drvdata(filp); - unsigned int n; - - n = parm->parm.capture.readbuffers; - if (n == 0 || n >= GSPCA_MAX_FRAMES) - parm->parm.capture.readbuffers = gspca_dev->nbufread; - else - gspca_dev->nbufread = n; - - if (gspca_dev->sd_desc->set_streamparm) { - gspca_dev->usb_err = 0; - gspca_dev->sd_desc->set_streamparm(gspca_dev, parm); - return gspca_dev->usb_err; - } - - return 0; -} - -static int dev_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct gspca_dev *gspca_dev = video_drvdata(file); - struct gspca_frame *frame; - struct page *page; - unsigned long addr, start, size; - int i, ret; - - start = vma->vm_start; - size = vma->vm_end - vma->vm_start; - gspca_dbg(gspca_dev, D_STREAM, "mmap start:%08x size:%d\n", - (int) start, (int)size); - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - if (gspca_dev->capt_file != file) { - ret = -EINVAL; - goto out; - } - - frame = NULL; - for (i = 0; i < gspca_dev->nframes; ++i) { - if (gspca_dev->frame[i].v4l2_buf.memory != V4L2_MEMORY_MMAP) { - gspca_dbg(gspca_dev, D_STREAM, "mmap bad memory type\n"); - break; - } - if ((gspca_dev->frame[i].v4l2_buf.m.offset >> PAGE_SHIFT) - == vma->vm_pgoff) { - frame = &gspca_dev->frame[i]; - break; - } - } - if (frame == NULL) { - gspca_dbg(gspca_dev, D_STREAM, "mmap no frame buffer found\n"); - ret = -EINVAL; - goto out; - } - if (size != frame->v4l2_buf.length) { - gspca_dbg(gspca_dev, D_STREAM, "mmap bad size\n"); - ret = -EINVAL; - goto out; - } - /* - * - VM_IO marks the area as being a mmaped region for I/O to a - * device. It also prevents the region from being core dumped. - */ - vma->vm_flags |= VM_IO; + parm->parm.capture.readbuffers = gspca_dev->queue.min_buffers_needed; - addr = (unsigned long) frame->data; - while (size > 0) { - page = vmalloc_to_page((void *) addr); - ret = vm_insert_page(vma, start, page); - if (ret < 0) - goto out; - start += PAGE_SIZE; - addr += PAGE_SIZE; - size -= PAGE_SIZE; + if (!gspca_dev->sd_desc->set_streamparm) { + parm->parm.capture.capability = 0; + return 0; } - vma->vm_ops = &gspca_vm_ops; - vma->vm_private_data = frame; - gspca_vm_open(vma); - ret = 0; -out: - mutex_unlock(&gspca_dev->queue_lock); - return ret; + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + gspca_dev->usb_err = 0; + gspca_dev->sd_desc->set_streamparm(gspca_dev, parm); + return gspca_dev->usb_err; } -static int frame_ready_nolock(struct gspca_dev *gspca_dev, struct file *file, - enum v4l2_memory memory) +static int gspca_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) { - if (!gspca_dev->present) - return -ENODEV; - if (gspca_dev->capt_file != file || gspca_dev->memory != memory || - !gspca_dev->streaming) - return -EINVAL; + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq); - /* check if a frame is ready */ - return gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i); + if (*nplanes) + return sizes[0] < gspca_dev->pixfmt.sizeimage ? -EINVAL : 0; + *nplanes = 1; + sizes[0] = gspca_dev->pixfmt.sizeimage; + return 0; } -static int frame_ready(struct gspca_dev *gspca_dev, struct file *file, - enum v4l2_memory memory) +static int gspca_buffer_prepare(struct vb2_buffer *vb) { - int ret; + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue); + unsigned long size = gspca_dev->pixfmt.sizeimage; - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - ret = frame_ready_nolock(gspca_dev, file, memory); - mutex_unlock(&gspca_dev->queue_lock); - return ret; + if (vb2_plane_size(vb, 0) < size) { + gspca_err(gspca_dev, "buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + return 0; } -/* - * dequeue a video buffer - * - * If nonblock_ing is false, block until a buffer is available. - */ -static int vidioc_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *v4l2_buf) +static void gspca_buffer_finish(struct vb2_buffer *vb) { - struct gspca_dev *gspca_dev = video_drvdata(file); - struct gspca_frame *frame; - int i, j, ret; - - gspca_dbg(gspca_dev, D_FRAM, "dqbuf\n"); - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue); - for (;;) { - ret = frame_ready_nolock(gspca_dev, file, v4l2_buf->memory); - if (ret < 0) - goto out; - if (ret > 0) - break; - - mutex_unlock(&gspca_dev->queue_lock); - - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - - /* wait till a frame is ready */ - ret = wait_event_interruptible_timeout(gspca_dev->wq, - frame_ready(gspca_dev, file, v4l2_buf->memory), - msecs_to_jiffies(3000)); - if (ret < 0) - return ret; - if (ret == 0) - return -EIO; - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - } - - i = gspca_dev->fr_o; - j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; - - gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES; - - frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; - memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); - gspca_dbg(gspca_dev, D_FRAM, "dqbuf %d\n", j); - ret = 0; - - if (gspca_dev->memory == V4L2_MEMORY_USERPTR) { - if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr, - frame->data, - frame->v4l2_buf.bytesused)) { - gspca_err(gspca_dev, "dqbuf cp to user failed\n"); - ret = -EFAULT; - } - } -out: - mutex_unlock(&gspca_dev->queue_lock); - - if (ret == 0 && gspca_dev->sd_desc->dq_callback) { - mutex_lock(&gspca_dev->usb_lock); - gspca_dev->usb_err = 0; - if (gspca_dev->present) - gspca_dev->sd_desc->dq_callback(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - } + if (!gspca_dev->sd_desc->dq_callback) + return; - return ret; + gspca_dev->usb_err = 0; + if (gspca_dev->present) + gspca_dev->sd_desc->dq_callback(gspca_dev); } -/* - * queue a video buffer - * - * Attempting to queue a buffer that has already been - * queued will return -EINVAL. - */ -static int vidioc_qbuf(struct file *file, void *priv, - struct v4l2_buffer *v4l2_buf) +static void gspca_buffer_queue(struct vb2_buffer *vb) { - struct gspca_dev *gspca_dev = video_drvdata(file); - struct gspca_frame *frame; - int i, index, ret; - - gspca_dbg(gspca_dev, D_FRAM, "qbuf %d\n", v4l2_buf->index); - - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - - index = v4l2_buf->index; - if ((unsigned) index >= gspca_dev->nframes) { - gspca_dbg(gspca_dev, D_FRAM, - "qbuf idx %d >= %d\n", index, gspca_dev->nframes); - ret = -EINVAL; - goto out; - } - if (v4l2_buf->memory != gspca_dev->memory) { - gspca_dbg(gspca_dev, D_FRAM, "qbuf bad memory type\n"); - ret = -EINVAL; - goto out; - } - - frame = &gspca_dev->frame[index]; - if (frame->v4l2_buf.flags & BUF_ALL_FLAGS) { - gspca_dbg(gspca_dev, D_FRAM, "qbuf bad state\n"); - ret = -EINVAL; - goto out; - } - - frame->v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED; - - if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) { - frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr; - frame->v4l2_buf.length = v4l2_buf->length; - } - - /* put the buffer in the 'queued' queue */ - i = atomic_read(&gspca_dev->fr_q); - gspca_dev->fr_queue[i] = index; - atomic_set(&gspca_dev->fr_q, (i + 1) % GSPCA_MAX_FRAMES); + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vb->vb2_queue); + struct gspca_buffer *buf = to_gspca_buffer(vb); + unsigned long flags; - v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED; - v4l2_buf->flags &= ~V4L2_BUF_FLAG_DONE; - ret = 0; -out: - mutex_unlock(&gspca_dev->queue_lock); - return ret; + spin_lock_irqsave(&gspca_dev->qlock, flags); + list_add_tail(&buf->list, &gspca_dev->buf_list); + spin_unlock_irqrestore(&gspca_dev->qlock, flags); } -/* - * allocate the resources for read() - */ -static int read_alloc(struct gspca_dev *gspca_dev, - struct file *file) +static void gspca_return_all_buffers(struct gspca_dev *gspca_dev, + enum vb2_buffer_state state) { - struct v4l2_buffer v4l2_buf; - int i, ret; + struct gspca_buffer *buf, *node; + unsigned long flags; - gspca_dbg(gspca_dev, D_STREAM, "read alloc\n"); - - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - - if (gspca_dev->nframes == 0) { - struct v4l2_requestbuffers rb; - - memset(&rb, 0, sizeof rb); - rb.count = gspca_dev->nbufread; - rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - rb.memory = GSPCA_MEMORY_READ; - ret = vidioc_reqbufs(file, gspca_dev, &rb); - if (ret != 0) { - gspca_dbg(gspca_dev, D_STREAM, "read reqbuf err %d\n", - ret); - goto out; - } - memset(&v4l2_buf, 0, sizeof v4l2_buf); - v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - v4l2_buf.memory = GSPCA_MEMORY_READ; - for (i = 0; i < gspca_dev->nbufread; i++) { - v4l2_buf.index = i; - ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); - if (ret != 0) { - gspca_dbg(gspca_dev, D_STREAM, "read qbuf err: %d\n", - ret); - goto out; - } - } + spin_lock_irqsave(&gspca_dev->qlock, flags); + list_for_each_entry_safe(buf, node, &gspca_dev->buf_list, list) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->list); } - - /* start streaming */ - ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE); - if (ret != 0) - gspca_dbg(gspca_dev, D_STREAM, "read streamon err %d\n", ret); -out: - mutex_unlock(&gspca_dev->usb_lock); - return ret; + spin_unlock_irqrestore(&gspca_dev->qlock, flags); } -static __poll_t dev_poll(struct file *file, poll_table *wait) +static int gspca_start_streaming(struct vb2_queue *vq, unsigned int count) { - struct gspca_dev *gspca_dev = video_drvdata(file); - __poll_t req_events = poll_requested_events(wait); - __poll_t ret = 0; - - gspca_dbg(gspca_dev, D_FRAM, "poll\n"); - - if (req_events & EPOLLPRI) - ret |= v4l2_ctrl_poll(file, wait); - - if (req_events & (EPOLLIN | EPOLLRDNORM)) { - /* if reqbufs is not done, the user would use read() */ - if (gspca_dev->memory == GSPCA_MEMORY_NO) { - if (read_alloc(gspca_dev, file) != 0) { - ret |= EPOLLERR; - goto out; - } - } - - poll_wait(file, &gspca_dev->wq, wait); - - /* check if an image has been received */ - if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) { - ret |= EPOLLERR; - goto out; - } - if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i)) - ret |= EPOLLIN | EPOLLRDNORM; - mutex_unlock(&gspca_dev->queue_lock); - } + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq); + int ret; -out: - if (!gspca_dev->present) - ret |= EPOLLHUP; + gspca_dev->sequence = 0; + ret = gspca_init_transfer(gspca_dev); + if (ret) + gspca_return_all_buffers(gspca_dev, VB2_BUF_STATE_QUEUED); return ret; } -static ssize_t dev_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) +static void gspca_stop_streaming(struct vb2_queue *vq) { - struct gspca_dev *gspca_dev = video_drvdata(file); - struct gspca_frame *frame; - struct v4l2_buffer v4l2_buf; - struct timeval timestamp; - int n, ret, ret2; - - gspca_dbg(gspca_dev, D_FRAM, "read (%zd)\n", count); - if (gspca_dev->memory == GSPCA_MEMORY_NO) { /* first time ? */ - ret = read_alloc(gspca_dev, file); - if (ret != 0) - return ret; - } + struct gspca_dev *gspca_dev = vb2_get_drv_priv(vq); - /* get a frame */ - v4l2_get_timestamp(×tamp); - timestamp.tv_sec--; - n = 2; - for (;;) { - memset(&v4l2_buf, 0, sizeof v4l2_buf); - v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - v4l2_buf.memory = GSPCA_MEMORY_READ; - ret = vidioc_dqbuf(file, gspca_dev, &v4l2_buf); - if (ret != 0) { - gspca_dbg(gspca_dev, D_STREAM, "read dqbuf err %d\n", - ret); - return ret; - } - - /* if the process slept for more than 1 second, - * get a newer frame */ - frame = &gspca_dev->frame[v4l2_buf.index]; - if (--n < 0) - break; /* avoid infinite loop */ - if (frame->v4l2_buf.timestamp.tv_sec >= timestamp.tv_sec) - break; - ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); - if (ret != 0) { - gspca_dbg(gspca_dev, D_STREAM, "read qbuf err %d\n", - ret); - return ret; - } - } + gspca_stream_off(gspca_dev); - /* copy the frame */ - if (count > frame->v4l2_buf.bytesused) - count = frame->v4l2_buf.bytesused; - ret = copy_to_user(data, frame->data, count); - if (ret != 0) { - gspca_err(gspca_dev, "read cp to user lack %d / %zd\n", - ret, count); - ret = -EFAULT; - goto out; - } - ret = count; -out: - /* in each case, requeue the buffer */ - ret2 = vidioc_qbuf(file, gspca_dev, &v4l2_buf); - if (ret2 != 0) - return ret2; - return ret; + /* Release all active buffers */ + gspca_return_all_buffers(gspca_dev, VB2_BUF_STATE_ERROR); } +static const struct vb2_ops gspca_qops = { + .queue_setup = gspca_queue_setup, + .buf_prepare = gspca_buffer_prepare, + .buf_finish = gspca_buffer_finish, + .buf_queue = gspca_buffer_queue, + .start_streaming = gspca_start_streaming, + .stop_streaming = gspca_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + static const struct v4l2_file_operations dev_fops = { .owner = THIS_MODULE, - .open = dev_open, - .release = dev_close, - .read = dev_read, - .mmap = dev_mmap, + .open = v4l2_fh_open, + .release = vb2_fop_release, .unlocked_ioctl = video_ioctl2, - .poll = dev_poll, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, }; static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_querycap = vidioc_querycap, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_qbuf = vidioc_qbuf, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_streamon = vidioc_streamon, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_streamoff = vidioc_streamoff, .vidioc_g_jpegcomp = vidioc_g_jpegcomp, .vidioc_s_jpegcomp = vidioc_s_jpegcomp, .vidioc_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_enum_frameintervals = vidioc_enum_frameintervals, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_chip_info = vidioc_g_chip_info, .vidioc_g_register = vidioc_g_register, @@ -2034,6 +1454,7 @@ int gspca_dev_probe2(struct usb_interface *intf, { struct gspca_dev *gspca_dev; struct usb_device *dev = interface_to_usbdev(intf); + struct vb2_queue *q; int ret; pr_info("%s-" GSPCA_VERSION " probing %04x:%04x\n", @@ -2078,20 +1499,37 @@ int gspca_dev_probe2(struct usb_interface *intf, ret = v4l2_device_register(&intf->dev, &gspca_dev->v4l2_dev); if (ret) goto out; + gspca_dev->present = true; gspca_dev->sd_desc = sd_desc; - gspca_dev->nbufread = 2; gspca_dev->empty_packet = -1; /* don't check the empty packets */ gspca_dev->vdev = gspca_template; gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev; video_set_drvdata(&gspca_dev->vdev, gspca_dev); gspca_dev->module = module; - gspca_dev->present = 1; mutex_init(&gspca_dev->usb_lock); gspca_dev->vdev.lock = &gspca_dev->usb_lock; - mutex_init(&gspca_dev->queue_lock); init_waitqueue_head(&gspca_dev->wq); + /* Initialize the vb2 queue */ + q = &gspca_dev->queue; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; + q->drv_priv = gspca_dev; + q->buf_struct_size = sizeof(struct gspca_buffer); + q->ops = &gspca_qops; + q->mem_ops = &vb2_vmalloc_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->min_buffers_needed = 2; + q->lock = &gspca_dev->usb_lock; + ret = vb2_queue_init(q); + if (ret) + goto out; + gspca_dev->vdev.queue = q; + + INIT_LIST_HEAD(&gspca_dev->buf_list); + spin_lock_init(&gspca_dev->qlock); + /* configure the subdriver and initialize the USB device */ ret = sd_desc->config(gspca_dev, id); if (ret < 0) @@ -2109,14 +1547,6 @@ int gspca_dev_probe2(struct usb_interface *intf, if (ret) goto out; - /* - * Don't take usb_lock for these ioctls. This improves latency if - * usb_lock is taken for a long time, e.g. when changing a control - * value, and a new frame is ready to be dequeued. - */ - v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF); - v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF); - v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF); #ifdef CONFIG_VIDEO_ADV_DEBUG if (!gspca_dev->sd_desc->get_register) v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_REGISTER); @@ -2198,24 +1628,17 @@ void gspca_disconnect(struct usb_interface *intf) video_device_node_name(&gspca_dev->vdev)); mutex_lock(&gspca_dev->usb_lock); + gspca_dev->present = false; - gspca_dev->present = 0; - destroy_urbs(gspca_dev); + vb2_queue_error(&gspca_dev->queue); #if IS_ENABLED(CONFIG_INPUT) - gspca_input_destroy_urb(gspca_dev); input_dev = gspca_dev->input_dev; if (input_dev) { gspca_dev->input_dev = NULL; input_unregister_device(input_dev); } #endif - /* Free subdriver's streaming resources / stop sd workqueue(s) */ - if (gspca_dev->sd_desc->stop0 && gspca_dev->streaming) - gspca_dev->sd_desc->stop0(gspca_dev); - gspca_dev->streaming = 0; - gspca_dev->dev = NULL; - wake_up_interruptible(&gspca_dev->wq); v4l2_device_disconnect(&gspca_dev->v4l2_dev); video_unregister_device(&gspca_dev->vdev); @@ -2234,7 +1657,7 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message) gspca_input_destroy_urb(gspca_dev); - if (!gspca_dev->streaming) + if (!vb2_start_streaming_called(&gspca_dev->queue)) return 0; mutex_lock(&gspca_dev->usb_lock); @@ -2266,8 +1689,7 @@ int gspca_resume(struct usb_interface *intf) * only write to the device registers on s_ctrl when streaming -> * Clear streaming to avoid setting all ctrls twice. */ - streaming = gspca_dev->streaming; - gspca_dev->streaming = 0; + streaming = vb2_start_streaming_called(&gspca_dev->queue); if (streaming) ret = gspca_init_transfer(gspca_dev); else diff --git a/drivers/media/usb/gspca/gspca.h b/drivers/media/usb/gspca/gspca.h index 249cb38a542f..b0ced2e14006 100644 --- a/drivers/media/usb/gspca/gspca.h +++ b/drivers/media/usb/gspca/gspca.h @@ -9,6 +9,8 @@ #include <media/v4l2-common.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-vmalloc.h> #include <linux/mutex.h> @@ -138,19 +140,22 @@ enum gspca_packet_type { LAST_PACKET }; -struct gspca_frame { - __u8 *data; /* frame buffer */ - int vma_use_count; - struct v4l2_buffer v4l2_buf; +struct gspca_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; }; +static inline struct gspca_buffer *to_gspca_buffer(struct vb2_buffer *vb2) +{ + return container_of(vb2, struct gspca_buffer, vb.vb2_buf); +} + struct gspca_dev { struct video_device vdev; /* !! must be the first item */ struct module *module; /* subdriver handling the device */ struct v4l2_device v4l2_dev; struct usb_device *dev; - struct file *capt_file; /* file doing video capture */ - /* protected by queue_lock */ + #if IS_ENABLED(CONFIG_INPUT) struct input_dev *input_dev; char phys[64]; /* physical device path */ @@ -176,34 +181,29 @@ struct gspca_dev { struct urb *int_urb; #endif - __u8 *frbuf; /* buffer for nframes */ - struct gspca_frame frame[GSPCA_MAX_FRAMES]; - u8 *image; /* image beeing filled */ - __u32 frsz; /* frame size */ + u8 *image; /* image being filled */ u32 image_len; /* current length of image */ - atomic_t fr_q; /* next frame to queue */ - atomic_t fr_i; /* frame being filled */ - signed char fr_queue[GSPCA_MAX_FRAMES]; /* frame queue */ - char nframes; /* number of frames */ - u8 fr_o; /* next frame to dequeue */ __u8 last_packet_type; __s8 empty_packet; /* if (-1) don't check empty packets */ - __u8 streaming; /* protected by both mutexes (*) */ + bool streaming; __u8 curr_mode; /* current camera mode */ struct v4l2_pix_format pixfmt; /* current mode parameters */ __u32 sequence; /* frame sequence number */ + struct vb2_queue queue; + + spinlock_t qlock; + struct list_head buf_list; + wait_queue_head_t wq; /* wait queue */ struct mutex usb_lock; /* usb exchange protection */ - struct mutex queue_lock; /* ISOC queue protection */ int usb_err; /* USB error - protected by usb_lock */ u16 pkt_size; /* ISOC packet size */ #ifdef CONFIG_PM char frozen; /* suspend - resume */ #endif - char present; /* device connected */ - char nbufread; /* number of buffers for read() */ + bool present; char memory; /* memory type (V4L2_MEMORY_xxx) */ __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c index d668589598d6..c40245950553 100644 --- a/drivers/media/usb/gspca/jl2005bcd.c +++ b/drivers/media/usb/gspca/jl2005bcd.c @@ -321,7 +321,7 @@ static void jl2005c_dostream(struct work_struct *work) int ret; u8 *buffer; - buffer = kmalloc(JL2005C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); + buffer = kmalloc(JL2005C_MAX_TRANSFER, GFP_KERNEL); if (!buffer) { pr_err("Couldn't allocate USB buffer\n"); goto quit_stream; diff --git a/drivers/media/usb/gspca/m5602/m5602_core.c b/drivers/media/usb/gspca/m5602/m5602_core.c index b83ec4285a0b..30b7cf1feedd 100644 --- a/drivers/media/usb/gspca/m5602/m5602_core.c +++ b/drivers/media/usb/gspca/m5602/m5602_core.c @@ -342,7 +342,7 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev, data += 4; len -= 4; - if (cur_frame_len + len <= gspca_dev->frsz) { + if (cur_frame_len + len <= gspca_dev->pixfmt.sizeimage) { gspca_dbg(gspca_dev, D_FRAM, "Continuing frame %d copying %d bytes\n", sd->frame_count, len); @@ -351,7 +351,7 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev, } else { /* Add the remaining data up to frame size */ gspca_frame_add(gspca_dev, INTER_PACKET, data, - gspca_dev->frsz - cur_frame_len); + gspca_dev->pixfmt.sizeimage - cur_frame_len); } } } diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index f293921a1f2b..d06dc0755b9a 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -1476,7 +1476,6 @@ static void sd_get_streamparm(struct gspca_dev *gspca_dev, struct v4l2_fract *tpf = &cp->timeperframe; struct sd *sd = (struct sd *) gspca_dev; - cp->capability |= V4L2_CAP_TIMEPERFRAME; tpf->numerator = 1; tpf->denominator = sd->frame_rate; } diff --git a/drivers/media/usb/gspca/sq905.c b/drivers/media/usb/gspca/sq905.c index cc8ff41b8ab3..ffea9c35b0a0 100644 --- a/drivers/media/usb/gspca/sq905.c +++ b/drivers/media/usb/gspca/sq905.c @@ -217,7 +217,7 @@ static void sq905_dostream(struct work_struct *work) u8 *data; u8 *buffer; - buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); + buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL); if (!buffer) { pr_err("Couldn't allocate USB buffer\n"); goto quit_stream; diff --git a/drivers/media/usb/gspca/sq905c.c b/drivers/media/usb/gspca/sq905c.c index 5e1269eb7c50..274921c0bb46 100644 --- a/drivers/media/usb/gspca/sq905c.c +++ b/drivers/media/usb/gspca/sq905c.c @@ -138,7 +138,7 @@ static void sq905c_dostream(struct work_struct *work) int ret; u8 *buffer; - buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); + buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL); if (!buffer) { pr_err("Couldn't allocate USB buffer\n"); goto quit_stream; diff --git a/drivers/media/usb/gspca/topro.c b/drivers/media/usb/gspca/topro.c index 82e2be14cad8..6f3ec0366a2f 100644 --- a/drivers/media/usb/gspca/topro.c +++ b/drivers/media/usb/gspca/topro.c @@ -4780,7 +4780,6 @@ static void sd_get_streamparm(struct gspca_dev *gspca_dev, struct v4l2_fract *tpf = &cp->timeperframe; int fr, i; - cp->capability |= V4L2_CAP_TIMEPERFRAME; tpf->numerator = 1; i = get_fr_idx(gspca_dev); if (i & 0x80) { diff --git a/drivers/media/usb/gspca/vc032x.c b/drivers/media/usb/gspca/vc032x.c index 6b11597977c9..52d071659634 100644 --- a/drivers/media/usb/gspca/vc032x.c +++ b/drivers/media/usb/gspca/vc032x.c @@ -3642,7 +3642,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, int size, l; l = gspca_dev->image_len; - size = gspca_dev->frsz; + size = gspca_dev->pixfmt.sizeimage; if (len > size - l) len = size - l; } diff --git a/drivers/media/usb/gspca/vicam.c b/drivers/media/usb/gspca/vicam.c index 554b90ef2200..8562bda0ef88 100644 --- a/drivers/media/usb/gspca/vicam.c +++ b/drivers/media/usb/gspca/vicam.c @@ -182,7 +182,7 @@ static void vicam_dostream(struct work_struct *work) frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage + HEADER_SIZE; - buffer = kmalloc(frame_sz, GFP_KERNEL | GFP_DMA); + buffer = kmalloc(frame_sz, GFP_KERNEL); if (!buffer) { pr_err("Couldn't allocate USB buffer\n"); goto exit; diff --git a/drivers/media/usb/gspca/zc3xx.c b/drivers/media/usb/gspca/zc3xx.c index 25b4dbe8e049..cf21991e3d99 100644 --- a/drivers/media/usb/gspca/zc3xx.c +++ b/drivers/media/usb/gspca/zc3xx.c @@ -3184,10 +3184,10 @@ static const struct usb_action ov7620_InitialScale[] = { /* 320x240 */ {} }; static const struct usb_action ov7620_50HZ[] = { - {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */ - {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */ + /* enable 1/120s & 1/100s exposures for banding filter */ + {0xaa, 0x75, 0x008e}, {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ @@ -3195,18 +3195,16 @@ static const struct usb_action ov7620_50HZ[] = { {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */ - {0xaa, 0x10, 0x0082}, /* 00,10,82,aa */ {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */ /* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc * if mode0 (640x480) */ {} }; static const struct usb_action ov7620_60HZ[] = { - {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ - /* (bug in zs211.inf) */ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */ - {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */ + /* enable 1/120s & 1/100s exposures for banding filter */ + {0xaa, 0x75, 0x008e}, {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ @@ -3214,7 +3212,6 @@ static const struct usb_action ov7620_60HZ[] = { {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */ - {0xaa, 0x10, 0x0020}, /* 00,10,20,aa */ {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */ /* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc * if mode0 (640x480) */ @@ -3224,11 +3221,10 @@ static const struct usb_action ov7620_60HZ[] = { {} }; static const struct usb_action ov7620_NoFliker[] = { - {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ - /* (bug in zs211.inf) */ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */ - {0xaa, 0x75, 0x008e}, /* 00,75,8e,aa */ + /* disable 1/120s & 1/100s exposures for banding filter */ + {0xaa, 0x75, 0x008a}, {0xaa, 0x2d, 0x0001}, /* 00,2d,01,aa */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ @@ -5778,16 +5774,34 @@ static void setcontrast(struct gspca_dev *gspca_dev, static s32 getexposure(struct gspca_dev *gspca_dev) { - return (i2c_read(gspca_dev, 0x25) << 9) - | (i2c_read(gspca_dev, 0x26) << 1) - | (i2c_read(gspca_dev, 0x27) >> 7); + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->sensor) { + case SENSOR_HV7131R: + return (i2c_read(gspca_dev, 0x25) << 9) + | (i2c_read(gspca_dev, 0x26) << 1) + | (i2c_read(gspca_dev, 0x27) >> 7); + case SENSOR_OV7620: + return i2c_read(gspca_dev, 0x10); + default: + return -1; + } } static void setexposure(struct gspca_dev *gspca_dev, s32 val) { - i2c_write(gspca_dev, 0x25, val >> 9, 0x00); - i2c_write(gspca_dev, 0x26, val >> 1, 0x00); - i2c_write(gspca_dev, 0x27, val << 7, 0x00); + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->sensor) { + case SENSOR_HV7131R: + i2c_write(gspca_dev, 0x25, val >> 9, 0x00); + i2c_write(gspca_dev, 0x26, val >> 1, 0x00); + i2c_write(gspca_dev, 0x27, val << 7, 0x00); + break; + case SENSOR_OV7620: + i2c_write(gspca_dev, 0x10, val, 0x00); + break; + } } static void setquality(struct gspca_dev *gspca_dev) @@ -5918,7 +5932,12 @@ static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) static void setautogain(struct gspca_dev *gspca_dev, s32 val) { - reg_w(gspca_dev, val ? 0x42 : 0x02, 0x0180); + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_OV7620) + i2c_write(gspca_dev, 0x13, val ? 0xa3 : 0x80, 0x00); + else + reg_w(gspca_dev, val ? 0x42 : 0x02, 0x0180); } /* @@ -6439,6 +6458,9 @@ static int sd_init_controls(struct gspca_dev *gspca_dev) if (sd->sensor == SENSOR_HV7131R) sd->exposure = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, V4L2_CID_EXPOSURE, 0x30d, 0x493e, 1, 0x927); + else if (sd->sensor == SENSOR_OV7620) + sd->exposure = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 255, 1, 0x41); sd->autogain = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 1); if (sd->sensor != SENSOR_OV7630C) @@ -6458,7 +6480,7 @@ static int sd_init_controls(struct gspca_dev *gspca_dev) return hdl->error; } v4l2_ctrl_cluster(3, &sd->gamma); - if (sd->sensor == SENSOR_HV7131R) + if (sd->sensor == SENSOR_HV7131R || sd->sensor == SENSOR_OV7620) v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true); return 0; } diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c index 7eb53517a82f..6d692fb3e8dd 100644 --- a/drivers/media/usb/hackrf/hackrf.c +++ b/drivers/media/usb/hackrf/hackrf.c @@ -909,18 +909,15 @@ static int hackrf_querycap(struct file *file, void *fh, dev_dbg(&intf->dev, "\n"); + cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; if (vdev->vfl_dir == VFL_DIR_RX) - cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER | - V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; - + cap->device_caps |= V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER; else - cap->device_caps = V4L2_CAP_SDR_OUTPUT | V4L2_CAP_MODULATOR | - V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + cap->device_caps |= V4L2_CAP_SDR_OUTPUT | V4L2_CAP_MODULATOR; cap->capabilities = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_SDR_OUTPUT | V4L2_CAP_MODULATOR | - V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | - V4L2_CAP_DEVICE_CAPS; + V4L2_CAP_DEVICE_CAPS | cap->device_caps; strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); strlcpy(cap->card, dev->rx_vdev.name, sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c index 4720d79b0282..c71ddefd2e58 100644 --- a/drivers/media/usb/hdpvr/hdpvr-i2c.c +++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c @@ -173,7 +173,7 @@ static const struct i2c_algorithm hdpvr_algo = { }; static const struct i2c_adapter hdpvr_i2c_adapter_template = { - .name = "Hauppage HD PVR I2C", + .name = "Hauppauge HD PVR I2C", .owner = THIS_MODULE, .algo = &hdpvr_algo, }; diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index 77c3d331ff31..1b89c77bad66 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -873,7 +873,7 @@ static int vidioc_g_audio(struct file *file, void *private_data, audio->index = dev->options.audio_input; audio->capability = V4L2_AUDCAP_STEREO; - strncpy(audio->name, audio_iname[audio->index], sizeof(audio->name)); + strlcpy(audio->name, audio_iname[audio->index], sizeof(audio->name)); audio->name[sizeof(audio->name) - 1] = '\0'; return 0; } diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c index 242b213b7599..d5bec0f69bec 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -23,7 +23,6 @@ */ #include "pvrusb2-cx2584x-v4l.h" -#include "pvrusb2-video-v4l.h" #include "pvrusb2-hdw-internal.h" diff --git a/drivers/media/usb/pvrusb2/pvrusb2-devattr.c b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c index 71537097c13f..06de1c83f444 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c @@ -182,7 +182,6 @@ static const struct pvr2_device_desc pvr2_device_av400 = { #ifdef CONFIG_VIDEO_PVRUSB2_DVB static struct lgdt330x_config pvr2_lgdt3303_config = { - .demod_address = 0x0e, .demod_chip = LGDT3303, .clock_polarity_flip = 1, }; @@ -190,6 +189,7 @@ static struct lgdt330x_config pvr2_lgdt3303_config = { static int pvr2_lgdt3303_attach(struct pvr2_dvb_adapter *adap) { adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3303_config, + 0x0e, &adap->channel.hdw->i2c_adap); if (adap->fe) return 0; @@ -243,13 +243,13 @@ static const struct pvr2_device_desc pvr2_device_onair_creator = { #ifdef CONFIG_VIDEO_PVRUSB2_DVB static struct lgdt330x_config pvr2_lgdt3302_config = { - .demod_address = 0x0e, .demod_chip = LGDT3302, }; static int pvr2_lgdt3302_attach(struct pvr2_dvb_adapter *adap) { adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3302_config, + 0x0e, &adap->channel.hdw->i2c_adap); if (adap->fe) return 0; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index 9fdc57c1658f..e53a80b589a1 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -159,9 +159,12 @@ static int pvr2_s_std(struct file *file, void *priv, v4l2_std_id std) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int ret; - return pvr2_ctrl_set_value( + ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), std); + pvr2_hdw_commit_ctl(hdw); + return ret; } static int pvr2_querystd(struct file *file, void *priv, v4l2_std_id *std) @@ -251,12 +254,15 @@ static int pvr2_s_input(struct file *file, void *priv, unsigned int inp) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int ret; if (inp >= fh->input_cnt) return -EINVAL; - return pvr2_ctrl_set_value( + ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT), fh->input_map[inp]); + pvr2_hdw_commit_ctl(hdw); + return ret; } static int pvr2_enumaudio(struct file *file, void *priv, struct v4l2_audio *vin) @@ -315,13 +321,16 @@ static int pvr2_s_tuner(struct file *file, void *priv, const struct v4l2_tuner * { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int ret; if (vt->index != 0) return -EINVAL; - return pvr2_ctrl_set_value( + ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_AUDIOMODE), vt->audmode); + pvr2_hdw_commit_ctl(hdw); + return ret; } static int pvr2_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *vf) @@ -353,8 +362,10 @@ static int pvr2_s_frequency(struct file *file, void *priv, const struct v4l2_fre fv = (fv * 125) / 2; else fv = fv * 62500; - return pvr2_ctrl_set_value( + ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv); + pvr2_hdw_commit_ctl(hdw); + return ret; } static int pvr2_g_frequency(struct file *file, void *priv, struct v4l2_frequency *vf) @@ -470,6 +481,7 @@ static int pvr2_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES); pvr2_ctrl_set_value(hcp, vf->fmt.pix.width); pvr2_ctrl_set_value(vcp, vf->fmt.pix.height); + pvr2_hdw_commit_ctl(hdw); return 0; } @@ -597,9 +609,12 @@ static int pvr2_s_ctrl(struct file *file, void *priv, struct v4l2_control *vc) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int ret; - return pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id), + ret = pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id), vc->value); + pvr2_hdw_commit_ctl(hdw); + return ret; } static int pvr2_g_ext_ctrls(struct file *file, void *priv, @@ -658,10 +673,12 @@ static int pvr2_s_ext_ctrls(struct file *file, void *priv, ctrl->value); if (ret) { ctls->error_idx = idx; - return ret; + goto commit; } } - return 0; +commit: + pvr2_hdw_commit_ctl(hdw); + return ret; } static int pvr2_try_ext_ctrls(struct file *file, void *priv, @@ -764,23 +781,23 @@ static int pvr2_s_selection(struct file *file, void *priv, pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), sel->r.left); if (ret != 0) - return -EINVAL; + goto commit; ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), sel->r.top); if (ret != 0) - return -EINVAL; + goto commit; ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), sel->r.width); if (ret != 0) - return -EINVAL; + goto commit; ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), sel->r.height); - if (ret != 0) - return -EINVAL; - return 0; +commit: + pvr2_hdw_commit_ctl(hdw); + return ret; } static int pvr2_log_status(struct file *file, void *priv) @@ -905,44 +922,6 @@ static void pvr2_v4l2_internal_check(struct pvr2_channel *chp) } -static long pvr2_v4l2_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - - struct pvr2_v4l2_fh *fh = file->private_data; - struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - long ret = -EINVAL; - - if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) - v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); - - if (!pvr2_hdw_dev_ok(hdw)) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "ioctl failed - bad or no context"); - return -EFAULT; - } - - ret = video_ioctl2(file, cmd, arg); - - pvr2_hdw_commit_ctl(hdw); - - if (ret < 0) { - if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "pvr2_v4l2_do_ioctl failure, ret=%ld command was:", -ret); - v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); - } - } else { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)", - ret, ret); - } - return ret; - -} - - static int pvr2_v4l2_release(struct file *file) { struct pvr2_v4l2_fh *fhp = file->private_data; @@ -1205,7 +1184,7 @@ static const struct v4l2_file_operations vdev_fops = { .open = pvr2_v4l2_open, .release = pvr2_v4l2_release, .read = pvr2_v4l2_read, - .unlocked_ioctl = pvr2_v4l2_ioctl, + .unlocked_ioctl = video_ioctl2, .poll = pvr2_v4l2_poll, }; diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c index 6d436e9e454f..be3634407f1f 100644 --- a/drivers/media/usb/siano/smsusb.c +++ b/drivers/media/usb/siano/smsusb.c @@ -455,7 +455,7 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id) mdev = siano_media_device_register(dev, board_id); /* register in smscore */ - rc = smscore_register_device(¶ms, &dev->coredev, mdev); + rc = smscore_register_device(¶ms, &dev->coredev, 0, mdev); if (rc < 0) { pr_err("smscore_register_device(...) failed, rc %d\n", rc); smsusb_term_device(intf); diff --git a/drivers/media/usb/stk1160/stk1160-core.c b/drivers/media/usb/stk1160/stk1160-core.c index bea8bbbb84fb..72bd893c9659 100644 --- a/drivers/media/usb/stk1160/stk1160-core.c +++ b/drivers/media/usb/stk1160/stk1160-core.c @@ -167,6 +167,8 @@ static void stk1160_release(struct v4l2_device *v4l2_dev) v4l2_ctrl_handler_free(&dev->ctrl_handler); v4l2_device_unregister(&dev->v4l2_dev); + mutex_destroy(&dev->v4l_lock); + mutex_destroy(&dev->vb_queue_lock); kfree(dev->alt_max_pkt_size); kfree(dev); } @@ -423,7 +425,7 @@ static void stk1160_disconnect(struct usb_interface *interface) /* * This calls stk1160_release if it's the last reference. - * therwise, release is posponed until there are no users left. + * Otherwise, release is posponed until there are no users left. */ v4l2_device_put(&dev->v4l2_dev); } diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index 3668a04359e8..ce79df643c7e 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -54,7 +54,7 @@ static struct usbtv_norm_params norm_params[] = { .cap_height = 480, }, { - .norm = V4L2_STD_PAL, + .norm = V4L2_STD_625_50, .cap_width = 720, .cap_height = 576, } @@ -77,7 +77,7 @@ static int usbtv_configure_for_norm(struct usbtv *usbtv, v4l2_std_id norm) usbtv->height = params->cap_height; usbtv->n_chunks = usbtv->width * usbtv->height / 4 / USBTV_CHUNK; - usbtv->norm = params->norm; + usbtv->norm = norm; } else ret = -EINVAL; @@ -121,52 +121,144 @@ static int usbtv_select_input(struct usbtv *usbtv, int input) return ret; } +static uint16_t usbtv_norm_to_16f_reg(v4l2_std_id norm) +{ + /* NTSC M/M-JP/M-KR */ + if (norm & V4L2_STD_NTSC) + return 0x00b8; + /* PAL BG/DK/H/I */ + if (norm & V4L2_STD_PAL) + return 0x00ee; + /* SECAM B/D/G/H/K/K1/L/Lc */ + if (norm & V4L2_STD_SECAM) + return 0x00ff; + if (norm & V4L2_STD_NTSC_443) + return 0x00a8; + if (norm & (V4L2_STD_PAL_M | V4L2_STD_PAL_60)) + return 0x00bc; + /* Fallback to automatic detection for other standards */ + return 0x0000; +} + static int usbtv_select_norm(struct usbtv *usbtv, v4l2_std_id norm) { int ret; + /* These are the series of register values used to configure the + * decoder for a specific standard. + * The first 21 register writes are copied from the + * Settings\DecoderDefaults registry keys present in the Windows driver + * .INF file, and control various image tuning parameters (color + * correction, sharpness, ...). + */ static const u16 pal[][2] = { + /* "AVPAL" tuning sequence from .INF file */ + { USBTV_BASE + 0x0003, 0x0004 }, { USBTV_BASE + 0x001a, 0x0068 }, + { USBTV_BASE + 0x0100, 0x00d3 }, { USBTV_BASE + 0x010e, 0x0072 }, { USBTV_BASE + 0x010f, 0x00a2 }, { USBTV_BASE + 0x0112, 0x00b0 }, + { USBTV_BASE + 0x0115, 0x0015 }, { USBTV_BASE + 0x0117, 0x0001 }, { USBTV_BASE + 0x0118, 0x002c }, { USBTV_BASE + 0x012d, 0x0010 }, { USBTV_BASE + 0x012f, 0x0020 }, + { USBTV_BASE + 0x0220, 0x002e }, + { USBTV_BASE + 0x0225, 0x0008 }, + { USBTV_BASE + 0x024e, 0x0002 }, { USBTV_BASE + 0x024f, 0x0002 }, { USBTV_BASE + 0x0254, 0x0059 }, { USBTV_BASE + 0x025a, 0x0016 }, { USBTV_BASE + 0x025b, 0x0035 }, { USBTV_BASE + 0x0263, 0x0017 }, { USBTV_BASE + 0x0266, 0x0016 }, - { USBTV_BASE + 0x0267, 0x0036 } + { USBTV_BASE + 0x0267, 0x0036 }, + /* End image tuning */ + { USBTV_BASE + 0x024e, 0x0002 }, + { USBTV_BASE + 0x024f, 0x0002 }, }; static const u16 ntsc[][2] = { + /* "AVNTSC" tuning sequence from .INF file */ + { USBTV_BASE + 0x0003, 0x0004 }, { USBTV_BASE + 0x001a, 0x0079 }, + { USBTV_BASE + 0x0100, 0x00d3 }, { USBTV_BASE + 0x010e, 0x0068 }, { USBTV_BASE + 0x010f, 0x009c }, { USBTV_BASE + 0x0112, 0x00f0 }, + { USBTV_BASE + 0x0115, 0x0015 }, { USBTV_BASE + 0x0117, 0x0000 }, { USBTV_BASE + 0x0118, 0x00fc }, { USBTV_BASE + 0x012d, 0x0004 }, { USBTV_BASE + 0x012f, 0x0008 }, + { USBTV_BASE + 0x0220, 0x002e }, + { USBTV_BASE + 0x0225, 0x0008 }, + { USBTV_BASE + 0x024e, 0x0002 }, { USBTV_BASE + 0x024f, 0x0001 }, { USBTV_BASE + 0x0254, 0x005f }, { USBTV_BASE + 0x025a, 0x0012 }, { USBTV_BASE + 0x025b, 0x0001 }, { USBTV_BASE + 0x0263, 0x001c }, { USBTV_BASE + 0x0266, 0x0011 }, - { USBTV_BASE + 0x0267, 0x0005 } + { USBTV_BASE + 0x0267, 0x0005 }, + /* End image tuning */ + { USBTV_BASE + 0x024e, 0x0002 }, + { USBTV_BASE + 0x024f, 0x0002 }, + }; + + static const u16 secam[][2] = { + /* "AVSECAM" tuning sequence from .INF file */ + { USBTV_BASE + 0x0003, 0x0004 }, + { USBTV_BASE + 0x001a, 0x0073 }, + { USBTV_BASE + 0x0100, 0x00dc }, + { USBTV_BASE + 0x010e, 0x0072 }, + { USBTV_BASE + 0x010f, 0x00a2 }, + { USBTV_BASE + 0x0112, 0x0090 }, + { USBTV_BASE + 0x0115, 0x0035 }, + { USBTV_BASE + 0x0117, 0x0001 }, + { USBTV_BASE + 0x0118, 0x0030 }, + { USBTV_BASE + 0x012d, 0x0004 }, + { USBTV_BASE + 0x012f, 0x0008 }, + { USBTV_BASE + 0x0220, 0x002d }, + { USBTV_BASE + 0x0225, 0x0028 }, + { USBTV_BASE + 0x024e, 0x0008 }, + { USBTV_BASE + 0x024f, 0x0002 }, + { USBTV_BASE + 0x0254, 0x0069 }, + { USBTV_BASE + 0x025a, 0x0016 }, + { USBTV_BASE + 0x025b, 0x0035 }, + { USBTV_BASE + 0x0263, 0x0021 }, + { USBTV_BASE + 0x0266, 0x0016 }, + { USBTV_BASE + 0x0267, 0x0036 }, + /* End image tuning */ + { USBTV_BASE + 0x024e, 0x0002 }, + { USBTV_BASE + 0x024f, 0x0002 }, }; ret = usbtv_configure_for_norm(usbtv, norm); if (!ret) { - if (norm & V4L2_STD_525_60) + /* Masks for norms using a NTSC or PAL color encoding. */ + static const v4l2_std_id ntsc_mask = + V4L2_STD_NTSC | V4L2_STD_NTSC_443; + static const v4l2_std_id pal_mask = + V4L2_STD_PAL | V4L2_STD_PAL_60 | V4L2_STD_PAL_M; + + if (norm & ntsc_mask) ret = usbtv_set_regs(usbtv, ntsc, ARRAY_SIZE(ntsc)); - else if (norm & V4L2_STD_PAL) + else if (norm & pal_mask) ret = usbtv_set_regs(usbtv, pal, ARRAY_SIZE(pal)); + else if (norm & V4L2_STD_SECAM) + ret = usbtv_set_regs(usbtv, secam, ARRAY_SIZE(secam)); + else + ret = -EINVAL; + } + + if (!ret) { + /* Configure the decoder for the color standard */ + const u16 cfg[][2] = { + { USBTV_BASE + 0x016f, usbtv_norm_to_16f_reg(norm) } + }; + ret = usbtv_set_regs(usbtv, cfg, ARRAY_SIZE(cfg)); } return ret; @@ -236,15 +328,6 @@ static int usbtv_setup_capture(struct usbtv *usbtv) { USBTV_BASE + 0x0158, 0x001f }, { USBTV_BASE + 0x0159, 0x0006 }, { USBTV_BASE + 0x015d, 0x0000 }, - - { USBTV_BASE + 0x0003, 0x0004 }, - { USBTV_BASE + 0x0100, 0x00d3 }, - { USBTV_BASE + 0x0115, 0x0015 }, - { USBTV_BASE + 0x0220, 0x002e }, - { USBTV_BASE + 0x0225, 0x0008 }, - { USBTV_BASE + 0x024e, 0x0002 }, - { USBTV_BASE + 0x024e, 0x0002 }, - { USBTV_BASE + 0x024f, 0x0002 }, }; ret = usbtv_set_regs(usbtv, setup, ARRAY_SIZE(setup)); @@ -587,7 +670,7 @@ static int usbtv_s_std(struct file *file, void *priv, v4l2_std_id norm) int ret = -EINVAL; struct usbtv *usbtv = video_drvdata(file); - if ((norm & V4L2_STD_525_60) || (norm & V4L2_STD_PAL)) + if (norm & USBTV_TV_STD) ret = usbtv_select_norm(usbtv, norm); return ret; @@ -698,6 +781,8 @@ static const struct vb2_ops usbtv_vb2_ops = { .buf_queue = usbtv_buf_queue, .start_streaming = usbtv_start_streaming, .stop_streaming = usbtv_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static int usbtv_s_ctrl(struct v4l2_ctrl *ctrl) diff --git a/drivers/media/usb/usbtv/usbtv.h b/drivers/media/usb/usbtv/usbtv.h index 0231e449877e..77a368e90fd0 100644 --- a/drivers/media/usb/usbtv/usbtv.h +++ b/drivers/media/usb/usbtv/usbtv.h @@ -68,7 +68,7 @@ #define USBTV_ODD(chunk) ((be32_to_cpu(chunk[0]) & 0x0000f000) >> 15) #define USBTV_CHUNK_NO(chunk) (be32_to_cpu(chunk[0]) & 0x00000fff) -#define USBTV_TV_STD (V4L2_STD_525_60 | V4L2_STD_PAL) +#define USBTV_TV_STD (V4L2_STD_525_60 | V4L2_STD_PAL | V4L2_STD_SECAM) /* parameters for supported TV norms */ struct usbtv_norm_params { diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c index 3f87fbc80be2..7138c2b606cc 100644 --- a/drivers/media/usb/usbvision/usbvision-core.c +++ b/drivers/media/usb/usbvision/usbvision-core.c @@ -1857,7 +1857,7 @@ int usbvision_stream_interrupt(struct usb_usbvision *usbvision) static int usbvision_set_compress_params(struct usb_usbvision *usbvision) { - static const char proc[] = "usbvision_set_compresion_params: "; + static const char proc[] = "usbvision_set_compression_params: "; int rc; unsigned char *value = usbvision->ctrl_urb_buffer; diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 102594ec3e97..a36b4fb949fa 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1607,14 +1607,12 @@ static int uvc_ctrl_get_flags(struct uvc_device *dev, ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum, info->selector, data, 1); if (!ret) - info->flags = UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX - | UVC_CTRL_FLAG_GET_RES | UVC_CTRL_FLAG_GET_DEF - | (data[0] & UVC_CONTROL_CAP_GET ? - UVC_CTRL_FLAG_GET_CUR : 0) - | (data[0] & UVC_CONTROL_CAP_SET ? - UVC_CTRL_FLAG_SET_CUR : 0) - | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ? - UVC_CTRL_FLAG_AUTO_UPDATE : 0); + info->flags |= (data[0] & UVC_CONTROL_CAP_GET ? + UVC_CTRL_FLAG_GET_CUR : 0) + | (data[0] & UVC_CONTROL_CAP_SET ? + UVC_CTRL_FLAG_SET_CUR : 0) + | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ? + UVC_CTRL_FLAG_AUTO_UPDATE : 0); kfree(data); return ret; @@ -1689,6 +1687,9 @@ static int uvc_ctrl_fill_xu_info(struct uvc_device *dev, info->size = le16_to_cpup((__le16 *)data); + info->flags = UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX + | UVC_CTRL_FLAG_GET_RES | UVC_CTRL_FLAG_GET_DEF; + ret = uvc_ctrl_get_flags(dev, ctrl, info); if (ret < 0) { uvc_trace(UVC_TRACE_CONTROL, diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 2469b49b2b30..8e138201330f 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -1871,13 +1871,6 @@ static void uvc_unregister_video(struct uvc_device *dev) { struct uvc_streaming *stream; - /* Unregistering all video devices might result in uvc_delete() being - * called from inside the loop if there's no open file handle. To avoid - * that, increment the refcount before iterating over the streams and - * decrement it when done. - */ - kref_get(&dev->ref); - list_for_each_entry(stream, &dev->streams, list) { if (!video_is_registered(&stream->vdev)) continue; @@ -1887,8 +1880,6 @@ static void uvc_unregister_video(struct uvc_device *dev) uvc_debugfs_cleanup_stream(stream); } - - kref_put(&dev->ref, uvc_delete); } int uvc_register_video_device(struct uvc_device *dev, @@ -2184,6 +2175,7 @@ static int uvc_probe(struct usb_interface *intf, error: uvc_unregister_video(dev); + kref_put(&dev->ref, uvc_delete); return -ENODEV; } @@ -2201,6 +2193,7 @@ static void uvc_disconnect(struct usb_interface *intf) return; uvc_unregister_video(dev); + kref_put(&dev->ref, uvc_delete); } static int uvc_suspend(struct usb_interface *intf, pm_message_t message) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index aa0082fe5833..b28c997a7ab0 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -163,14 +163,27 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, } } +static size_t uvc_video_ctrl_size(struct uvc_streaming *stream) +{ + /* + * Return the size of the video probe and commit controls, which depends + * on the protocol version. + */ + if (stream->dev->uvc_version < 0x0110) + return 26; + else if (stream->dev->uvc_version < 0x0150) + return 34; + else + return 48; +} + static int uvc_get_video_ctrl(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl, int probe, u8 query) { + u16 size = uvc_video_ctrl_size(stream); u8 *data; - u16 size; int ret; - size = stream->dev->uvc_version >= 0x0110 ? 34 : 26; if ((stream->dev->quirks & UVC_QUIRK_PROBE_DEF) && query == UVC_GET_DEF) return -EIO; @@ -225,7 +238,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream, ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]); ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]); - if (size == 34) { + if (size >= 34) { ctrl->dwClockFrequency = get_unaligned_le32(&data[26]); ctrl->bmFramingInfo = data[30]; ctrl->bPreferedVersion = data[31]; @@ -254,11 +267,10 @@ out: static int uvc_set_video_ctrl(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl, int probe) { + u16 size = uvc_video_ctrl_size(stream); u8 *data; - u16 size; int ret; - size = stream->dev->uvc_version >= 0x0110 ? 34 : 26; data = kzalloc(size, GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -275,7 +287,7 @@ static int uvc_set_video_ctrl(struct uvc_streaming *stream, put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]); put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]); - if (size == 34) { + if (size >= 34) { put_unaligned_le32(ctrl->dwClockFrequency, &data[26]); data[30] = ctrl->bmFramingInfo; data[31] = ctrl->bPreferedVersion; diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 8e37e7c5e0f7..b97090e85996 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -65,7 +65,6 @@ config VIDEOBUF_GEN config VIDEOBUF_DMA_SG tristate - depends on HAS_DMA select VIDEOBUF_GEN config VIDEOBUF_VMALLOC @@ -74,9 +73,4 @@ config VIDEOBUF_VMALLOC config VIDEOBUF_DMA_CONTIG tristate - depends on HAS_DMA - select VIDEOBUF_GEN - -config VIDEOBUF_DVB - tristate select VIDEOBUF_GEN diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index 7df54582e956..9ee57e1efefe 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -31,7 +31,6 @@ obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o -obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o ccflags-y += -I$(srctree)/drivers/media/dvb-frontends ccflags-y += -I$(srctree)/drivers/media/tuners diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 4312935f1dfc..6481212fda77 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -22,7 +22,18 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-ioctl.h> -/* Use the same argument order as copy_in_user */ +/** + * assign_in_user() - Copy from one __user var to another one + * + * @to: __user var where data will be stored + * @from: __user var where data will be retrieved. + * + * As this code very often needs to allocate userspace memory, it is easier + * to have a macro that will do both get_user() and put_user() at once. + * + * This function complements the macros defined at asm-generic/uaccess.h. + * It uses the same argument order as copy_in_user() + */ #define assign_in_user(to, from) \ ({ \ typeof(*from) __assign_tmp; \ @@ -30,6 +41,73 @@ get_user(__assign_tmp, from) || put_user(__assign_tmp, to); \ }) +/** + * get_user_cast() - Stores at a kernelspace local var the contents from a + * pointer with userspace data that is not tagged with __user. + * + * @__x: var where data will be stored + * @__ptr: var where data will be retrieved. + * + * Sometimes we need to declare a pointer without __user because it + * comes from a pointer struct field that will be retrieved from userspace + * by the 64-bit native ioctl handler. This function ensures that the + * @__ptr will be cast to __user before calling get_user() in order to + * avoid warnings with static code analyzers like smatch. + */ +#define get_user_cast(__x, __ptr) \ +({ \ + get_user(__x, (typeof(*__ptr) __user *)(__ptr)); \ +}) + +/** + * put_user_force() - Stores the contents of a kernelspace local var + * into a userspace pointer, removing any __user cast. + * + * @__x: var where data will be stored + * @__ptr: var where data will be retrieved. + * + * Sometimes we need to remove the __user attribute from some data, + * by passing the __force macro. This function ensures that the + * @__ptr will be cast with __force before calling put_user(), in order to + * avoid warnings with static code analyzers like smatch. + */ +#define put_user_force(__x, __ptr) \ +({ \ + put_user((typeof(*__x) __force *)(__x), __ptr); \ +}) + +/** + * assign_in_user_cast() - Copy from one __user var to another one + * + * @to: __user var where data will be stored + * @from: var where data will be retrieved that needs to be cast to __user. + * + * As this code very often needs to allocate userspace memory, it is easier + * to have a macro that will do both get_user_cast() and put_user() at once. + * + * This function should be used instead of assign_in_user() when the @from + * variable was not declared as __user. See get_user_cast() for more details. + * + * This function complements the macros defined at asm-generic/uaccess.h. + * It uses the same argument order as copy_in_user() + */ +#define assign_in_user_cast(to, from) \ +({ \ + typeof(*from) __assign_tmp; \ + \ + get_user_cast(__assign_tmp, from) || put_user(__assign_tmp, to);\ +}) + +/** + * native_ioctl - Ancillary function that calls the native 64 bits ioctl + * handler. + * + * @file: pointer to &struct file with the file handler + * @cmd: ioctl to be called + * @arg: arguments passed from/to the ioctl handler + * + * This function calls the native ioctl handler at v4l2-dev, e. g. v4l2_ioctl() + */ static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret = -ENOIOCTLCMD; @@ -41,6 +119,21 @@ static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } +/* + * Per-ioctl data copy handlers. + * + * Those come in pairs, with a get_v4l2_foo() and a put_v4l2_foo() routine, + * where "v4l2_foo" is the name of the V4L2 struct. + * + * They basically get two __user pointers, one with a 32-bits struct that + * came from the userspace call and a 64-bits struct, also allocated as + * userspace, but filled internally by do_video_ioctl(). + * + * For ioctls that have pointers inside it, the functions will also + * receive an ancillary buffer with extra space, used to pass extra + * data to the routine. + */ + struct v4l2_clip32 { struct v4l2_rect c; compat_caddr_t next; @@ -56,8 +149,8 @@ struct v4l2_window32 { __u8 global_alpha; }; -static int get_v4l2_window32(struct v4l2_window __user *kp, - struct v4l2_window32 __user *up, +static int get_v4l2_window32(struct v4l2_window __user *p64, + struct v4l2_window32 __user *p32, void __user *aux_buf, u32 aux_space) { struct v4l2_clip32 __user *uclips; @@ -65,26 +158,26 @@ static int get_v4l2_window32(struct v4l2_window __user *kp, compat_caddr_t p; u32 clipcount; - if (!access_ok(VERIFY_READ, up, sizeof(*up)) || - copy_in_user(&kp->w, &up->w, sizeof(up->w)) || - assign_in_user(&kp->field, &up->field) || - assign_in_user(&kp->chromakey, &up->chromakey) || - assign_in_user(&kp->global_alpha, &up->global_alpha) || - get_user(clipcount, &up->clipcount) || - put_user(clipcount, &kp->clipcount)) + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + copy_in_user(&p64->w, &p32->w, sizeof(p32->w)) || + assign_in_user(&p64->field, &p32->field) || + assign_in_user(&p64->chromakey, &p32->chromakey) || + assign_in_user(&p64->global_alpha, &p32->global_alpha) || + get_user(clipcount, &p32->clipcount) || + put_user(clipcount, &p64->clipcount)) return -EFAULT; if (clipcount > 2048) return -EINVAL; if (!clipcount) - return put_user(NULL, &kp->clips); + return put_user(NULL, &p64->clips); - if (get_user(p, &up->clips)) + if (get_user(p, &p32->clips)) return -EFAULT; uclips = compat_ptr(p); if (aux_space < clipcount * sizeof(*kclips)) return -EFAULT; kclips = aux_buf; - if (put_user(kclips, &kp->clips)) + if (put_user(kclips, &p64->clips)) return -EFAULT; while (clipcount--) { @@ -98,27 +191,27 @@ static int get_v4l2_window32(struct v4l2_window __user *kp, return 0; } -static int put_v4l2_window32(struct v4l2_window __user *kp, - struct v4l2_window32 __user *up) +static int put_v4l2_window32(struct v4l2_window __user *p64, + struct v4l2_window32 __user *p32) { struct v4l2_clip __user *kclips; struct v4l2_clip32 __user *uclips; compat_caddr_t p; u32 clipcount; - if (copy_in_user(&up->w, &kp->w, sizeof(kp->w)) || - assign_in_user(&up->field, &kp->field) || - assign_in_user(&up->chromakey, &kp->chromakey) || - assign_in_user(&up->global_alpha, &kp->global_alpha) || - get_user(clipcount, &kp->clipcount) || - put_user(clipcount, &up->clipcount)) + if (copy_in_user(&p32->w, &p64->w, sizeof(p64->w)) || + assign_in_user(&p32->field, &p64->field) || + assign_in_user(&p32->chromakey, &p64->chromakey) || + assign_in_user(&p32->global_alpha, &p64->global_alpha) || + get_user(clipcount, &p64->clipcount) || + put_user(clipcount, &p32->clipcount)) return -EFAULT; if (!clipcount) return 0; - if (get_user(kclips, &kp->clips)) + if (get_user(kclips, &p64->clips)) return -EFAULT; - if (get_user(p, &up->clips)) + if (get_user(p, &p32->clips)) return -EFAULT; uclips = compat_ptr(p); while (clipcount--) { @@ -161,11 +254,11 @@ struct v4l2_create_buffers32 { __u32 reserved[8]; }; -static int __bufsize_v4l2_format(struct v4l2_format32 __user *up, u32 *size) +static int __bufsize_v4l2_format(struct v4l2_format32 __user *p32, u32 *size) { u32 type; - if (get_user(type, &up->type)) + if (get_user(type, &p32->type)) return -EFAULT; switch (type) { @@ -173,7 +266,7 @@ static int __bufsize_v4l2_format(struct v4l2_format32 __user *up, u32 *size) case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: { u32 clipcount; - if (get_user(clipcount, &up->fmt.win.clipcount)) + if (get_user(clipcount, &p32->fmt.win.clipcount)) return -EFAULT; if (clipcount > 2048) return -EINVAL; @@ -186,141 +279,141 @@ static int __bufsize_v4l2_format(struct v4l2_format32 __user *up, u32 *size) } } -static int bufsize_v4l2_format(struct v4l2_format32 __user *up, u32 *size) +static int bufsize_v4l2_format(struct v4l2_format32 __user *p32, u32 *size) { - if (!access_ok(VERIFY_READ, up, sizeof(*up))) + if (!access_ok(VERIFY_READ, p32, sizeof(*p32))) return -EFAULT; - return __bufsize_v4l2_format(up, size); + return __bufsize_v4l2_format(p32, size); } -static int __get_v4l2_format32(struct v4l2_format __user *kp, - struct v4l2_format32 __user *up, +static int __get_v4l2_format32(struct v4l2_format __user *p64, + struct v4l2_format32 __user *p32, void __user *aux_buf, u32 aux_space) { u32 type; - if (get_user(type, &up->type) || put_user(type, &kp->type)) + if (get_user(type, &p32->type) || put_user(type, &p64->type)) return -EFAULT; switch (type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: - return copy_in_user(&kp->fmt.pix, &up->fmt.pix, - sizeof(kp->fmt.pix)) ? -EFAULT : 0; + return copy_in_user(&p64->fmt.pix, &p32->fmt.pix, + sizeof(p64->fmt.pix)) ? -EFAULT : 0; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - return copy_in_user(&kp->fmt.pix_mp, &up->fmt.pix_mp, - sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0; + return copy_in_user(&p64->fmt.pix_mp, &p32->fmt.pix_mp, + sizeof(p64->fmt.pix_mp)) ? -EFAULT : 0; case V4L2_BUF_TYPE_VIDEO_OVERLAY: case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - return get_v4l2_window32(&kp->fmt.win, &up->fmt.win, + return get_v4l2_window32(&p64->fmt.win, &p32->fmt.win, aux_buf, aux_space); case V4L2_BUF_TYPE_VBI_CAPTURE: case V4L2_BUF_TYPE_VBI_OUTPUT: - return copy_in_user(&kp->fmt.vbi, &up->fmt.vbi, - sizeof(kp->fmt.vbi)) ? -EFAULT : 0; + return copy_in_user(&p64->fmt.vbi, &p32->fmt.vbi, + sizeof(p64->fmt.vbi)) ? -EFAULT : 0; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - return copy_in_user(&kp->fmt.sliced, &up->fmt.sliced, - sizeof(kp->fmt.sliced)) ? -EFAULT : 0; + return copy_in_user(&p64->fmt.sliced, &p32->fmt.sliced, + sizeof(p64->fmt.sliced)) ? -EFAULT : 0; case V4L2_BUF_TYPE_SDR_CAPTURE: case V4L2_BUF_TYPE_SDR_OUTPUT: - return copy_in_user(&kp->fmt.sdr, &up->fmt.sdr, - sizeof(kp->fmt.sdr)) ? -EFAULT : 0; + return copy_in_user(&p64->fmt.sdr, &p32->fmt.sdr, + sizeof(p64->fmt.sdr)) ? -EFAULT : 0; case V4L2_BUF_TYPE_META_CAPTURE: - return copy_in_user(&kp->fmt.meta, &up->fmt.meta, - sizeof(kp->fmt.meta)) ? -EFAULT : 0; + return copy_in_user(&p64->fmt.meta, &p32->fmt.meta, + sizeof(p64->fmt.meta)) ? -EFAULT : 0; default: return -EINVAL; } } -static int get_v4l2_format32(struct v4l2_format __user *kp, - struct v4l2_format32 __user *up, +static int get_v4l2_format32(struct v4l2_format __user *p64, + struct v4l2_format32 __user *p32, void __user *aux_buf, u32 aux_space) { - if (!access_ok(VERIFY_READ, up, sizeof(*up))) + if (!access_ok(VERIFY_READ, p32, sizeof(*p32))) return -EFAULT; - return __get_v4l2_format32(kp, up, aux_buf, aux_space); + return __get_v4l2_format32(p64, p32, aux_buf, aux_space); } -static int bufsize_v4l2_create(struct v4l2_create_buffers32 __user *up, +static int bufsize_v4l2_create(struct v4l2_create_buffers32 __user *p32, u32 *size) { - if (!access_ok(VERIFY_READ, up, sizeof(*up))) + if (!access_ok(VERIFY_READ, p32, sizeof(*p32))) return -EFAULT; - return __bufsize_v4l2_format(&up->format, size); + return __bufsize_v4l2_format(&p32->format, size); } -static int get_v4l2_create32(struct v4l2_create_buffers __user *kp, - struct v4l2_create_buffers32 __user *up, +static int get_v4l2_create32(struct v4l2_create_buffers __user *p64, + struct v4l2_create_buffers32 __user *p32, void __user *aux_buf, u32 aux_space) { - if (!access_ok(VERIFY_READ, up, sizeof(*up)) || - copy_in_user(kp, up, + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + copy_in_user(p64, p32, offsetof(struct v4l2_create_buffers32, format))) return -EFAULT; - return __get_v4l2_format32(&kp->format, &up->format, + return __get_v4l2_format32(&p64->format, &p32->format, aux_buf, aux_space); } -static int __put_v4l2_format32(struct v4l2_format __user *kp, - struct v4l2_format32 __user *up) +static int __put_v4l2_format32(struct v4l2_format __user *p64, + struct v4l2_format32 __user *p32) { u32 type; - if (get_user(type, &kp->type)) + if (get_user(type, &p64->type)) return -EFAULT; switch (type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: - return copy_in_user(&up->fmt.pix, &kp->fmt.pix, - sizeof(kp->fmt.pix)) ? -EFAULT : 0; + return copy_in_user(&p32->fmt.pix, &p64->fmt.pix, + sizeof(p64->fmt.pix)) ? -EFAULT : 0; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - return copy_in_user(&up->fmt.pix_mp, &kp->fmt.pix_mp, - sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0; + return copy_in_user(&p32->fmt.pix_mp, &p64->fmt.pix_mp, + sizeof(p64->fmt.pix_mp)) ? -EFAULT : 0; case V4L2_BUF_TYPE_VIDEO_OVERLAY: case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - return put_v4l2_window32(&kp->fmt.win, &up->fmt.win); + return put_v4l2_window32(&p64->fmt.win, &p32->fmt.win); case V4L2_BUF_TYPE_VBI_CAPTURE: case V4L2_BUF_TYPE_VBI_OUTPUT: - return copy_in_user(&up->fmt.vbi, &kp->fmt.vbi, - sizeof(kp->fmt.vbi)) ? -EFAULT : 0; + return copy_in_user(&p32->fmt.vbi, &p64->fmt.vbi, + sizeof(p64->fmt.vbi)) ? -EFAULT : 0; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - return copy_in_user(&up->fmt.sliced, &kp->fmt.sliced, - sizeof(kp->fmt.sliced)) ? -EFAULT : 0; + return copy_in_user(&p32->fmt.sliced, &p64->fmt.sliced, + sizeof(p64->fmt.sliced)) ? -EFAULT : 0; case V4L2_BUF_TYPE_SDR_CAPTURE: case V4L2_BUF_TYPE_SDR_OUTPUT: - return copy_in_user(&up->fmt.sdr, &kp->fmt.sdr, - sizeof(kp->fmt.sdr)) ? -EFAULT : 0; + return copy_in_user(&p32->fmt.sdr, &p64->fmt.sdr, + sizeof(p64->fmt.sdr)) ? -EFAULT : 0; case V4L2_BUF_TYPE_META_CAPTURE: - return copy_in_user(&up->fmt.meta, &kp->fmt.meta, - sizeof(kp->fmt.meta)) ? -EFAULT : 0; + return copy_in_user(&p32->fmt.meta, &p64->fmt.meta, + sizeof(p64->fmt.meta)) ? -EFAULT : 0; default: return -EINVAL; } } -static int put_v4l2_format32(struct v4l2_format __user *kp, - struct v4l2_format32 __user *up) +static int put_v4l2_format32(struct v4l2_format __user *p64, + struct v4l2_format32 __user *p32) { - if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) + if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32))) return -EFAULT; - return __put_v4l2_format32(kp, up); + return __put_v4l2_format32(p64, p32); } -static int put_v4l2_create32(struct v4l2_create_buffers __user *kp, - struct v4l2_create_buffers32 __user *up) +static int put_v4l2_create32(struct v4l2_create_buffers __user *p64, + struct v4l2_create_buffers32 __user *p32) { - if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || - copy_in_user(up, kp, + if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + copy_in_user(p32, p64, offsetof(struct v4l2_create_buffers32, format)) || - copy_in_user(up->reserved, kp->reserved, sizeof(kp->reserved))) + copy_in_user(p32->reserved, p64->reserved, sizeof(p64->reserved))) return -EFAULT; - return __put_v4l2_format32(&kp->format, &up->format); + return __put_v4l2_format32(&p64->format, &p32->format); } struct v4l2_standard32 { @@ -332,27 +425,27 @@ struct v4l2_standard32 { __u32 reserved[4]; }; -static int get_v4l2_standard32(struct v4l2_standard __user *kp, - struct v4l2_standard32 __user *up) +static int get_v4l2_standard32(struct v4l2_standard __user *p64, + struct v4l2_standard32 __user *p32) { /* other fields are not set by the user, nor used by the driver */ - if (!access_ok(VERIFY_READ, up, sizeof(*up)) || - assign_in_user(&kp->index, &up->index)) + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + assign_in_user(&p64->index, &p32->index)) return -EFAULT; return 0; } -static int put_v4l2_standard32(struct v4l2_standard __user *kp, - struct v4l2_standard32 __user *up) +static int put_v4l2_standard32(struct v4l2_standard __user *p64, + struct v4l2_standard32 __user *p32) { - if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || - assign_in_user(&up->index, &kp->index) || - assign_in_user(&up->id, &kp->id) || - copy_in_user(up->name, kp->name, sizeof(up->name)) || - copy_in_user(&up->frameperiod, &kp->frameperiod, - sizeof(up->frameperiod)) || - assign_in_user(&up->framelines, &kp->framelines) || - copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved))) + if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + assign_in_user(&p32->index, &p64->index) || + assign_in_user(&p32->id, &p64->id) || + copy_in_user(p32->name, p64->name, sizeof(p32->name)) || + copy_in_user(&p32->frameperiod, &p64->frameperiod, + sizeof(p32->frameperiod)) || + assign_in_user(&p32->framelines, &p64->framelines) || + copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved))) return -EFAULT; return 0; } @@ -392,31 +485,31 @@ struct v4l2_buffer32 { __u32 reserved; }; -static int get_v4l2_plane32(struct v4l2_plane __user *up, - struct v4l2_plane32 __user *up32, +static int get_v4l2_plane32(struct v4l2_plane __user *p64, + struct v4l2_plane32 __user *p32, enum v4l2_memory memory) { compat_ulong_t p; - if (copy_in_user(up, up32, 2 * sizeof(__u32)) || - copy_in_user(&up->data_offset, &up32->data_offset, - sizeof(up->data_offset))) + if (copy_in_user(p64, p32, 2 * sizeof(__u32)) || + copy_in_user(&p64->data_offset, &p32->data_offset, + sizeof(p64->data_offset))) return -EFAULT; switch (memory) { case V4L2_MEMORY_MMAP: case V4L2_MEMORY_OVERLAY: - if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset, - sizeof(up32->m.mem_offset))) + if (copy_in_user(&p64->m.mem_offset, &p32->m.mem_offset, + sizeof(p32->m.mem_offset))) return -EFAULT; break; case V4L2_MEMORY_USERPTR: - if (get_user(p, &up32->m.userptr) || - put_user((unsigned long)compat_ptr(p), &up->m.userptr)) + if (get_user(p, &p32->m.userptr) || + put_user((unsigned long)compat_ptr(p), &p64->m.userptr)) return -EFAULT; break; case V4L2_MEMORY_DMABUF: - if (copy_in_user(&up->m.fd, &up32->m.fd, sizeof(up32->m.fd))) + if (copy_in_user(&p64->m.fd, &p32->m.fd, sizeof(p32->m.fd))) return -EFAULT; break; } @@ -424,32 +517,32 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, return 0; } -static int put_v4l2_plane32(struct v4l2_plane __user *up, - struct v4l2_plane32 __user *up32, +static int put_v4l2_plane32(struct v4l2_plane __user *p64, + struct v4l2_plane32 __user *p32, enum v4l2_memory memory) { unsigned long p; - if (copy_in_user(up32, up, 2 * sizeof(__u32)) || - copy_in_user(&up32->data_offset, &up->data_offset, - sizeof(up->data_offset))) + if (copy_in_user(p32, p64, 2 * sizeof(__u32)) || + copy_in_user(&p32->data_offset, &p64->data_offset, + sizeof(p64->data_offset))) return -EFAULT; switch (memory) { case V4L2_MEMORY_MMAP: case V4L2_MEMORY_OVERLAY: - if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset, - sizeof(up->m.mem_offset))) + if (copy_in_user(&p32->m.mem_offset, &p64->m.mem_offset, + sizeof(p64->m.mem_offset))) return -EFAULT; break; case V4L2_MEMORY_USERPTR: - if (get_user(p, &up->m.userptr) || - put_user((compat_ulong_t)ptr_to_compat((__force void *)p), - &up32->m.userptr)) + if (get_user(p, &p64->m.userptr) || + put_user((compat_ulong_t)ptr_to_compat((void __user *)p), + &p32->m.userptr)) return -EFAULT; break; case V4L2_MEMORY_DMABUF: - if (copy_in_user(&up32->m.fd, &up->m.fd, sizeof(up->m.fd))) + if (copy_in_user(&p32->m.fd, &p64->m.fd, sizeof(p64->m.fd))) return -EFAULT; break; } @@ -457,14 +550,14 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, return 0; } -static int bufsize_v4l2_buffer(struct v4l2_buffer32 __user *up, u32 *size) +static int bufsize_v4l2_buffer(struct v4l2_buffer32 __user *p32, u32 *size) { u32 type; u32 length; - if (!access_ok(VERIFY_READ, up, sizeof(*up)) || - get_user(type, &up->type) || - get_user(length, &up->length)) + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + get_user(type, &p32->type) || + get_user(length, &p32->length)) return -EFAULT; if (V4L2_TYPE_IS_MULTIPLANAR(type)) { @@ -482,8 +575,8 @@ static int bufsize_v4l2_buffer(struct v4l2_buffer32 __user *up, u32 *size) return 0; } -static int get_v4l2_buffer32(struct v4l2_buffer __user *kp, - struct v4l2_buffer32 __user *up, +static int get_v4l2_buffer32(struct v4l2_buffer __user *p64, + struct v4l2_buffer32 __user *p32, void __user *aux_buf, u32 aux_space) { u32 type; @@ -494,24 +587,24 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *kp, compat_caddr_t p; int ret; - if (!access_ok(VERIFY_READ, up, sizeof(*up)) || - assign_in_user(&kp->index, &up->index) || - get_user(type, &up->type) || - put_user(type, &kp->type) || - assign_in_user(&kp->flags, &up->flags) || - get_user(memory, &up->memory) || - put_user(memory, &kp->memory) || - get_user(length, &up->length) || - put_user(length, &kp->length)) + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + assign_in_user(&p64->index, &p32->index) || + get_user(type, &p32->type) || + put_user(type, &p64->type) || + assign_in_user(&p64->flags, &p32->flags) || + get_user(memory, &p32->memory) || + put_user(memory, &p64->memory) || + get_user(length, &p32->length) || + put_user(length, &p64->length)) return -EFAULT; if (V4L2_TYPE_IS_OUTPUT(type)) - if (assign_in_user(&kp->bytesused, &up->bytesused) || - assign_in_user(&kp->field, &up->field) || - assign_in_user(&kp->timestamp.tv_sec, - &up->timestamp.tv_sec) || - assign_in_user(&kp->timestamp.tv_usec, - &up->timestamp.tv_usec)) + if (assign_in_user(&p64->bytesused, &p32->bytesused) || + assign_in_user(&p64->field, &p32->field) || + assign_in_user(&p64->timestamp.tv_sec, + &p32->timestamp.tv_sec) || + assign_in_user(&p64->timestamp.tv_usec, + &p32->timestamp.tv_usec)) return -EFAULT; if (V4L2_TYPE_IS_MULTIPLANAR(type)) { @@ -522,12 +615,12 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *kp, * num_planes == 0 is legal, e.g. when userspace doesn't * need planes array on DQBUF */ - return put_user(NULL, &kp->m.planes); + return put_user(NULL, &p64->m.planes); } if (num_planes > VIDEO_MAX_PLANES) return -EINVAL; - if (get_user(p, &up->m.planes)) + if (get_user(p, &p32->m.planes)) return -EFAULT; uplane32 = compat_ptr(p); @@ -543,8 +636,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *kp, return -EFAULT; uplane = aux_buf; - if (put_user((__force struct v4l2_plane *)uplane, - &kp->m.planes)) + if (put_user_force(uplane, &p64->m.planes)) return -EFAULT; while (num_planes--) { @@ -558,20 +650,20 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *kp, switch (memory) { case V4L2_MEMORY_MMAP: case V4L2_MEMORY_OVERLAY: - if (assign_in_user(&kp->m.offset, &up->m.offset)) + if (assign_in_user(&p64->m.offset, &p32->m.offset)) return -EFAULT; break; case V4L2_MEMORY_USERPTR: { compat_ulong_t userptr; - if (get_user(userptr, &up->m.userptr) || + if (get_user(userptr, &p32->m.userptr) || put_user((unsigned long)compat_ptr(userptr), - &kp->m.userptr)) + &p64->m.userptr)) return -EFAULT; break; } case V4L2_MEMORY_DMABUF: - if (assign_in_user(&kp->m.fd, &up->m.fd)) + if (assign_in_user(&p64->m.fd, &p32->m.fd)) return -EFAULT; break; } @@ -580,36 +672,36 @@ static int get_v4l2_buffer32(struct v4l2_buffer __user *kp, return 0; } -static int put_v4l2_buffer32(struct v4l2_buffer __user *kp, - struct v4l2_buffer32 __user *up) +static int put_v4l2_buffer32(struct v4l2_buffer __user *p64, + struct v4l2_buffer32 __user *p32) { u32 type; u32 length; enum v4l2_memory memory; struct v4l2_plane32 __user *uplane32; - struct v4l2_plane __user *uplane; + struct v4l2_plane *uplane; compat_caddr_t p; int ret; - if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || - assign_in_user(&up->index, &kp->index) || - get_user(type, &kp->type) || - put_user(type, &up->type) || - assign_in_user(&up->flags, &kp->flags) || - get_user(memory, &kp->memory) || - put_user(memory, &up->memory)) + if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + assign_in_user(&p32->index, &p64->index) || + get_user(type, &p64->type) || + put_user(type, &p32->type) || + assign_in_user(&p32->flags, &p64->flags) || + get_user(memory, &p64->memory) || + put_user(memory, &p32->memory)) return -EFAULT; - if (assign_in_user(&up->bytesused, &kp->bytesused) || - assign_in_user(&up->field, &kp->field) || - assign_in_user(&up->timestamp.tv_sec, &kp->timestamp.tv_sec) || - assign_in_user(&up->timestamp.tv_usec, &kp->timestamp.tv_usec) || - copy_in_user(&up->timecode, &kp->timecode, sizeof(kp->timecode)) || - assign_in_user(&up->sequence, &kp->sequence) || - assign_in_user(&up->reserved2, &kp->reserved2) || - assign_in_user(&up->reserved, &kp->reserved) || - get_user(length, &kp->length) || - put_user(length, &up->length)) + if (assign_in_user(&p32->bytesused, &p64->bytesused) || + assign_in_user(&p32->field, &p64->field) || + assign_in_user(&p32->timestamp.tv_sec, &p64->timestamp.tv_sec) || + assign_in_user(&p32->timestamp.tv_usec, &p64->timestamp.tv_usec) || + copy_in_user(&p32->timecode, &p64->timecode, sizeof(p64->timecode)) || + assign_in_user(&p32->sequence, &p64->sequence) || + assign_in_user(&p32->reserved2, &p64->reserved2) || + assign_in_user(&p32->reserved, &p64->reserved) || + get_user(length, &p64->length) || + put_user(length, &p32->length)) return -EFAULT; if (V4L2_TYPE_IS_MULTIPLANAR(type)) { @@ -617,15 +709,23 @@ static int put_v4l2_buffer32(struct v4l2_buffer __user *kp, if (num_planes == 0) return 0; - - if (get_user(uplane, ((__force struct v4l2_plane __user **)&kp->m.planes))) + /* We need to define uplane without __user, even though + * it does point to data in userspace here. The reason is + * that v4l2-ioctl.c copies it from userspace to kernelspace, + * so its definition in videodev2.h doesn't have a + * __user markup. Defining uplane with __user causes + * smatch warnings, so instead declare it without __user + * and cast it as a userspace pointer to put_v4l2_plane32(). + */ + if (get_user(uplane, &p64->m.planes)) return -EFAULT; - if (get_user(p, &up->m.planes)) + if (get_user(p, &p32->m.planes)) return -EFAULT; uplane32 = compat_ptr(p); while (num_planes--) { - ret = put_v4l2_plane32(uplane, uplane32, memory); + ret = put_v4l2_plane32((void __user *)uplane, + uplane32, memory); if (ret) return ret; ++uplane; @@ -635,15 +735,15 @@ static int put_v4l2_buffer32(struct v4l2_buffer __user *kp, switch (memory) { case V4L2_MEMORY_MMAP: case V4L2_MEMORY_OVERLAY: - if (assign_in_user(&up->m.offset, &kp->m.offset)) + if (assign_in_user(&p32->m.offset, &p64->m.offset)) return -EFAULT; break; case V4L2_MEMORY_USERPTR: - if (assign_in_user(&up->m.userptr, &kp->m.userptr)) + if (assign_in_user(&p32->m.userptr, &p64->m.userptr)) return -EFAULT; break; case V4L2_MEMORY_DMABUF: - if (assign_in_user(&up->m.fd, &kp->m.fd)) + if (assign_in_user(&p32->m.fd, &p64->m.fd)) return -EFAULT; break; } @@ -668,32 +768,32 @@ struct v4l2_framebuffer32 { } fmt; }; -static int get_v4l2_framebuffer32(struct v4l2_framebuffer __user *kp, - struct v4l2_framebuffer32 __user *up) +static int get_v4l2_framebuffer32(struct v4l2_framebuffer __user *p64, + struct v4l2_framebuffer32 __user *p32) { compat_caddr_t tmp; - if (!access_ok(VERIFY_READ, up, sizeof(*up)) || - get_user(tmp, &up->base) || - put_user((__force void *)compat_ptr(tmp), &kp->base) || - assign_in_user(&kp->capability, &up->capability) || - assign_in_user(&kp->flags, &up->flags) || - copy_in_user(&kp->fmt, &up->fmt, sizeof(kp->fmt))) + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + get_user(tmp, &p32->base) || + put_user_force(compat_ptr(tmp), &p64->base) || + assign_in_user(&p64->capability, &p32->capability) || + assign_in_user(&p64->flags, &p32->flags) || + copy_in_user(&p64->fmt, &p32->fmt, sizeof(p64->fmt))) return -EFAULT; return 0; } -static int put_v4l2_framebuffer32(struct v4l2_framebuffer __user *kp, - struct v4l2_framebuffer32 __user *up) +static int put_v4l2_framebuffer32(struct v4l2_framebuffer __user *p64, + struct v4l2_framebuffer32 __user *p32) { void *base; - if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || - get_user(base, &kp->base) || - put_user(ptr_to_compat(base), &up->base) || - assign_in_user(&up->capability, &kp->capability) || - assign_in_user(&up->flags, &kp->flags) || - copy_in_user(&up->fmt, &kp->fmt, sizeof(kp->fmt))) + if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + get_user(base, &p64->base) || + put_user(ptr_to_compat((void __user *)base), &p32->base) || + assign_in_user(&p32->capability, &p64->capability) || + assign_in_user(&p32->flags, &p64->flags) || + copy_in_user(&p32->fmt, &p64->fmt, sizeof(p64->fmt))) return -EFAULT; return 0; } @@ -714,18 +814,18 @@ struct v4l2_input32 { * The 64-bit v4l2_input struct has extra padding at the end of the struct. * Otherwise it is identical to the 32-bit version. */ -static inline int get_v4l2_input32(struct v4l2_input __user *kp, - struct v4l2_input32 __user *up) +static inline int get_v4l2_input32(struct v4l2_input __user *p64, + struct v4l2_input32 __user *p32) { - if (copy_in_user(kp, up, sizeof(*up))) + if (copy_in_user(p64, p32, sizeof(*p32))) return -EFAULT; return 0; } -static inline int put_v4l2_input32(struct v4l2_input __user *kp, - struct v4l2_input32 __user *up) +static inline int put_v4l2_input32(struct v4l2_input __user *p64, + struct v4l2_input32 __user *p32) { - if (copy_in_user(up, kp, sizeof(*up))) + if (copy_in_user(p32, p64, sizeof(*p32))) return -EFAULT; return 0; } @@ -779,13 +879,13 @@ static inline bool ctrl_is_pointer(struct file *file, u32 id) (qec.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD); } -static int bufsize_v4l2_ext_controls(struct v4l2_ext_controls32 __user *up, +static int bufsize_v4l2_ext_controls(struct v4l2_ext_controls32 __user *p32, u32 *size) { u32 count; - if (!access_ok(VERIFY_READ, up, sizeof(*up)) || - get_user(count, &up->count)) + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + get_user(count, &p32->count)) return -EFAULT; if (count > V4L2_CID_MAX_CTRLS) return -EINVAL; @@ -794,8 +894,8 @@ static int bufsize_v4l2_ext_controls(struct v4l2_ext_controls32 __user *up, } static int get_v4l2_ext_controls32(struct file *file, - struct v4l2_ext_controls __user *kp, - struct v4l2_ext_controls32 __user *up, + struct v4l2_ext_controls __user *p64, + struct v4l2_ext_controls32 __user *p32, void __user *aux_buf, u32 aux_space) { struct v4l2_ext_control32 __user *ucontrols; @@ -804,19 +904,19 @@ static int get_v4l2_ext_controls32(struct file *file, u32 n; compat_caddr_t p; - if (!access_ok(VERIFY_READ, up, sizeof(*up)) || - assign_in_user(&kp->which, &up->which) || - get_user(count, &up->count) || - put_user(count, &kp->count) || - assign_in_user(&kp->error_idx, &up->error_idx) || - copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved))) + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + assign_in_user(&p64->which, &p32->which) || + get_user(count, &p32->count) || + put_user(count, &p64->count) || + assign_in_user(&p64->error_idx, &p32->error_idx) || + copy_in_user(p64->reserved, p32->reserved, sizeof(p64->reserved))) return -EFAULT; if (count == 0) - return put_user(NULL, &kp->controls); + return put_user(NULL, &p64->controls); if (count > V4L2_CID_MAX_CTRLS) return -EINVAL; - if (get_user(p, &up->controls)) + if (get_user(p, &p32->controls)) return -EFAULT; ucontrols = compat_ptr(p); if (!access_ok(VERIFY_READ, ucontrols, count * sizeof(*ucontrols))) @@ -824,8 +924,7 @@ static int get_v4l2_ext_controls32(struct file *file, if (aux_space < count * sizeof(*kcontrols)) return -EFAULT; kcontrols = aux_buf; - if (put_user((__force struct v4l2_ext_control *)kcontrols, - &kp->controls)) + if (put_user_force(kcontrols, &p64->controls)) return -EFAULT; for (n = 0; n < count; n++) { @@ -853,27 +952,35 @@ static int get_v4l2_ext_controls32(struct file *file, } static int put_v4l2_ext_controls32(struct file *file, - struct v4l2_ext_controls __user *kp, - struct v4l2_ext_controls32 __user *up) + struct v4l2_ext_controls __user *p64, + struct v4l2_ext_controls32 __user *p32) { struct v4l2_ext_control32 __user *ucontrols; - struct v4l2_ext_control __user *kcontrols; + struct v4l2_ext_control *kcontrols; u32 count; u32 n; compat_caddr_t p; - if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || - assign_in_user(&up->which, &kp->which) || - get_user(count, &kp->count) || - put_user(count, &up->count) || - assign_in_user(&up->error_idx, &kp->error_idx) || - copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)) || - get_user(kcontrols, &kp->controls)) + /* + * We need to define kcontrols without __user, even though it does + * point to data in userspace here. The reason is that v4l2-ioctl.c + * copies it from userspace to kernelspace, so its definition in + * videodev2.h doesn't have a __user markup. Defining kcontrols + * with __user causes smatch warnings, so instead declare it + * without __user and cast it as a userspace pointer where needed. + */ + if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + assign_in_user(&p32->which, &p64->which) || + get_user(count, &p64->count) || + put_user(count, &p32->count) || + assign_in_user(&p32->error_idx, &p64->error_idx) || + copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved)) || + get_user(kcontrols, &p64->controls)) return -EFAULT; - if (!count) + if (!count || count > (U32_MAX/sizeof(*ucontrols))) return 0; - if (get_user(p, &up->controls)) + if (get_user(p, &p32->controls)) return -EFAULT; ucontrols = compat_ptr(p); if (!access_ok(VERIFY_WRITE, ucontrols, count * sizeof(*ucontrols))) @@ -883,10 +990,11 @@ static int put_v4l2_ext_controls32(struct file *file, unsigned int size = sizeof(*ucontrols); u32 id; - if (get_user(id, &kcontrols->id) || + if (get_user_cast(id, &kcontrols->id) || put_user(id, &ucontrols->id) || - assign_in_user(&ucontrols->size, &kcontrols->size) || - copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2, + assign_in_user_cast(&ucontrols->size, &kcontrols->size) || + copy_in_user(&ucontrols->reserved2, + (void __user *)&kcontrols->reserved2, sizeof(ucontrols->reserved2))) return -EFAULT; @@ -898,7 +1006,8 @@ static int put_v4l2_ext_controls32(struct file *file, if (ctrl_is_pointer(file, id)) size -= sizeof(ucontrols->value64); - if (copy_in_user(ucontrols, kcontrols, size)) + if (copy_in_user(ucontrols, + (void __user *)kcontrols, size)) return -EFAULT; ucontrols++; @@ -920,18 +1029,18 @@ struct v4l2_event32 { __u32 reserved[8]; }; -static int put_v4l2_event32(struct v4l2_event __user *kp, - struct v4l2_event32 __user *up) +static int put_v4l2_event32(struct v4l2_event __user *p64, + struct v4l2_event32 __user *p32) { - if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || - assign_in_user(&up->type, &kp->type) || - copy_in_user(&up->u, &kp->u, sizeof(kp->u)) || - assign_in_user(&up->pending, &kp->pending) || - assign_in_user(&up->sequence, &kp->sequence) || - assign_in_user(&up->timestamp.tv_sec, &kp->timestamp.tv_sec) || - assign_in_user(&up->timestamp.tv_nsec, &kp->timestamp.tv_nsec) || - assign_in_user(&up->id, &kp->id) || - copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved))) + if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + assign_in_user(&p32->type, &p64->type) || + copy_in_user(&p32->u, &p64->u, sizeof(p64->u)) || + assign_in_user(&p32->pending, &p64->pending) || + assign_in_user(&p32->sequence, &p64->sequence) || + assign_in_user(&p32->timestamp.tv_sec, &p64->timestamp.tv_sec) || + assign_in_user(&p32->timestamp.tv_nsec, &p64->timestamp.tv_nsec) || + assign_in_user(&p32->id, &p64->id) || + copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved))) return -EFAULT; return 0; } @@ -944,38 +1053,45 @@ struct v4l2_edid32 { compat_caddr_t edid; }; -static int get_v4l2_edid32(struct v4l2_edid __user *kp, - struct v4l2_edid32 __user *up) +static int get_v4l2_edid32(struct v4l2_edid __user *p64, + struct v4l2_edid32 __user *p32) { compat_uptr_t tmp; - if (!access_ok(VERIFY_READ, up, sizeof(*up)) || - assign_in_user(&kp->pad, &up->pad) || - assign_in_user(&kp->start_block, &up->start_block) || - assign_in_user(&kp->blocks, &up->blocks) || - get_user(tmp, &up->edid) || - put_user(compat_ptr(tmp), &kp->edid) || - copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved))) + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + assign_in_user(&p64->pad, &p32->pad) || + assign_in_user(&p64->start_block, &p32->start_block) || + assign_in_user_cast(&p64->blocks, &p32->blocks) || + get_user(tmp, &p32->edid) || + put_user_force(compat_ptr(tmp), &p64->edid) || + copy_in_user(p64->reserved, p32->reserved, sizeof(p64->reserved))) return -EFAULT; return 0; } -static int put_v4l2_edid32(struct v4l2_edid __user *kp, - struct v4l2_edid32 __user *up) +static int put_v4l2_edid32(struct v4l2_edid __user *p64, + struct v4l2_edid32 __user *p32) { void *edid; - if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) || - assign_in_user(&up->pad, &kp->pad) || - assign_in_user(&up->start_block, &kp->start_block) || - assign_in_user(&up->blocks, &kp->blocks) || - get_user(edid, &kp->edid) || - put_user(ptr_to_compat(edid), &up->edid) || - copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved))) + if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + assign_in_user(&p32->pad, &p64->pad) || + assign_in_user(&p32->start_block, &p64->start_block) || + assign_in_user(&p32->blocks, &p64->blocks) || + get_user(edid, &p64->edid) || + put_user(ptr_to_compat((void __user *)edid), &p32->edid) || + copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved))) return -EFAULT; return 0; } +/* + * List of ioctls that require 32-bits/64-bits conversion + * + * The V4L2 ioctls that aren't listed there don't have pointer arguments + * and the struct size is identical for both 32 and 64 bits versions, so + * they don't need translations. + */ #define VIDIOC_G_FMT32 _IOWR('V', 4, struct v4l2_format32) #define VIDIOC_S_FMT32 _IOWR('V', 5, struct v4l2_format32) @@ -1004,27 +1120,61 @@ static int put_v4l2_edid32(struct v4l2_edid __user *kp, #define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32) #define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32) +/** + * alloc_userspace() - Allocates a 64-bits userspace pointer compatible + * for calling the native 64-bits version of an ioctl. + * + * @size: size of the structure itself to be allocated. + * @aux_space: extra size needed to store "extra" data, e.g. space for + * other __user data that is pointed to fields inside the + * structure. + * @new_p64: pointer to a pointer to be filled with the allocated struct. + * + * Return: + * + * if it can't allocate memory, either -ENOMEM or -EFAULT will be returned. + * Zero otherwise. + */ static int alloc_userspace(unsigned int size, u32 aux_space, - void __user **up_native) + void __user **new_p64) { - *up_native = compat_alloc_user_space(size + aux_space); - if (!*up_native) + *new_p64 = compat_alloc_user_space(size + aux_space); + if (!*new_p64) return -ENOMEM; - if (clear_user(*up_native, size)) + if (clear_user(*new_p64, size)) return -EFAULT; return 0; } +/** + * do_video_ioctl() - Ancillary function with handles a compat32 ioctl call + * + * @file: pointer to &struct file with the file handler + * @cmd: ioctl to be called + * @arg: arguments passed from/to the ioctl handler + * + * This function is called when a 32 bits application calls a V4L2 ioctl + * and the Kernel is compiled with 64 bits. + * + * This function is called by v4l2_compat_ioctl32() when the function is + * not private to some specific driver. + * + * It converts a 32-bits struct into a 64 bits one, calls the native 64-bits + * ioctl handler and fills back the 32-bits struct with the results of the + * native call. + */ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - void __user *up = compat_ptr(arg); - void __user *up_native = NULL; + void __user *p32 = compat_ptr(arg); + void __user *new_p64 = NULL; void __user *aux_buf; u32 aux_space; int compatible_arg = 1; long err = 0; - /* First, convert the command. */ + /* + * 1. When struct size is different, converts the command. + */ switch (cmd) { case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break; case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break; @@ -1053,56 +1203,61 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar case VIDIOC_S_EDID32: cmd = VIDIOC_S_EDID; break; } + /* + * 2. Allocates a 64-bits userspace pointer to store the + * values of the ioctl and copy data from the 32-bits __user + * argument into it. + */ switch (cmd) { case VIDIOC_OVERLAY: case VIDIOC_STREAMON: case VIDIOC_STREAMOFF: case VIDIOC_S_INPUT: case VIDIOC_S_OUTPUT: - err = alloc_userspace(sizeof(unsigned int), 0, &up_native); - if (!err && assign_in_user((unsigned int __user *)up_native, - (compat_uint_t __user *)up)) + err = alloc_userspace(sizeof(unsigned int), 0, &new_p64); + if (!err && assign_in_user((unsigned int __user *)new_p64, + (compat_uint_t __user *)p32)) err = -EFAULT; compatible_arg = 0; break; case VIDIOC_G_INPUT: case VIDIOC_G_OUTPUT: - err = alloc_userspace(sizeof(unsigned int), 0, &up_native); + err = alloc_userspace(sizeof(unsigned int), 0, &new_p64); compatible_arg = 0; break; case VIDIOC_G_EDID: case VIDIOC_S_EDID: - err = alloc_userspace(sizeof(struct v4l2_edid), 0, &up_native); + err = alloc_userspace(sizeof(struct v4l2_edid), 0, &new_p64); if (!err) - err = get_v4l2_edid32(up_native, up); + err = get_v4l2_edid32(new_p64, p32); compatible_arg = 0; break; case VIDIOC_G_FMT: case VIDIOC_S_FMT: case VIDIOC_TRY_FMT: - err = bufsize_v4l2_format(up, &aux_space); + err = bufsize_v4l2_format(p32, &aux_space); if (!err) err = alloc_userspace(sizeof(struct v4l2_format), - aux_space, &up_native); + aux_space, &new_p64); if (!err) { - aux_buf = up_native + sizeof(struct v4l2_format); - err = get_v4l2_format32(up_native, up, + aux_buf = new_p64 + sizeof(struct v4l2_format); + err = get_v4l2_format32(new_p64, p32, aux_buf, aux_space); } compatible_arg = 0; break; case VIDIOC_CREATE_BUFS: - err = bufsize_v4l2_create(up, &aux_space); + err = bufsize_v4l2_create(p32, &aux_space); if (!err) err = alloc_userspace(sizeof(struct v4l2_create_buffers), - aux_space, &up_native); + aux_space, &new_p64); if (!err) { - aux_buf = up_native + sizeof(struct v4l2_create_buffers); - err = get_v4l2_create32(up_native, up, + aux_buf = new_p64 + sizeof(struct v4l2_create_buffers); + err = get_v4l2_create32(new_p64, p32, aux_buf, aux_space); } compatible_arg = 0; @@ -1112,13 +1267,13 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar case VIDIOC_QUERYBUF: case VIDIOC_QBUF: case VIDIOC_DQBUF: - err = bufsize_v4l2_buffer(up, &aux_space); + err = bufsize_v4l2_buffer(p32, &aux_space); if (!err) err = alloc_userspace(sizeof(struct v4l2_buffer), - aux_space, &up_native); + aux_space, &new_p64); if (!err) { - aux_buf = up_native + sizeof(struct v4l2_buffer); - err = get_v4l2_buffer32(up_native, up, + aux_buf = new_p64 + sizeof(struct v4l2_buffer); + err = get_v4l2_buffer32(new_p64, p32, aux_buf, aux_space); } compatible_arg = 0; @@ -1126,133 +1281,165 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar case VIDIOC_S_FBUF: err = alloc_userspace(sizeof(struct v4l2_framebuffer), 0, - &up_native); + &new_p64); if (!err) - err = get_v4l2_framebuffer32(up_native, up); + err = get_v4l2_framebuffer32(new_p64, p32); compatible_arg = 0; break; case VIDIOC_G_FBUF: err = alloc_userspace(sizeof(struct v4l2_framebuffer), 0, - &up_native); + &new_p64); compatible_arg = 0; break; case VIDIOC_ENUMSTD: err = alloc_userspace(sizeof(struct v4l2_standard), 0, - &up_native); + &new_p64); if (!err) - err = get_v4l2_standard32(up_native, up); + err = get_v4l2_standard32(new_p64, p32); compatible_arg = 0; break; case VIDIOC_ENUMINPUT: - err = alloc_userspace(sizeof(struct v4l2_input), 0, &up_native); + err = alloc_userspace(sizeof(struct v4l2_input), 0, &new_p64); if (!err) - err = get_v4l2_input32(up_native, up); + err = get_v4l2_input32(new_p64, p32); compatible_arg = 0; break; case VIDIOC_G_EXT_CTRLS: case VIDIOC_S_EXT_CTRLS: case VIDIOC_TRY_EXT_CTRLS: - err = bufsize_v4l2_ext_controls(up, &aux_space); + err = bufsize_v4l2_ext_controls(p32, &aux_space); if (!err) err = alloc_userspace(sizeof(struct v4l2_ext_controls), - aux_space, &up_native); + aux_space, &new_p64); if (!err) { - aux_buf = up_native + sizeof(struct v4l2_ext_controls); - err = get_v4l2_ext_controls32(file, up_native, up, + aux_buf = new_p64 + sizeof(struct v4l2_ext_controls); + err = get_v4l2_ext_controls32(file, new_p64, p32, aux_buf, aux_space); } compatible_arg = 0; break; case VIDIOC_DQEVENT: - err = alloc_userspace(sizeof(struct v4l2_event), 0, &up_native); + err = alloc_userspace(sizeof(struct v4l2_event), 0, &new_p64); compatible_arg = 0; break; } if (err) return err; + /* + * 3. Calls the native 64-bits ioctl handler. + * + * For the functions where a conversion was not needed, + * compatible_arg is true, and it will call it with the arguments + * provided by userspace and stored at @p32 var. + * + * Otherwise, it will pass the newly allocated @new_p64 argument. + */ if (compatible_arg) - err = native_ioctl(file, cmd, (unsigned long)up); + err = native_ioctl(file, cmd, (unsigned long)p32); else - err = native_ioctl(file, cmd, (unsigned long)up_native); + err = native_ioctl(file, cmd, (unsigned long)new_p64); if (err == -ENOTTY) return err; /* - * Special case: even after an error we need to put the - * results back for these ioctls since the error_idx will - * contain information on which control failed. + * 4. Special case: even after an error we need to put the + * results back for some ioctls. + * + * In the case of EXT_CTRLS, the error_idx will contain information + * on which control failed. + * + * In the case of S_EDID, the driver can return E2BIG and set + * the blocks to maximum allowed value. */ switch (cmd) { case VIDIOC_G_EXT_CTRLS: case VIDIOC_S_EXT_CTRLS: case VIDIOC_TRY_EXT_CTRLS: - if (put_v4l2_ext_controls32(file, up_native, up)) + if (put_v4l2_ext_controls32(file, new_p64, p32)) err = -EFAULT; break; case VIDIOC_S_EDID: - if (put_v4l2_edid32(up_native, up)) + if (put_v4l2_edid32(new_p64, p32)) err = -EFAULT; break; } if (err) return err; + /* + * 5. Copy the data returned at the 64 bits userspace pointer to + * the original 32 bits structure. + */ switch (cmd) { case VIDIOC_S_INPUT: case VIDIOC_S_OUTPUT: case VIDIOC_G_INPUT: case VIDIOC_G_OUTPUT: - if (assign_in_user((compat_uint_t __user *)up, - ((unsigned int __user *)up_native))) + if (assign_in_user((compat_uint_t __user *)p32, + ((unsigned int __user *)new_p64))) err = -EFAULT; break; case VIDIOC_G_FBUF: - err = put_v4l2_framebuffer32(up_native, up); + err = put_v4l2_framebuffer32(new_p64, p32); break; case VIDIOC_DQEVENT: - err = put_v4l2_event32(up_native, up); + err = put_v4l2_event32(new_p64, p32); break; case VIDIOC_G_EDID: - err = put_v4l2_edid32(up_native, up); + err = put_v4l2_edid32(new_p64, p32); break; case VIDIOC_G_FMT: case VIDIOC_S_FMT: case VIDIOC_TRY_FMT: - err = put_v4l2_format32(up_native, up); + err = put_v4l2_format32(new_p64, p32); break; case VIDIOC_CREATE_BUFS: - err = put_v4l2_create32(up_native, up); + err = put_v4l2_create32(new_p64, p32); break; case VIDIOC_PREPARE_BUF: case VIDIOC_QUERYBUF: case VIDIOC_QBUF: case VIDIOC_DQBUF: - err = put_v4l2_buffer32(up_native, up); + err = put_v4l2_buffer32(new_p64, p32); break; case VIDIOC_ENUMSTD: - err = put_v4l2_standard32(up_native, up); + err = put_v4l2_standard32(new_p64, p32); break; case VIDIOC_ENUMINPUT: - err = put_v4l2_input32(up_native, up); + err = put_v4l2_input32(new_p64, p32); break; } return err; } +/** + * v4l2_compat_ioctl32() - Handles a compat32 ioctl call + * + * @file: pointer to &struct file with the file handler + * @cmd: ioctl to be called + * @arg: arguments passed from/to the ioctl handler + * + * This function is meant to be used as .compat_ioctl fops at v4l2-dev.c + * in order to deal with 32-bit calls on a 64-bits Kernel. + * + * This function calls do_video_ioctl() for non-private V4L2 ioctls. + * If the function is a private one it calls vdev->fops->compat_ioctl32 + * instead. + */ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) { struct video_device *vdev = video_devdata(file); diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index c080dcc75393..4ffd7d60a901 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -16,6 +16,8 @@ * - Added procfs support */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> @@ -34,6 +36,12 @@ #define VIDEO_NUM_DEVICES 256 #define VIDEO_NAME "video4linux" +#define dprintk(fmt, arg...) do { \ + printk(KERN_DEBUG pr_fmt("%s: " fmt), \ + __func__, ##arg); \ +} while (0) + + /* * sysfs stuff */ @@ -91,7 +99,7 @@ ATTRIBUTE_GROUPS(video_device); /* * Active devices */ -static struct video_device *video_device[VIDEO_NUM_DEVICES]; +static struct video_device *video_devices[VIDEO_NUM_DEVICES]; static DEFINE_MUTEX(videodev_lock); static DECLARE_BITMAP(devnode_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES); @@ -173,14 +181,14 @@ static void v4l2_device_release(struct device *cd) struct v4l2_device *v4l2_dev = vdev->v4l2_dev; mutex_lock(&videodev_lock); - if (WARN_ON(video_device[vdev->minor] != vdev)) { + if (WARN_ON(video_devices[vdev->minor] != vdev)) { /* should not happen */ mutex_unlock(&videodev_lock); return; } /* Free up this device for reuse */ - video_device[vdev->minor] = NULL; + video_devices[vdev->minor] = NULL; /* Delete the cdev on this minor as well */ cdev_del(vdev->cdev); @@ -229,7 +237,7 @@ static struct class video_class = { struct video_device *video_devdata(struct file *file) { - return video_device[iminor(file_inode(file))]; + return video_devices[iminor(file_inode(file))]; } EXPORT_SYMBOL(video_devdata); @@ -309,7 +317,7 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf, ret = vdev->fops->read(filp, buf, sz, off); if ((vdev->dev_debug & V4L2_DEV_DEBUG_FOP) && (vdev->dev_debug & V4L2_DEV_DEBUG_STREAMING)) - printk(KERN_DEBUG "%s: read: %zd (%d)\n", + dprintk("%s: read: %zd (%d)\n", video_device_node_name(vdev), sz, ret); return ret; } @@ -326,7 +334,7 @@ static ssize_t v4l2_write(struct file *filp, const char __user *buf, ret = vdev->fops->write(filp, buf, sz, off); if ((vdev->dev_debug & V4L2_DEV_DEBUG_FOP) && (vdev->dev_debug & V4L2_DEV_DEBUG_STREAMING)) - printk(KERN_DEBUG "%s: write: %zd (%d)\n", + dprintk("%s: write: %zd (%d)\n", video_device_node_name(vdev), sz, ret); return ret; } @@ -341,7 +349,7 @@ static __poll_t v4l2_poll(struct file *filp, struct poll_table_struct *poll) if (video_is_registered(vdev)) res = vdev->fops->poll(filp, poll); if (vdev->dev_debug & V4L2_DEV_DEBUG_POLL) - printk(KERN_DEBUG "%s: poll: %08x\n", + dprintk("%s: poll: %08x\n", video_device_node_name(vdev), res); return res; } @@ -352,14 +360,8 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) int ret = -ENODEV; if (vdev->fops->unlocked_ioctl) { - struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd); - - if (lock && mutex_lock_interruptible(lock)) - return -ERESTARTSYS; if (video_is_registered(vdev)) ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); - if (lock) - mutex_unlock(lock); } else ret = -ENOTTY; @@ -382,7 +384,7 @@ static unsigned long v4l2_get_unmapped_area(struct file *filp, return -ENODEV; ret = vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags); if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP) - printk(KERN_DEBUG "%s: get_unmapped_area (%d)\n", + dprintk("%s: get_unmapped_area (%d)\n", video_device_node_name(vdev), ret); return ret; } @@ -398,7 +400,7 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) if (video_is_registered(vdev)) ret = vdev->fops->mmap(filp, vm); if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP) - printk(KERN_DEBUG "%s: mmap (%d)\n", + dprintk("%s: mmap (%d)\n", video_device_node_name(vdev), ret); return ret; } @@ -428,7 +430,7 @@ static int v4l2_open(struct inode *inode, struct file *filp) } if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP) - printk(KERN_DEBUG "%s: open (%d)\n", + dprintk("%s: open (%d)\n", video_device_node_name(vdev), ret); /* decrease the refcount in case of an error */ if (ret) @@ -445,7 +447,7 @@ static int v4l2_release(struct inode *inode, struct file *filp) if (vdev->fops->release) ret = vdev->fops->release(filp); if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP) - printk(KERN_DEBUG "%s: release\n", + dprintk("%s: release\n", video_device_node_name(vdev)); /* decrease the refcount unconditionally since the release() @@ -493,9 +495,9 @@ static int get_index(struct video_device *vdev) bitmap_zero(used, VIDEO_NUM_DEVICES); for (i = 0; i < VIDEO_NUM_DEVICES; i++) { - if (video_device[i] != NULL && - video_device[i]->v4l2_dev == vdev->v4l2_dev) { - set_bit(video_device[i]->index, used); + if (video_devices[i] != NULL && + video_devices[i]->v4l2_dev == vdev->v4l2_dev) { + set_bit(video_devices[i]->index, used); } } @@ -786,8 +788,7 @@ static int video_register_media_controller(struct video_device *vdev, int type) ret = media_device_register_entity(vdev->v4l2_dev->mdev, &vdev->entity); if (ret < 0) { - printk(KERN_WARNING - "%s: media_device_register_entity failed\n", + pr_warn("%s: media_device_register_entity failed\n", __func__); return ret; } @@ -869,7 +870,7 @@ int __video_register_device(struct video_device *vdev, name_base = "v4l-touch"; break; default: - printk(KERN_ERR "%s called with unknown type: %d\n", + pr_err("%s called with unknown type: %d\n", __func__, type); return -EINVAL; } @@ -918,7 +919,7 @@ int __video_register_device(struct video_device *vdev, if (nr == minor_cnt) nr = devnode_find(vdev, 0, minor_cnt); if (nr == minor_cnt) { - printk(KERN_ERR "could not get a free device node number\n"); + pr_err("could not get a free device node number\n"); mutex_unlock(&videodev_lock); return -ENFILE; } @@ -929,11 +930,11 @@ int __video_register_device(struct video_device *vdev, /* The device node number and minor numbers are independent, so we just find the first free minor number. */ for (i = 0; i < VIDEO_NUM_DEVICES; i++) - if (video_device[i] == NULL) + if (video_devices[i] == NULL) break; if (i == VIDEO_NUM_DEVICES) { mutex_unlock(&videodev_lock); - printk(KERN_ERR "could not get a free minor\n"); + pr_err("could not get a free minor\n"); return -ENFILE; } #endif @@ -941,14 +942,14 @@ int __video_register_device(struct video_device *vdev, vdev->num = nr; /* Should not happen since we thought this minor was free */ - if (WARN_ON(video_device[vdev->minor])) { + if (WARN_ON(video_devices[vdev->minor])) { mutex_unlock(&videodev_lock); - printk(KERN_ERR "video_device not empty!\n"); + pr_err("video_device not empty!\n"); return -ENFILE; } devnode_set(vdev); vdev->index = get_index(vdev); - video_device[vdev->minor] = vdev; + video_devices[vdev->minor] = vdev; mutex_unlock(&videodev_lock); if (vdev->ioctl_ops) @@ -964,7 +965,7 @@ int __video_register_device(struct video_device *vdev, vdev->cdev->owner = owner; ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); if (ret < 0) { - printk(KERN_ERR "%s: cdev_add failed\n", __func__); + pr_err("%s: cdev_add failed\n", __func__); kfree(vdev->cdev); vdev->cdev = NULL; goto cleanup; @@ -977,7 +978,7 @@ int __video_register_device(struct video_device *vdev, dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num); ret = device_register(&vdev->dev); if (ret < 0) { - printk(KERN_ERR "%s: device_register failed\n", __func__); + pr_err("%s: device_register failed\n", __func__); goto cleanup; } /* Register the release callback that will be called when the last @@ -985,7 +986,7 @@ int __video_register_device(struct video_device *vdev, vdev->dev.release = v4l2_device_release; if (nr != -1 && nr != vdev->num && warn_if_nr_in_use) - printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__, + pr_warn("%s: requested %s%d, got %s\n", __func__, name_base, nr, video_device_node_name(vdev)); /* Increase v4l2_device refcount */ @@ -1003,7 +1004,7 @@ cleanup: mutex_lock(&videodev_lock); if (vdev->cdev) cdev_del(vdev->cdev); - video_device[vdev->minor] = NULL; + video_devices[vdev->minor] = NULL; devnode_clear(vdev); mutex_unlock(&videodev_lock); /* Mark this video device as never having been registered. */ @@ -1043,10 +1044,10 @@ static int __init videodev_init(void) dev_t dev = MKDEV(VIDEO_MAJOR, 0); int ret; - printk(KERN_INFO "Linux video capture interface: v2.00\n"); + pr_info("Linux video capture interface: v2.00\n"); ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME); if (ret < 0) { - printk(KERN_WARNING "videodev: unable to get major %d\n", + pr_warn("videodev: unable to get major %d\n", VIDEO_MAJOR); return ret; } @@ -1054,7 +1055,7 @@ static int __init videodev_init(void) ret = class_register(&video_class); if (ret < 0) { unregister_chrdev_region(dev, VIDEO_NUM_DEVICES); - printk(KERN_WARNING "video_dev: class_register failed\n"); + pr_warn("video_dev: class_register failed\n"); return -EIO; } diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index d630640642ee..3f77aa318035 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -819,17 +819,25 @@ static int v4l2_fwnode_reference_parse_int_props( unsigned int index; int ret; - for (index = 0; !IS_ERR((fwnode = v4l2_fwnode_reference_get_int_prop( - dev_fwnode(dev), prop, index, props, - nprops))); index++) + index = 0; + do { + fwnode = v4l2_fwnode_reference_get_int_prop(dev_fwnode(dev), + prop, index, + props, nprops); + if (IS_ERR(fwnode)) { + /* + * Note that right now both -ENODATA and -ENOENT may + * signal out-of-bounds access. Return the error in + * cases other than that. + */ + if (PTR_ERR(fwnode) != -ENOENT && + PTR_ERR(fwnode) != -ENODATA) + return PTR_ERR(fwnode); + break; + } fwnode_handle_put(fwnode); - - /* - * Note that right now both -ENODATA and -ENOENT may signal - * out-of-bounds access. Return the error in cases other than that. - */ - if (PTR_ERR(fwnode) != -ENOENT && PTR_ERR(fwnode) != -ENODATA) - return PTR_ERR(fwnode); + index++; + } while (1); ret = v4l2_async_notifier_realloc(notifier, notifier->num_subdevs + index); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index de5d96dbe69e..dd210067151f 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1952,7 +1952,22 @@ static int v4l_s_parm(const struct v4l2_ioctl_ops *ops, struct v4l2_streamparm *p = arg; int ret = check_fmt(file, p->type); - return ret ? ret : ops->vidioc_s_parm(file, fh, p); + if (ret) + return ret; + + /* Note: extendedmode is never used in drivers */ + if (V4L2_TYPE_IS_OUTPUT(p->type)) { + memset(p->parm.output.reserved, 0, + sizeof(p->parm.output.reserved)); + p->parm.output.extendedmode = 0; + p->parm.output.outputmode &= V4L2_MODE_HIGHQUALITY; + } else { + memset(p->parm.capture.reserved, 0, + sizeof(p->parm.capture.reserved)); + p->parm.capture.extendedmode = 0; + p->parm.capture.capturemode &= V4L2_MODE_HIGHQUALITY; + } + return ops->vidioc_s_parm(file, fh, p); } static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops, @@ -2489,11 +2504,8 @@ struct v4l2_ioctl_info { unsigned int ioctl; u32 flags; const char * const name; - union { - u32 offset; - int (*func)(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *p); - } u; + int (*func)(const struct v4l2_ioctl_ops *ops, struct file *file, + void *fh, void *p); void (*debug)(const void *arg, bool write_only); }; @@ -2501,137 +2513,160 @@ struct v4l2_ioctl_info { #define INFO_FL_PRIO (1 << 0) /* This control can be valid if the filehandle passes a control handler. */ #define INFO_FL_CTRL (1 << 1) -/* This is a standard ioctl, no need for special code */ -#define INFO_FL_STD (1 << 2) -/* This is ioctl has its own function */ -#define INFO_FL_FUNC (1 << 3) /* Queuing ioctl */ -#define INFO_FL_QUEUE (1 << 4) +#define INFO_FL_QUEUE (1 << 2) /* Always copy back result, even on error */ -#define INFO_FL_ALWAYS_COPY (1 << 5) +#define INFO_FL_ALWAYS_COPY (1 << 3) /* Zero struct from after the field to the end */ #define INFO_FL_CLEAR(v4l2_struct, field) \ ((offsetof(struct v4l2_struct, field) + \ sizeof(((struct v4l2_struct *)0)->field)) << 16) #define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16) -#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags) \ - [_IOC_NR(_ioctl)] = { \ - .ioctl = _ioctl, \ - .flags = _flags | INFO_FL_STD, \ - .name = #_ioctl, \ - .u.offset = offsetof(struct v4l2_ioctl_ops, _vidioc), \ - .debug = _debug, \ +#define DEFINE_V4L_STUB_FUNC(_vidioc) \ + static int v4l_stub_ ## _vidioc( \ + const struct v4l2_ioctl_ops *ops, \ + struct file *file, void *fh, void *p) \ + { \ + return ops->vidioc_ ## _vidioc(file, fh, p); \ } -#define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags) \ - [_IOC_NR(_ioctl)] = { \ - .ioctl = _ioctl, \ - .flags = _flags | INFO_FL_FUNC, \ - .name = #_ioctl, \ - .u.func = _func, \ - .debug = _debug, \ +#define IOCTL_INFO(_ioctl, _func, _debug, _flags) \ + [_IOC_NR(_ioctl)] = { \ + .ioctl = _ioctl, \ + .flags = _flags, \ + .name = #_ioctl, \ + .func = _func, \ + .debug = _debug, \ } +DEFINE_V4L_STUB_FUNC(g_fbuf) +DEFINE_V4L_STUB_FUNC(s_fbuf) +DEFINE_V4L_STUB_FUNC(expbuf) +DEFINE_V4L_STUB_FUNC(g_std) +DEFINE_V4L_STUB_FUNC(g_audio) +DEFINE_V4L_STUB_FUNC(s_audio) +DEFINE_V4L_STUB_FUNC(g_input) +DEFINE_V4L_STUB_FUNC(g_edid) +DEFINE_V4L_STUB_FUNC(s_edid) +DEFINE_V4L_STUB_FUNC(g_output) +DEFINE_V4L_STUB_FUNC(g_audout) +DEFINE_V4L_STUB_FUNC(s_audout) +DEFINE_V4L_STUB_FUNC(g_jpegcomp) +DEFINE_V4L_STUB_FUNC(s_jpegcomp) +DEFINE_V4L_STUB_FUNC(enumaudio) +DEFINE_V4L_STUB_FUNC(enumaudout) +DEFINE_V4L_STUB_FUNC(enum_framesizes) +DEFINE_V4L_STUB_FUNC(enum_frameintervals) +DEFINE_V4L_STUB_FUNC(g_enc_index) +DEFINE_V4L_STUB_FUNC(encoder_cmd) +DEFINE_V4L_STUB_FUNC(try_encoder_cmd) +DEFINE_V4L_STUB_FUNC(decoder_cmd) +DEFINE_V4L_STUB_FUNC(try_decoder_cmd) +DEFINE_V4L_STUB_FUNC(s_dv_timings) +DEFINE_V4L_STUB_FUNC(g_dv_timings) +DEFINE_V4L_STUB_FUNC(enum_dv_timings) +DEFINE_V4L_STUB_FUNC(query_dv_timings) +DEFINE_V4L_STUB_FUNC(dv_timings_cap) + static struct v4l2_ioctl_info v4l2_ioctls[] = { - IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0), - IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)), - IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, 0), - IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE), - IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)), - IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0), - IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_OVERLAY, v4l_overlay, v4l_print_u32, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE), - IOCTL_INFO_STD(VIDIOC_EXPBUF, vidioc_expbuf, v4l_print_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)), - IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE), - IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), - IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), - IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)), - IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_STD, vidioc_g_std, v4l_print_std, 0), - IOCTL_INFO_FNC(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)), - IOCTL_INFO_FNC(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)), - IOCTL_INFO_FNC(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)), - IOCTL_INFO_FNC(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL), - IOCTL_INFO_FNC(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)), - IOCTL_INFO_FNC(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_AUDIO, vidioc_g_audio, v4l_print_audio, 0), - IOCTL_INFO_STD(VIDIOC_S_AUDIO, vidioc_s_audio, v4l_print_audio, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)), - IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)), - IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0), - IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_EDID, vidioc_g_edid, v4l_print_edid, INFO_FL_ALWAYS_COPY), - IOCTL_INFO_STD(VIDIOC_S_EDID, vidioc_s_edid, v4l_print_edid, INFO_FL_PRIO | INFO_FL_ALWAYS_COPY), - IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0), - IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)), - IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0), - IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)), - IOCTL_INFO_FNC(VIDIOC_S_MODULATOR, v4l_s_modulator, v4l_print_modulator, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)), - IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)), - IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)), - IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_G_SELECTION, v4l_g_selection, v4l_print_selection, INFO_FL_CLEAR(v4l2_selection, r)), - IOCTL_INFO_FNC(VIDIOC_S_SELECTION, v4l_s_selection, v4l_print_selection, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_selection, r)), - IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0), - IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0), - IOCTL_INFO_FNC(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0), - IOCTL_INFO_STD(VIDIOC_ENUMAUDIO, vidioc_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)), - IOCTL_INFO_STD(VIDIOC_ENUMAUDOUT, vidioc_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)), - IOCTL_INFO_FNC(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0), - IOCTL_INFO_FNC(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)), - IOCTL_INFO_FNC(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0), - IOCTL_INFO_FNC(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL), - IOCTL_INFO_FNC(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL), - IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL), - IOCTL_INFO_STD(VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)), - IOCTL_INFO_STD(VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)), - IOCTL_INFO_STD(VIDIOC_G_ENC_INDEX, vidioc_g_enc_index, v4l_print_enc_idx, 0), - IOCTL_INFO_STD(VIDIOC_ENCODER_CMD, vidioc_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)), - IOCTL_INFO_STD(VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)), - IOCTL_INFO_STD(VIDIOC_DECODER_CMD, vidioc_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd, v4l_print_decoder_cmd, 0), - IOCTL_INFO_FNC(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0), - IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0), - IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_dv_timings, bt.flags)), - IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0), - IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0), - IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0), - IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0), - IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE), - IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE), - IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, INFO_FL_CLEAR(v4l2_enum_dv_timings, pad)), - IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, INFO_FL_ALWAYS_COPY), - IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, pad)), - IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0), - IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)), - IOCTL_INFO_FNC(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)), + IOCTL_INFO(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0), + IOCTL_INFO(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)), + IOCTL_INFO(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, 0), + IOCTL_INFO(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE), + IOCTL_INFO(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)), + IOCTL_INFO(VIDIOC_G_FBUF, v4l_stub_g_fbuf, v4l_print_framebuffer, 0), + IOCTL_INFO(VIDIOC_S_FBUF, v4l_stub_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_OVERLAY, v4l_overlay, v4l_print_u32, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE), + IOCTL_INFO(VIDIOC_EXPBUF, v4l_stub_expbuf, v4l_print_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)), + IOCTL_INFO(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE), + IOCTL_INFO(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), + IOCTL_INFO(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), + IOCTL_INFO(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)), + IOCTL_INFO(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_STD, v4l_stub_g_std, v4l_print_std, 0), + IOCTL_INFO(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)), + IOCTL_INFO(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)), + IOCTL_INFO(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)), + IOCTL_INFO(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)), + IOCTL_INFO(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_AUDIO, v4l_stub_g_audio, v4l_print_audio, 0), + IOCTL_INFO(VIDIOC_S_AUDIO, v4l_stub_s_audio, v4l_print_audio, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)), + IOCTL_INFO(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)), + IOCTL_INFO(VIDIOC_G_INPUT, v4l_stub_g_input, v4l_print_u32, 0), + IOCTL_INFO(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_EDID, v4l_stub_g_edid, v4l_print_edid, INFO_FL_ALWAYS_COPY), + IOCTL_INFO(VIDIOC_S_EDID, v4l_stub_s_edid, v4l_print_edid, INFO_FL_PRIO | INFO_FL_ALWAYS_COPY), + IOCTL_INFO(VIDIOC_G_OUTPUT, v4l_stub_g_output, v4l_print_u32, 0), + IOCTL_INFO(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)), + IOCTL_INFO(VIDIOC_G_AUDOUT, v4l_stub_g_audout, v4l_print_audioout, 0), + IOCTL_INFO(VIDIOC_S_AUDOUT, v4l_stub_s_audout, v4l_print_audioout, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)), + IOCTL_INFO(VIDIOC_S_MODULATOR, v4l_s_modulator, v4l_print_modulator, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)), + IOCTL_INFO(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)), + IOCTL_INFO(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)), + IOCTL_INFO(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_SELECTION, v4l_g_selection, v4l_print_selection, INFO_FL_CLEAR(v4l2_selection, r)), + IOCTL_INFO(VIDIOC_S_SELECTION, v4l_s_selection, v4l_print_selection, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_selection, r)), + IOCTL_INFO(VIDIOC_G_JPEGCOMP, v4l_stub_g_jpegcomp, v4l_print_jpegcompression, 0), + IOCTL_INFO(VIDIOC_S_JPEGCOMP, v4l_stub_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0), + IOCTL_INFO(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0), + IOCTL_INFO(VIDIOC_ENUMAUDIO, v4l_stub_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)), + IOCTL_INFO(VIDIOC_ENUMAUDOUT, v4l_stub_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)), + IOCTL_INFO(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0), + IOCTL_INFO(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)), + IOCTL_INFO(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0), + IOCTL_INFO(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_ENUM_FRAMESIZES, v4l_stub_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)), + IOCTL_INFO(VIDIOC_ENUM_FRAMEINTERVALS, v4l_stub_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)), + IOCTL_INFO(VIDIOC_G_ENC_INDEX, v4l_stub_g_enc_index, v4l_print_enc_idx, 0), + IOCTL_INFO(VIDIOC_ENCODER_CMD, v4l_stub_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)), + IOCTL_INFO(VIDIOC_TRY_ENCODER_CMD, v4l_stub_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)), + IOCTL_INFO(VIDIOC_DECODER_CMD, v4l_stub_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_TRY_DECODER_CMD, v4l_stub_try_decoder_cmd, v4l_print_decoder_cmd, 0), + IOCTL_INFO(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0), + IOCTL_INFO(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0), + IOCTL_INFO(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_S_DV_TIMINGS, v4l_stub_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_dv_timings, bt.flags)), + IOCTL_INFO(VIDIOC_G_DV_TIMINGS, v4l_stub_g_dv_timings, v4l_print_dv_timings, 0), + IOCTL_INFO(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0), + IOCTL_INFO(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0), + IOCTL_INFO(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0), + IOCTL_INFO(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE), + IOCTL_INFO(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE), + IOCTL_INFO(VIDIOC_ENUM_DV_TIMINGS, v4l_stub_enum_dv_timings, v4l_print_enum_dv_timings, INFO_FL_CLEAR(v4l2_enum_dv_timings, pad)), + IOCTL_INFO(VIDIOC_QUERY_DV_TIMINGS, v4l_stub_query_dv_timings, v4l_print_dv_timings, INFO_FL_ALWAYS_COPY), + IOCTL_INFO(VIDIOC_DV_TIMINGS_CAP, v4l_stub_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, pad)), + IOCTL_INFO(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0), + IOCTL_INFO(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)), + IOCTL_INFO(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)), }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) -bool v4l2_is_known_ioctl(unsigned int cmd) +static bool v4l2_is_known_ioctl(unsigned int cmd) { if (_IOC_NR(cmd) >= V4L2_IOCTLS) return false; return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd; } -struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd) +static struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, + unsigned int cmd) { if (_IOC_NR(cmd) >= V4L2_IOCTLS) return vdev->lock; - if (test_bit(_IOC_NR(cmd), vdev->disable_locking)) - return NULL; if (vdev->queue && vdev->queue->lock && (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE)) return vdev->queue->lock; @@ -2679,6 +2714,7 @@ static long __video_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vfd = video_devdata(file); + struct mutex *lock; /* ioctl serialization mutex */ const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; bool write_only = false; struct v4l2_ioctl_info default_info; @@ -2697,6 +2733,16 @@ static long __video_do_ioctl(struct file *file, if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) vfh = file->private_data; + lock = v4l2_ioctl_get_lock(vfd, cmd); + + if (lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + + if (!video_is_registered(vfd)) { + ret = -ENODEV; + goto unlock; + } + if (v4l2_is_known_ioctl(cmd)) { info = &v4l2_ioctls[_IOC_NR(cmd)]; @@ -2717,14 +2763,8 @@ static long __video_do_ioctl(struct file *file, } write_only = _IOC_DIR(cmd) == _IOC_WRITE; - if (info->flags & INFO_FL_STD) { - typedef int (*vidioc_op)(struct file *file, void *fh, void *p); - const void *p = vfd->ioctl_ops; - const vidioc_op *vidioc = p + info->u.offset; - - ret = (*vidioc)(file, fh, arg); - } else if (info->flags & INFO_FL_FUNC) { - ret = info->u.func(ops, file, fh, arg); + if (info != &default_info) { + ret = info->func(ops, file, fh, arg); } else if (!ops->vidioc_default) { ret = -ENOTTY; } else { @@ -2737,7 +2777,7 @@ done: if (dev_debug & (V4L2_DEV_DEBUG_IOCTL | V4L2_DEV_DEBUG_IOCTL_ARG)) { if (!(dev_debug & V4L2_DEV_DEBUG_STREAMING) && (cmd == VIDIOC_QBUF || cmd == VIDIOC_DQBUF)) - return ret; + goto unlock; v4l_printk_ioctl(video_device_node_name(vfd), cmd); if (ret < 0) @@ -2752,6 +2792,9 @@ done: } } +unlock: + if (lock) + mutex_unlock(lock); return ret; } @@ -2941,7 +2984,6 @@ out: kvfree(mbuf); return err; } -EXPORT_SYMBOL(video_usercopy); long video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index f9eed938d348..6a7f7f75dfd7 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -502,10 +502,25 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) return 0; } +static long subdev_do_ioctl_lock(struct file *file, unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct mutex *lock = vdev->lock; + long ret = -ENODEV; + + if (lock && mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + if (video_is_registered(vdev)) + ret = subdev_do_ioctl(file, cmd, arg); + if (lock) + mutex_unlock(lock); + return ret; +} + static long subdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(file, cmd, arg, subdev_do_ioctl); + return video_usercopy(file, cmd, arg, subdev_do_ioctl_lock); } #ifdef CONFIG_COMPAT diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c index 7770034aae28..2e5c346f9c30 100644 --- a/drivers/media/v4l2-core/videobuf-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf-dma-sg.c @@ -334,7 +334,7 @@ int videobuf_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma) if (!dma->sglen) return 0; - dma_unmap_sg(dev, dma->sglist, dma->sglen, dma->direction); + dma_unmap_sg(dev, dma->sglist, dma->nr_pages, dma->direction); vfree(dma->sglist); dma->sglist = NULL; @@ -434,7 +434,7 @@ static void videobuf_vm_close(struct vm_area_struct *vma) * now ...). Bounce buffers don't work very well for the data rates * video capture has. */ -static int videobuf_vm_fault(struct vm_fault *vmf) +static vm_fault_t videobuf_vm_fault(struct vm_fault *vmf) { struct vm_area_struct *vma = vmf->vma; struct page *page; @@ -581,7 +581,7 @@ static int __videobuf_sync(struct videobuf_queue *q, MAGIC_CHECK(mem->dma.magic, MAGIC_DMABUF); dma_sync_sg_for_cpu(q->dev, mem->dma.sglist, - mem->dma.sglen, mem->dma.direction); + mem->dma.nr_pages, mem->dma.direction); return 0; } diff --git a/drivers/media/v4l2-core/videobuf-dvb.c b/drivers/media/v4l2-core/videobuf-dvb.c deleted file mode 100644 index b7efa4516d36..000000000000 --- a/drivers/media/v4l2-core/videobuf-dvb.c +++ /dev/null @@ -1,398 +0,0 @@ -/* - * - * some helper function for simple DVB cards which simply DMA the - * complete transport stream and let the computer sort everything else - * (i.e. we are using the software demux, ...). Also uses the - * video-buf to manage DMA buffers. - * - * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs] - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/device.h> -#include <linux/fs.h> -#include <linux/kthread.h> -#include <linux/file.h> -#include <linux/slab.h> - -#include <linux/freezer.h> - -#include <media/videobuf-core.h> -#include <media/videobuf-dvb.h> - -/* ------------------------------------------------------------------ */ - -MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); -MODULE_LICENSE("GPL"); - -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug,"enable debug messages"); - -#define dprintk(fmt, arg...) if (debug) \ - printk(KERN_DEBUG "%s/dvb: " fmt, dvb->name , ## arg) - -/* ------------------------------------------------------------------ */ - -static int videobuf_dvb_thread(void *data) -{ - struct videobuf_dvb *dvb = data; - struct videobuf_buffer *buf; - unsigned long flags; - void *outp; - - dprintk("dvb thread started\n"); - set_freezable(); - videobuf_read_start(&dvb->dvbq); - - for (;;) { - /* fetch next buffer */ - buf = list_entry(dvb->dvbq.stream.next, - struct videobuf_buffer, stream); - list_del(&buf->stream); - videobuf_waiton(&dvb->dvbq, buf, 0, 1); - - /* no more feeds left or stop_feed() asked us to quit */ - if (0 == dvb->nfeeds) - break; - if (kthread_should_stop()) - break; - try_to_freeze(); - - /* feed buffer data to demux */ - outp = videobuf_queue_to_vaddr(&dvb->dvbq, buf); - - if (buf->state == VIDEOBUF_DONE) - dvb_dmx_swfilter(&dvb->demux, outp, - buf->size); - - /* requeue buffer */ - list_add_tail(&buf->stream,&dvb->dvbq.stream); - spin_lock_irqsave(dvb->dvbq.irqlock,flags); - dvb->dvbq.ops->buf_queue(&dvb->dvbq,buf); - spin_unlock_irqrestore(dvb->dvbq.irqlock,flags); - } - - videobuf_read_stop(&dvb->dvbq); - dprintk("dvb thread stopped\n"); - - /* Hmm, linux becomes *very* unhappy without this ... */ - while (!kthread_should_stop()) { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - } - return 0; -} - -static int videobuf_dvb_start_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct videobuf_dvb *dvb = demux->priv; - int rc; - - if (!demux->dmx.frontend) - return -EINVAL; - - mutex_lock(&dvb->lock); - dvb->nfeeds++; - rc = dvb->nfeeds; - - if (NULL != dvb->thread) - goto out; - dvb->thread = kthread_run(videobuf_dvb_thread, - dvb, "%s dvb", dvb->name); - if (IS_ERR(dvb->thread)) { - rc = PTR_ERR(dvb->thread); - dvb->thread = NULL; - } - -out: - mutex_unlock(&dvb->lock); - return rc; -} - -static int videobuf_dvb_stop_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct videobuf_dvb *dvb = demux->priv; - int err = 0; - - mutex_lock(&dvb->lock); - dvb->nfeeds--; - if (0 == dvb->nfeeds && NULL != dvb->thread) { - err = kthread_stop(dvb->thread); - dvb->thread = NULL; - } - mutex_unlock(&dvb->lock); - return err; -} - -static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe, - struct module *module, - void *adapter_priv, - struct device *device, - char *adapter_name, - short *adapter_nr, - int mfe_shared) -{ - int result; - - mutex_init(&fe->lock); - - /* register adapter */ - result = dvb_register_adapter(&fe->adapter, adapter_name, module, - device, adapter_nr); - if (result < 0) { - printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n", - adapter_name, result); - } - fe->adapter.priv = adapter_priv; - fe->adapter.mfe_shared = mfe_shared; - - return result; -} - -static int videobuf_dvb_register_frontend(struct dvb_adapter *adapter, - struct videobuf_dvb *dvb) -{ - int result; - - /* register frontend */ - result = dvb_register_frontend(adapter, dvb->frontend); - if (result < 0) { - printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n", - dvb->name, result); - goto fail_frontend; - } - - /* register demux stuff */ - dvb->demux.dmx.capabilities = - DMX_TS_FILTERING | DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING; - dvb->demux.priv = dvb; - dvb->demux.filternum = 256; - dvb->demux.feednum = 256; - dvb->demux.start_feed = videobuf_dvb_start_feed; - dvb->demux.stop_feed = videobuf_dvb_stop_feed; - result = dvb_dmx_init(&dvb->demux); - if (result < 0) { - printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", - dvb->name, result); - goto fail_dmx; - } - - dvb->dmxdev.filternum = 256; - dvb->dmxdev.demux = &dvb->demux.dmx; - dvb->dmxdev.capabilities = 0; - result = dvb_dmxdev_init(&dvb->dmxdev, adapter); - - if (result < 0) { - printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n", - dvb->name, result); - goto fail_dmxdev; - } - - dvb->fe_hw.source = DMX_FRONTEND_0; - result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); - if (result < 0) { - printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", - dvb->name, result); - goto fail_fe_hw; - } - - dvb->fe_mem.source = DMX_MEMORY_FE; - result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); - if (result < 0) { - printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n", - dvb->name, result); - goto fail_fe_mem; - } - - result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); - if (result < 0) { - printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n", - dvb->name, result); - goto fail_fe_conn; - } - - /* register network adapter */ - result = dvb_net_init(adapter, &dvb->net, &dvb->demux.dmx); - if (result < 0) { - printk(KERN_WARNING "%s: dvb_net_init failed (errno = %d)\n", - dvb->name, result); - goto fail_fe_conn; - } - return 0; - -fail_fe_conn: - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); -fail_fe_mem: - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); -fail_fe_hw: - dvb_dmxdev_release(&dvb->dmxdev); -fail_dmxdev: - dvb_dmx_release(&dvb->demux); -fail_dmx: - dvb_unregister_frontend(dvb->frontend); -fail_frontend: - dvb_frontend_detach(dvb->frontend); - dvb->frontend = NULL; - - return result; -} - -/* ------------------------------------------------------------------ */ -/* Register a single adapter and one or more frontends */ -int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f, - struct module *module, - void *adapter_priv, - struct device *device, - short *adapter_nr, - int mfe_shared) -{ - struct list_head *list, *q; - struct videobuf_dvb_frontend *fe; - int res; - - fe = videobuf_dvb_get_frontend(f, 1); - if (!fe) { - printk(KERN_WARNING "Unable to register the adapter which has no frontends\n"); - return -EINVAL; - } - - /* Bring up the adapter */ - res = videobuf_dvb_register_adapter(f, module, adapter_priv, device, - fe->dvb.name, adapter_nr, mfe_shared); - if (res < 0) { - printk(KERN_WARNING "videobuf_dvb_register_adapter failed (errno = %d)\n", res); - return res; - } - - /* Attach all of the frontends to the adapter */ - mutex_lock(&f->lock); - list_for_each_safe(list, q, &f->felist) { - fe = list_entry(list, struct videobuf_dvb_frontend, felist); - res = videobuf_dvb_register_frontend(&f->adapter, &fe->dvb); - if (res < 0) { - printk(KERN_WARNING "%s: videobuf_dvb_register_frontend failed (errno = %d)\n", - fe->dvb.name, res); - goto err; - } - } - mutex_unlock(&f->lock); - return 0; - -err: - mutex_unlock(&f->lock); - videobuf_dvb_unregister_bus(f); - return res; -} -EXPORT_SYMBOL(videobuf_dvb_register_bus); - -void videobuf_dvb_unregister_bus(struct videobuf_dvb_frontends *f) -{ - videobuf_dvb_dealloc_frontends(f); - - dvb_unregister_adapter(&f->adapter); -} -EXPORT_SYMBOL(videobuf_dvb_unregister_bus); - -struct videobuf_dvb_frontend *videobuf_dvb_get_frontend( - struct videobuf_dvb_frontends *f, int id) -{ - struct list_head *list, *q; - struct videobuf_dvb_frontend *fe, *ret = NULL; - - mutex_lock(&f->lock); - - list_for_each_safe(list, q, &f->felist) { - fe = list_entry(list, struct videobuf_dvb_frontend, felist); - if (fe->id == id) { - ret = fe; - break; - } - } - - mutex_unlock(&f->lock); - - return ret; -} -EXPORT_SYMBOL(videobuf_dvb_get_frontend); - -int videobuf_dvb_find_frontend(struct videobuf_dvb_frontends *f, - struct dvb_frontend *p) -{ - struct list_head *list, *q; - struct videobuf_dvb_frontend *fe = NULL; - int ret = 0; - - mutex_lock(&f->lock); - - list_for_each_safe(list, q, &f->felist) { - fe = list_entry(list, struct videobuf_dvb_frontend, felist); - if (fe->dvb.frontend == p) { - ret = fe->id; - break; - } - } - - mutex_unlock(&f->lock); - - return ret; -} -EXPORT_SYMBOL(videobuf_dvb_find_frontend); - -struct videobuf_dvb_frontend *videobuf_dvb_alloc_frontend( - struct videobuf_dvb_frontends *f, int id) -{ - struct videobuf_dvb_frontend *fe; - - fe = kzalloc(sizeof(struct videobuf_dvb_frontend), GFP_KERNEL); - if (fe == NULL) - goto fail_alloc; - - fe->id = id; - mutex_init(&fe->dvb.lock); - - mutex_lock(&f->lock); - list_add_tail(&fe->felist, &f->felist); - mutex_unlock(&f->lock); - -fail_alloc: - return fe; -} -EXPORT_SYMBOL(videobuf_dvb_alloc_frontend); - -void videobuf_dvb_dealloc_frontends(struct videobuf_dvb_frontends *f) -{ - struct list_head *list, *q; - struct videobuf_dvb_frontend *fe; - - mutex_lock(&f->lock); - list_for_each_safe(list, q, &f->felist) { - fe = list_entry(list, struct videobuf_dvb_frontend, felist); - if (fe->dvb.net.dvbdev) { - dvb_net_release(&fe->dvb.net); - fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx, - &fe->dvb.fe_mem); - fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx, - &fe->dvb.fe_hw); - dvb_dmxdev_release(&fe->dvb.dmxdev); - dvb_dmx_release(&fe->dvb.demux); - dvb_unregister_frontend(fe->dvb.frontend); - } - if (fe->dvb.frontend) - /* always allocated, may have been reset */ - dvb_frontend_detach(fe->dvb.frontend); - list_del(list); /* remove list entry */ - kfree(fe); /* free frontend allocation */ - } - mutex_unlock(&f->lock); -} -EXPORT_SYMBOL(videobuf_dvb_dealloc_frontends); |