From 9ab66912e0cd671fbea1b99e8a37d11b14d50baf Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 23 Oct 2010 13:28:33 -0300 Subject: [PATCH] [media] cx231xx: Add a driver for I2C-based IR Although cx231xx has a very good IR support, already supported by mceusb driver, some designs decided to add a separate I2C microcontroller chip in order to handle IR. Due to that, add a glue to ir-kbd-i2c is needed, in order to support those devices. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx231xx/Kconfig | 13 +++ drivers/media/video/cx231xx/Makefile | 5 +- drivers/media/video/cx231xx/cx231xx-cards.c | 5 ++ drivers/media/video/cx231xx/cx231xx-core.c | 2 +- drivers/media/video/cx231xx/cx231xx-input.c | 122 ++++++++++++++++++++++++++++ drivers/media/video/cx231xx/cx231xx.h | 32 +++++++- 6 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 drivers/media/video/cx231xx/cx231xx-input.c diff --git a/drivers/media/video/cx231xx/Kconfig b/drivers/media/video/cx231xx/Kconfig index bad836f..c37eac1 100644 --- a/drivers/media/video/cx231xx/Kconfig +++ b/drivers/media/video/cx231xx/Kconfig @@ -14,6 +14,19 @@ config VIDEO_CX231XX To compile this driver as a module, choose M here: the module will be called cx231xx +config VIDEO_CX231XX_RC + bool "Conexant cx231xx Remote Controller additional support" + depends on VIDEO_IR + depends on VIDEO_CX231XX + default y + ---help--- + cx231xx hardware has a builtin RX/TX support. However, a few + designs opted to not use it, but, instead, some other hardware. + This module enables the usage of those other hardware, like the + ones used with ISDB-T boards. + + On most cases, all you need for IR is mceusb module. + config VIDEO_CX231XX_ALSA tristate "Conexant Cx231xx ALSA audio module" depends on VIDEO_CX231XX && SND diff --git a/drivers/media/video/cx231xx/Makefile b/drivers/media/video/cx231xx/Makefile index a6bc4cc..2c24843 100644 --- a/drivers/media/video/cx231xx/Makefile +++ b/drivers/media/video/cx231xx/Makefile @@ -1,5 +1,6 @@ -cx231xx-objs := cx231xx-video.o cx231xx-i2c.o cx231xx-cards.o cx231xx-core.o \ - cx231xx-avcore.o cx231xx-417.o cx231xx-pcb-cfg.o cx231xx-vbi.o +cx231xx-y += cx231xx-video.o cx231xx-i2c.o cx231xx-cards.o cx231xx-core.o +cx231xx-y += cx231xx-avcore.o cx231xx-417.o cx231xx-pcb-cfg.o cx231xx-vbi.o +cx231xx-$(CONFIG_VIDEO_CX231XX_RC) += cx231xx-input.o cx231xx-alsa-objs := cx231xx-audio.o diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c index 400447f..0a06fca 100644 --- a/drivers/media/video/cx231xx/cx231xx-cards.c +++ b/drivers/media/video/cx231xx/cx231xx-cards.c @@ -663,8 +663,11 @@ void cx231xx_release_resources(struct cx231xx *dev) cx231xx_remove_from_devlist(dev); + /* Release I2C buses */ cx231xx_dev_uninit(dev); + cx231xx_ir_exit(dev); + usb_put_dev(dev->udev); /* Mark device as unused */ @@ -782,6 +785,8 @@ static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev, goto fail_reg_devices; } + cx231xx_ir_init(dev); + cx231xx_init_extension(dev); return 0; diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c index d6028c9..44d124c 100644 --- a/drivers/media/video/cx231xx/cx231xx-core.c +++ b/drivers/media/video/cx231xx/cx231xx-core.c @@ -1289,7 +1289,7 @@ int cx231xx_dev_init(struct cx231xx *dev) /* Internal Master 3 Bus */ dev->i2c_bus[2].nr = 2; dev->i2c_bus[2].dev = dev; - dev->i2c_bus[2].i2c_period = I2C_SPEED_400K; /* 400kHz */ + dev->i2c_bus[2].i2c_period = I2C_SPEED_100K; /* 100kHz */ dev->i2c_bus[2].i2c_nostop = 0; dev->i2c_bus[2].i2c_reserve = 0; diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c new file mode 100644 index 0000000..6725244 --- /dev/null +++ b/drivers/media/video/cx231xx/cx231xx-input.c @@ -0,0 +1,122 @@ +/* + * cx231xx IR glue driver + * + * Copyright (C) 2010 Mauro Carvalho Chehab + * + * Polaris (cx231xx) has its support for IR's with a design close to MCE. + * however, a few designs are using an external I2C chip for IR, instead + * of using the one provided by the chip. + * This driver provides support for those extra devices + * + * 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 "cx231xx.h" +#include +#include + +#define MODULE_NAME "cx231xx-input" + +static int get_key_isdbt(struct IR_i2c *ir, u32 *ir_key, + u32 *ir_raw) +{ + u8 cmd; + + /* poll IR chip */ + if (1 != i2c_master_recv(ir->c, &cmd, 1)) + return -EIO; + + /* it seems that 0xFE indicates that a button is still hold + down, while 0xff indicates that no button is hold + down. 0xfe sequences are sometimes interrupted by 0xFF */ + + if (cmd == 0xff) + return 0; + + dev_dbg(&ir->input->dev, "scancode = %02x\n", cmd); + + *ir_key = cmd; + *ir_raw = cmd; + return 1; +} + +int cx231xx_ir_init(struct cx231xx *dev) +{ + struct input_dev *input_dev; + struct i2c_board_info info; + int rc; + u8 ir_i2c_bus; + + dev_dbg(&dev->udev->dev, "%s\n", __func__); + + /* Only initialize if a rc keycode map is defined */ + if (!cx231xx_boards[dev->model].rc_map) + return -ENODEV; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + request_module("ir-kbd-i2c"); + + memset(&info, 0, sizeof(struct i2c_board_info)); + memset(&dev->ir.init_data, 0, sizeof(dev->ir.init_data)); + + dev->ir.input_dev = input_dev; + dev->ir.init_data.name = cx231xx_boards[dev->model].name; + dev->ir.props.priv = dev; + dev->ir.props.allowed_protos = IR_TYPE_NEC; + snprintf(dev->ir.name, sizeof(dev->ir.name), + "cx231xx IR (%s)", cx231xx_boards[dev->model].name); + usb_make_path(dev->udev, dev->ir.phys, sizeof(dev->ir.phys)); + strlcat(dev->ir.phys, "/input0", sizeof(dev->ir.phys)); + + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + info.platform_data = &dev->ir.init_data; + + input_dev->name = dev->ir.name; + input_dev->phys = dev->ir.phys; + input_dev->dev.parent = &dev->udev->dev; + input_dev->id.bustype = BUS_USB; + input_dev->id.version = 1; + input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + + /* + * Board-dependent values + * + * For now, there's just one type of hardware design using + * an i2c device. + */ + dev->ir.init_data.get_key = get_key_isdbt; + dev->ir.init_data.ir_codes = cx231xx_boards[dev->model].rc_map; + /* The i2c micro-controller only outputs the cmd part of NEC protocol */ + dev->ir.props.scanmask = 0xff; + info.addr = 0x30; + + rc = ir_input_register(input_dev, cx231xx_boards[dev->model].rc_map, + &dev->ir.props, MODULE_NAME); + if (rc < 0) + return rc; + + /* Load and bind ir-kbd-i2c */ + ir_i2c_bus = cx231xx_boards[dev->model].ir_i2c_master; + i2c_new_device(&dev->i2c_bus[ir_i2c_bus].i2c_adap, &info); + + return rc; +} + +void cx231xx_ir_exit(struct cx231xx *dev) +{ + if (dev->ir.input_dev) { + ir_input_unregister(dev->ir.input_dev); + dev->ir.input_dev = NULL; + } +} diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h index 8a7d0a4..a09cef4 100644 --- a/drivers/media/video/cx231xx/cx231xx.h +++ b/drivers/media/video/cx231xx/cx231xx.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "cx231xx-reg.h" @@ -345,6 +346,10 @@ struct cx231xx_board { /* i2c masters */ u8 tuner_i2c_master; u8 demod_i2c_master; + u8 ir_i2c_master; + + /* for devices with I2C chips for IR */ + char *rc_map; unsigned int max_range_640_480:1; unsigned int has_dvb:1; @@ -597,6 +602,17 @@ struct cx231xx_tsport { void *port_priv; }; +struct cx231xx_ir_t { + struct input_dev *input_dev; + char name[40]; + char phys[32]; + + struct ir_dev_props props; + + /* I2C keyboard data */ + struct IR_i2c_init_data init_data; +}; + /* main device struct */ struct cx231xx { /* generic device properties */ @@ -606,6 +622,9 @@ struct cx231xx { struct cx231xx_board board; + /* For I2C IR support */ + struct cx231xx_ir_t ir; + unsigned int stream_on:1; /* Locks streams */ unsigned int vbi_stream_on:1; /* Locks streams for VBI */ unsigned int has_audio_class:1; @@ -617,8 +636,6 @@ struct cx231xx { struct v4l2_subdev *sd_cx25840; struct v4l2_subdev *sd_tuner; - struct cx231xx_IR *ir; - struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ atomic_t stream_started; /* stream should be running if true */ @@ -955,6 +972,17 @@ int cx231xx_tuner_callback(void *ptr, int component, int command, int arg); extern int cx231xx_417_register(struct cx231xx *dev); extern void cx231xx_417_unregister(struct cx231xx *dev); +/* cx23885-input.c */ + +#if defined(CONFIG_VIDEO_CX231XX_RC) +int cx231xx_ir_init(struct cx231xx *dev); +void cx231xx_ir_exit(struct cx231xx *dev); +#else +#define cx231xx_ir_init(dev) (0) +#define cx231xx_ir_exit(dev) (0) +#endif + + /* printk macros */ #define cx231xx_err(fmt, arg...) do {\ -- 2.7.4