/* * We use this union internally for convenience, but callers to tx_write * and rx_read will be expecting records of type struct ir_raw_event. * Always ensure the size of this union is dictated by struct ir_raw_event.
*/ union cx23888_ir_fifo_rec {
u32 hw_fifo_data; struct ir_raw_event ir_core_data;
};
/* * Rx and Tx Clock Divider register computations * * Note the largest clock divider value of 0xffff corresponds to: * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns * which fits in 21 bits, so we'll use unsigned int for time arguments.
*/ staticinline u16 count_to_clock_divider(unsignedint d)
{ if (d > RXCLK_RCD + 1)
d = RXCLK_RCD; elseif (d < 2)
d = 1; else
d--; return (u16) d;
}
/* * Low Pass Filter register calculations * * Note the largest count value of 0xffff corresponds to: * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns * which fits in 21 bits, so we'll use unsigned int for time arguments.
*/ staticinline u16 count_to_lpf_count(unsignedint d)
{ if (d > FILTR_LPF)
d = FILTR_LPF; elseif (d < 4)
d = 0; return (u16) d;
}
staticinlineunsignedint lpf_count_to_ns(unsignedint count)
{ /* Duration of the Low Pass Filter rejection window in ns */ return DIV_ROUND_CLOSEST(count * 1000,
CX23888_IR_REFCLK_FREQ / 1000000);
}
staticinlineunsignedint lpf_count_to_us(unsignedint count)
{ /* Duration of the Low Pass Filter rejection window in us */ return DIV_ROUND_CLOSEST(count, CX23888_IR_REFCLK_FREQ / 1000000);
}
/* * FIFO register pulse width count computations
*/ static u32 clock_divider_to_resolution(u16 divider)
{ /* * Resolution is the duration of 1 tick of the readable portion of * the pulse width counter as read from the FIFO. The two lsb's are * not readable, hence the << 2. This function returns ns.
*/ return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000,
CX23888_IR_REFCLK_FREQ / 1000000);
}
/* * The 2 lsb's of the pulse width timer count are not readable, hence * the (count << 2) | 0x3
*/
n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */
rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000); /* / MHz => us */ if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2)
n++; return (unsignedint) n;
}
/* * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts * * The total pulse clock count is an 18 bit pulse width timer count as the most * significant part and (up to) 16 bit clock divider count as a modulus. * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse * width timer count's least significant bit.
*/ static u64 ns_to_pulse_clocks(u32 ns)
{
u64 clocks;
u32 rem;
clocks = CX23888_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */
rem = do_div(clocks, 1000); /* /1000 = cycles */ if (rem >= 1000 / 2)
clocks++; return clocks;
}
/* net result needs to be rounded down and decremented by 1 */ if (count > RXCLK_RCD + 1)
count = RXCLK_RCD; elseif (count < 2)
count = 1; else
count--; return (u16) count;
}
/* * IR Control Register helpers
*/ enum tx_fifo_watermark {
TX_FIFO_HALF_EMPTY = 0,
TX_FIFO_EMPTY = CNTRL_TIC,
};
/* * 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,
(unsignedchar *) 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 resetting hardware */
cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl & ~v);
cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl);
*handled = true;
}
unsignedint i, n; union cx23888_ir_fifo_rec *p; unsigned u, v, w;
n = count / sizeof(union cx23888_ir_fifo_rec)
* sizeof(union cx23888_ir_fifo_rec); if (n == 0) {
*num = 0; return 0;
}
n = kfifo_out_locked(&state->rx_kfifo, buf, n, &state->rx_kfifo_lock);
n /= sizeof(union cx23888_ir_fifo_rec);
*num = n * sizeof(union cx23888_ir_fifo_rec);
for (p = (union cx23888_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) {
if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) { /* Assume RTO was because of no IR light input */
u = 0;
w = 1;
} else {
u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0; if (invert)
u = u ? 0 : 1;
w = 0;
}
v = (unsigned) pulse_width_count_to_ns(
(u16)(p->hw_fifo_data & FIFO_RXTX), divider) / 1000; if (v > IR_MAX_DURATION)
v = IR_MAX_DURATION;
p->ir_core_data = (struct ir_raw_event)
{ .pulse = u, .duration = v, .timeout = w };
v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s %s\n",
v, u ? "mark" : "space", w ? "(timed out)" : ""); if (w)
v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n");
} return 0;
}
/* 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);
spin_lock_irqsave(&state->rx_kfifo_lock, flags);
kfifo_reset(&state->rx_kfifo); /* reset tx_fifo too if there is one... */
spin_unlock_irqrestore(&state->rx_kfifo_lock, flags); if (p->interrupt_enable)
irqenable_rx(dev, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE);
control_rx_enable(dev, p->enable);
}
mutex_unlock(&state->rx_params_lock); return 0;
}
/* Transmitter */ staticint cx23888_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count,
ssize_t *num)
{ struct cx23888_ir_state *state = to_state(sd); struct cx23885_dev *dev = state->dev; /* For now enable the Tx FIFO Service interrupt & pretend we did work */
irqenable_tx(dev, IRQEN_TSE);
*num = count; return 0;
}
/* 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);
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 (kfifo_alloc(&state->rx_kfifo, CX23888_IR_RX_KFIFO_SIZE,
GFP_KERNEL)) {
kfree(state); return -ENOMEM;
}
state->dev = dev;
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);
default_params = default_rx_params;
v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params);
state = to_state(sd);
v4l2_device_unregister_subdev(sd);
kfifo_free(&state->rx_kfifo);
kfree(state); /* Nothing more to free() as state held the actual v4l2_subdev object */ return 0;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.4 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.