blob: 79c7d27b2fb54f171ac29c75d7ce9e410a151e03 [file] [log] [blame]
/*
* drivers/amlogic/irblaster/aml-irblaster.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/spinlock.h>
#include <linux/kfifo.h>
#include <linux/amlogic/irblaster.h>
#include <linux/amlogic/irblaster_consumer.h>
#include <linux/amlogic/irblaster_encoder.h>
#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 DEFAULT_CARRIER_FREQ (38000)
#define DEFAULT_DUTY_CYCLE (50)
#define BLASTER_DEVICE_COUNT (32)
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
#define DEFAULT_IRBLASTER_PROTOCOL IRBLASTER_PROTOCOL_NEC
#endif
#define IR_TX_EVENT_SIZE 4
#define IR_TX_BUFFER_SIZE 1024
struct tx_event {
struct list_head list;
unsigned int size;
unsigned int buffer[IR_TX_BUFFER_SIZE];
};
DECLARE_KFIFO(fifo, struct tx_event *, IR_TX_EVENT_SIZE);
struct aml_irblaster_dev {
struct irblaster_chip chip;
struct device *dev;
struct task_struct *thread;
struct completion blaster_completion;
struct mutex lock;
spinlock_t irblaster_lock; /* use to send data */
void __iomem *reg_base;
void __iomem *reset_base;
unsigned int winnum;
unsigned int winarray[MAX_PLUSE];
};
static struct aml_irblaster_dev *
to_aml_irblaster(struct irblaster_chip *chip)
{
return container_of(chip, struct aml_irblaster_dev, chip);
}
static struct tx_event *event_get(struct aml_irblaster_dev *cw)
{
struct tx_event *ev = NULL;
ev = devm_kzalloc(cw->dev,
sizeof(struct tx_event), GFP_KERNEL);
return ev;
}
static void event_put(struct aml_irblaster_dev *cw, struct tx_event *ev)
{
devm_kfree(cw->dev, ev);
}
static int send_bit(struct aml_irblaster_dev *cw, unsigned int hightime,
unsigned int lowtime, unsigned int cycle)
{
unsigned int count_delay;
uint32_t val;
int n = 0;
int tb[3] = {
1, 10, 100
};
/*
* MODULATOR_TB:
* 00: system clock clk
* 01: mpeg_xtal3_tick
* 10: mpeg_1uS_tick
* 11: mpeg_10uS_tick
* lowtime<1024,n=0,timebase=1us
* 1024<=lowtime<10240,n=1,timebase=10us
* 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) & 0x3ff;
val = (0x10000 | (1 << 12)) | (3 << 10) | (count_delay << 0);
writel_relaxed(val, cw->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) & 0x3ff;
val = (0x10000 | (0 << 12)) |
(n << 10) | (count_delay << 0);
writel_relaxed(val, cw->reg_base + AO_IR_BLASTER_ADDR2);
return 0;
}
static void send_all_frame(struct aml_irblaster_dev *cw)
{
int i, k;
int exp = 0x00;
unsigned int *pData;
unsigned int consumerir_cycle;
unsigned int high_ct, low_ct;
unsigned long cnt, flags;
consumerir_cycle = 1000 / (cw->chip.state.freq / 1000);
/*reset*/
writel_relaxed(readl_relaxed(cw->reset_base) |
(1 << 23), cw->reset_base);
udelay(2);
writel_relaxed(readl_relaxed(cw->reset_base) &
~(1 << 23), cw->reset_base);
/*
* 1.disable ir blaster
* 2.set the modulator_tb = 2'10; mpeg_1uS_tick 1us
*/
writel_relaxed((1 << 2) | (2 << 12) | (1<<2),
cw->reg_base + AO_IR_BLASTER_ADDR0);
/*
* 1. set mod_high_count = 13
* 2. set mod_low_count = 13
* 3. 60khz 8, 38k-13us, 12
*/
high_ct = consumerir_cycle * cw->chip.state.duty/100;
low_ct = consumerir_cycle - high_ct;
writel_relaxed(((high_ct - 1) << 16) | ((low_ct - 1) << 0),
cw->reg_base + AO_IR_BLASTER_ADDR1);
/* Setting this bit to 1 initializes the output to be high.*/
writel_relaxed(readl_relaxed(cw->reg_base + AO_IR_BLASTER_ADDR0) &
~(1 << 2), cw->reg_base + AO_IR_BLASTER_ADDR0);
/*enable irblaster*/
writel_relaxed(readl_relaxed(cw->reg_base + AO_IR_BLASTER_ADDR0) |
(1 << 0), cw->reg_base + AO_IR_BLASTER_ADDR0);
k = cw->winnum;
#define SEND_BIT_NUM 64
exp = cw->winnum / SEND_BIT_NUM;
pData = cw->winarray;
while (exp) {
spin_lock_irqsave(&cw->irblaster_lock, flags);
for (i = 0; i < SEND_BIT_NUM/2; i++) {
send_bit(cw, *pData, *(pData+1), consumerir_cycle);
pData += 2;
}
spin_unlock_irqrestore(&cw->irblaster_lock, flags);
cnt = jiffies + msecs_to_jiffies(1000);
while (!(readl_relaxed(cw->reg_base + AO_IR_BLASTER_ADDR0) &
(1<<24)) && time_is_after_eq_jiffies(cnt))
;
cnt = jiffies + msecs_to_jiffies(1000);
while ((readl_relaxed(cw->reg_base + AO_IR_BLASTER_ADDR0) &
(1<<26)) && time_is_after_eq_jiffies(cnt))
;
/*reset*/
writel_relaxed(readl_relaxed(cw->reset_base) | (1 << 23),
cw->reset_base);
udelay(2);
writel_relaxed(readl_relaxed(cw->reset_base) & ~(1 << 23),
cw->reset_base);
exp--;
}
exp = (cw->winnum % SEND_BIT_NUM) & (~(1));
spin_lock_irqsave(&cw->irblaster_lock, flags);
for (i = 0; i < exp; ) {
send_bit(cw, *pData, *(pData+1), consumerir_cycle);
pData += 2;
i += 2;
}
spin_unlock_irqrestore(&cw->irblaster_lock, flags);
cnt = jiffies + msecs_to_jiffies(1000);
while (!(readl_relaxed(cw->reg_base + AO_IR_BLASTER_ADDR0) &
(1<<24)) && time_is_after_eq_jiffies(cnt))
;
cnt = jiffies + msecs_to_jiffies(1000);
while ((readl_relaxed(cw->reg_base + AO_IR_BLASTER_ADDR0) &
(1<<26)) && time_is_after_eq_jiffies(cnt))
;
complete(&cw->blaster_completion);
}
int aml_irblaster_send(struct irblaster_chip *chip,
unsigned int *data,
unsigned int len)
{
int i, ret;
struct tx_event *ev;
struct aml_irblaster_dev *irblaster_dev = to_aml_irblaster(chip);
init_completion(&irblaster_dev->blaster_completion);
ev = event_get(irblaster_dev);
ev->size = len;
for (i = 0; i < ev->size; i++)
ev->buffer[i] = data[i];
/* to send cycle */
kfifo_put(&fifo, (const struct tx_event *)ev);
/* to wake up ir_tx_thread */
wake_up_process(irblaster_dev->thread);
/* return after processing */
ret = wait_for_completion_interruptible_timeout
(&irblaster_dev->blaster_completion,
msecs_to_jiffies(chip->sum_time / 1000));
if (!ret) {
pr_err("failed to send all data ret = %d\n", ret);
return -ETIMEDOUT;
}
return 0;
}
static int ir_tx_thread(void *data)
{
int retval, i;
unsigned long cnt;
struct aml_irblaster_dev *irblaster_dev
= (struct aml_irblaster_dev *)data;
struct tx_event *ev = NULL;
while (!kthread_should_stop()) {
retval = kfifo_len(&fifo);
if (retval <= 0) {
set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop())
set_current_state(TASK_RUNNING);
schedule();
continue;
}
retval = kfifo_get(&fifo, &ev);
if (retval) {
irblaster_dev->winnum = ev->size;
for (i = 0; i < irblaster_dev->winnum; i++)
irblaster_dev->winarray[i] = ev->buffer[i];
send_all_frame(irblaster_dev);
event_put(irblaster_dev, ev);
cnt = jiffies + msecs_to_jiffies(1000);
while (!(readl_relaxed(irblaster_dev->reg_base +
AO_IR_BLASTER_ADDR0) & (1<<24)) &&
time_is_after_eq_jiffies(cnt))
;
cnt = jiffies + msecs_to_jiffies(1000);
while ((readl_relaxed(irblaster_dev->reg_base +
AO_IR_BLASTER_ADDR0) & (1<<26)) &&
time_is_after_eq_jiffies(cnt))
;
} else
pr_err("kfifo_get fail\n");
}
return 0;
}
static struct irblaster_ops aml_irblaster_ops = {
.send = aml_irblaster_send,
};
static int aml_irblaster_probe(struct platform_device *pdev)
{
struct aml_irblaster_dev *irblaster_dev = NULL;
struct resource *reg_mem = NULL;
struct resource *reset_mem = NULL;
void __iomem *reg_base = NULL;
void __iomem *reset_base = NULL;
int err;
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 aml_irblaster_dev),
GFP_KERNEL);
if (!irblaster_dev)
return -ENOMEM;
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);
}
reset_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!IS_ERR_OR_NULL(reset_mem)) {
reset_base = devm_ioremap_resource(&pdev->dev,
reset_mem);
if (IS_ERR(reset_base)) {
dev_err(&pdev->dev, "reg1: cannot obtain I/O memory region.\n");
return PTR_ERR(reset_base);
}
} else {
dev_err(&pdev->dev, "get IORESOURCE_MEM error.\n");
return PTR_ERR(reset_mem);
}
spin_lock_init(&irblaster_dev->irblaster_lock);
init_completion(&irblaster_dev->blaster_completion);
irblaster_dev->reg_base = reg_base;
irblaster_dev->reset_base = reset_base;
irblaster_dev->chip.dev = &pdev->dev;
irblaster_dev->chip.ops = &aml_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;
#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
irblaster_dev->chip.state.protocol = DEFAULT_IRBLASTER_PROTOCOL;
irblaster_set_protocol(&irblaster_dev->chip,
DEFAULT_IRBLASTER_PROTOCOL);
#endif
err = irblasterchip_add(&irblaster_dev->chip);
if (err < 0) {
dev_err(&pdev->dev, "failed to register irblaster chip: %d\n",
err);
return err;
}
irblaster_dev->thread = kthread_run(ir_tx_thread, irblaster_dev,
"ir-blaster-thread");
return 0;
}
static int aml_irblaster_remove(struct platform_device *pdev)
{
struct aml_irblaster_dev *irblaster_dev = platform_get_drvdata(pdev);
irblasterchip_remove(&irblaster_dev->chip);
return 0;
}
static const struct of_device_id irblaster_dt_match[] = {
{
.compatible = "amlogic, aml_irblaster",
},
{},
};
static struct platform_driver aml_irblaster_driver = {
.probe = aml_irblaster_probe,
.remove = aml_irblaster_remove,
.suspend = NULL,
.resume = NULL,
.driver = {
.name = "aml-irblaster",
.owner = THIS_MODULE,
.of_match_table = irblaster_dt_match,
},
};
static int __init aml_irblaster_init(void)
{
return platform_driver_register(&aml_irblaster_driver);
}
static void __exit aml_irblaster_exit(void)
{
platform_driver_unregister(&aml_irblaster_driver);
}
fs_initcall_sync(aml_irblaster_init);
module_exit(aml_irblaster_exit);
MODULE_AUTHOR("Amlogic, Inc.");
MODULE_DESCRIPTION("Amlogic ir blaster driver");
MODULE_LICENSE("GPL");