From 563bf7a72eac823b06953cd3dce9b71b499fa438 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 31 Jul 2016 07:50:08 -0300 Subject: [media] tw686x-kh: remove obsolete driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The functionality that was part of the tw686x-kh driver but not of the mainlined tw686x driver has now been added to the tw686x driver as well. So this driver no longer adds any functionality not available through the mainlined driver and it can now be removed. Signed-off-by: Hans Verkuil Cc: Krzysztof HaƂasa Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/Kconfig | 2 - drivers/staging/media/Makefile | 1 - drivers/staging/media/tw686x-kh/Kconfig | 17 - drivers/staging/media/tw686x-kh/Makefile | 3 - drivers/staging/media/tw686x-kh/TODO | 6 - drivers/staging/media/tw686x-kh/tw686x-kh-core.c | 140 ---- drivers/staging/media/tw686x-kh/tw686x-kh-regs.h | 103 --- drivers/staging/media/tw686x-kh/tw686x-kh-video.c | 813 ---------------------- drivers/staging/media/tw686x-kh/tw686x-kh.h | 117 ---- 9 files changed, 1202 deletions(-) delete mode 100644 drivers/staging/media/tw686x-kh/Kconfig delete mode 100644 drivers/staging/media/tw686x-kh/Makefile delete mode 100644 drivers/staging/media/tw686x-kh/TODO delete mode 100644 drivers/staging/media/tw686x-kh/tw686x-kh-core.c delete mode 100644 drivers/staging/media/tw686x-kh/tw686x-kh-regs.h delete mode 100644 drivers/staging/media/tw686x-kh/tw686x-kh-video.c delete mode 100644 drivers/staging/media/tw686x-kh/tw686x-kh.h (limited to 'drivers/staging') diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 7292f23954df..53827778b414 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -31,8 +31,6 @@ source "drivers/staging/media/omap4iss/Kconfig" source "drivers/staging/media/pulse8-cec/Kconfig" -source "drivers/staging/media/tw686x-kh/Kconfig" - source "drivers/staging/media/s5p-cec/Kconfig" # Keep LIRC at the end, as it has sub-menus diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 87ce8ad1e22a..6316e8c7a497 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -6,4 +6,3 @@ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_USB_PULSE8_CEC) += pulse8-cec/ -obj-$(CONFIG_VIDEO_TW686X_KH) += tw686x-kh/ diff --git a/drivers/staging/media/tw686x-kh/Kconfig b/drivers/staging/media/tw686x-kh/Kconfig deleted file mode 100644 index 6264d30edf5a..000000000000 --- a/drivers/staging/media/tw686x-kh/Kconfig +++ /dev/null @@ -1,17 +0,0 @@ -config VIDEO_TW686X_KH - tristate "Intersil/Techwell TW686x Video For Linux" - depends on VIDEO_DEV && PCI && VIDEO_V4L2 - depends on !(VIDEO_TW686X=y || VIDEO_TW686X=m) || COMPILE_TEST - select VIDEOBUF2_DMA_SG - help - Support for Intersil/Techwell TW686x-based frame grabber cards. - - Currently supported chips: - - TW6864 (4 video channels), - - TW6865 (4 video channels, not tested, second generation chip), - - TW6868 (8 video channels but only 4 first channels using - built-in video decoder are supported, not tested), - - TW6869 (8 video channels, second generation chip). - - To compile this driver as a module, choose M here: the module - will be named tw686x-kh. diff --git a/drivers/staging/media/tw686x-kh/Makefile b/drivers/staging/media/tw686x-kh/Makefile deleted file mode 100644 index 2a36a38cf30e..000000000000 --- a/drivers/staging/media/tw686x-kh/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -tw686x-kh-objs := tw686x-kh-core.o tw686x-kh-video.o - -obj-$(CONFIG_VIDEO_TW686X_KH) += tw686x-kh.o diff --git a/drivers/staging/media/tw686x-kh/TODO b/drivers/staging/media/tw686x-kh/TODO deleted file mode 100644 index 480a495b11fb..000000000000 --- a/drivers/staging/media/tw686x-kh/TODO +++ /dev/null @@ -1,6 +0,0 @@ -TODO: - -- implement V4L2_FIELD_INTERLACED* mode(s). -- add audio support - -Please Cc: patches to Krzysztof Halasa . diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh-core.c b/drivers/staging/media/tw686x-kh/tw686x-kh-core.c deleted file mode 100644 index 03b3b62c59c4..000000000000 --- a/drivers/staging/media/tw686x-kh/tw686x-kh-core.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2015 Industrial Research Institute for Automation - * and Measurements PIAP - * - * Written by Krzysztof Ha?asa. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License - * as published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include "tw686x-kh.h" -#include "tw686x-kh-regs.h" - -static irqreturn_t tw686x_irq(int irq, void *dev_id) -{ - struct tw686x_dev *dev = (struct tw686x_dev *)dev_id; - u32 int_status = reg_read(dev, INT_STATUS); /* cleared on read */ - unsigned long flags; - unsigned int handled = 0; - - if (int_status) { - spin_lock_irqsave(&dev->irq_lock, flags); - dev->dma_requests |= int_status; - spin_unlock_irqrestore(&dev->irq_lock, flags); - - if (int_status & 0xFF0000FF) - handled = tw686x_kh_video_irq(dev); - } - - return IRQ_RETVAL(handled); -} - -static int tw686x_probe(struct pci_dev *pci_dev, - const struct pci_device_id *pci_id) -{ - struct tw686x_dev *dev; - int err; - - dev = devm_kzalloc(&pci_dev->dev, sizeof(*dev) + - (pci_id->driver_data & TYPE_MAX_CHANNELS) * - sizeof(dev->video_channels[0]), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - sprintf(dev->name, "TW%04X", pci_dev->device); - dev->type = pci_id->driver_data; - - pr_info("%s: PCI %s, IRQ %d, MMIO 0x%lx\n", dev->name, - pci_name(pci_dev), pci_dev->irq, - (unsigned long)pci_resource_start(pci_dev, 0)); - - dev->pci_dev = pci_dev; - if (pcim_enable_device(pci_dev)) - return -EIO; - - pci_set_master(pci_dev); - - if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) { - pr_err("%s: 32-bit PCI DMA not supported\n", dev->name); - return -EIO; - } - - err = pci_request_regions(pci_dev, dev->name); - if (err < 0) { - pr_err("%s: Unable to get MMIO region\n", dev->name); - return err; - } - - dev->mmio = pci_ioremap_bar(pci_dev, 0); - if (!dev->mmio) { - pr_err("%s: Unable to remap MMIO region\n", dev->name); - return -EIO; - } - - reg_write(dev, SYS_SOFT_RST, 0x0F); /* Reset all subsystems */ - mdelay(1); - - reg_write(dev, SRST[0], 0x3F); - if (max_channels(dev) > 4) - reg_write(dev, SRST[1], 0x3F); - reg_write(dev, DMA_CMD, 0); - reg_write(dev, DMA_CHANNEL_ENABLE, 0); - reg_write(dev, DMA_CHANNEL_TIMEOUT, 0x3EFF0FF0); - reg_write(dev, DMA_TIMER_INTERVAL, 0x38000); - reg_write(dev, DMA_CONFIG, 0xFFFFFF04); - - spin_lock_init(&dev->irq_lock); - - err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw686x_irq, - IRQF_SHARED, dev->name, dev); - if (err < 0) { - pr_err("%s: Unable to get IRQ\n", dev->name); - return err; - } - - err = tw686x_kh_video_init(dev); - if (err) - return err; - - pci_set_drvdata(pci_dev, dev); - return 0; -} - -static void tw686x_remove(struct pci_dev *pci_dev) -{ - struct tw686x_dev *dev = pci_get_drvdata(pci_dev); - - tw686x_kh_video_free(dev); -} - -/* driver_data is number of A/V channels */ -static const struct pci_device_id tw686x_pci_tbl[] = { - {PCI_DEVICE(0x1797, 0x6864), .driver_data = 4}, - /* not tested */ - {PCI_DEVICE(0x1797, 0x6865), .driver_data = 4 | TYPE_SECOND_GEN}, - /* TW6868 supports 8 A/V channels with an external TW2865 chip - - not supported by the driver */ - {PCI_DEVICE(0x1797, 0x6868), .driver_data = 4}, /* not tested */ - {PCI_DEVICE(0x1797, 0x6869), .driver_data = 8 | TYPE_SECOND_GEN}, - {} -}; - -static struct pci_driver tw686x_pci_driver = { - .name = "tw686x-kh", - .id_table = tw686x_pci_tbl, - .probe = tw686x_probe, - .remove = tw686x_remove, -}; - -MODULE_DESCRIPTION("Driver for video frame grabber cards based on Intersil/Techwell TW686[4589]"); -MODULE_AUTHOR("Krzysztof Halasa"); -MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(pci, tw686x_pci_tbl); -module_pci_driver(tw686x_pci_driver); diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh-regs.h b/drivers/staging/media/tw686x-kh/tw686x-kh-regs.h deleted file mode 100644 index 53e1889babd0..000000000000 --- a/drivers/staging/media/tw686x-kh/tw686x-kh-regs.h +++ /dev/null @@ -1,103 +0,0 @@ -/* DMA controller registers */ -#define REG8_1(a0) ((const u16[8]) {a0, a0 + 1, a0 + 2, a0 + 3, \ - a0 + 4, a0 + 5, a0 + 6, a0 + 7}) -#define REG8_2(a0) ((const u16[8]) {a0, a0 + 2, a0 + 4, a0 + 6, \ - a0 + 8, a0 + 0xA, a0 + 0xC, a0 + 0xE}) -#define REG8_8(a0) ((const u16[8]) {a0, a0 + 8, a0 + 0x10, a0 + 0x18, \ - a0 + 0x20, a0 + 0x28, a0 + 0x30, a0 + 0x38}) -#define INT_STATUS 0x00 -#define PB_STATUS 0x01 -#define DMA_CMD 0x02 -#define VIDEO_FIFO_STATUS 0x03 -#define VIDEO_CHANNEL_ID 0x04 -#define VIDEO_PARSER_STATUS 0x05 -#define SYS_SOFT_RST 0x06 -#define DMA_PAGE_TABLE0_ADDR ((const u16[8]) {0x08, 0xD0, 0xD2, 0xD4, \ - 0xD6, 0xD8, 0xDA, 0xDC}) -#define DMA_PAGE_TABLE1_ADDR ((const u16[8]) {0x09, 0xD1, 0xD3, 0xD5, \ - 0xD7, 0xD9, 0xDB, 0xDD}) -#define DMA_CHANNEL_ENABLE 0x0A -#define DMA_CONFIG 0x0B -#define DMA_TIMER_INTERVAL 0x0C -#define DMA_CHANNEL_TIMEOUT 0x0D -#define VDMA_CHANNEL_CONFIG REG8_1(0x10) -#define ADMA_P_ADDR REG8_2(0x18) -#define ADMA_B_ADDR REG8_2(0x19) -#define DMA10_P_ADDR 0x28 /* ??? */ -#define DMA10_B_ADDR 0x29 -#define VIDEO_CONTROL1 0x2A -#define VIDEO_CONTROL2 0x2B -#define AUDIO_CONTROL1 0x2C -#define AUDIO_CONTROL2 0x2D -#define PHASE_REF 0x2E -#define GPIO_REG 0x2F -#define INTL_HBAR_CTRL REG8_1(0x30) -#define AUDIO_CONTROL3 0x38 -#define VIDEO_FIELD_CTRL REG8_1(0x39) -#define HSCALER_CTRL REG8_1(0x42) -#define VIDEO_SIZE REG8_1(0x4A) -#define VIDEO_SIZE_F2 REG8_1(0x52) -#define MD_CONF REG8_1(0x60) -#define MD_INIT REG8_1(0x68) -#define MD_MAP0 REG8_1(0x70) -#define VDMA_P_ADDR REG8_8(0x80) /* not used in DMA SG mode */ -#define VDMA_WHP REG8_8(0x81) -#define VDMA_B_ADDR REG8_8(0x82) -#define VDMA_F2_P_ADDR REG8_8(0x84) -#define VDMA_F2_WHP REG8_8(0x85) -#define VDMA_F2_B_ADDR REG8_8(0x86) -#define EP_REG_ADDR 0xFE -#define EP_REG_DATA 0xFF - -/* Video decoder registers */ -#define VDREG8(a0) ((const u16[8]) { \ - a0 + 0x000, a0 + 0x010, a0 + 0x020, a0 + 0x030, \ - a0 + 0x100, a0 + 0x110, a0 + 0x120, a0 + 0x130}) -#define VIDSTAT VDREG8(0x100) -#define BRIGHT VDREG8(0x101) -#define CONTRAST VDREG8(0x102) -#define SHARPNESS VDREG8(0x103) -#define SAT_U VDREG8(0x104) -#define SAT_V VDREG8(0x105) -#define HUE VDREG8(0x106) -#define CROP_HI VDREG8(0x107) -#define VDELAY_LO VDREG8(0x108) -#define VACTIVE_LO VDREG8(0x109) -#define HDELAY_LO VDREG8(0x10A) -#define HACTIVE_LO VDREG8(0x10B) -#define MVSN VDREG8(0x10C) -#define STATUS2 VDREG8(0x10C) -#define SDT VDREG8(0x10E) -#define SDT_EN VDREG8(0x10F) - -#define VSCALE_LO VDREG8(0x144) -#define SCALE_HI VDREG8(0x145) -#define HSCALE_LO VDREG8(0x146) -#define F2CROP_HI VDREG8(0x147) -#define F2VDELAY_LO VDREG8(0x148) -#define F2VACTIVE_LO VDREG8(0x149) -#define F2HDELAY_LO VDREG8(0x14A) -#define F2HACTIVE_LO VDREG8(0x14B) -#define F2VSCALE_LO VDREG8(0x14C) -#define F2SCALE_HI VDREG8(0x14D) -#define F2HSCALE_LO VDREG8(0x14E) -#define F2CNT VDREG8(0x14F) - -#define VDREG2(a0) ((const u16[2]) {a0, a0 + 0x100}) -#define SRST VDREG2(0x180) -#define ACNTL VDREG2(0x181) -#define ACNTL2 VDREG2(0x182) -#define CNTRL1 VDREG2(0x183) -#define CKHY VDREG2(0x184) -#define SHCOR VDREG2(0x185) -#define CORING VDREG2(0x186) -#define CLMPG VDREG2(0x187) -#define IAGC VDREG2(0x188) -#define VCTRL1 VDREG2(0x18F) -#define MISC1 VDREG2(0x194) -#define LOOP VDREG2(0x195) -#define MISC2 VDREG2(0x196) - -#define CLMD VDREG2(0x197) -#define AIGAIN ((const u16[8]) {0x1D0, 0x1D1, 0x1D2, 0x1D3, \ - 0x2D0, 0x2D1, 0x2D2, 0x2D3}) diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh-video.c b/drivers/staging/media/tw686x-kh/tw686x-kh-video.c deleted file mode 100644 index 9bf32aec2fc6..000000000000 --- a/drivers/staging/media/tw686x-kh/tw686x-kh-video.c +++ /dev/null @@ -1,813 +0,0 @@ -/* - * Copyright (C) 2015 Industrial Research Institute for Automation - * and Measurements PIAP - * - * Written by Krzysztof Ha?asa. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License - * as published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include "tw686x-kh.h" -#include "tw686x-kh-regs.h" - -#define MAX_SG_ENTRY_SIZE (/* 8192 - 128 */ 4096) -#define MAX_SG_DESC_COUNT 256 /* PAL 704x576 needs up to 198 4-KB pages */ - -static const struct tw686x_format formats[] = { - { - .name = "4:2:2 packed, UYVY", /* aka Y422 */ - .fourcc = V4L2_PIX_FMT_UYVY, - .mode = 0, - .depth = 16, - }, { -#if 0 - .name = "4:2:0 packed, YUV", - .mode = 1, /* non-standard */ - .depth = 12, - }, { - .name = "4:1:1 packed, YUV", - .mode = 2, /* non-standard */ - .depth = 12, - }, { -#endif - .name = "4:1:1 packed, YUV", - .fourcc = V4L2_PIX_FMT_Y41P, - .mode = 3, - .depth = 12, - }, { - .name = "15 bpp RGB", - .fourcc = V4L2_PIX_FMT_RGB555, - .mode = 4, - .depth = 16, - }, { - .name = "16 bpp RGB", - .fourcc = V4L2_PIX_FMT_RGB565, - .mode = 5, - .depth = 16, - }, { - .name = "4:2:2 packed, YUYV", - .fourcc = V4L2_PIX_FMT_YUYV, - .mode = 6, - .depth = 16, - } - /* mode 7 is "reserved" */ -}; - -static const v4l2_std_id video_standards[7] = { - V4L2_STD_NTSC, - V4L2_STD_PAL, - V4L2_STD_SECAM, - V4L2_STD_NTSC_443, - V4L2_STD_PAL_M, - V4L2_STD_PAL_N, - V4L2_STD_PAL_60, -}; - -static const struct tw686x_format *format_by_fourcc(unsigned int fourcc) -{ - unsigned int cnt; - - for (cnt = 0; cnt < ARRAY_SIZE(formats); cnt++) - if (formats[cnt].fourcc == fourcc) - return &formats[cnt]; - return NULL; -} - -static void tw686x_get_format(struct tw686x_video_channel *vc, - struct v4l2_format *f) -{ - const struct tw686x_format *format; - unsigned int width, height, height_div = 1; - - format = format_by_fourcc(f->fmt.pix.pixelformat); - if (!format) { - format = &formats[0]; - f->fmt.pix.pixelformat = format->fourcc; - } - - width = 704; - if (f->fmt.pix.width < width * 3 / 4 /* halfway */) - width /= 2; - - height = (vc->video_standard & V4L2_STD_625_50) ? 576 : 480; - if (f->fmt.pix.height < height * 3 / 4 /* halfway */) - height_div = 2; - - switch (f->fmt.pix.field) { - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - height_div = 2; - break; - case V4L2_FIELD_SEQ_BT: - if (height_div > 1) - f->fmt.pix.field = V4L2_FIELD_BOTTOM; - break; - default: - if (height_div > 1) - f->fmt.pix.field = V4L2_FIELD_TOP; - else - f->fmt.pix.field = V4L2_FIELD_SEQ_TB; - } - height /= height_div; - - f->fmt.pix.width = width; - f->fmt.pix.height = height; - f->fmt.pix.bytesperline = f->fmt.pix.width * format->depth / 8; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; -} - -/* video queue operations */ - -static int tw686x_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); - unsigned int size = vc->width * vc->height * vc->format->depth / 8; - - if (*nbuffers < 2) - *nbuffers = 2; - - if (*nplanes) - return sizes[0] < size ? -EINVAL : 0; - - sizes[0] = size; - *nplanes = 1; /* packed formats only */ - return 0; -} - -static void tw686x_buf_queue(struct vb2_buffer *vb) -{ - struct tw686x_video_channel *vc = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct tw686x_vb2_buf *buf; - - buf = container_of(vbuf, struct tw686x_vb2_buf, vb); - - spin_lock(&vc->qlock); - list_add_tail(&buf->list, &vc->vidq_queued); - spin_unlock(&vc->qlock); -} - -static void setup_descs(struct tw686x_video_channel *vc, unsigned int n) -{ -loop: - while (!list_empty(&vc->vidq_queued)) { - struct vdma_desc *descs = vc->sg_descs[n]; - struct tw686x_vb2_buf *buf; - struct sg_table *vbuf; - struct scatterlist *sg; - unsigned int buf_len, count = 0; - int i; - - buf = list_first_entry(&vc->vidq_queued, struct tw686x_vb2_buf, - list); - list_del(&buf->list); - - buf_len = vc->width * vc->height * vc->format->depth / 8; - if (vb2_plane_size(&buf->vb.vb2_buf, 0) < buf_len) { - pr_err("Video buffer size too small\n"); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - goto loop; /* try another */ - } - - vbuf = vb2_dma_sg_plane_desc(&buf->vb.vb2_buf, 0); - for_each_sg(vbuf->sgl, sg, vbuf->nents, i) { - dma_addr_t phys = sg_dma_address(sg); - unsigned int len = sg_dma_len(sg); - - while (len && buf_len) { - unsigned int entry_len = min_t(unsigned int, len, - MAX_SG_ENTRY_SIZE); - entry_len = min(entry_len, buf_len); - if (count == MAX_SG_DESC_COUNT) { - pr_err("Video buffer size too fragmented\n"); - vb2_buffer_done(&buf->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - goto loop; - } - descs[count].phys = cpu_to_le32(phys); - descs[count++].flags_length = - cpu_to_le32(0x40000000 /* available */ | - entry_len); - phys += entry_len; - len -= entry_len; - buf_len -= entry_len; - } - if (!buf_len) - break; - } - - /* clear the remaining entries */ - while (count < MAX_SG_DESC_COUNT) { - descs[count].phys = 0; - descs[count++].flags_length = 0; /* unavailable */ - } - - buf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; - vc->curr_bufs[n] = buf; - return; - } - vc->curr_bufs[n] = NULL; -} - -/* On TW6864 and TW6868, all channels share the pair of video DMA SG tables, - with 10-bit start_idx and end_idx determining start and end of frame buffer - for particular channel. - TW6868 with all its 8 channels would be problematic (only 127 SG entries per - channel) but we support only 4 channels on this chip anyway (the first - 4 channels are driven with internal video decoder, the other 4 would require - an external TW286x part). - - On TW6865 and TW6869, each channel has its own DMA SG table, with indexes - starting with 0. Both chips have complete sets of internal video decoders - (respectively 4 or 8-channel). - - All chips have separate SG tables for two video frames. */ - -static void setup_dma_cfg(struct tw686x_video_channel *vc) -{ - unsigned int field_width = 704; - unsigned int field_height = (vc->video_standard & V4L2_STD_625_50) ? - 288 : 240; - unsigned int start_idx = is_second_gen(vc->dev) ? 0 : - vc->ch * MAX_SG_DESC_COUNT; - unsigned int end_idx = start_idx + MAX_SG_DESC_COUNT - 1; - u32 dma_cfg = (0 << 30) /* input selection */ | - (1 << 29) /* field2 dropped (if any) */ | - ((vc->height < 300) << 28) /* field dropping */ | - (1 << 27) /* master */ | - (0 << 25) /* master channel (for slave only) */ | - (0 << 24) /* (no) vertical (line) decimation */ | - ((vc->width < 400) << 23) /* horizontal decimation */ | - (vc->format->mode << 20) /* output video format */ | - (end_idx << 10) /* DMA end index */ | - start_idx /* DMA start index */; - u32 reg; - - reg_write(vc->dev, VDMA_CHANNEL_CONFIG[vc->ch], dma_cfg); - reg_write(vc->dev, VIDEO_SIZE[vc->ch], (1 << 31) | (field_height << 16) - | field_width); - reg = reg_read(vc->dev, VIDEO_CONTROL1); - if (vc->video_standard & V4L2_STD_625_50) - reg |= 1 << (vc->ch + 13); - else - reg &= ~(1 << (vc->ch + 13)); - reg_write(vc->dev, VIDEO_CONTROL1, reg); -} - -static int tw686x_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); - struct tw686x_dev *dev = vc->dev; - u32 dma_ch_mask; - unsigned int n; - - setup_dma_cfg(vc); - - /* queue video buffers if available */ - spin_lock(&vc->qlock); - for (n = 0; n < 2; n++) - setup_descs(vc, n); - spin_unlock(&vc->qlock); - - dev->video_active |= 1 << vc->ch; - vc->seq = 0; - dma_ch_mask = reg_read(dev, DMA_CHANNEL_ENABLE) | (1 << vc->ch); - reg_write(dev, DMA_CHANNEL_ENABLE, dma_ch_mask); - reg_write(dev, DMA_CMD, (1 << 31) | dma_ch_mask); - return 0; -} - -static void tw686x_stop_streaming(struct vb2_queue *vq) -{ - struct tw686x_video_channel *vc = vb2_get_drv_priv(vq); - struct tw686x_dev *dev = vc->dev; - u32 dma_ch_mask = reg_read(dev, DMA_CHANNEL_ENABLE); - u32 dma_cmd = reg_read(dev, DMA_CMD); - unsigned int n; - - dma_ch_mask &= ~(1 << vc->ch); - reg_write(dev, DMA_CHANNEL_ENABLE, dma_ch_mask); - - dev->video_active &= ~(1 << vc->ch); - - dma_cmd &= ~(1 << vc->ch); - reg_write(dev, DMA_CMD, dma_cmd); - - if (!dev->video_active) { - reg_write(dev, DMA_CMD, 0); - reg_write(dev, DMA_CHANNEL_ENABLE, 0); - } - - spin_lock(&vc->qlock); - while (!list_empty(&vc->vidq_queued)) { - struct tw686x_vb2_buf *buf; - - buf = list_entry(vc->vidq_queued.next, struct tw686x_vb2_buf, - list); - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } - - for (n = 0; n < 2; n++) - if (vc->curr_bufs[n]) - vb2_buffer_done(&vc->curr_bufs[n]->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - - spin_unlock(&vc->qlock); -} - -static struct vb2_ops tw686x_video_qops = { - .queue_setup = tw686x_queue_setup, - .buf_queue = tw686x_buf_queue, - .start_streaming = tw686x_start_streaming, - .stop_streaming = tw686x_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static int tw686x_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct tw686x_video_channel *vc; - struct tw686x_dev *dev; - unsigned int ch; - - vc = container_of(ctrl->handler, struct tw686x_video_channel, - ctrl_handler); - dev = vc->dev; - ch = vc->ch; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - reg_write(dev, BRIGHT[ch], ctrl->val & 0xFF); - return 0; - - case V4L2_CID_CONTRAST: - reg_write(dev, CONTRAST[ch], ctrl->val); - return 0; - - case V4L2_CID_SATURATION: - reg_write(dev, SAT_U[ch], ctrl->val); - reg_write(dev, SAT_V[ch], ctrl->val); - return 0; - - case V4L2_CID_HUE: - reg_write(dev, HUE[ch], ctrl->val & 0xFF); - return 0; - } - - return -EINVAL; -} - -static const struct v4l2_ctrl_ops ctrl_ops = { - .s_ctrl = tw686x_s_ctrl, -}; - -static int tw686x_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct tw686x_video_channel *vc = video_drvdata(file); - - f->fmt.pix.width = vc->width; - f->fmt.pix.height = vc->height; - f->fmt.pix.field = vc->field; - f->fmt.pix.pixelformat = vc->format->fourcc; - f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.bytesperline = f->fmt.pix.width * vc->format->depth / 8; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - return 0; -} - -static int tw686x_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - tw686x_get_format(video_drvdata(file), f); - return 0; -} - -static int tw686x_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct tw686x_video_channel *vc = video_drvdata(file); - - tw686x_get_format(vc, f); - vc->format = format_by_fourcc(f->fmt.pix.pixelformat); - vc->field = f->fmt.pix.field; - vc->width = f->fmt.pix.width; - vc->height = f->fmt.pix.height; - return 0; -} - -static int tw686x_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct tw686x_video_channel *vc = video_drvdata(file); - struct tw686x_dev *dev = vc->dev; - - strcpy(cap->driver, "tw686x-kh"); - strcpy(cap->card, dev->name); - sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci_dev)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int tw686x_s_std(struct file *file, void *priv, v4l2_std_id id) -{ - struct tw686x_video_channel *vc = video_drvdata(file); - unsigned int cnt; - u32 sdt = 0; /* default */ - - for (cnt = 0; cnt < ARRAY_SIZE(video_standards); cnt++) - if (id & video_standards[cnt]) { - sdt = cnt; - break; - } - - reg_write(vc->dev, SDT[vc->ch], sdt); - vc->video_standard = video_standards[sdt]; - return 0; -} - -static int tw686x_g_std(struct file *file, void *priv, v4l2_std_id *id) -{ - struct tw686x_video_channel *vc = video_drvdata(file); - - *id = vc->video_standard; - return 0; -} - -static int tw686x_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (f->index >= ARRAY_SIZE(formats)) - return -EINVAL; - - strlcpy(f->description, formats[f->index].name, sizeof(f->description)); - f->pixelformat = formats[f->index].fourcc; - return 0; -} - -static int tw686x_g_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct tw686x_video_channel *vc = video_drvdata(file); - - if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - memset(&sp->parm.capture, 0, sizeof(sp->parm.capture)); - sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - v4l2_video_std_frame_period(vc->video_standard, - &sp->parm.capture.timeperframe); - - return 0; -} - -static int tw686x_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - /* the chip has internal multiplexer, support can be added - if the actual hw uses it */ - if (inp->index) - return -EINVAL; - - snprintf(inp->name, sizeof(inp->name), "Composite"); - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = V4L2_STD_ALL; - inp->capabilities = V4L2_IN_CAP_STD; - return 0; -} - -static int tw686x_g_input(struct file *file, void *priv, unsigned int *v) -{ - *v = 0; - return 0; -} - -static int tw686x_s_input(struct file *file, void *priv, unsigned int v) -{ - if (v) - return -EINVAL; - return 0; -} - -static const struct v4l2_file_operations tw686x_video_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .unlocked_ioctl = video_ioctl2, - .release = vb2_fop_release, - .poll = vb2_fop_poll, - .read = vb2_fop_read, - .mmap = vb2_fop_mmap, -}; - -static const struct v4l2_ioctl_ops tw686x_video_ioctl_ops = { - .vidioc_querycap = tw686x_querycap, - .vidioc_enum_fmt_vid_cap = tw686x_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = tw686x_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = tw686x_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = tw686x_try_fmt_vid_cap, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_g_std = tw686x_g_std, - .vidioc_s_std = tw686x_s_std, - .vidioc_g_parm = tw686x_g_parm, - .vidioc_enum_input = tw686x_enum_input, - .vidioc_g_input = tw686x_g_input, - .vidioc_s_input = tw686x_s_input, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static int video_thread(void *arg) -{ - struct tw686x_dev *dev = arg; - DECLARE_WAITQUEUE(wait, current); - - set_freezable(); - add_wait_queue(&dev->video_thread_wait, &wait); - - while (1) { - long timeout = schedule_timeout_interruptible(HZ); - unsigned int ch; - - if (timeout == -ERESTARTSYS || kthread_should_stop()) - break; - - for (ch = 0; ch < max_channels(dev); ch++) { - struct tw686x_video_channel *vc; - unsigned long flags; - u32 request, n, stat = VB2_BUF_STATE_DONE; - - vc = &dev->video_channels[ch]; - if (!(dev->video_active & (1 << ch))) - continue; - - spin_lock_irq(&dev->irq_lock); - request = dev->dma_requests & (0x01000001 << ch); - if (request) - dev->dma_requests &= ~request; - spin_unlock_irq(&dev->irq_lock); - - if (!request) - continue; - - request >>= ch; - - /* handle channel events */ - if ((request & 0x01000000) | - (reg_read(dev, VIDEO_FIFO_STATUS) & (0x01010001 << ch)) | - (reg_read(dev, VIDEO_PARSER_STATUS) & (0x00000101 << ch))) { - /* DMA Errors - reset channel */ - u32 reg; - - spin_lock_irqsave(&dev->irq_lock, flags); - reg = reg_read(dev, DMA_CMD); - /* Reset DMA channel */ - reg_write(dev, DMA_CMD, reg & ~(1 << ch)); - reg_write(dev, DMA_CMD, reg); - spin_unlock_irqrestore(&dev->irq_lock, flags); - stat = VB2_BUF_STATE_ERROR; - } - - /* handle video stream */ - mutex_lock(&vc->vb_mutex); - spin_lock(&vc->qlock); - n = !!(reg_read(dev, PB_STATUS) & (1 << ch)); - if (vc->curr_bufs[n]) { - struct vb2_v4l2_buffer *vb; - - vb = &vc->curr_bufs[n]->vb; - vb->vb2_buf.timestamp = ktime_get_ns(); - vb->field = vc->field; - if (V4L2_FIELD_HAS_BOTH(vc->field)) - vb->sequence = vc->seq++; - else - vb->sequence = (vc->seq++) / 2; - vb2_set_plane_payload(&vb->vb2_buf, 0, - vc->width * vc->height * vc->format->depth / 8); - vb2_buffer_done(&vb->vb2_buf, stat); - } - setup_descs(vc, n); - spin_unlock(&vc->qlock); - mutex_unlock(&vc->vb_mutex); - } - try_to_freeze(); - } - - remove_wait_queue(&dev->video_thread_wait, &wait); - return 0; -} - -int tw686x_kh_video_irq(struct tw686x_dev *dev) -{ - unsigned long flags, handled = 0; - u32 requests; - - spin_lock_irqsave(&dev->irq_lock, flags); - requests = dev->dma_requests; - spin_unlock_irqrestore(&dev->irq_lock, flags); - - if (requests & dev->video_active) { - wake_up_interruptible_all(&dev->video_thread_wait); - handled = 1; - } - return handled; -} - -void tw686x_kh_video_free(struct tw686x_dev *dev) -{ - unsigned int ch, n; - - if (dev->video_thread) - kthread_stop(dev->video_thread); - - for (ch = 0; ch < max_channels(dev); ch++) { - struct tw686x_video_channel *vc = &dev->video_channels[ch]; - - v4l2_ctrl_handler_free(&vc->ctrl_handler); - if (vc->device) - video_unregister_device(vc->device); - for (n = 0; n < 2; n++) { - struct dma_desc *descs = &vc->sg_tables[n]; - - if (descs->virt) - pci_free_consistent(dev->pci_dev, descs->size, - descs->virt, descs->phys); - } - } - - v4l2_device_unregister(&dev->v4l2_dev); -} - -#define SG_TABLE_SIZE (MAX_SG_DESC_COUNT * sizeof(struct vdma_desc)) - -int tw686x_kh_video_init(struct tw686x_dev *dev) -{ - unsigned int ch, n; - int err; - - init_waitqueue_head(&dev->video_thread_wait); - - err = v4l2_device_register(&dev->pci_dev->dev, &dev->v4l2_dev); - if (err) - return err; - - reg_write(dev, VIDEO_CONTROL1, 0); /* NTSC, disable scaler */ - reg_write(dev, PHASE_REF, 0x00001518); /* Scatter-gather DMA mode */ - - /* setup required SG table sizes */ - for (n = 0; n < 2; n++) - if (is_second_gen(dev)) { - /* TW 6865, TW6869 - each channel needs a pair of - descriptor tables */ - for (ch = 0; ch < max_channels(dev); ch++) - dev->video_channels[ch].sg_tables[n].size = - SG_TABLE_SIZE; - - } else - /* TW 6864, TW6868 - we need to allocate a pair of - descriptor tables, common for all channels. - Each table will be bigger than 4 KB. */ - dev->video_channels[0].sg_tables[n].size = - max_channels(dev) * SG_TABLE_SIZE; - - /* allocate SG tables and initialize video channels */ - for (ch = 0; ch < max_channels(dev); ch++) { - struct tw686x_video_channel *vc = &dev->video_channels[ch]; - struct video_device *vdev; - - mutex_init(&vc->vb_mutex); - spin_lock_init(&vc->qlock); - INIT_LIST_HEAD(&vc->vidq_queued); - - vc->dev = dev; - vc->ch = ch; - - /* default settings: NTSC */ - vc->format = &formats[0]; - vc->video_standard = V4L2_STD_NTSC; - reg_write(vc->dev, SDT[vc->ch], 0); - vc->field = V4L2_FIELD_SEQ_BT; - vc->width = 704; - vc->height = 480; - - for (n = 0; n < 2; n++) { - void *cpu; - - if (vc->sg_tables[n].size) { - unsigned int reg = n ? DMA_PAGE_TABLE1_ADDR[ch] : - DMA_PAGE_TABLE0_ADDR[ch]; - - cpu = pci_alloc_consistent(dev->pci_dev, - vc->sg_tables[n].size, - &vc->sg_tables[n].phys); - if (!cpu) { - pr_err("Error allocating video DMA scatter-gather tables\n"); - err = -ENOMEM; - goto error; - } - vc->sg_tables[n].virt = cpu; - reg_write(dev, reg, vc->sg_tables[n].phys); - } else - cpu = dev->video_channels[0].sg_tables[n].virt + - ch * SG_TABLE_SIZE; - - vc->sg_descs[n] = cpu; - } - - reg_write(dev, VCTRL1[0], 0x24); - reg_write(dev, LOOP[0], 0xA5); - if (max_channels(dev) > 4) { - reg_write(dev, VCTRL1[1], 0x24); - reg_write(dev, LOOP[1], 0xA5); - } - reg_write(dev, VIDEO_FIELD_CTRL[ch], 0); - reg_write(dev, VDELAY_LO[ch], 0x14); - - vdev = video_device_alloc(); - if (!vdev) { - pr_warn("Unable to allocate video device\n"); - err = -ENOMEM; - goto error; - } - - vc->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - vc->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; - vc->vidq.drv_priv = vc; - vc->vidq.buf_struct_size = sizeof(struct tw686x_vb2_buf); - vc->vidq.ops = &tw686x_video_qops; - vc->vidq.mem_ops = &vb2_dma_sg_memops; - vc->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vc->vidq.min_buffers_needed = 2; - vc->vidq.lock = &vc->vb_mutex; - vc->vidq.dev = &dev->pci_dev->dev; - vc->vidq.gfp_flags = GFP_DMA32; - - err = vb2_queue_init(&vc->vidq); - if (err) - goto error; - - strcpy(vdev->name, "TW686x-video"); - snprintf(vdev->name, sizeof(vdev->name), "%s video", dev->name); - vdev->fops = &tw686x_video_fops; - vdev->ioctl_ops = &tw686x_video_ioctl_ops; - vdev->release = video_device_release; - vdev->v4l2_dev = &dev->v4l2_dev; - vdev->queue = &vc->vidq; - vdev->tvnorms = V4L2_STD_ALL; - vdev->minor = -1; - vdev->lock = &vc->vb_mutex; - - dev->video_channels[ch].device = vdev; - video_set_drvdata(vdev, vc); - err = video_register_device(vdev, VFL_TYPE_GRABBER, -1); - if (err < 0) - goto error; - - v4l2_ctrl_handler_init(&vc->ctrl_handler, - 4 /* number of controls */); - vdev->ctrl_handler = &vc->ctrl_handler; - v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, - V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); - v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 64); - v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 128); - v4l2_ctrl_new_std(&vc->ctrl_handler, &ctrl_ops, V4L2_CID_HUE, - -124, 127, 1, 0); - err = vc->ctrl_handler.error; - if (err) - goto error; - - v4l2_ctrl_handler_setup(&vc->ctrl_handler); - } - - dev->video_thread = kthread_run(video_thread, dev, "tw686x_video"); - if (IS_ERR(dev->video_thread)) { - err = PTR_ERR(dev->video_thread); - goto error; - } - - return 0; - -error: - tw686x_kh_video_free(dev); - return err; -} diff --git a/drivers/staging/media/tw686x-kh/tw686x-kh.h b/drivers/staging/media/tw686x-kh/tw686x-kh.h deleted file mode 100644 index 6284a90d6fe3..000000000000 --- a/drivers/staging/media/tw686x-kh/tw686x-kh.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2015 Industrial Research Institute for Automation - * and Measurements PIAP - * - * Written by Krzysztof Ha?asa. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License - * as published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define TYPE_MAX_CHANNELS 0x0F -#define TYPE_SECOND_GEN 0x10 - -struct tw686x_format { - char *name; - unsigned int fourcc; - unsigned int depth; - unsigned int mode; -}; - -struct dma_desc { - dma_addr_t phys; - void *virt; - unsigned int size; -}; - -struct vdma_desc { - __le32 flags_length; /* 3 MSBits for flags, 13 LSBits for length */ - __le32 phys; -}; - -struct tw686x_vb2_buf { - struct vb2_v4l2_buffer vb; - struct list_head list; -}; - -struct tw686x_video_channel { - struct tw686x_dev *dev; - - struct vb2_queue vidq; - struct list_head vidq_queued; - struct video_device *device; - struct dma_desc sg_tables[2]; - struct tw686x_vb2_buf *curr_bufs[2]; - struct vdma_desc *sg_descs[2]; - - struct v4l2_ctrl_handler ctrl_handler; - const struct tw686x_format *format; - struct mutex vb_mutex; - spinlock_t qlock; - v4l2_std_id video_standard; - unsigned int width, height; - enum v4l2_field field; /* supported TOP, BOTTOM, SEQ_TB and SEQ_BT */ - unsigned int seq; /* video field or frame counter */ - unsigned int ch; -}; - -/* global device status */ -struct tw686x_dev { - spinlock_t irq_lock; - - struct v4l2_device v4l2_dev; - struct snd_card *card; /* sound card */ - - unsigned int video_active; /* active video channel mask */ - - char name[32]; - unsigned int type; - struct pci_dev *pci_dev; - __u32 __iomem *mmio; - - struct task_struct *video_thread; - wait_queue_head_t video_thread_wait; - u32 dma_requests; - - struct tw686x_video_channel video_channels[0]; -}; - -static inline uint32_t reg_read(struct tw686x_dev *dev, unsigned int reg) -{ - return readl(dev->mmio + reg); -} - -static inline void reg_write(struct tw686x_dev *dev, unsigned int reg, - uint32_t value) -{ - writel(value, dev->mmio + reg); -} - -static inline unsigned int max_channels(struct tw686x_dev *dev) -{ - return dev->type & TYPE_MAX_CHANNELS; /* 4 or 8 channels */ -} - -static inline unsigned int is_second_gen(struct tw686x_dev *dev) -{ - /* each channel has its own DMA SG table */ - return dev->type & TYPE_SECOND_GEN; -} - -int tw686x_kh_video_irq(struct tw686x_dev *dev); -int tw686x_kh_video_init(struct tw686x_dev *dev); -void tw686x_kh_video_free(struct tw686x_dev *dev); -- cgit v1.2.3 From d8eddb15e4e436ec44464783687f0057a38fb013 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 17 Jul 2016 17:52:49 -0300 Subject: [media] cec: Delete an unnecessary check before the function call "rc_free_device" The rc_free_device() function tests whether its argument is NULL and then returns immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/cec/cec-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/cec/cec-core.c b/drivers/staging/media/cec/cec-core.c index 112a5fae12f5..80fcb692f4f5 100644 --- a/drivers/staging/media/cec/cec-core.c +++ b/drivers/staging/media/cec/cec-core.c @@ -354,8 +354,7 @@ void cec_delete_adapter(struct cec_adapter *adap) if (adap->kthread_config) kthread_stop(adap->kthread_config); #if IS_REACHABLE(CONFIG_RC_CORE) - if (adap->rc) - rc_free_device(adap->rc); + rc_free_device(adap->rc); #endif kfree(adap); } -- cgit v1.2.3 From 10d5509c8d50a2c2f761a08a616530dced35e2d8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 14 Dec 2015 08:25:32 -0200 Subject: [media] v4l2: remove g/s_crop from video ops Replace all calls to g/s_crop by calls to the get/set_selection pad ops. Remove the old g/s_crop video ops since they are now no longer used. The cropcap video op is now only used to pass pixelaspect information, and is only needed if the pixelaspect is not 1:1. Signed-off-by: Hans Verkuil Cc: Guennadi Liakhovetski Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/omap4iss/iss_video.c | 99 ++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) (limited to 'drivers/staging') diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 90b7ff56722d..c16927ac8eb0 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -645,6 +645,103 @@ iss_video_try_format(struct file *file, void *fh, struct v4l2_format *format) return 0; } +static int +iss_video_get_selection(struct file *file, void *fh, struct v4l2_selection *sel) +{ + struct iss_video *video = video_drvdata(file); + struct v4l2_subdev_format format; + struct v4l2_subdev *subdev; + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + }; + u32 pad; + int ret; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE: + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + default: + return -EINVAL; + } + subdev = iss_video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EINVAL; + + /* Try the get selection operation first and fallback to get format if not + * implemented. + */ + sdsel.pad = pad; + ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel); + if (!ret) + sel->r = sdsel.r; + if (ret != -ENOIOCTLCMD) + return ret; + + format.pad = pad; + format.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format); + if (ret < 0) + return ret == -ENOIOCTLCMD ? -ENOTTY : ret; + + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = format.format.width; + sel->r.height = format.format.height; + + return 0; +} + +static int +iss_video_set_selection(struct file *file, void *fh, struct v4l2_selection *sel) +{ + struct iss_video *video = video_drvdata(file); + struct v4l2_subdev *subdev; + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + .flags = sel->flags, + .r = sel->r, + }; + u32 pad; + int ret; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + break; + case V4L2_SEL_TGT_COMPOSE: + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + break; + default: + return -EINVAL; + } + subdev = iss_video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EINVAL; + + sdsel.pad = pad; + mutex_lock(&video->mutex); + ret = v4l2_subdev_call(subdev, pad, set_selection, NULL, &sdsel); + mutex_unlock(&video->mutex); + if (!ret) + sel->r = sdsel.r; + + return ret == -ENOIOCTLCMD ? -ENOTTY : ret; +} + static int iss_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a) { @@ -971,6 +1068,8 @@ static const struct v4l2_ioctl_ops iss_video_ioctl_ops = { .vidioc_g_fmt_vid_out = iss_video_get_format, .vidioc_s_fmt_vid_out = iss_video_set_format, .vidioc_try_fmt_vid_out = iss_video_try_format, + .vidioc_g_selection = iss_video_get_selection, + .vidioc_s_selection = iss_video_set_selection, .vidioc_g_parm = iss_video_get_param, .vidioc_s_parm = iss_video_set_param, .vidioc_reqbufs = iss_video_reqbufs, -- cgit v1.2.3 From c07c8ba8443c25c3372c712c62204ec86b6e38ad Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 11 Aug 2016 18:23:39 -0300 Subject: [media] staging: media: lirc: lirc_imon: don't print error when allocating urb fails kmalloc will print enough information in case of failure. Signed-off-by: Wolfram Sang Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_imon.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c index ff1926ca1f96..a183e68ec320 100644 --- a/drivers/staging/media/lirc/lirc_imon.c +++ b/drivers/staging/media/lirc/lirc_imon.c @@ -797,16 +797,11 @@ static int imon_probe(struct usb_interface *interface, goto free_rbuf; } rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!rx_urb) { - dev_err(dev, "%s: usb_alloc_urb failed for IR urb\n", __func__); + if (!rx_urb) goto free_lirc_buf; - } tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!tx_urb) { - dev_err(dev, "%s: usb_alloc_urb failed for display urb\n", - __func__); + if (!tx_urb) goto free_rx_urb; - } mutex_init(&context->ctx_lock); context->vfd_proto_6p = vfd_proto_6p; -- cgit v1.2.3 From 5e015c0e6d1e83bff625f2596d8e123f616002fe Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 11 Aug 2016 18:23:40 -0300 Subject: [media] staging: media: lirc: lirc_sasem: don't print error when allocating urb fails kmalloc will print enough information in case of failure. Signed-off-by: Wolfram Sang Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_sasem.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c index 2218d0042030..b080fde6d740 100644 --- a/drivers/staging/media/lirc/lirc_sasem.c +++ b/drivers/staging/media/lirc/lirc_sasem.c @@ -758,17 +758,12 @@ static int sasem_probe(struct usb_interface *interface, } rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!rx_urb) { - dev_err(&interface->dev, - "%s: usb_alloc_urb failed for IR urb\n", __func__); alloc_status = 5; goto alloc_status_switch; } if (vfd_ep_found) { tx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!tx_urb) { - dev_err(&interface->dev, - "%s: usb_alloc_urb failed for VFD urb", - __func__); alloc_status = 6; goto alloc_status_switch; } -- cgit v1.2.3 From a2fdc9df5e02ac3ab91d7888372cbe9512273171 Mon Sep 17 00:00:00 2001 From: Johan Fjeldtvedt Date: Mon, 22 Aug 2016 06:04:51 -0300 Subject: [media] cec: allow configuration both from within driver and from user space It makes sense for adapters such as the Pulse-Eight to be configurable both from within the driver and from user space, so remove the requirement that drivers only can call cec_s_log_addrs or cec_s_phys_addr if they don't expose those capabilities to user space. Signed-off-by: Johan Fjeldtvedt Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/cec/cec-adap.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/cec/cec-adap.c b/drivers/staging/media/cec/cec-adap.c index b2393bbacb26..608e3e7915e0 100644 --- a/drivers/staging/media/cec/cec-adap.c +++ b/drivers/staging/media/cec/cec-adap.c @@ -1153,8 +1153,6 @@ void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block) if (IS_ERR_OR_NULL(adap)) return; - if (WARN_ON(adap->capabilities & CEC_CAP_PHYS_ADDR)) - return; mutex_lock(&adap->lock); __cec_s_phys_addr(adap, phys_addr, block); mutex_unlock(&adap->lock); @@ -1295,8 +1293,6 @@ int cec_s_log_addrs(struct cec_adapter *adap, { int err; - if (WARN_ON(adap->capabilities & CEC_CAP_LOG_ADDRS)) - return -EINVAL; mutex_lock(&adap->lock); err = __cec_s_log_addrs(adap, log_addrs, block); mutex_unlock(&adap->lock); -- cgit v1.2.3 From 05f3e58b8633590b1319db22646dd78ba6de2660 Mon Sep 17 00:00:00 2001 From: Johan Fjeldtvedt Date: Mon, 22 Aug 2016 06:04:52 -0300 Subject: [media] pulse8-cec: serialize communication with adapter Make sending messages to the adapter serialized within the driver. send_and_wait is split into send_and_wait_once, which only sends once and checks for the result, and the higher level send_and_wait, which performs locking and retries. Signed-off-by: Johan Fjeldtvedt Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/pulse8-cec/pulse8-cec.c | 52 ++++++++++++++++----------- 1 file changed, 32 insertions(+), 20 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/pulse8-cec/pulse8-cec.c b/drivers/staging/media/pulse8-cec/pulse8-cec.c index 94f8590492dc..dcda68a569a7 100644 --- a/drivers/staging/media/pulse8-cec/pulse8-cec.c +++ b/drivers/staging/media/pulse8-cec/pulse8-cec.c @@ -99,6 +99,7 @@ struct pulse8 { unsigned int idx; bool escape; bool started; + struct mutex write_lock; }; static void pulse8_irq_work_handler(struct work_struct *work) @@ -233,8 +234,9 @@ static int pulse8_send(struct serio *serio, const u8 *command, u8 cmd_len) return err; } -static int pulse8_send_and_wait(struct pulse8 *pulse8, - const u8 *cmd, u8 cmd_len, u8 response, u8 size) +static int pulse8_send_and_wait_once(struct pulse8 *pulse8, + const u8 *cmd, u8 cmd_len, + u8 response, u8 size) { int err; @@ -250,24 +252,8 @@ static int pulse8_send_and_wait(struct pulse8 *pulse8, if ((pulse8->data[0] & 0x3f) == MSGCODE_COMMAND_REJECTED && cmd[0] != MSGCODE_SET_CONTROLLED && cmd[0] != MSGCODE_SET_AUTO_ENABLED && - cmd[0] != MSGCODE_GET_BUILDDATE) { - u8 cmd_sc[2]; - - cmd_sc[0] = MSGCODE_SET_CONTROLLED; - cmd_sc[1] = 1; - err = pulse8_send_and_wait(pulse8, cmd_sc, 2, - MSGCODE_COMMAND_ACCEPTED, 1); - if (err) - return err; - init_completion(&pulse8->cmd_done); - - err = pulse8_send(pulse8->serio, cmd, cmd_len); - if (err) - return err; - - if (!wait_for_completion_timeout(&pulse8->cmd_done, HZ)) - return -ETIMEDOUT; - } + cmd[0] != MSGCODE_GET_BUILDDATE) + return -ENOTTY; if (response && ((pulse8->data[0] & 0x3f) != response || pulse8->len < size + 1)) { dev_info(pulse8->dev, "transmit: failed %02x\n", @@ -277,6 +263,31 @@ static int pulse8_send_and_wait(struct pulse8 *pulse8, return 0; } +static int pulse8_send_and_wait(struct pulse8 *pulse8, + const u8 *cmd, u8 cmd_len, u8 response, u8 size) +{ + u8 cmd_sc[2]; + int err; + + mutex_lock(&pulse8->write_lock); + err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len, response, size); + + if (err == -ENOTTY) { + cmd_sc[0] = MSGCODE_SET_CONTROLLED; + cmd_sc[1] = 1; + err = pulse8_send_and_wait_once(pulse8, cmd_sc, 2, + MSGCODE_COMMAND_ACCEPTED, 1); + if (err) + goto unlock; + err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len, + response, size); + } + +unlock: + mutex_unlock(&pulse8->write_lock); + return err == -ENOTTY ? -EIO : err; +} + static int pulse8_setup(struct pulse8 *pulse8, struct serio *serio) { u8 *data = pulse8->data + 1; @@ -453,6 +464,7 @@ static int pulse8_connect(struct serio *serio, struct serio_driver *drv) pulse8->dev = &serio->dev; serio_set_drvdata(serio, pulse8); INIT_WORK(&pulse8->work, pulse8_irq_work_handler); + mutex_init(&pulse8->write_lock); err = serio_open(serio, drv); if (err) -- cgit v1.2.3 From 89c2e228718cf0d545de7a589e971dbbe70cb8a3 Mon Sep 17 00:00:00 2001 From: Johan Fjeldtvedt Date: Mon, 22 Aug 2016 06:04:53 -0300 Subject: [media] pulse8-cec: add notes about behavior in autonomous mode The pulse8 dongle has some quirky behaviors when in autonomous mode. Document these. Signed-off-by: Johan Fjeldtvedt Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/pulse8-cec/pulse8-cec.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers/staging') diff --git a/drivers/staging/media/pulse8-cec/pulse8-cec.c b/drivers/staging/media/pulse8-cec/pulse8-cec.c index dcda68a569a7..3361f4acfdaf 100644 --- a/drivers/staging/media/pulse8-cec/pulse8-cec.c +++ b/drivers/staging/media/pulse8-cec/pulse8-cec.c @@ -10,6 +10,29 @@ * this archive for more details. */ +/* + * Notes: + * + * - Devices with firmware version < 2 do not store their configuration in + * EEPROM. + * + * - In autonomous mode, only messages from a TV will be acknowledged, even + * polling messages. Upon receiving a message from a TV, the dongle will + * respond to messages from any logical address. + * + * - In autonomous mode, the dongle will by default reply Feature Abort + * [Unrecognized Opcode] when it receives Give Device Vendor ID. It will + * however observe vendor ID's reported by other devices and possibly + * alter this behavior. When TV's (and TV's only) report that their vendor ID + * is LG (0x00e091), the dongle will itself reply that it has the same vendor + * ID, and it will respond to at least one vendor specific command. + * + * - In autonomous mode, the dongle is known to attempt wakeup if it receives + * ["Power On"], ["Power] or ["Power Toggle"], or if it + * receives with its own physical address. It also does this + * if it receives [0x03 0x00] from an LG TV. + */ + #include #include #include -- cgit v1.2.3 From e28a6c8b3fcc166254596bbbf9ebe45facfa48eb Mon Sep 17 00:00:00 2001 From: Johan Fjeldtvedt Date: Mon, 22 Aug 2016 06:04:54 -0300 Subject: [media] pulse8-cec: sync configuration with adapter When the configuration is changed, they are also written to the adapter. This allows the adapter to continue operating in autonomous mode with the same settings when it is disconnected from the driver (typically by going into suspend). For adapters with firmware version 2 or greater, the settings are also persisted in EEPROM. A new module parameter is added to optionally also use the configuration already present in the adapter when it is connected. This option is enabled by default. When a new configuration is written, the autonomous mode is automatically enabled. When the device is unconfigured, autonomous mode is disabled. Signed-off-by: Johan Fjeldtvedt Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/pulse8-cec/pulse8-cec.c | 266 ++++++++++++++++++++++---- 1 file changed, 228 insertions(+), 38 deletions(-) (limited to 'drivers/staging') diff --git a/drivers/staging/media/pulse8-cec/pulse8-cec.c b/drivers/staging/media/pulse8-cec/pulse8-cec.c index 3361f4acfdaf..e27eca307a38 100644 --- a/drivers/staging/media/pulse8-cec/pulse8-cec.c +++ b/drivers/staging/media/pulse8-cec/pulse8-cec.c @@ -51,8 +51,11 @@ MODULE_DESCRIPTION("Pulse Eight HDMI CEC driver"); MODULE_LICENSE("GPL"); static int debug; +static int persistent_config = 1; module_param(debug, int, 0644); +module_param(persistent_config, int, 0644); MODULE_PARM_DESC(debug, "debug level (0-1)"); +MODULE_PARM_DESC(persistent_config, "read config from persistent memory (0-1)"); enum pulse8_msgcodes { MSGCODE_NOTHING = 0, @@ -109,12 +112,16 @@ enum pulse8_msgcodes { #define DATA_SIZE 256 +#define PING_PERIOD (15 * HZ) + struct pulse8 { struct device *dev; struct serio *serio; struct cec_adapter *adap; + unsigned int vers; struct completion cmd_done; struct work_struct work; + struct delayed_work ping_eeprom_work; struct cec_msg rx_msg; u8 data[DATA_SIZE]; unsigned int len; @@ -122,9 +129,15 @@ struct pulse8 { unsigned int idx; bool escape; bool started; + struct mutex config_lock; struct mutex write_lock; + bool config_pending; + bool restoring_config; + bool autonomous; }; +static void pulse8_ping_eeprom_work_handler(struct work_struct *work); + static void pulse8_irq_work_handler(struct work_struct *work) { struct pulse8 *pulse8 = @@ -229,6 +242,7 @@ static void pulse8_disconnect(struct serio *serio) struct pulse8 *pulse8 = serio_get_drvdata(serio); cec_unregister_adapter(pulse8->adap); + cancel_delayed_work_sync(&pulse8->ping_eeprom_work); dev_info(&serio->dev, "disconnected\n"); serio_close(serio); serio_set_drvdata(serio, NULL); @@ -311,14 +325,15 @@ unlock: return err == -ENOTTY ? -EIO : err; } -static int pulse8_setup(struct pulse8 *pulse8, struct serio *serio) +static int pulse8_setup(struct pulse8 *pulse8, struct serio *serio, + struct cec_log_addrs *log_addrs, u16 *pa) { u8 *data = pulse8->data + 1; - unsigned int count = 0; - unsigned int vers = 0; u8 cmd[2]; int err; + pulse8->vers = 0; + cmd[0] = MSGCODE_PING; err = pulse8_send_and_wait(pulse8, cmd, 1, MSGCODE_COMMAND_ACCEPTED, 0); @@ -328,10 +343,10 @@ static int pulse8_setup(struct pulse8 *pulse8, struct serio *serio) if (err) return err; - vers = (data[0] << 8) | data[1]; + pulse8->vers = (data[0] << 8) | data[1]; - dev_info(pulse8->dev, "Firmware version %04x\n", vers); - if (vers < 2) + dev_info(pulse8->dev, "Firmware version %04x\n", pulse8->vers); + if (pulse8->vers < 2) return 0; cmd[0] = MSGCODE_GET_BUILDDATE; @@ -348,37 +363,103 @@ static int pulse8_setup(struct pulse8 *pulse8, struct serio *serio) tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); } + if (err) + return err; - do { - if (count) - msleep(500); - cmd[0] = MSGCODE_SET_AUTO_ENABLED; - cmd[1] = 0; - err = pulse8_send_and_wait(pulse8, cmd, 2, - MSGCODE_COMMAND_ACCEPTED, 1); - if (err && count == 0) { - dev_info(pulse8->dev, "No Auto Enabled supported\n"); - return 0; - } + dev_dbg(pulse8->dev, "Persistent config:\n"); + cmd[0] = MSGCODE_GET_AUTO_ENABLED; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); + if (err) + return err; + pulse8->autonomous = data[0]; + dev_dbg(pulse8->dev, "Autonomous mode: %s", + data[0] ? "on" : "off"); - cmd[0] = MSGCODE_GET_AUTO_ENABLED; - if (!err) - err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); - if (!err && !data[0]) { - cmd[0] = MSGCODE_WRITE_EEPROM; - err = pulse8_send_and_wait(pulse8, cmd, 1, - MSGCODE_COMMAND_ACCEPTED, 1); - cmd[0] = MSGCODE_GET_AUTO_ENABLED; - if (!err) - err = pulse8_send_and_wait(pulse8, cmd, 1, - cmd[0], 1); - } - } while (!err && data[0] && count++ < 5); + cmd[0] = MSGCODE_GET_DEVICE_TYPE; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); + if (err) + return err; + log_addrs->primary_device_type[0] = data[0]; + dev_dbg(pulse8->dev, "Primary device type: %d\n", data[0]); + switch (log_addrs->primary_device_type[0]) { + case CEC_OP_PRIM_DEVTYPE_TV: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_TV; + break; + case CEC_OP_PRIM_DEVTYPE_RECORD: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_RECORD; + break; + case CEC_OP_PRIM_DEVTYPE_TUNER: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_TUNER; + break; + case CEC_OP_PRIM_DEVTYPE_PLAYBACK: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK; + break; + case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK; + break; + case CEC_OP_PRIM_DEVTYPE_SWITCH: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED; + break; + case CEC_OP_PRIM_DEVTYPE_PROCESSOR: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_SPECIFIC; + break; + default: + log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED; + dev_info(pulse8->dev, "Unknown Primary Device Type: %d\n", + log_addrs->primary_device_type[0]); + break; + } - if (!err && data[0]) - err = -EIO; + cmd[0] = MSGCODE_GET_LOGICAL_ADDRESS_MASK; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2); + if (err) + return err; + log_addrs->log_addr_mask = (data[0] << 8) | data[1]; + dev_dbg(pulse8->dev, "Logical address ACK mask: %x\n", + log_addrs->log_addr_mask); + if (log_addrs->log_addr_mask) + log_addrs->num_log_addrs = 1; + + cmd[0] = MSGCODE_GET_PHYSICAL_ADDRESS; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); + if (err) + return err; + *pa = (data[0] << 8) | data[1]; + dev_dbg(pulse8->dev, "Physical address: %x.%x.%x.%x\n", + cec_phys_addr_exp(*pa)); - return err; + cmd[0] = MSGCODE_GET_HDMI_VERSION; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1); + if (err) + return err; + log_addrs->cec_version = data[0]; + dev_dbg(pulse8->dev, "CEC version: %d\n", log_addrs->cec_version); + + cmd[0] = MSGCODE_GET_OSD_NAME; + err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 0); + if (err) + return err; + strncpy(log_addrs->osd_name, data, 13); + dev_dbg(pulse8->dev, "OSD name: %s\n", log_addrs->osd_name); + + return 0; +} + +static int pulse8_apply_persistent_config(struct pulse8 *pulse8, + struct cec_log_addrs *log_addrs, + u16 pa) +{ + int err; + + err = cec_s_log_addrs(pulse8->adap, log_addrs, false); + if (err) + return err; + + cec_s_phys_addr(pulse8->adap, pa, false); + if (err) + return err; + + return 0; } static int pulse8_cec_adap_enable(struct cec_adapter *adap, bool enable) @@ -398,9 +479,11 @@ static int pulse8_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) { struct pulse8 *pulse8 = adap->priv; u16 mask = 0; - u8 cmd[3]; - int err; + u16 pa = adap->phys_addr; + u8 cmd[16]; + int err = 0; + mutex_lock(&pulse8->config_lock); if (log_addr != CEC_LOG_ADDR_INVALID) mask = 1 << log_addr; cmd[0] = MSGCODE_SET_ACK_MASK; @@ -408,8 +491,72 @@ static int pulse8_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) cmd[2] = mask & 0xff; err = pulse8_send_and_wait(pulse8, cmd, 3, MSGCODE_COMMAND_ACCEPTED, 0); - if (mask == 0) - return 0; + if ((err && mask != 0) || pulse8->restoring_config) + goto unlock; + + cmd[0] = MSGCODE_SET_AUTO_ENABLED; + cmd[1] = log_addr == CEC_LOG_ADDR_INVALID ? 0 : 1; + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + pulse8->autonomous = cmd[1]; + if (log_addr == CEC_LOG_ADDR_INVALID) + goto unlock; + + cmd[0] = MSGCODE_SET_DEVICE_TYPE; + cmd[1] = adap->log_addrs.primary_device_type[0]; + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + + cmd[0] = MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS; + cmd[1] = log_addr; + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + + cmd[0] = MSGCODE_SET_PHYSICAL_ADDRESS; + cmd[1] = pa >> 8; + cmd[2] = pa & 0xff; + err = pulse8_send_and_wait(pulse8, cmd, 3, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + + cmd[0] = MSGCODE_SET_HDMI_VERSION; + cmd[1] = adap->log_addrs.cec_version; + err = pulse8_send_and_wait(pulse8, cmd, 2, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + + if (adap->log_addrs.osd_name[0]) { + size_t osd_len = strlen(adap->log_addrs.osd_name); + char *osd_str = cmd + 1; + + cmd[0] = MSGCODE_SET_OSD_NAME; + strncpy(cmd + 1, adap->log_addrs.osd_name, 13); + if (osd_len < 4) { + memset(osd_str + osd_len, ' ', 4 - osd_len); + osd_len = 4; + osd_str[osd_len] = '\0'; + strcpy(adap->log_addrs.osd_name, osd_str); + } + err = pulse8_send_and_wait(pulse8, cmd, 1 + osd_len, + MSGCODE_COMMAND_ACCEPTED, 0); + if (err) + goto unlock; + } + +unlock: + if (pulse8->restoring_config) + pulse8->restoring_config = false; + else + pulse8->config_pending = true; + mutex_unlock(&pulse8->config_lock); return err; } @@ -471,6 +618,8 @@ static int pulse8_connect(struct serio *serio, struct serio_driver *drv) CEC_CAP_PASSTHROUGH | CEC_CAP_RC | CEC_CAP_MONITOR_ALL; struct pulse8 *pulse8; int err = -ENOMEM; + struct cec_log_addrs log_addrs = {}; + u16 pa; pulse8 = kzalloc(sizeof(*pulse8), GFP_KERNEL); @@ -488,12 +637,14 @@ static int pulse8_connect(struct serio *serio, struct serio_driver *drv) serio_set_drvdata(serio, pulse8); INIT_WORK(&pulse8->work, pulse8_irq_work_handler); mutex_init(&pulse8->write_lock); + mutex_init(&pulse8->config_lock); + pulse8->config_pending = false; err = serio_open(serio, drv); if (err) goto delete_adap; - err = pulse8_setup(pulse8, serio); + err = pulse8_setup(pulse8, serio, &log_addrs, &pa); if (err) goto close_serio; @@ -502,6 +653,18 @@ static int pulse8_connect(struct serio *serio, struct serio_driver *drv) goto close_serio; pulse8->dev = &pulse8->adap->devnode.dev; + + if (persistent_config && pulse8->autonomous) { + err = pulse8_apply_persistent_config(pulse8, &log_addrs, pa); + if (err) + goto close_serio; + pulse8->restoring_config = true; + } + + INIT_DELAYED_WORK(&pulse8->ping_eeprom_work, + pulse8_ping_eeprom_work_handler); + schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD); + return 0; close_serio: @@ -514,6 +677,33 @@ free_device: ret