162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * cx18 gpio functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Derived from ivtv-gpio.c 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> 862306a36Sopenharmony_ci * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "cx18-driver.h" 1262306a36Sopenharmony_ci#include "cx18-io.h" 1362306a36Sopenharmony_ci#include "cx18-cards.h" 1462306a36Sopenharmony_ci#include "cx18-gpio.h" 1562306a36Sopenharmony_ci#include "xc2028.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/********************* GPIO stuffs *********************/ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* GPIO registers */ 2062306a36Sopenharmony_ci#define CX18_REG_GPIO_IN 0xc72010 2162306a36Sopenharmony_ci#define CX18_REG_GPIO_OUT1 0xc78100 2262306a36Sopenharmony_ci#define CX18_REG_GPIO_DIR1 0xc78108 2362306a36Sopenharmony_ci#define CX18_REG_GPIO_OUT2 0xc78104 2462306a36Sopenharmony_ci#define CX18_REG_GPIO_DIR2 0xc7810c 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * HVR-1600 GPIO pins, courtesy of Hauppauge: 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * gpio0: zilog ir process reset pin 3062306a36Sopenharmony_ci * gpio1: zilog programming pin (you should never use this) 3162306a36Sopenharmony_ci * gpio12: cx24227 reset pin 3262306a36Sopenharmony_ci * gpio13: cs5345 reset pin 3362306a36Sopenharmony_ci*/ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * File scope utility functions 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_cistatic void gpio_write(struct cx18 *cx) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci u32 dir_lo = cx->gpio_dir & 0xffff; 4162306a36Sopenharmony_ci u32 val_lo = cx->gpio_val & 0xffff; 4262306a36Sopenharmony_ci u32 dir_hi = cx->gpio_dir >> 16; 4362306a36Sopenharmony_ci u32 val_hi = cx->gpio_val >> 16; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci cx18_write_reg_expect(cx, dir_lo << 16, 4662306a36Sopenharmony_ci CX18_REG_GPIO_DIR1, ~dir_lo, dir_lo); 4762306a36Sopenharmony_ci cx18_write_reg_expect(cx, (dir_lo << 16) | val_lo, 4862306a36Sopenharmony_ci CX18_REG_GPIO_OUT1, val_lo, dir_lo); 4962306a36Sopenharmony_ci cx18_write_reg_expect(cx, dir_hi << 16, 5062306a36Sopenharmony_ci CX18_REG_GPIO_DIR2, ~dir_hi, dir_hi); 5162306a36Sopenharmony_ci cx18_write_reg_expect(cx, (dir_hi << 16) | val_hi, 5262306a36Sopenharmony_ci CX18_REG_GPIO_OUT2, val_hi, dir_hi); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic void gpio_update(struct cx18 *cx, u32 mask, u32 data) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci if (mask == 0) 5862306a36Sopenharmony_ci return; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci mutex_lock(&cx->gpio_lock); 6162306a36Sopenharmony_ci cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask); 6262306a36Sopenharmony_ci gpio_write(cx); 6362306a36Sopenharmony_ci mutex_unlock(&cx->gpio_lock); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void gpio_reset_seq(struct cx18 *cx, u32 active_lo, u32 active_hi, 6762306a36Sopenharmony_ci unsigned int assert_msecs, 6862306a36Sopenharmony_ci unsigned int recovery_msecs) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci u32 mask; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci mask = active_lo | active_hi; 7362306a36Sopenharmony_ci if (mask == 0) 7462306a36Sopenharmony_ci return; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * Assuming that active_hi and active_lo are a subsets of the bits in 7862306a36Sopenharmony_ci * gpio_dir. Also assumes that active_lo and active_hi don't overlap 7962306a36Sopenharmony_ci * in any bit position 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* Assert */ 8362306a36Sopenharmony_ci gpio_update(cx, mask, ~active_lo); 8462306a36Sopenharmony_ci schedule_timeout_uninterruptible(msecs_to_jiffies(assert_msecs)); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* Deassert */ 8762306a36Sopenharmony_ci gpio_update(cx, mask, ~active_hi); 8862306a36Sopenharmony_ci schedule_timeout_uninterruptible(msecs_to_jiffies(recovery_msecs)); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * GPIO Multiplexer - logical device 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_cistatic int gpiomux_log_status(struct v4l2_subdev *sd) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct cx18 *cx = v4l2_get_subdevdata(sd); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci mutex_lock(&cx->gpio_lock); 9962306a36Sopenharmony_ci CX18_INFO_DEV(sd, "GPIO: direction 0x%08x, value 0x%08x\n", 10062306a36Sopenharmony_ci cx->gpio_dir, cx->gpio_val); 10162306a36Sopenharmony_ci mutex_unlock(&cx->gpio_lock); 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int gpiomux_s_radio(struct v4l2_subdev *sd) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct cx18 *cx = v4l2_get_subdevdata(sd); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* 11062306a36Sopenharmony_ci * FIXME - work out the cx->active/audio_input mess - this is 11162306a36Sopenharmony_ci * intended to handle the switch to radio mode and set the 11262306a36Sopenharmony_ci * audio routing, but we need to update the state in cx 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci gpio_update(cx, cx->card->gpio_audio_input.mask, 11562306a36Sopenharmony_ci cx->card->gpio_audio_input.radio); 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int gpiomux_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct cx18 *cx = v4l2_get_subdevdata(sd); 12262306a36Sopenharmony_ci u32 data; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci switch (cx->card->audio_inputs[cx->audio_input].muxer_input) { 12562306a36Sopenharmony_ci case 1: 12662306a36Sopenharmony_ci data = cx->card->gpio_audio_input.linein; 12762306a36Sopenharmony_ci break; 12862306a36Sopenharmony_ci case 0: 12962306a36Sopenharmony_ci data = cx->card->gpio_audio_input.tuner; 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci default: 13262306a36Sopenharmony_ci /* 13362306a36Sopenharmony_ci * FIXME - work out the cx->active/audio_input mess - this is 13462306a36Sopenharmony_ci * intended to handle the switch from radio mode and set the 13562306a36Sopenharmony_ci * audio routing, but we need to update the state in cx 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci data = cx->card->gpio_audio_input.tuner; 13862306a36Sopenharmony_ci break; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci gpio_update(cx, cx->card->gpio_audio_input.mask, data); 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int gpiomux_s_audio_routing(struct v4l2_subdev *sd, 14562306a36Sopenharmony_ci u32 input, u32 output, u32 config) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct cx18 *cx = v4l2_get_subdevdata(sd); 14862306a36Sopenharmony_ci u32 data; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci switch (input) { 15162306a36Sopenharmony_ci case 0: 15262306a36Sopenharmony_ci data = cx->card->gpio_audio_input.tuner; 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci case 1: 15562306a36Sopenharmony_ci data = cx->card->gpio_audio_input.linein; 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci case 2: 15862306a36Sopenharmony_ci data = cx->card->gpio_audio_input.radio; 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci default: 16162306a36Sopenharmony_ci return -EINVAL; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci gpio_update(cx, cx->card->gpio_audio_input.mask, data); 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic const struct v4l2_subdev_core_ops gpiomux_core_ops = { 16862306a36Sopenharmony_ci .log_status = gpiomux_log_status, 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic const struct v4l2_subdev_tuner_ops gpiomux_tuner_ops = { 17262306a36Sopenharmony_ci .s_radio = gpiomux_s_radio, 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic const struct v4l2_subdev_audio_ops gpiomux_audio_ops = { 17662306a36Sopenharmony_ci .s_routing = gpiomux_s_audio_routing, 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic const struct v4l2_subdev_video_ops gpiomux_video_ops = { 18062306a36Sopenharmony_ci .s_std = gpiomux_s_std, 18162306a36Sopenharmony_ci}; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic const struct v4l2_subdev_ops gpiomux_ops = { 18462306a36Sopenharmony_ci .core = &gpiomux_core_ops, 18562306a36Sopenharmony_ci .tuner = &gpiomux_tuner_ops, 18662306a36Sopenharmony_ci .audio = &gpiomux_audio_ops, 18762306a36Sopenharmony_ci .video = &gpiomux_video_ops, 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* 19162306a36Sopenharmony_ci * GPIO Reset Controller - logical device 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_cistatic int resetctrl_log_status(struct v4l2_subdev *sd) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct cx18 *cx = v4l2_get_subdevdata(sd); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci mutex_lock(&cx->gpio_lock); 19862306a36Sopenharmony_ci CX18_INFO_DEV(sd, "GPIO: direction 0x%08x, value 0x%08x\n", 19962306a36Sopenharmony_ci cx->gpio_dir, cx->gpio_val); 20062306a36Sopenharmony_ci mutex_unlock(&cx->gpio_lock); 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int resetctrl_reset(struct v4l2_subdev *sd, u32 val) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct cx18 *cx = v4l2_get_subdevdata(sd); 20762306a36Sopenharmony_ci const struct cx18_gpio_i2c_slave_reset *p; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci p = &cx->card->gpio_i2c_slave_reset; 21062306a36Sopenharmony_ci switch (val) { 21162306a36Sopenharmony_ci case CX18_GPIO_RESET_I2C: 21262306a36Sopenharmony_ci gpio_reset_seq(cx, p->active_lo_mask, p->active_hi_mask, 21362306a36Sopenharmony_ci p->msecs_asserted, p->msecs_recovery); 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci case CX18_GPIO_RESET_Z8F0811: 21662306a36Sopenharmony_ci /* 21762306a36Sopenharmony_ci * Assert timing for the Z8F0811 on HVR-1600 boards: 21862306a36Sopenharmony_ci * 1. Assert RESET for min of 4 clock cycles at 18.432 MHz to 21962306a36Sopenharmony_ci * initiate 22062306a36Sopenharmony_ci * 2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock 22162306a36Sopenharmony_ci * cycles (6,601,085 nanoseconds ~= 7 milliseconds) 22262306a36Sopenharmony_ci * 3. DBG pin must be high before chip exits reset for normal 22362306a36Sopenharmony_ci * operation. DBG is open drain and hopefully pulled high 22462306a36Sopenharmony_ci * since we don't normally drive it (GPIO 1?) for the 22562306a36Sopenharmony_ci * HVR-1600 22662306a36Sopenharmony_ci * 4. Z8F0811 won't exit reset until RESET is deasserted 22762306a36Sopenharmony_ci * 5. Zilog comes out of reset, loads reset vector address and 22862306a36Sopenharmony_ci * executes from there. Required recovery delay unknown. 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_ci gpio_reset_seq(cx, p->ir_reset_mask, 0, 23162306a36Sopenharmony_ci p->msecs_asserted, p->msecs_recovery); 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci case CX18_GPIO_RESET_XC2028: 23462306a36Sopenharmony_ci if (cx->card->tuners[0].tuner == TUNER_XC2028) 23562306a36Sopenharmony_ci gpio_reset_seq(cx, (1 << cx->card->xceive_pin), 0, 23662306a36Sopenharmony_ci 1, 1); 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic const struct v4l2_subdev_core_ops resetctrl_core_ops = { 24362306a36Sopenharmony_ci .log_status = resetctrl_log_status, 24462306a36Sopenharmony_ci .reset = resetctrl_reset, 24562306a36Sopenharmony_ci}; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic const struct v4l2_subdev_ops resetctrl_ops = { 24862306a36Sopenharmony_ci .core = &resetctrl_core_ops, 24962306a36Sopenharmony_ci}; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci/* 25262306a36Sopenharmony_ci * External entry points 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_civoid cx18_gpio_init(struct cx18 *cx) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci mutex_lock(&cx->gpio_lock); 25762306a36Sopenharmony_ci cx->gpio_dir = cx->card->gpio_init.direction; 25862306a36Sopenharmony_ci cx->gpio_val = cx->card->gpio_init.initial_value; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (cx->card->tuners[0].tuner == TUNER_XC2028) { 26162306a36Sopenharmony_ci cx->gpio_dir |= 1 << cx->card->xceive_pin; 26262306a36Sopenharmony_ci cx->gpio_val |= 1 << cx->card->xceive_pin; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (cx->gpio_dir == 0) { 26662306a36Sopenharmony_ci mutex_unlock(&cx->gpio_lock); 26762306a36Sopenharmony_ci return; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci CX18_DEBUG_INFO("GPIO initial dir: %08x/%08x out: %08x/%08x\n", 27162306a36Sopenharmony_ci cx18_read_reg(cx, CX18_REG_GPIO_DIR1), 27262306a36Sopenharmony_ci cx18_read_reg(cx, CX18_REG_GPIO_DIR2), 27362306a36Sopenharmony_ci cx18_read_reg(cx, CX18_REG_GPIO_OUT1), 27462306a36Sopenharmony_ci cx18_read_reg(cx, CX18_REG_GPIO_OUT2)); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci gpio_write(cx); 27762306a36Sopenharmony_ci mutex_unlock(&cx->gpio_lock); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ciint cx18_gpio_register(struct cx18 *cx, u32 hw) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct v4l2_subdev *sd; 28362306a36Sopenharmony_ci const struct v4l2_subdev_ops *ops; 28462306a36Sopenharmony_ci char *str; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci switch (hw) { 28762306a36Sopenharmony_ci case CX18_HW_GPIO_MUX: 28862306a36Sopenharmony_ci sd = &cx->sd_gpiomux; 28962306a36Sopenharmony_ci ops = &gpiomux_ops; 29062306a36Sopenharmony_ci str = "gpio-mux"; 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci case CX18_HW_GPIO_RESET_CTRL: 29362306a36Sopenharmony_ci sd = &cx->sd_resetctrl; 29462306a36Sopenharmony_ci ops = &resetctrl_ops; 29562306a36Sopenharmony_ci str = "gpio-reset-ctrl"; 29662306a36Sopenharmony_ci break; 29762306a36Sopenharmony_ci default: 29862306a36Sopenharmony_ci return -EINVAL; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci v4l2_subdev_init(sd, ops); 30262306a36Sopenharmony_ci v4l2_set_subdevdata(sd, cx); 30362306a36Sopenharmony_ci snprintf(sd->name, sizeof(sd->name), "%s %s", cx->v4l2_dev.name, str); 30462306a36Sopenharmony_ci sd->grp_id = hw; 30562306a36Sopenharmony_ci return v4l2_device_register_subdev(&cx->v4l2_dev, sd); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_civoid cx18_reset_ir_gpio(void *data) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct cx18 *cx = to_cx18(data); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (cx->card->gpio_i2c_slave_reset.ir_reset_mask == 0) 31362306a36Sopenharmony_ci return; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci CX18_DEBUG_INFO("Resetting IR microcontroller\n"); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci v4l2_subdev_call(&cx->sd_resetctrl, 31862306a36Sopenharmony_ci core, reset, CX18_GPIO_RESET_Z8F0811); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ciEXPORT_SYMBOL(cx18_reset_ir_gpio); 32162306a36Sopenharmony_ci/* This symbol is exported for use by lirc_pvr150 for the IR-blaster */ 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci/* Xceive tuner reset function */ 32462306a36Sopenharmony_ciint cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct i2c_algo_bit_data *algo = dev; 32762306a36Sopenharmony_ci struct cx18_i2c_algo_callback_data *cb_data = algo->data; 32862306a36Sopenharmony_ci struct cx18 *cx = cb_data->cx; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (cmd != XC2028_TUNER_RESET || 33162306a36Sopenharmony_ci cx->card->tuners[0].tuner != TUNER_XC2028) 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci CX18_DEBUG_INFO("Resetting XCeive tuner\n"); 33562306a36Sopenharmony_ci return v4l2_subdev_call(&cx->sd_resetctrl, 33662306a36Sopenharmony_ci core, reset, CX18_GPIO_RESET_XC2028); 33762306a36Sopenharmony_ci} 338