V4L/DVB: cx18, cx23885, v4l2 doc, MAINTAINERS: Update Andy Walls' email address
[safe/jmp/linux-2.6] / drivers / media / video / cx18 / cx18-gpio.c
index 3d495db..5374aeb 100644 (file)
@@ -4,6 +4,7 @@
  *  Derived from ivtv-gpio.c
  *
  *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
+ *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.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
@@ -22,6 +23,7 @@
  */
 
 #include "cx18-driver.h"
+#include "cx18-io.h"
 #include "cx18-cards.h"
 #include "cx18-gpio.h"
 #include "tuner-xc2028.h"
  * gpio13: cs5345 reset pin
 */
 
+/*
+ * File scope utility functions
+ */
 static void gpio_write(struct cx18 *cx)
 {
-       u32 dir = cx->gpio_dir;
-       u32 val = cx->gpio_val;
-
-       write_reg((dir & 0xffff) << 16, CX18_REG_GPIO_DIR1);
-       write_reg(((dir & 0xffff) << 16) | (val & 0xffff),
-                       CX18_REG_GPIO_OUT1);
-       write_reg(dir & 0xffff0000, CX18_REG_GPIO_DIR2);
-       write_reg_sync((dir & 0xffff0000) | ((val & 0xffff0000) >> 16),
-                       CX18_REG_GPIO_OUT2);
+       u32 dir_lo = cx->gpio_dir & 0xffff;
+       u32 val_lo = cx->gpio_val & 0xffff;
+       u32 dir_hi = cx->gpio_dir >> 16;
+       u32 val_hi = cx->gpio_val >> 16;
+
+       cx18_write_reg_expect(cx, dir_lo << 16,
+                                       CX18_REG_GPIO_DIR1, ~dir_lo, dir_lo);
+       cx18_write_reg_expect(cx, (dir_lo << 16) | val_lo,
+                                       CX18_REG_GPIO_OUT1, val_lo, dir_lo);
+       cx18_write_reg_expect(cx, dir_hi << 16,
+                                       CX18_REG_GPIO_DIR2, ~dir_hi, dir_hi);
+       cx18_write_reg_expect(cx, (dir_hi << 16) | val_hi,
+                                       CX18_REG_GPIO_OUT2, val_hi, dir_hi);
 }
 
-void cx18_reset_i2c_slaves_gpio(struct cx18 *cx)
+static void gpio_update(struct cx18 *cx, u32 mask, u32 data)
 {
-       const struct cx18_gpio_i2c_slave_reset *p;
+       if (mask == 0)
+               return;
 
-       p = &cx->card->gpio_i2c_slave_reset;
+       mutex_lock(&cx->gpio_lock);
+       cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask);
+       gpio_write(cx);
+       mutex_unlock(&cx->gpio_lock);
+}
+
+static void gpio_reset_seq(struct cx18 *cx, u32 active_lo, u32 active_hi,
+                          unsigned int assert_msecs,
+                          unsigned int recovery_msecs)
+{
+       u32 mask;
 
-       if ((p->active_lo_mask | p->active_hi_mask) == 0)
+       mask = active_lo | active_hi;
+       if (mask == 0)
                return;
 
-       /* Assuming that the masks are a subset of the bits in gpio_dir */
+       /*
+        * Assuming that active_hi and active_lo are a subsets of the bits in
+        * gpio_dir.  Also assumes that active_lo and active_hi don't overlap
+        * in any bit position
+        */
 
        /* Assert */
-       mutex_lock(&cx->gpio_lock);
-       cx->gpio_val =
-               (cx->gpio_val | p->active_hi_mask) & ~(p->active_lo_mask);
-       gpio_write(cx);
-       schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted));
+       gpio_update(cx, mask, ~active_lo);
+       schedule_timeout_uninterruptible(msecs_to_jiffies(assert_msecs));
 
        /* Deassert */
