You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
pwm: rockchip: add support for IR NEC transmit
Using RK3506 pwm1 channel0 as an DT configuration example:
&pwm1_8ch_0 {
status = "okay";
pinctrl-names = "active";
pinctrl-0 = <&rm_io12_pwm1_ch0>;
assigned-clocks = <&cru CLK_PWM1>;
assigned-clock-rates = <100000000>;
rockchip,pwm-ir-transmit;
};
Change-Id: I5e7a7a0925d6cba674ff91b117025b41b5be0387
Signed-off-by: Damon Ding <damon.ding@rock-chips.com>
This commit is contained in:
@@ -20,10 +20,13 @@
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/pwm-rockchip.h>
|
||||
#include <linux/time.h>
|
||||
#include <media/rc-core.h>
|
||||
#include "pwm-rockchip-irq-callbacks.h"
|
||||
|
||||
#define PWM_MAX_CHANNEL_NUM 8
|
||||
|
||||
#define PWM_IR_TRANSMIT_BUFFER_SIZE 7
|
||||
|
||||
/*
|
||||
* regs for pwm v1-v3
|
||||
*/
|
||||
@@ -101,6 +104,9 @@
|
||||
#define CLK_PRESCALE(v) HIWORD_UPDATE(v, 0, 2)
|
||||
#define CLK_SCALE(v) HIWORD_UPDATE(v, 4, 12)
|
||||
#define CLK_SRC_SEL(v) HIWORD_UPDATE(v, 13, 14)
|
||||
#define SRC_CLK_PWM 0
|
||||
#define SRC_CLK_PWM_OSC 1
|
||||
#define SRC_CLK_PWM_RC 2
|
||||
#define CLK_GLOBAL_SEL(v) HIWORD_UPDATE(v, 15, 15)
|
||||
/* CTRL */
|
||||
#define CTRL_V4 0xc
|
||||
@@ -220,6 +226,46 @@
|
||||
#define GLOBAL_CTRL 0xc4
|
||||
#define GLOBAL_PWM_EN(v) HIWORD_UPDATE(v, 0, 0)
|
||||
#define GLOBAL_PWM_UPDATE_EN(v) HIWORD_UPDATE(v, 1, 1)
|
||||
/* IR_TRANS_ARBITER */
|
||||
#define IR_TRANS_ARBITER 0x180
|
||||
#define IR_TRANS_GRANT_SHIFT 0
|
||||
#define IR_TRANS_READ_LOCK_SHIFT 16
|
||||
/* IR_TRANS_CTRL0 */
|
||||
#define IR_TRANS_CTRL0 0x184
|
||||
#define IR_TRANS_OUT_ENABLE(v) HIWORD_UPDATE(v, 0, 0)
|
||||
#define IR_TRANS_DUTY_POL(v) HIWORD_UPDATE(v, 1, 1)
|
||||
#define IR_TRANS_INACTIVE_POL(v) HIWORD_UPDATE(v, 2, 2)
|
||||
#define IR_TRANS_MODE(v) HIWORD_UPDATE(v, 3, 3)
|
||||
#define IR_TRANS_FORMAT(v) HIWORD_UPDATE(v, 4, 7)
|
||||
#define NEC_WITH_SIMPLE_REPEAT_CODE 0
|
||||
#define NEC_WITH_FULL_REPEAT_CODE 1
|
||||
#define TC9012 2
|
||||
#define SONY 3
|
||||
/* IR_TRANS_CTRL1 */
|
||||
#define IR_TRANS_CTRL1 0x188
|
||||
#define IR_TRANS_RPT(v) HIWORD_UPDATE(v, 0, 15)
|
||||
/* IR_TRANS_PRE */
|
||||
#define IR_TRANS_PRE 0x18c
|
||||
#define IR_TRANS_OUT_LOW_PRELOAD_SHIFT 0
|
||||
#define IR_TRANS_OUT_HIGH_PRELOAD_SHIFT 16
|
||||
/* IR_TRANS_SPRE */
|
||||
#define IR_TRANS_SPRE 0x190
|
||||
#define IR_TRANS_OUT_HIGH_SIMPLE_PRELOAD_SHIFT 0
|
||||
/* IR_TRANS_LD */
|
||||
#define IR_TRANS_LD 0x194
|
||||
#define IR_TRANS_OUT_DATA_LOW_PERIOD_SHIFT 0
|
||||
/* IR_TRANS_HD */
|
||||
#define IR_TRANS_HD 0x198
|
||||
#define IR_TRANS_OUT_HIGH_PERIOD_FOR_ZERO_SHIFT 0
|
||||
#define IR_TRANS_OUT_HIGH_PERIOD_FOR_ONE_SHIFT 16
|
||||
/* IR_TRANS_BURST_FRAME */
|
||||
#define IR_TRANS_BURST_FRAME 0x19c
|
||||
#define IR_TRANS_OUT_FRAME_PERIOD_SHIFT 0
|
||||
#define IR_TRANS_OUT_FRAME_PERIOD_MASK (0x3ffff << IR_TRANS_OUT_FRAME_PERIOD_SHIFT)
|
||||
#define IR_TRANS_OUT_BURST_PERIOD_SHIFT 20
|
||||
/* IR_TRANS_DATA_VALUE */
|
||||
#define IR_TRANS_DATA_VALUE 0x1a0
|
||||
#define IR_TRANS_OUT_VALUE_SHIFT 0
|
||||
/* FREQ_ARBITER */
|
||||
#define FREQ_ARBITER 0x1c0
|
||||
#define FREQ_GRANT_SHIFT 0
|
||||
@@ -264,6 +310,7 @@ struct rockchip_pwm_chip {
|
||||
const struct rockchip_pwm_biphasic_config *biphasic_config;
|
||||
struct resource *res;
|
||||
struct dentry *debugfs;
|
||||
struct completion ir_trans_completion;
|
||||
void __iomem *base;
|
||||
unsigned long clk_rate;
|
||||
unsigned long is_clk_enabled;
|
||||
@@ -273,6 +320,7 @@ struct rockchip_pwm_chip {
|
||||
bool capture_en;
|
||||
bool wave_en;
|
||||
bool global_ctrl_grant;
|
||||
bool ir_trans_support;
|
||||
bool freq_meter_support;
|
||||
bool counter_support;
|
||||
bool wave_support;
|
||||
@@ -322,6 +370,7 @@ struct rockchip_pwm_funcs {
|
||||
struct rockchip_pwm_biphasic_config *config);
|
||||
int (*get_biphasic_result)(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
unsigned long *biphasic_res);
|
||||
int (*ir_transmit)(struct pwm_chip *chip, unsigned int *txbuf, unsigned int count);
|
||||
irqreturn_t (*irq_handler)(int irq, void *data);
|
||||
};
|
||||
|
||||
@@ -713,6 +762,13 @@ static irqreturn_t rockchip_pwm_irq_v4(int irq, void *data)
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (val & IR_TRANS_END_INT) {
|
||||
writel_relaxed(IR_TRANS_END_INT, pc->base + INTSTS);
|
||||
complete(&pc->ir_trans_completion);
|
||||
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1844,6 +1900,143 @@ int rockchip_pwm_get_biphasic_result(struct pwm_device *pwm, unsigned long *biph
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rockchip_pwm_get_biphasic_result);
|
||||
|
||||
#ifdef CONFIG_RC_CORE
|
||||
static int rockchip_pwm_ir_transmit_v4(struct pwm_chip *chip, unsigned int *txbuf,
|
||||
unsigned int count)
|
||||
{
|
||||
struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
|
||||
u32 arbiter;
|
||||
u32 preload, spreload;
|
||||
u32 low_period, high_period;
|
||||
u32 tx_value;
|
||||
u32 timeout_ms;
|
||||
u32 val;
|
||||
int ret = 0;
|
||||
|
||||
if (count != PWM_IR_TRANSMIT_BUFFER_SIZE) {
|
||||
dev_err(chip->dev, "Unsupported ir transmit buf size: %d\n", count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = clk_enable(pc->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
arbiter = BIT(pc->channel_id) << IR_TRANS_READ_LOCK_SHIFT |
|
||||
BIT(pc->channel_id) << IR_TRANS_GRANT_SHIFT;
|
||||
writel_relaxed(arbiter, pc->base + IR_TRANS_ARBITER);
|
||||
val = readl_relaxed(pc->base + IR_TRANS_ARBITER);
|
||||
if (!(val & arbiter)) {
|
||||
dev_err(chip->dev, "Failed to abtain ir transmit arbitration for PWM%d\n",
|
||||
pc->channel_id);
|
||||
ret = -EINVAL;
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
reinit_completion(&pc->ir_trans_completion);
|
||||
|
||||
/*
|
||||
* Each value in the txbuf[] is in microseconds(us).
|
||||
* txbuf[0]: the low duration of NEC leader code.
|
||||
* txbuf[1]: the high duration of NEC leader code.
|
||||
* txbuf[2]: the high duration of NEC repeat code.
|
||||
* txbuf[3]: the low duration of NEC logic '0' and '1'.
|
||||
* txbuf[4]: the high duration of NEC logic '0'.
|
||||
* txbuf[5]: the high duration of NEC logic '1'.
|
||||
* txbuf[6]:
|
||||
* bit[31:24] bit[23:16] bit[15:8] bit[7:0]
|
||||
* address code address inverted code command code command inverted code
|
||||
*/
|
||||
preload = txbuf[0] << IR_TRANS_OUT_LOW_PRELOAD_SHIFT |
|
||||
txbuf[1] << IR_TRANS_OUT_HIGH_PRELOAD_SHIFT;
|
||||
spreload = txbuf[2] << IR_TRANS_OUT_HIGH_SIMPLE_PRELOAD_SHIFT;
|
||||
low_period = txbuf[3] << IR_TRANS_OUT_DATA_LOW_PERIOD_SHIFT;
|
||||
high_period = txbuf[4] << IR_TRANS_OUT_HIGH_PERIOD_FOR_ZERO_SHIFT |
|
||||
txbuf[5] << IR_TRANS_OUT_HIGH_PERIOD_FOR_ONE_SHIFT;
|
||||
tx_value = txbuf[6] << IR_TRANS_OUT_VALUE_SHIFT;
|
||||
|
||||
/* Set the dclk to 1M */
|
||||
writel_relaxed(CLK_SCALE(0x32), pc->base + CLK_CTRL);
|
||||
writel_relaxed(PWM_CLK_EN(true), pc->base + ENABLE);
|
||||
writel_relaxed(IR_TRANS_END_INT_EN(true), pc->base + INT_EN);
|
||||
|
||||
writel_relaxed(preload, pc->base + IR_TRANS_PRE);
|
||||
writel_relaxed(spreload, pc->base + IR_TRANS_SPRE);
|
||||
writel_relaxed(low_period, pc->base + IR_TRANS_LD);
|
||||
writel_relaxed(high_period, pc->base + IR_TRANS_HD);
|
||||
writel_relaxed(tx_value, pc->base + IR_TRANS_DATA_VALUE);
|
||||
|
||||
val = readl_relaxed(pc->base + IR_TRANS_BURST_FRAME);
|
||||
timeout_ms = ((val & IR_TRANS_OUT_FRAME_PERIOD_MASK) >>
|
||||
IR_TRANS_OUT_FRAME_PERIOD_SHIFT) / 1000;
|
||||
|
||||
writel_relaxed(IR_TRANS_INACTIVE_POL(true) | IR_TRANS_OUT_ENABLE(true),
|
||||
pc->base + IR_TRANS_CTRL0);
|
||||
|
||||
ret = wait_for_completion_timeout(&pc->ir_trans_completion,
|
||||
msecs_to_jiffies(timeout_ms * 3 / 2));
|
||||
if (!ret) {
|
||||
dev_err(chip->dev, "Failed to wait for PWM%d ir transmit to complete\n",
|
||||
pc->channel_id);
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
writel_relaxed(IR_TRANS_OUT_ENABLE(false), pc->base + IR_TRANS_CTRL0);
|
||||
writel_relaxed(IR_TRANS_END_INT_EN(false), pc->base + INT_EN);
|
||||
writel_relaxed(PWM_CLK_EN(false), pc->base + ENABLE);
|
||||
writel_relaxed(0, pc->base + IR_TRANS_ARBITER);
|
||||
|
||||
err_clk:
|
||||
clk_disable(pc->clk);
|
||||
|
||||
return ret ? ret : count;
|
||||
}
|
||||
|
||||
static int rockchip_pwm_ir_transmit(struct rc_dev *dev, unsigned int *txbuf, unsigned int count)
|
||||
{
|
||||
struct rockchip_pwm_chip *pc = dev->priv;
|
||||
struct pwm_chip *chip = &pc->chip;
|
||||
struct pwm_state curstate;
|
||||
int ret;
|
||||
|
||||
if (!pc->data->funcs.ir_transmit) {
|
||||
dev_err(chip->dev, "Unsupported ir transmit mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pwm_get_state(&pc->chip.pwms[0], &curstate);
|
||||
if (curstate.enabled) {
|
||||
dev_err(chip->dev, "Failed to enable ir transmit mode because PWM%d is busy\n",
|
||||
pc->channel_id);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = pinctrl_select_state(pc->pinctrl, pc->active_state);
|
||||
if (ret) {
|
||||
dev_err(chip->dev, "Failed to select pinctrl state\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_enable(pc->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pc->data->funcs.ir_transmit(chip, txbuf, count);
|
||||
if (ret < 0)
|
||||
dev_err(chip->dev, "Failed to transmit ir buf\n");
|
||||
|
||||
clk_disable(pc->pclk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static int rockchip_pwm_ir_transmit_v4(struct pwm_chip *chip, unsigned int *txbuf,
|
||||
unsigned int count)
|
||||
{
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int rockchip_pwm_debugfs_show(struct seq_file *s, void *data)
|
||||
{
|
||||
@@ -2034,6 +2227,7 @@ static const struct rockchip_pwm_data pwm_data_v4 = {
|
||||
.set_wave = rockchip_pwm_set_wave_v4,
|
||||
.set_biphasic = rockchip_pwm_set_biphasic_v4,
|
||||
.get_biphasic_result = rockchip_pwm_get_biphasic_result_v4,
|
||||
.ir_transmit = rockchip_pwm_ir_transmit_v4,
|
||||
.irq_handler = rockchip_pwm_irq_v4,
|
||||
},
|
||||
};
|
||||
@@ -2140,6 +2334,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
|
||||
if (pc->main_version >= 4) {
|
||||
version = readl_relaxed(pc->base + pc->data->regs.version);
|
||||
pc->channel_id = (version & CHANNLE_INDEX_MASK) >> CHANNLE_INDEX_SHIFT;
|
||||
pc->ir_trans_support = !!(version & IR_TRANS_SUPPORT);
|
||||
pc->freq_meter_support = !!(version & FREQ_METER_SUPPORT);
|
||||
pc->counter_support = !!(version & COUNTER_SUPPORT);
|
||||
pc->wave_support = !!(version & WAVE_SUPPORT);
|
||||
@@ -2218,6 +2413,27 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RC_CORE
|
||||
if (pc->ir_trans_support &&
|
||||
device_property_present(&pdev->dev, "rockchip,pwm-ir-transmit")) {
|
||||
struct rc_dev *rcdev;
|
||||
|
||||
init_completion(&pc->ir_trans_completion);
|
||||
|
||||
rcdev = devm_rc_allocate_device(&pdev->dev, RC_DRIVER_IR_RAW_TX);
|
||||
if (!rcdev)
|
||||
goto err_pclk;
|
||||
|
||||
rcdev->priv = pc;
|
||||
rcdev->driver_name = "rockchip-pwm-ir-tx";
|
||||
rcdev->device_name = "Rockchip IR TX";
|
||||
rcdev->tx_ir = rockchip_pwm_ir_transmit;
|
||||
ret = devm_rc_register_device(&pdev->dev, rcdev);
|
||||
if (ret < 0)
|
||||
goto err_pclk;
|
||||
}
|
||||
#endif
|
||||
|
||||
rockchip_pwm_debugfs_init(pc);
|
||||
|
||||
/* Keep the PWM clk enabled if the PWM appears to be up and running. */
|
||||
|
||||
Reference in New Issue
Block a user