/* * 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 cx25840_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,
CX25840_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, CX25840_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,
CX25840_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, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => us */ if (rem >= CX25840_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 = CX25840_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(sd, 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 = cx25840_read4(c, CX25840_IR_FIFO_REG);
rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV;
i++;
} if (i == 0) break;
j = i * sizeof(union cx25840_ir_fifo_rec);
k = kfifo_in_locked(&ir_state->rx_kfifo,
(unsignedchar *) rx_data, j,
&ir_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 */
cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl & ~v);
cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl);
*handled = true;
}
spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags); if (kfifo_len(&ir_state->rx_kfifo) >= CX25840_IR_RX_KFIFO_SIZE / 2)
events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags);
if (events)
v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events); return 0;
}
n = count / sizeof(union cx25840_ir_fifo_rec)
* sizeof(union cx25840_ir_fifo_rec); if (n == 0) {
*num = 0; return 0;
}
n = kfifo_out_locked(&ir_state->rx_kfifo, buf, n,
&ir_state->rx_kfifo_lock);
n /= sizeof(union cx25840_ir_fifo_rec);
*num = n * sizeof(union cx25840_ir_fifo_rec);
for (p = (union cx25840_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_debug, sd, "rx read: %10u ns %s %s\n",
v, u ? "mark" : "space", w ? "(timed out)" : ""); if (w)
v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n");
} return 0;
}
c = ir_state->c;
mutex_lock(&ir_state->rx_params_lock);
/* Disable or slow down all IR Rx circuits and counters */
irqenable_rx(sd, 0);
control_rx_enable(c, false);
control_rx_demodulation_enable(c, false);
control_rx_s_edge_detection(c, CNTRL_EDG_NONE);
filter_rx_s_min_width(c, 0);
cx25840_write4(c, CX25840_IR_RXCLK_REG, RXCLK_RCD);
#if 0 /* * FIXME - the code below is an incomplete and untested sketch of what * may need to be done. The critical part is to get 4 (or 8) pulses * from the tx_kfifo, or converted from ns to the proper units from the * input, and push them off to the hardware Tx FIFO right away, if the * HW TX fifo needs service. The rest can be pushed to the tx_kfifo in * a less critical timeframe. Also watch out for overruning the * tx_kfifo - don't let it happen and let the caller know not all his * pulses were written.
*/
u32 *ns_pulse = (u32 *) buf; unsignedint n;
u32 fifo_pulse[FIFO_TX_DEPTH];
u32 mark;
/* Compute how much we can fit in the tx kfifo */
n = CX25840_IR_TX_KFIFO_SIZE - kfifo_len(ir_state->tx_kfifo);
n = min(n, (unsignedint) count);
n /= sizeof(u32);
/* FIXME - turn on Tx Fifo service interrupt * check hardware fifo level, and other stuff
*/ for (i = 0; i < n; ) { for (j = 0; j < FIFO_TX_DEPTH / 2 && i < n; j++) {
mark = ns_pulse[i] & LEVEL_MASK;
fifo_pulse[j] = ns_to_pulse_width_count(
ns_pulse[i] &
~LEVEL_MASK,
ir_state->txclk_divider); if (mark)
fifo_pulse[j] &= FIFO_RXTX_LVL;
i++;
}
kfifo_put(ir_state->tx_kfifo, (u8 *) fifo_pulse,
j * sizeof(u32));
}
*num = n * sizeof(u32); #else /* For now enable the Tx FIFO Service interrupt & pretend we did work */
irqenable_tx(sd, IRQEN_TSE);
*num = count; #endif return 0;
}
c = ir_state->c;
mutex_lock(&ir_state->tx_params_lock);
/* Disable or slow down all IR Tx circuits and counters */
irqenable_tx(sd, 0);
control_tx_enable(c, false);
control_tx_modulation_enable(c, false);
cx25840_write4(c, CX25840_IR_TXCLK_REG, TXCLK_TCD);
/* * FIXME: we don't have hardware help for IO pin level inversion * here like we have on the CX23888. * Act on this with some mix of logical inversion of data levels, * carrier polarity, and carrier duty cycle.
*/
o->invert_level = p->invert_level;
o->interrupt_enable = p->interrupt_enable;
o->enable = p->enable; if (p->enable) { /* reset tx_fifo here */ if (p->interrupt_enable)
irqenable_tx(sd, IRQEN_TSE);
control_tx_enable(c, p->enable);
}
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.