int cx23888_ir_probe(struct cx23885_dev *dev) { struct cx23888_ir_state *state; struct v4l2_subdev *sd; struct v4l2_subdev_ir_parameters default_params; int ret; state = kzalloc(sizeof(struct cx23888_ir_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; spin_lock_init(&state->rx_kfifo_lock); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) state->rx_kfifo = kfifo_alloc(CX23888_IR_RX_KFIFO_SIZE, GFP_KERNEL, &state->rx_kfifo_lock); if (state->rx_kfifo == NULL) return -ENOMEM; #else if (kfifo_alloc(&state->rx_kfifo, CX23888_IR_RX_KFIFO_SIZE, GFP_KERNEL)) return -ENOMEM; #endif state->dev = dev; state->id = V4L2_IDENT_CX23888_IR; state->rev = 0; sd = &state->sd; v4l2_subdev_init(sd, &cx23888_ir_controller_ops); v4l2_set_subdevdata(sd, state); /* FIXME - fix the formatting of dev->v4l2_dev.name and use it */ snprintf(sd->name, sizeof(sd->name), "%s/888-ir", dev->name); sd->grp_id = CX23885_HW_888_IR; ret = v4l2_device_register_subdev(&dev->v4l2_dev, sd); if (ret == 0) { /* * Ensure no interrupts arrive from '888 specific conditions, * since we ignore them in this driver to have commonality with * similar IR controller cores. */ cx23888_ir_write4(dev, CX23888_IR_IRQEN_REG, 0); mutex_init(&state->rx_params_lock); memcpy(&default_params, &default_rx_params, sizeof(struct v4l2_subdev_ir_parameters)); v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); mutex_init(&state->tx_params_lock); memcpy(&default_params, &default_tx_params, sizeof(struct v4l2_subdev_ir_parameters)); v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); } else { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) kfifo_free(state->rx_kfifo); #else kfifo_free(&state->rx_kfifo); #endif } return ret; }
static unsigned int rxclk_rx_s_carrier(struct cx23885_dev *dev, unsigned int freq, u16 *divider) { *divider = carrier_freq_to_clock_divider(freq); cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider); return clock_divider_to_carrier_freq(*divider); }
/* * IR Tx Carrier Duty Cycle register helpers */ static unsigned int cduty_tx_s_duty_cycle(struct cx23885_dev *dev, unsigned int duty_cycle) { u32 n; n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */ if (n != 0) n--; if (n > 15) n = 15; cx23888_ir_write4(dev, CX23888_IR_CDUTY_REG, n); return DIV_ROUND_CLOSEST((n + 1) * 100, 16); }
static u32 rxclk_rx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns, u16 *divider) { u64 pulse_clocks; if (ns > IR_MAX_DURATION) ns = IR_MAX_DURATION; pulse_clocks = ns_to_pulse_clocks(ns); *divider = pulse_clocks_to_clock_divider(pulse_clocks); cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider); return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); }
static u32 rxclk_rx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns, u16 *divider) { u64 pulse_clocks; if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS) ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; pulse_clocks = ns_to_pulse_clocks(ns); *divider = pulse_clocks_to_clock_divider(pulse_clocks); cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider); return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); }
static int cx23888_ir_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { struct cx23888_ir_state *state = to_state(sd); u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg; if ((addr & 0x3) != 0) return -EINVAL; if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG) return -EINVAL; cx23888_ir_write4(state->dev, addr, reg->val); return 0; }
static int cx23888_ir_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct cx23888_ir_state *state = to_state(sd); u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg; if (!cx23888_ir_dbg_match(®->match)) return -EINVAL; if ((addr & 0x3) != 0) return -EINVAL; if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; cx23888_ir_write4(state->dev, addr, reg->val); return 0; }
static int cx23888_ir_tx_shutdown(struct v4l2_subdev *sd) { struct cx23888_ir_state *state = to_state(sd); struct cx23885_dev *dev = state->dev; mutex_lock(&state->tx_params_lock); /* Disable or slow down all IR Tx circuits and counters */ irqenable_tx(dev, 0); control_tx_enable(dev, false); control_tx_modulation_enable(dev, false); cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, TXCLK_TCD); state->tx_params.shutdown = true; mutex_unlock(&state->tx_params_lock); return 0; }
static int cx23888_ir_rx_shutdown(struct v4l2_subdev *sd) { struct cx23888_ir_state *state = to_state(sd); struct cx23885_dev *dev = state->dev; mutex_lock(&state->rx_params_lock); /* Disable or slow down all IR Rx circuits and counters */ irqenable_rx(dev, 0); control_rx_enable(dev, false); control_rx_demodulation_enable(dev, false); control_rx_s_edge_detection(dev, CNTRL_EDG_NONE); filter_rx_s_min_width(dev, 0); cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, RXCLK_RCD); state->rx_params.shutdown = true; mutex_unlock(&state->rx_params_lock); return 0; }
/* * V4L2 Subdevice IR Ops */ static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled) { struct cx23888_ir_state *state = to_state(sd); struct cx23885_dev *dev = state->dev; unsigned long flags; u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG); u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG); u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG); union cx23888_ir_fifo_rec rx_data[FIFO_RX_DEPTH]; unsigned int i, j, k; u32 events, v; int tsr, rsr, rto, ror, tse, rse, rte, roe, kror; tsr = stats & STATS_TSR; /* Tx FIFO Service Request */ rsr = stats & STATS_RSR; /* Rx FIFO Service Request */ rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */ ror = stats & STATS_ROR; /* Rx FIFO Over Run */ tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */ rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */ rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */ roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */ *handled = false; v4l2_dbg(2, ir_888_debug, sd, "IRQ Status: %s %s %s %s %s %s\n", tsr ? "tsr" : " ", rsr ? "rsr" : " ", rto ? "rto" : " ", ror ? "ror" : " ", stats & STATS_TBY ? "tby" : " ", stats & STATS_RBY ? "rby" : " "); v4l2_dbg(2, ir_888_debug, sd, "IRQ Enables: %s %s %s %s\n", tse ? "tse" : " ", rse ? "rse" : " ", rte ? "rte" : " ", roe ? "roe" : " "); /* * Transmitter interrupt service */ if (tse && tsr) { /* * TODO: * Check the watermark threshold setting * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo * Push the data to the hardware FIFO. * If there was nothing more to send in the tx_kfifo, disable * the TSR IRQ and notify the v4l2_device. * If there was something in the tx_kfifo, check the tx_kfifo * level and notify the v4l2_device, if it is low. */ /* For now, inhibit TSR interrupt until Tx is implemented */ irqenable_tx(dev, 0); events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ; v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events); *handled = true; } /* * Receiver interrupt service */ kror = 0; if ((rse && rsr) || (rte && rto)) { /* * Receive data on RSR to clear the STATS_RSR. * Receive data on RTO, since we may not have yet hit the RSR * watermark when we receive the RTO. */ for (i = 0, v = FIFO_RX_NDV; (v & FIFO_RX_NDV) && !kror; i = 0) { for (j = 0; (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) { v = cx23888_ir_read4(dev, CX23888_IR_FIFO_REG); rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV; i++; } if (i == 0) break; j = i * sizeof(union cx23888_ir_fifo_rec); k = kfifo_in_locked(&state->rx_kfifo, (unsigned char *) rx_data, j, &state->rx_kfifo_lock); if (k != j) kror++; /* rx_kfifo over run */ } *handled = true; } events = 0; v = 0; if (kror) { events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN; v4l2_err(sd, "IR receiver software FIFO overrun\n"); } if (roe && ror) { /* * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear * the Rx FIFO Over Run status (STATS_ROR) */ v |= CNTRL_RFE; events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN; v4l2_err(sd, "IR receiver hardware FIFO overrun\n"); } if (rte && rto) { /* * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear * the Rx Pulse Width Timer Time Out (STATS_RTO) */ v |= CNTRL_RXE; events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED; } if (v) { /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */ cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl & ~v); cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl); *handled = true; } spin_lock_irqsave(&state->rx_kfifo_lock, flags); if (kfifo_len(&state->rx_kfifo) >= CX23888_IR_RX_KFIFO_SIZE / 2) events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ; spin_unlock_irqrestore(&state->rx_kfifo_lock, flags); if (events) v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events); return 0; }
/* * IR Filter Register helpers */ static u32 filter_rx_s_min_width(struct cx23885_dev *dev, u32 min_width_ns) { u32 count = ns_to_lpf_count(min_width_ns); cx23888_ir_write4(dev, CX23888_IR_FILTR_REG, count); return lpf_count_to_ns(count); }