-       cx->gpio_val =
-               (cx->gpio_val | p->active_lo_mask) & ~(p->active_hi_mask);
-       gpio_write(cx);
-       schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery));
+       gpio_update(cx, mask, ~active_hi);
+       schedule_timeout_uninterruptible(msecs_to_jiffies(recovery_msecs));
+}
+
+/*
+ * GPIO Multiplexer - logical device
+ */
+static int gpiomux_log_status(struct v4l2_subdev *sd)
+{
+       struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+       mutex_lock(&cx->gpio_lock);
+       CX18_INFO_DEV(sd, "GPIO:  direction 0x%08x, value 0x%08x\n",
+                     cx->gpio_dir, cx->gpio_val);
        mutex_unlock(&cx->gpio_lock);
+       return 0;
 }
 
-void cx18_reset_ir_gpio(void *data)
+static int gpiomux_s_radio(struct v4l2_subdev *sd)
 {
-       struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
-       const struct cx18_gpio_i2c_slave_reset *p;
+       struct cx18 *cx = v4l2_get_subdevdata(sd);
 
-       p = &cx->card->gpio_i2c_slave_reset;
+       /*
+        * FIXME - work out the cx->active/audio_input mess - this is
+        * intended to handle the switch to radio mode and set the
+        * audio routing, but we need to update the state in cx
+        */
+       gpio_update(cx, cx->card->gpio_audio_input.mask,
+                       cx->card->gpio_audio_input.radio);
+       return 0;
+}
 
-       if (p->ir_reset_mask == 0)
-               return;
+static int gpiomux_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+       struct cx18 *cx = v4l2_get_subdevdata(sd);
+       u32 data;
 
-       CX18_DEBUG_INFO("Resetting IR microcontroller\n");
+       switch (cx->card->audio_inputs[cx->audio_input].muxer_input) {
+       case 1:
+               data = cx->card->gpio_audio_input.linein;
+               break;
+       case 0:
+               data = cx->card->gpio_audio_input.tuner;
+               break;
+       default:
+               /*
+                * FIXME - work out the cx->active/audio_input mess - this is
+                * intended to handle the switch from radio mode and set the
+                * audio routing, but we need to update the state in cx
+                */
+               data = cx->card->gpio_audio_input.tuner;
+               break;
+       }
+       gpio_update(cx, cx->card->gpio_audio_input.mask, data);
+       return 0;
+}
 
-       /*
-          Assert timing for the Z8F0811 on HVR-1600 boards:
-          1. Assert RESET for min of 4 clock cycles at 18.432 MHz to initiate
-          2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock cycles
-               (6,601,085 nanoseconds ~= 7 milliseconds)
-          3. DBG pin must be high before chip exits reset for normal operation.
-               DBG is open drain and hopefully pulled high since we don't
-               normally drive it (GPIO 1?) for the HVR-1600
-          4. Z8F0811 won't exit reset until RESET is deasserted
-       */
-       mutex_lock(&cx->gpio_lock);
-       cx->gpio_val = cx->gpio_val & ~p->ir_reset_mask;
-       gpio_write(cx);
-       mutex_unlock(&cx->gpio_lock);
-       schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted));
+static int gpiomux_s_audio_routing(struct v4l2_subdev *sd,
+                                  u32 input, u32 output, u32 config)
+{
+       struct cx18 *cx = v4l2_get_subdevdata(sd);
+       u32 data;
+
+       switch (input) {
+       case 0:
+               data = cx->card->gpio_audio_input.tuner;
+               break;
+       case 1:
+               data = cx->card->gpio_audio_input.linein;
+               break;
+       case 2:
+               data = cx->card->gpio_audio_input.radio;
+               break;
+       default:
+               return -EINVAL;
+       }
+       gpio_update(cx, cx->card->gpio_audio_input.mask, data);
+       return 0;
+}
+
+static const struct v4l2_subdev_core_ops gpiomux_core_ops = {
+       .log_status = gpiomux_log_status,
+       .s_std = gpiomux_s_std,
+};
+
+static const struct v4l2_subdev_tuner_ops gpiomux_tuner_ops = {
+       .s_radio = gpiomux_s_radio,
+};
+
+static const struct v4l2_subdev_audio_ops gpiomux_audio_ops = {
+       .s_routing = gpiomux_s_audio_routing,
+};
+
+static const struct v4l2_subdev_ops gpiomux_ops = {
+       .core = &gpiomux_core_ops,
+       .tuner = &gpiomux_tuner_ops,
+       .audio = &gpiomux_audio_ops,
+};
+
+/*
+ * GPIO Reset Controller - logical device
+ */
+static int resetctrl_log_status(struct v4l2_subdev *sd)
+{
+       struct cx18 *cx = v4l2_get_subdevdata(sd);
 
-       /*
-          Zilog comes out of reset, loads reset vector address and executes
-          from there. Required recovery delay unknown.
-       */
        mutex_lock(&cx->gpio_lock);
-       cx->gpio_val = cx->gpio_val | p->ir_reset_mask;
-       gpio_write(cx);
+       CX18_INFO_DEV(sd, "GPIO:  direction 0x%08x, value 0x%08x\n",
+                     cx->gpio_dir, cx->gpio_val);
        mutex_unlock(&cx->gpio_lock);
-       schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery));
+       return 0;
 }
