mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 10:58:48 +09: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.h>
|
||||||
#include <linux/pwm-rockchip.h>
|
#include <linux/pwm-rockchip.h>
|
||||||
#include <linux/time.h>
|
#include <linux/time.h>
|
||||||
|
#include <media/rc-core.h>
|
||||||
#include "pwm-rockchip-irq-callbacks.h"
|
#include "pwm-rockchip-irq-callbacks.h"
|
||||||
|
|
||||||
#define PWM_MAX_CHANNEL_NUM 8
|
#define PWM_MAX_CHANNEL_NUM 8
|
||||||
|
|
||||||
|
#define PWM_IR_TRANSMIT_BUFFER_SIZE 7
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* regs for pwm v1-v3
|
* regs for pwm v1-v3
|
||||||
*/
|
*/
|
||||||
@@ -101,6 +104,9 @@
|
|||||||
#define CLK_PRESCALE(v) HIWORD_UPDATE(v, 0, 2)
|
#define CLK_PRESCALE(v) HIWORD_UPDATE(v, 0, 2)
|
||||||
#define CLK_SCALE(v) HIWORD_UPDATE(v, 4, 12)
|
#define CLK_SCALE(v) HIWORD_UPDATE(v, 4, 12)
|
||||||
#define CLK_SRC_SEL(v) HIWORD_UPDATE(v, 13, 14)
|
#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)
|
#define CLK_GLOBAL_SEL(v) HIWORD_UPDATE(v, 15, 15)
|
||||||
/* CTRL */
|
/* CTRL */
|
||||||
#define CTRL_V4 0xc
|
#define CTRL_V4 0xc
|
||||||
@@ -220,6 +226,46 @@
|
|||||||
#define GLOBAL_CTRL 0xc4
|
#define GLOBAL_CTRL 0xc4
|
||||||
#define GLOBAL_PWM_EN(v) HIWORD_UPDATE(v, 0, 0)
|
#define GLOBAL_PWM_EN(v) HIWORD_UPDATE(v, 0, 0)
|
||||||
#define GLOBAL_PWM_UPDATE_EN(v) HIWORD_UPDATE(v, 1, 1)
|
#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 */
|
/* FREQ_ARBITER */
|
||||||
#define FREQ_ARBITER 0x1c0
|
#define FREQ_ARBITER 0x1c0
|
||||||
#define FREQ_GRANT_SHIFT 0
|
#define FREQ_GRANT_SHIFT 0
|
||||||
@@ -264,6 +310,7 @@ struct rockchip_pwm_chip {
|
|||||||
const struct rockchip_pwm_biphasic_config *biphasic_config;
|
const struct rockchip_pwm_biphasic_config *biphasic_config;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
struct dentry *debugfs;
|
struct dentry *debugfs;
|
||||||
|
struct completion ir_trans_completion;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
unsigned long clk_rate;
|
unsigned long clk_rate;
|
||||||
unsigned long is_clk_enabled;
|
unsigned long is_clk_enabled;
|
||||||
@@ -273,6 +320,7 @@ struct rockchip_pwm_chip {
|
|||||||
bool capture_en;
|
bool capture_en;
|
||||||
bool wave_en;
|
bool wave_en;
|
||||||
bool global_ctrl_grant;
|
bool global_ctrl_grant;
|
||||||
|
bool ir_trans_support;
|
||||||
bool freq_meter_support;
|
bool freq_meter_support;
|
||||||
bool counter_support;
|
bool counter_support;
|
||||||
bool wave_support;
|
bool wave_support;
|
||||||
@@ -322,6 +370,7 @@ struct rockchip_pwm_funcs {
|
|||||||
struct rockchip_pwm_biphasic_config *config);
|
struct rockchip_pwm_biphasic_config *config);
|
||||||
int (*get_biphasic_result)(struct pwm_chip *chip, struct pwm_device *pwm,
|
int (*get_biphasic_result)(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||||
unsigned long *biphasic_res);
|
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);
|
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;
|
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;
|
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);
|
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
|
#ifdef CONFIG_DEBUG_FS
|
||||||
static int rockchip_pwm_debugfs_show(struct seq_file *s, void *data)
|
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_wave = rockchip_pwm_set_wave_v4,
|
||||||
.set_biphasic = rockchip_pwm_set_biphasic_v4,
|
.set_biphasic = rockchip_pwm_set_biphasic_v4,
|
||||||
.get_biphasic_result = rockchip_pwm_get_biphasic_result_v4,
|
.get_biphasic_result = rockchip_pwm_get_biphasic_result_v4,
|
||||||
|
.ir_transmit = rockchip_pwm_ir_transmit_v4,
|
||||||
.irq_handler = rockchip_pwm_irq_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) {
|
if (pc->main_version >= 4) {
|
||||||
version = readl_relaxed(pc->base + pc->data->regs.version);
|
version = readl_relaxed(pc->base + pc->data->regs.version);
|
||||||
pc->channel_id = (version & CHANNLE_INDEX_MASK) >> CHANNLE_INDEX_SHIFT;
|
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->freq_meter_support = !!(version & FREQ_METER_SUPPORT);
|
||||||
pc->counter_support = !!(version & COUNTER_SUPPORT);
|
pc->counter_support = !!(version & COUNTER_SUPPORT);
|
||||||
pc->wave_support = !!(version & WAVE_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);
|
rockchip_pwm_debugfs_init(pc);
|
||||||
|
|
||||||
/* Keep the PWM clk enabled if the PWM appears to be up and running. */
|
/* Keep the PWM clk enabled if the PWM appears to be up and running. */
|
||||||
|
|||||||
Reference in New Issue
Block a user