media: rc: ir-hix5hd2: add hi3796cv300-ir support

The IR device on Hi3796CV300 SoC is mostly same as hix5hd2, except the
following two things.

 - IR_CLK offset is 0x60 instead of 0x48.
 - It needs to set an extra bit in IR_ENABLE register to enable IR.

The following changes are made to deal with them.

 - Define a SoC specific data to accommodate IR_CLK offset and the flag
   telling requirement of extra enable bit.
 - Create function hix5hd2_ir_enable() to handle IR enabling. The original
   hix5hd2_ir_enable() is all about managing IR clock, so gets renamed
   to hix5hd2_ir_clk_enable().
 - Device table hix5hd2_ir_table[] gets moved forward, as it's being
   used by hix5hd2_ir_probe() now.

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
This commit is contained in:
Shawn Guo 2019-12-26 13:02:22 +01:00 committed by Mauro Carvalho Chehab
parent 61cd2d5ca0
commit e4b9b6454d
1 changed files with 61 additions and 18 deletions

View File

@ -37,10 +37,13 @@
#define INT_CLR_RCV BIT(16)
#define INT_CLR_RCVTIMEOUT (BIT(16) | BIT(17))
#define IR_CLK 0x48
#define IR_CLK_ENABLE BIT(4)
#define IR_CLK_RESET BIT(5)
/* IR_ENABLE register bits */
#define IR_ENABLE_EN BIT(0)
#define IR_ENABLE_EN_EXTRA BIT(8)
#define IR_CFG_WIDTH_MASK 0xffff
#define IR_CFG_WIDTH_SHIFT 16
#define IR_CFG_FORMAT_MASK 0x3
@ -58,6 +61,23 @@
#define IR_HIX5HD2_NAME "hix5hd2-ir"
/* Need to set extra bit for enabling IR */
#define HIX5HD2_FLAG_EXTRA_ENABLE BIT(0)
struct hix5hd2_soc_data {
u32 clk_reg;
u32 flags;
};
static const struct hix5hd2_soc_data hix5hd2_data = {
.clk_reg = 0x48,
};
static const struct hix5hd2_soc_data hi3796cv300_data = {
.clk_reg = 0x60,
.flags = HIX5HD2_FLAG_EXTRA_ENABLE,
};
struct hix5hd2_ir_priv {
int irq;
void __iomem *base;
@ -66,15 +86,17 @@ struct hix5hd2_ir_priv {
struct regmap *regmap;
struct clk *clock;
unsigned long rate;
const struct hix5hd2_soc_data *socdata;
};
static int hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on)
static int hix5hd2_ir_clk_enable(struct hix5hd2_ir_priv *dev, bool on)
{
u32 clk_reg = dev->socdata->clk_reg;
u32 val;
int ret = 0;
if (dev->regmap) {
regmap_read(dev->regmap, IR_CLK, &val);
regmap_read(dev->regmap, clk_reg, &val);
if (on) {
val &= ~IR_CLK_RESET;
val |= IR_CLK_ENABLE;
@ -82,7 +104,7 @@ static int hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on)
val &= ~IR_CLK_ENABLE;
val |= IR_CLK_RESET;
}
regmap_write(dev->regmap, IR_CLK, val);
regmap_write(dev->regmap, clk_reg, val);
} else {
if (on)
ret = clk_prepare_enable(dev->clock);
@ -92,12 +114,23 @@ static int hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on)
return ret;
}
static inline void hix5hd2_ir_enable(struct hix5hd2_ir_priv *priv)
{
u32 val = IR_ENABLE_EN;
if (priv->socdata->flags & HIX5HD2_FLAG_EXTRA_ENABLE)
val |= IR_ENABLE_EN_EXTRA;
writel_relaxed(val, priv->base + IR_ENABLE);
}
static int hix5hd2_ir_config(struct hix5hd2_ir_priv *priv)
{
int timeout = 10000;
u32 val, rate;
writel_relaxed(0x01, priv->base + IR_ENABLE);
hix5hd2_ir_enable(priv);
while (readl_relaxed(priv->base + IR_BUSY)) {
if (timeout--) {
udelay(1);
@ -128,13 +161,13 @@ static int hix5hd2_ir_open(struct rc_dev *rdev)
struct hix5hd2_ir_priv *priv = rdev->priv;
int ret;
ret = hix5hd2_ir_enable(priv, true);
ret = hix5hd2_ir_clk_enable(priv, true);
if (ret)
return ret;
ret = hix5hd2_ir_config(priv);
if (ret) {
hix5hd2_ir_enable(priv, false);
hix5hd2_ir_clk_enable(priv, false);
return ret;
}
return 0;
@ -144,7 +177,7 @@ static void hix5hd2_ir_close(struct rc_dev *rdev)
{
struct hix5hd2_ir_priv *priv = rdev->priv;
hix5hd2_ir_enable(priv, false);
hix5hd2_ir_clk_enable(priv, false);
}
static irqreturn_t hix5hd2_ir_rx_interrupt(int irq, void *data)
@ -205,6 +238,13 @@ static irqreturn_t hix5hd2_ir_rx_interrupt(int irq, void *data)
return IRQ_HANDLED;
}
static const struct of_device_id hix5hd2_ir_table[] = {
{ .compatible = "hisilicon,hix5hd2-ir", &hix5hd2_data, },
{ .compatible = "hisilicon,hi3796cv300-ir", &hi3796cv300_data, },
{},
};
MODULE_DEVICE_TABLE(of, hix5hd2_ir_table);
static int hix5hd2_ir_probe(struct platform_device *pdev)
{
struct rc_dev *rdev;
@ -212,6 +252,7 @@ static int hix5hd2_ir_probe(struct platform_device *pdev)
struct resource *res;
struct hix5hd2_ir_priv *priv;
struct device_node *node = pdev->dev.of_node;
const struct of_device_id *of_id;
const char *map_name;
int ret;
@ -219,6 +260,13 @@ static int hix5hd2_ir_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
of_id = of_match_device(hix5hd2_ir_table, dev);
if (!of_id) {
dev_err(dev, "Unable to initialize IR data\n");
return -ENODEV;
}
priv->socdata = of_id->data;
priv->regmap = syscon_regmap_lookup_by_phandle(node,
"hisilicon,power-syscon");
if (IS_ERR(priv->regmap)) {
@ -309,7 +357,7 @@ static int hix5hd2_ir_suspend(struct device *dev)
struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev);
clk_disable_unprepare(priv->clock);
hix5hd2_ir_enable(priv, false);
hix5hd2_ir_clk_enable(priv, false);
return 0;
}
@ -319,17 +367,18 @@ static int hix5hd2_ir_resume(struct device *dev)
struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev);
int ret;
ret = hix5hd2_ir_enable(priv, true);
ret = hix5hd2_ir_clk_enable(priv, true);
if (ret)
return ret;
ret = clk_prepare_enable(priv->clock);
if (ret) {
hix5hd2_ir_enable(priv, false);
hix5hd2_ir_clk_enable(priv, false);
return ret;
}
writel_relaxed(0x01, priv->base + IR_ENABLE);
hix5hd2_ir_enable(priv);
writel_relaxed(0x00, priv->base + IR_INTM);
writel_relaxed(0xff, priv->base + IR_INTC);
writel_relaxed(0x01, priv->base + IR_START);
@ -341,12 +390,6 @@ static int hix5hd2_ir_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(hix5hd2_ir_pm_ops, hix5hd2_ir_suspend,
hix5hd2_ir_resume);
static const struct of_device_id hix5hd2_ir_table[] = {
{ .compatible = "hisilicon,hix5hd2-ir", },
{},
};
MODULE_DEVICE_TABLE(of, hix5hd2_ir_table);
static struct platform_driver hix5hd2_ir_driver = {
.driver = {
.name = IR_HIX5HD2_NAME,