// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Amlogic AO_IR_BLASTER_ADDR0 bits */ #define BLASTER_BUSY BIT(26) #define BLASTER_FIFO_FULL BIT(25) #define BLASTER_FIFO_EMPTY BIT(24) #define BLASTER_FIFO_LEVEL (0xff << 16) #define BLASTER_MODULATOR_TB_SYSTEM_CLOCK (0x0 << 12) #define BLASTER_MODULATOR_TB_XTAL3_TICK (0x1 << 12) #define BLASTER_MODULATOR_TB_1US_TICK (0x2 << 12) #define BLASTER_MODULATOR_TB_10US_TICK (0x3 << 12) #define BLASTER_SLOW_CLOCK_DIV (0xff << 4) #define BLASTER_SLOW_CLOCK_MODE BIT(3) #define BLASTER_INIT_HIGH BIT(2) #define BLASTER_INIT_LOW BIT(1) #define BLASTER_ENABLE BIT(0) /* Amlogic AO_IR_BLASTER_ADDR1 bits */ #define BLASTER_MODULATION_LOW_COUNT(c) ((c) << 16) #define BLASTER_MODULATION_HIGH_COUNT(c) ((c) << 0) /* Amlogic AO_IR_BLASTER_ADDR2 bits */ #define BLASTER_WRITE_FIFO BIT(16) #define BLASTER_MODULATION_ENABLE BIT(12) #define BLASTER_TIMEBASE_1US (0x0 << 10) #define BLASTER_TIMEBASE_10US (0x1 << 10) #define BLASTER_TIMEBASE_100US (0x2 << 10) #define BLASTER_TIMEBASE_MODULATION_CLOCK (0x3 << 10) /* Amlogic AO_IR_BLASTER_ADDR3 bits */ #define BLASTER_FIFO_THD_PENDING BIT(16) #define BLASTER_FIFO_IRQ_ENABLE BIT(8) #define BLASTER_FIFO_IRQ_THRESHOLD(c) (((c) & 0xff) << 0) #define DEFAULT_CARRIER_FREQ (38000) #define DEFAULT_DUTY_CYCLE (50) #define LIMIT_DUTY (25) #define MAX_DUTY (75) #define LIMIT_FREQ (25000) #define MAX_FREQ (60000) #define COUNT_DELAY_MASK (0X3ff) #define TIMEBASE_SHIFT (10) #define BLASTER_KFIFO_SIZE (4) #define AO_IR_BLASTER_ADDR0 (0x0) #define AO_IR_BLASTER_ADDR1 (0x4) #define AO_IR_BLASTER_ADDR2 (0x8) #define AO_IR_BLASTER_ADDR3 (0xc) #define AO_RTI_GEN_CTNL_REG0 (0x0) #define CONSUMERIR_TRANSMIT 0x5500 #define GET_CARRIER 0x5501 #define SET_CARRIER 0x5502 #define SET_DUTYCYCLE 0x5503 #define BLASTER_TIMEBASE_COUNT (BIT(10) - 1) #define BLASTER_DEBUG_HIGH (2) #define BLASTER_DELAY_MS 25 struct meson_irblaster_dev { struct device *dev; struct work_struct blaster_work; struct irblaster_chip chip; struct completion blaster_completion; unsigned int count; unsigned int irq; unsigned int buffer_size; unsigned int *buffer; spinlock_t irblaster_lock; /* use to send data */ void __iomem *reg_base; void __iomem *reset_base; }; #define IROUTDebug(fmt, x...) static void meson_irblaster_tasklet(unsigned long data); DECLARE_TASKLET_DISABLED_OLD(irblaster_tasklet, meson_irblaster_tasklet); static struct meson_irblaster_dev * to_meson_irblaster(struct irblaster_chip *chip) { return container_of(chip, struct meson_irblaster_dev, chip); } static void blaster_initialize(struct meson_irblaster_dev *dev) { unsigned int carrier_cycle = DIV_ROUND_CLOSEST(1000, (dev->chip.state.freq / 1000)); unsigned int high_ct, low_ct; /* *1. disable ir blaster *2. set the modulator_tb = 2'10; mpeg_1uS_tick 1us *3. set initializes the output to be high */ writel_relaxed((~BLASTER_ENABLE) & (BLASTER_MODULATOR_TB_1US_TICK | BLASTER_INIT_HIGH), dev->reg_base + AO_IR_BLASTER_ADDR0); /* *1. set mod_high_count = 13 *2. set mod_low_count = 13 *3. 60khz-8us, 38k-13us */ high_ct = carrier_cycle * dev->chip.state.duty / 100; low_ct = carrier_cycle - high_ct; writel_relaxed((BLASTER_MODULATION_LOW_COUNT(low_ct - 1) | BLASTER_MODULATION_HIGH_COUNT(high_ct - 1)), dev->reg_base + AO_IR_BLASTER_ADDR1); /*mask initialize output to be high*/ writel_relaxed(readl_relaxed(dev->reg_base + AO_IR_BLASTER_ADDR0) & ~BLASTER_INIT_HIGH, dev->reg_base + AO_IR_BLASTER_ADDR0); /* *1. set fifo irq enable *2. set fifo irq threshold */ writel_relaxed(BLASTER_FIFO_IRQ_ENABLE | BLASTER_FIFO_IRQ_THRESHOLD(8), dev->reg_base + AO_IR_BLASTER_ADDR3); /*enable irblaster*/ writel_relaxed(readl_relaxed(dev->reg_base + AO_IR_BLASTER_ADDR0) | BLASTER_ENABLE, dev->reg_base + AO_IR_BLASTER_ADDR0); } static int write_to_fifo(struct meson_irblaster_dev *dev, unsigned int hightime, unsigned int lowtime) { unsigned int count_delay; unsigned int cycle = DIV_ROUND_CLOSEST(1000, (dev->chip.state.freq / 1000)); u32 val; int n = 0; int tb[3] = { 1, 10, 100 }; /* * hightime: modulator signal. * MODULATOR_TB: * 00: system clock * 01: mpeg_xtal3_tick * 10: mpeg_1uS_tick * 11: mpeg_10uS_tick * * AO_IR_BLASTER_ADDR2 * bit12: output level(or modulation enable/disable:1=enable) * bit[11:10]: Timebase : * 00=1us * 01=10us * 10=100us * 11=Modulator clock * bit[9:0]: Count of timebase units to delay */ count_delay = ((hightime + cycle / 2) / cycle) - 1; if (count_delay < 1024) { count_delay &= COUNT_DELAY_MASK; val = (BLASTER_WRITE_FIFO | BLASTER_MODULATION_ENABLE | BLASTER_TIMEBASE_MODULATION_CLOCK | (count_delay << 0)); } else { count_delay = (((hightime + 100 / 2) / 100) - 1) & COUNT_DELAY_MASK; val = (BLASTER_WRITE_FIFO | BLASTER_MODULATION_ENABLE | BLASTER_TIMEBASE_100US | (count_delay << 0)); } writel_relaxed(val, dev->reg_base + AO_IR_BLASTER_ADDR2); /* * lowtime<1024,n=0,timebase=1us * 1024<=lowtime<10240,n=1,timebase=10us * 10240<=lowtime,n=2,timebase=100us */ n = lowtime >> 10; if (n > 0 && n < 10) n = 1; else if (n >= 10) n = 2; lowtime = (lowtime + (tb[n] >> 1)) / tb[n]; count_delay = (lowtime - 1) & COUNT_DELAY_MASK; val = (BLASTER_WRITE_FIFO & (~BLASTER_MODULATION_ENABLE)) | (n << TIMEBASE_SHIFT) | (count_delay << 0); writel_relaxed(val, dev->reg_base + AO_IR_BLASTER_ADDR2); return 0; } static int write_to_high_fifo(struct meson_irblaster_dev *dev, unsigned int hightime) { unsigned int count_delay; unsigned int cycle = DIV_ROUND_CLOSEST(1000, (dev->chip.state.freq / 1000)); u32 val; /* * hightime: modulator signal. * MODULATOR_TB: * 00: system clock * 01: mpeg_xtal3_tick * 10: mpeg_1uS_tick * 11: mpeg_10uS_tick * * AO_IR_BLASTER_ADDR2 * bit12: output level(or modulation enable/disable:1=enable) * bit[11:10]: Timebase : * 00=1us * 01=10us * 10=100us * 11=Modulator clock * bit[9:0]: Count of timebase units to delay */ count_delay = (((hightime + cycle / 2) / cycle) - 1) & COUNT_DELAY_MASK; val = (BLASTER_WRITE_FIFO | BLASTER_MODULATION_ENABLE | BLASTER_TIMEBASE_MODULATION_CLOCK | (count_delay << 0)); writel_relaxed(val, dev->reg_base + AO_IR_BLASTER_ADDR2); return 0; } static void send_all_data(struct meson_irblaster_dev *dev) { int i, j; unsigned int *pdata = NULL; unsigned long flags; unsigned int cycle, max_time, high_ct, low_ct; cycle = DIV_ROUND_CLOSEST(1000, (dev->chip.state.freq / 1000)); high_ct = cycle * dev->chip.state.duty / 100; low_ct = cycle - high_ct; max_time = (high_ct + low_ct) * BLASTER_TIMEBASE_COUNT; IROUTDebug("send data max_time = %d\n", max_time); pdata = &dev->buffer[dev->count]; spin_lock_irqsave(&dev->irblaster_lock, flags); for (i = 0; (i < 120) && (dev->count < dev->buffer_size);) { if (*pdata > max_time) { for (j = 0; j < (*pdata / max_time); j++) { write_to_high_fifo(dev, max_time); i++; } write_to_fifo(dev, *pdata % max_time, *(pdata + 1)); } else { write_to_fifo(dev, *pdata, *(pdata + 1)); } pdata += 2; dev->count += 2; i += 2; } spin_unlock_irqrestore(&dev->irblaster_lock, flags); } int meson_irblaster_send(struct irblaster_chip *chip, unsigned int *data, unsigned int len) { int ret, i, sum_time = 0; unsigned int high_ct, low_ct, cycle, long_len = 0; struct meson_irblaster_dev *irblaster_dev = to_meson_irblaster(chip); pm_runtime_get_sync(chip->dev); init_completion(&irblaster_dev->blaster_completion); /* * 1. set mod_high_count = 13 * 2. set mod_low_count = 13 * 3. 60khz-8us, 38k-13us */ cycle = DIV_ROUND_CLOSEST(1000, (irblaster_dev->chip.state.freq / 1000)); high_ct = cycle * irblaster_dev->chip.state.duty / 100; low_ct = cycle - high_ct; writel_relaxed((BLASTER_MODULATION_LOW_COUNT(low_ct - 1) | BLASTER_MODULATION_HIGH_COUNT(high_ct - 1)), irblaster_dev->reg_base + AO_IR_BLASTER_ADDR1); for (i = 0; i < len; i++) { sum_time += data[i]; if (irblaster_debug) { if (irblaster_debug >= BLASTER_DEBUG_HIGH) { IROUTDebug("ir[%d] = %d\n", i, data[i]); } if (data[i] > (high_ct + low_ct) * BLASTER_TIMEBASE_COUNT) long_len += DIV_ROUND_UP(data[i], (high_ct + low_ct) * BLASTER_TIMEBASE_COUNT); } } irblaster_dev->buffer = data; irblaster_dev->buffer_size = len; irblaster_dev->count = 0; send_all_data(irblaster_dev); IROUTDebug("wait time = %dus len = %d fifosize = %d\n", sum_time, irblaster_dev->buffer_size, len + long_len); ret = wait_for_completion_interruptible_timeout (&irblaster_dev->blaster_completion, msecs_to_jiffies(sum_time / 1000 + BLASTER_DELAY_MS)); if (!ret) { pr_err("failed to send all data ret = %d\n", ret); return -ETIMEDOUT; } IROUTDebug("send finish!\n"); pm_runtime_put_sync(chip->dev); return 0; } static struct irblaster_ops meson_irblaster_ops = { .send = meson_irblaster_send, }; static void meson_irblaster_tasklet(unsigned long data) { struct meson_irblaster_dev *dev = (struct meson_irblaster_dev *)data; if (dev->count < dev->buffer_size) send_all_data(dev); } static irqreturn_t meson_blaster_interrupt(int irq, void *dev_id) { struct meson_irblaster_dev *dev = dev_id; if (irblaster_debug >= BLASTER_DEBUG_HIGH) { IROUTDebug("enter irq count = %d size = %d\n", dev->count, dev->buffer_size); } /*clear pending bit*/ writel_relaxed(readl_relaxed(dev->reg_base + AO_IR_BLASTER_ADDR3) & (~BLASTER_FIFO_THD_PENDING), dev->reg_base + AO_IR_BLASTER_ADDR3); if (dev->count >= dev->buffer_size) { complete(&dev->blaster_completion); return IRQ_HANDLED; } tasklet_schedule(&irblaster_tasklet); return IRQ_HANDLED; } static int meson_irblaster_probe(struct platform_device *pdev) { struct meson_irblaster_dev *irblaster_dev = NULL; struct resource *reg_mem = NULL; void __iomem *reg_base = NULL; int err, ret; if (!pdev->dev.of_node) { dev_err(&pdev->dev, "pdev->dev.of_node == NULL!\n"); return -EINVAL; } irblaster_dev = devm_kzalloc(&pdev->dev, sizeof(struct meson_irblaster_dev), GFP_KERNEL); if (!irblaster_dev) return -ENOMEM; spin_lock_init(&irblaster_dev->irblaster_lock); platform_set_drvdata(pdev, irblaster_dev); irblaster_dev->dev = &pdev->dev; reg_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!IS_ERR_OR_NULL(reg_mem)) { reg_base = devm_ioremap_resource(&pdev->dev, reg_mem); if (IS_ERR(reg_base)) { dev_err(&pdev->dev, "reg0: cannot obtain I/O memory region.\n"); return PTR_ERR(reg_base); } } else { dev_err(&pdev->dev, "get IORESOURCE_MEM error.\n"); return PTR_ERR(reg_base); } irblaster_dev->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); if (!irblaster_dev->irq) { dev_err(&pdev->dev, "irq: Failed to request irq number.\n"); return -ENODEV; } init_completion(&irblaster_dev->blaster_completion); ret = devm_request_irq(&pdev->dev, irblaster_dev->irq, meson_blaster_interrupt, IRQF_TRIGGER_RISING, dev_name(&pdev->dev), irblaster_dev); if (ret) { pr_err("Failed to request irq.\n"); return ret; } /* init irblaster sysfs */ ret = irblaster_sysfs_init(); if (ret) { pr_err("Failed to init irblaster sysfs.\n"); return ret; } irblaster_dev->reg_base = reg_base; irblaster_dev->chip.dev = &pdev->dev; irblaster_dev->chip.ops = &meson_irblaster_ops; irblaster_dev->chip.of_irblaster_n_cells = 2; irblaster_dev->chip.state.freq = DEFAULT_CARRIER_FREQ; irblaster_dev->chip.state.duty = DEFAULT_DUTY_CYCLE; err = irblasterchip_add(&irblaster_dev->chip); if (err < 0) { dev_err(&pdev->dev, "failed to register irblaster chip: %d\n", err); return err; } irblaster_tasklet.data = (unsigned long)irblaster_dev; tasklet_enable(&irblaster_tasklet); /*initial blaster*/ blaster_initialize(irblaster_dev); pm_runtime_enable(&pdev->dev); return 0; } static void meson_irblaster_remove(struct platform_device *pdev) { struct meson_irblaster_dev *irblaster_dev = platform_get_drvdata(pdev); tasklet_disable(&irblaster_tasklet); tasklet_kill(&irblaster_tasklet); irblasterchip_remove(&irblaster_dev->chip); } static int meson_irblaster_runtime_suspend(struct device *dev) { return 0; } static int meson_irblaster_runtime_resume(struct device *dev) { struct meson_irblaster_dev *irblaster_dev = dev_get_drvdata(dev); /*initial blaster*/ blaster_initialize(irblaster_dev); return 0; } static const struct of_device_id irblaster_dt_match[] = { { .compatible = "amlogic, meson_irblaster", }, {}, }; MODULE_DEVICE_TABLE(of, irblaster_dt_match); static const struct dev_pm_ops meson_irblaster_pm_ops = { SET_RUNTIME_PM_OPS(meson_irblaster_runtime_suspend, meson_irblaster_runtime_resume, NULL) }; static struct platform_driver meson_irblaster_driver = { .probe = meson_irblaster_probe, .remove = meson_irblaster_remove, .driver = { .name = "meson_irblaster", .owner = THIS_MODULE, .of_match_table = irblaster_dt_match, .pm = &meson_irblaster_pm_ops, }, }; static int __init meson_irblaster_init(void) { return platform_driver_register(&meson_irblaster_driver); } static void __exit meson_irblaster_exit(void) { platform_driver_unregister(&meson_irblaster_driver); } fs_initcall_sync(meson_irblaster_init); module_exit(meson_irblaster_exit); MODULE_AUTHOR("Amlogic, Inc."); MODULE_DESCRIPTION("Amlogic ir blaster driver"); MODULE_LICENSE("GPL");