// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for Mediatek IR Receiver Controller * * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com>
*/
/* Register to enable PWM and IR */ #define MTK_CONFIG_HIGH_REG 0x0c
/* Bit to enable IR pulse width detection */ #define MTK_PWM_EN BIT(13)
/* * Register to setting ok count whose unit based on hardware sampling period * indicating IR receiving completion and then making IRQ fires
*/ #define MTK_OK_COUNT_MASK (GENMASK(22, 16)) #define MTK_OK_COUNT(x) ((x) << 16)
/* Bit to enable IR hardware function */ #define MTK_IR_EN BIT(0)
/* Bit to restart IR receiving */ #define MTK_IRCLR BIT(0)
/* Bit to enable interrupt */ #define MTK_IRINT_EN BIT(0)
/* Bit to clear interrupt status */ #define MTK_IRINT_CLR BIT(0)
/* Maximum count of samples */ #define MTK_MAX_SAMPLES 0xff /* Indicate the end of IR message */ #define MTK_IR_END(v, p) ((v) == MTK_MAX_SAMPLES && (p) == 0) /* Number of registers to record the pulse width */ #define MTK_CHKDATA_SZ 17 /* Sample period in us */ #define MTK_IR_SAMPLE 46
enum mtk_fields { /* Register to setting software sampling period */
MTK_CHK_PERIOD, /* Register to setting hardware sampling period */
MTK_HW_PERIOD,
};
enum mtk_regs { /* Register to clear state of state machine */
MTK_IRCLR_REG, /* Register containing pulse width data */
MTK_CHKDATA_REG, /* Register to enable IR interrupt */
MTK_IRINT_EN_REG, /* Register to ack IR interrupt */
MTK_IRINT_CLR_REG
};
/* * struct mtk_ir_data - This is the structure holding all differences among various hardwares * @regs: The pointer to the array holding registers offset * @fields: The pointer to the array holding fields location * @div: The internal divisor for the based reference clock * @ok_count: The count indicating the completion of IR data * receiving when count is reached * @hw_period: The value indicating the hardware sampling period
*/ struct mtk_ir_data { const u32 *regs; conststruct mtk_field_type *fields;
u8 div;
u8 ok_count;
u32 hw_period;
};
/* * struct mtk_ir - This is the main datasructure for holding the state * of the driver * @dev: The device pointer * @rc: The rc instrance * @base: The mapped register i/o base * @irq: The IRQ that we are using * @clk: The clock that IR internal is using * @bus: The clock that software decoder is using * @data: Holding specific data for vaious platform
*/ struct mtk_ir { struct device *dev; struct rc_dev *rc; void __iomem *base; int irq; struct clk *clk; struct clk *bus; conststruct mtk_ir_data *data;
};
/* * Period for software decoder used in the * unit of raw software sampling
*/
val = DIV_ROUND_CLOSEST(clk_get_rate(ir->bus),
USEC_PER_SEC * ir->data->div / MTK_IR_SAMPLE);
/* * Each pulse and space is encoded as a single byte, each byte * alternating between pulse and space. If a pulse or space is longer * than can be encoded in a single byte, it is encoded as the maximum * value 0xff. * * If a space is longer than ok_count (about 23ms), the value is * encoded as zero, and all following bytes are zero. Any IR that * follows will be presented in the next interrupt. * * If there are more than 68 (=MTK_CHKDATA_SZ * 4) pulses and spaces, * then the only the first 68 will be presented; the rest is lost.
*/
/* Handle all pulse and space IR controller captures */ for (i = 0 ; i < MTK_CHKDATA_SZ ; i++) {
val = mtk_r32(ir, mtk_chkdata_reg(ir, i));
dev_dbg(ir->dev, "@reg%d=0x%08x\n", i, val);
/* * The maximum number of edges the IR controller can * hold is MTK_CHKDATA_SZ * 4. So if received IR messages * is over the limit, the last incomplete IR message would * be appended trailing space and still would be sent into * ir-rc-raw to decode. That helps it is possible that it * has enough information to decode a scancode even if the * trailing end of the message is missing.
*/ if (!MTK_IR_END(wid, rawir.pulse)) {
rawir.pulse = false;
rawir.duration = MTK_MAX_SAMPLES * (MTK_IR_SAMPLE + 1);
ir_raw_event_store_with_filter(ir->rc, &rawir);
}
ir_raw_event_handle(ir->rc);
/* * Restart controller for the next receive that would * clear up all CHKDATA registers
*/
mtk_w32_mask(ir, 0x1, MTK_IRCLR, ir->data->regs[MTK_IRCLR_REG]);
/* Clear interrupt status */
mtk_w32_mask(ir, 0x1, MTK_IRINT_CLR,
ir->data->regs[MTK_IRINT_CLR_REG]);
ir->clk = devm_clk_get(dev, "clk"); if (IS_ERR(ir->clk)) {
dev_err(dev, "failed to get a ir clock.\n"); return PTR_ERR(ir->clk);
}
ir->bus = devm_clk_get(dev, "bus"); if (IS_ERR(ir->bus)) { /* * For compatibility with older device trees try unnamed * ir->bus uses the same clock as ir->clock.
*/
ir->bus = ir->clk;
}
ir->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ir->base)) return PTR_ERR(ir->base);
ir->rc = devm_rc_allocate_device(dev, RC_DRIVER_IR_RAW); if (!ir->rc) {
dev_err(dev, "failed to allocate device\n"); return -ENOMEM;
}
ret = devm_rc_register_device(dev, ir->rc); if (ret) {
dev_err(dev, "failed to register rc device\n"); return ret;
}
platform_set_drvdata(pdev, ir);
ir->irq = platform_get_irq(pdev, 0); if (ir->irq < 0) return -ENODEV;
if (clk_prepare_enable(ir->clk)) {
dev_err(dev, "try to enable ir_clk failed\n"); return -EINVAL;
}
if (clk_prepare_enable(ir->bus)) {
dev_err(dev, "try to enable ir_clk failed\n");
ret = -EINVAL; goto exit_clkdisable_clk;
}
/* * Enable interrupt after proper hardware * setup and IRQ handler registration
*/
mtk_irq_disable(ir, MTK_IRINT_EN);
ret = devm_request_irq(dev, ir->irq, mtk_ir_irq, 0, MTK_IR_DEV, ir); if (ret) {
dev_err(dev, "failed request irq\n"); goto exit_clkdisable_bus;
}
/* * Setup software sample period as the reference of software decoder
*/
val = (mtk_chk_period(ir) << ir->data->fields[MTK_CHK_PERIOD].offset) &
ir->data->fields[MTK_CHK_PERIOD].mask;
mtk_w32_mask(ir, val, ir->data->fields[MTK_CHK_PERIOD].mask,
ir->data->fields[MTK_CHK_PERIOD].reg);
/* * Setup hardware sampling period used to setup the proper timeout for * indicating end of IR receiving completion
*/
val = (ir->data->hw_period << ir->data->fields[MTK_HW_PERIOD].offset) &
ir->data->fields[MTK_HW_PERIOD].mask;
mtk_w32_mask(ir, val, ir->data->fields[MTK_HW_PERIOD].mask,
ir->data->fields[MTK_HW_PERIOD].reg);
/* Set de-glitch counter */
mtk_w32_mask(ir, MTK_DG_CNT(1), MTK_DG_CNT_MASK, MTK_IRTHD);
/* Enable IR and PWM */
val = mtk_r32(ir, MTK_CONFIG_HIGH_REG) & ~MTK_OK_COUNT_MASK;
val |= MTK_OK_COUNT(ir->data->ok_count) | MTK_PWM_EN | MTK_IR_EN;
mtk_w32(ir, val, MTK_CONFIG_HIGH_REG);
mtk_irq_enable(ir, MTK_IRINT_EN);
dev_info(dev, "Initialized MT7623 IR driver, sample period = %dus\n",
MTK_IR_SAMPLE);
/* * Avoid contention between remove handler and * IRQ handler so that disabling IR interrupt and * waiting for pending IRQ handler to complete
*/
mtk_irq_disable(ir, MTK_IRINT_EN);
synchronize_irq(ir->irq);
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.