-EXPORT_SYMBOL(cx18_reset_ir_gpio);
-/* This symbol is exported for use by an infrared module for the IR-blaster */
 
+static int resetctrl_reset(struct v4l2_subdev *sd, u32 val)
+{
+       struct cx18 *cx = v4l2_get_subdevdata(sd);
+       const struct cx18_gpio_i2c_slave_reset *p;
+
+       p = &cx->card->gpio_i2c_slave_reset;
+       switch (val) {
+       case CX18_GPIO_RESET_I2C:
+               gpio_reset_seq(cx, p->active_lo_mask, p->active_hi_mask,
+                              p->msecs_asserted, p->msecs_recovery);
+               break;
+       case CX18_GPIO_RESET_Z8F0811:
+               /*
+                * Assert timing for the Z8F0811 on HVR-1600 boards:
+                * 1. Assert RESET for min of 4 clock cycles at 18.432 MHz to
+                *    initiate
+                * 2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock
+                *    cycles (6,601,085 nanoseconds ~= 7 milliseconds)
+                * 3. DBG pin must be high before chip exits reset for normal
+                *    operation.  DBG is open drain and hopefully pulled high
+                *    since we don't normally drive it (GPIO 1?) for the
+                *    HVR-1600
+                * 4. Z8F0811 won't exit reset until RESET is deasserted
+                * 5. Zilog comes out of reset, loads reset vector address and
+                *    executes from there. Required recovery delay unknown.
+                */
+               gpio_reset_seq(cx, p->ir_reset_mask, 0,
+                              p->msecs_asserted, p->msecs_recovery);
+               break;
+       case CX18_GPIO_RESET_XC2028:
+               if (cx->card->tuners[0].tuner == TUNER_XC2028)
+                       gpio_reset_seq(cx, (1 << cx->card->xceive_pin), 0,
+                                      1, 1);
+               break;
+       }
+       return 0;
+}
+
+static const struct v4l2_subdev_core_ops resetctrl_core_ops = {
+       .log_status = resetctrl_log_status,
+       .reset = resetctrl_reset,
+};
+
+static const struct v4l2_subdev_ops resetctrl_ops = {
+       .core = &resetctrl_core_ops,
+};
+
+/*
+ * External entry points
+ */
 void cx18_gpio_init(struct cx18 *cx)
 {
        mutex_lock(&cx->gpio_lock);
@@ -141,70 +278,70 @@ void cx18_gpio_init(struct cx18 *cx)
        }
 
        CX18_DEBUG_INFO("GPIO initial dir: %08x/%08x out: %08x/%08x\n",
-                  read_reg(CX18_REG_GPIO_DIR1), read_reg(CX18_REG_GPIO_DIR2),
-                  read_reg(CX18_REG_GPIO_OUT1), read_reg(CX18_REG_GPIO_OUT2));
+                       cx18_read_reg(cx, CX18_REG_GPIO_DIR1),
+                       cx18_read_reg(cx, CX18_REG_GPIO_DIR2),
+                       cx18_read_reg(cx, CX18_REG_GPIO_OUT1),
+                       cx18_read_reg(cx, CX18_REG_GPIO_OUT2));
 
        gpio_write(cx);
        mutex_unlock(&cx->gpio_lock);
 }
 
-/* Xceive tuner reset function */
-int cx18_reset_tuner_gpio(void *dev, int cmd, int value)
+int cx18_gpio_register(struct cx18 *cx, u32 hw)
 {
-       struct i2c_algo_bit_data *algo = dev;
-       struct cx18_i2c_algo_callback_data *cb_data = algo->data;
-       struct cx18 *cx = cb_data->cx;
+       struct v4l2_subdev *sd;
+       const struct v4l2_subdev_ops *ops;
+       char *str;
 
-       if (cmd != XC2028_TUNER_RESET)
-               return 0;
-       CX18_DEBUG_INFO("Resetting tuner\n");
+       switch (hw) {
+       case CX18_HW_GPIO_MUX:
+               sd = &cx->sd_gpiomux;
+               ops = &gpiomux_ops;
+               str = "gpio-mux";
+               break;
+       case CX18_HW_GPIO_RESET_CTRL:
+               sd = &cx->sd_resetctrl;
+               ops = &resetctrl_ops;
+               str = "gpio-reset-ctrl";
+               break;
+       default:
+               return -EINVAL;
+       }
 
-       mutex_lock(&cx->gpio_lock);
-       cx->gpio_val &= ~(1 << cx->card->xceive_pin);
-       gpio_write(cx);
-       mutex_unlock(&cx->gpio_lock);
-       schedule_timeout_interruptible(msecs_to_jiffies(1));
+       v4l2_subdev_init(sd, ops);
+       v4l2_set_subdevdata(sd, cx);
+       snprintf(sd->name, sizeof(sd->name), "%s %s", cx->v4l2_dev.name, str);
+       sd->grp_id = hw;
+       return v4l2_device_register_subdev(&cx->v4l2_dev, sd);
+}
 
-       mutex_lock(&cx->gpio_lock);
-       cx->gpio_val |= 1 << cx->card->xceive_pin;
-       gpio_write(cx);
-       mutex_unlock(&cx->gpio_lock);
-       schedule_timeout_interruptible(msecs_to_jiffies(1));
-       return 0;
+void cx18_reset_ir_gpio(void *data)
+{
+       struct cx18 *cx = to_cx18((struct v4l2_device *)data);
+
+       if (cx->card->gpio_i2c_slave_reset.ir_reset_mask == 0)
+               return;
+
+       CX18_DEBUG_INFO("Resetting IR microcontroller\n");
+
+       v4l2_subdev_call(&cx->sd_resetctrl,
+                        core, reset, CX18_GPIO_RESET_Z8F0811);
 }
+EXPORT_SYMBOL(cx18_reset_ir_gpio);
+/* This symbol is exported for use by lirc_pvr150 for the IR-blaster */
 
-int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg)
+/* Xceive tuner reset function */
+int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value)
 {
-       struct v4l2_routing *route = arg;
-       u32 mask, data;
-
-       switch (command) {
-       case VIDIOC_INT_S_AUDIO_ROUTING:
-               if (route->input > 2)
-                       return -EINVAL;
-               mask = cx->card->gpio_audio_input.mask;
-               switch (route->input) {
-               case 0:
-                       data = cx->card->gpio_audio_input.tuner;
-                       break;
-               case 1:
-                       data = cx->card->gpio_audio_input.linein;
-                       break;
-               case 2:
-               default:
-                       data = cx->card->gpio_audio_input.radio;
-                       break;
-               }
-               break;
+       struct i2c_algo_bit_data *algo = dev;
+       struct cx18_i2c_algo_callback_data *cb_data = algo->data;
+       struct cx18 *cx = cb_data->cx;
 
-       default:
-               return -EINVAL;
-       }
-       if (mask) {
-               mutex_lock(&cx->gpio_lock);
-               cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask);
-               gpio_write(cx);
-               mutex_unlock(&cx->gpio_lock);
-       }
-       return 0;
+       if (cmd != XC2028_TUNER_RESET ||
+           cx->card->tuners[0].tuner != TUNER_XC2028)
+               return 0;
+
+       CX18_DEBUG_INFO("Resetting XCeive tuner\n");
+       return v4l2_subdev_call(&cx->sd_resetctrl,
+                               core, reset, CX18_GPIO_RESET_XC2028);
 }