M: Yun Cai <yun.cai@amlogic.com>
F: drivers/amlogic/clk/axg/axg_ao.c
-AMLOGIC Irblaster driver
-M: Zan Peng <zan.peng@amlogic.com>
-F: drivers/amlogic/irblaster/irblaster.c
-F: drivers/amlogic/irblaster/irblaster.h
-F: drivers/amlogic/irblaster/Kconfig
-F: drivers/amlogic/irblaster/Makefile
-
AMLOGIC AXG ADD CLKMSR INTERFACE
M: wang xing <xing.wang@amlogic.com>
F: include/linux/amlogic/clk_measure.h
F: drivers/amlogic/clk/clk-cpu-fclk-composite.c
AMLOGIC Irblaster driver
-M: yu.tu <yu.tu@amlogic.com>
-F: drivers/amlogic/irblaster/meson-irblaster.c
-F: drivers/amlogic/irblaster/meson-irblaster.h
+M: Bichao.Zheng <bichao.zheng@amlogic.com>
+F: drivers/amlogic/irblaster/*
+F: include/linux/amlogic/irblaster*
AMLOGIC THERMAL DRIVER
M: Huan Biao <huan.biao@amlogic.com>
pinctrl-0 = <&b_uart_pins>;
};
- meson-irblaster {
- compatible = "amlogic, am_irblaster";
- dev_name = "meson-irblaster";
- status = "disable";
- pinctrl-names = "default";
- pinctrl-0 = <&irblaster_pins>;
- };
-
vpu {
compatible = "amlogic, vpu-axg";
dev_name = "vpu";
pinctrl-0 = <&b_uart_pins>;
};
- meson-irblaster {
- compatible = "amlogic, am_irblaster";
- dev_name = "meson-irblaster";
- status = "disable";
- pinctrl-names = "default";
- pinctrl-0 = <&irblaster_pins>;
- };
-
/* Sound iomap */
aml_snd_iomap {
compatible = "amlogic, snd-iomap";
clock-names = "clk_i2c";
clock-frequency = <100000>;
};
+
+ irblaster: meson-irblaster@c0 {
+ compatible = "amlogic, aml_irblaster";
+ reg = <0xc0 0xc>,
+ <0x40 0x4>;
+ #irblaster-cells = <2>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&irblaster_pins>;
+ status = "disabled";
+ };
};/* end of aobus */
periphs: periphs@ff634400 {
};
};
- irblaster: meson-irblaster {
- compatible = "amlogic, meson_irblaster";
- reg = <0xff8000c0 0x10>,
- <0xff800040 0x4>;
- pinctrl-names = "default";
- pinctrl-0 = <&irblaster_pins>;
- interrupts = <0 198 1>;
- status = "disabled";
- };
-
saradc:saradc {
compatible = "amlogic,meson-axg-saradc";
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&ao_b_uart_pins>;
};
+
+ irblaster: meson-irblaster@14c {
+ compatible = "amlogic, meson_irblaster";
+ reg = <0x14c 0x10>,
+ <0x40 0x4>;
+ #irblaster-cells = <2>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&irblaster_pins>;
+ interrupts = <GIC_SPI 198 IRQ_TYPE_EDGE_RISING>;
+ status = "disabled";
+ };
};/* end of aobus */
periphs: periphs@ff634400 {
clocks = <&clkc CLKID_VPU_CLKC_MUX>;
clock-names = "vpu_clkc";
};
- irblaster: meson-irblaster {
- compatible = "amlogic, meson_irblaster";
- reg = <0xff80014c 0x10>,
- <0xff800040 0x4>;
- pinctrl-names = "default";
- pinctrl-0 = <&irblaster_pins>;
- interrupts = <0 198 1>;
- status = "okay";
- };
sd_emmc_c: emmc@ffe07000 {
status = "disabled";
pinctrl-names = "default";
pinctrl-0 = <&ao_b_uart_pins>;
};
+
+ irblaster: meson-irblaster@14c {
+ compatible = "amlogic, meson_irblaster";
+ reg = <0x14c 0x10>,
+ <0x40 0x4>;
+ #irblaster-cells = <2>;
+ interrupts = <GIC_SPI 198 IRQ_TYPE_EDGE_RISING>;
+ status = "disabled";
+ };
};/* end of aobus */
periphs: periphs@ff634400 {
clocks = <&clkc CLKID_VPU_CLKC_MUX>;
clock-names = "vpu_clkc";
};
- irblaster: meson-irblaster {
- compatible = "amlogic, meson_irblaster";
- reg = <0xff80014c 0x10>,
- <0xff800040 0x4>;
- pinctrl-names = "default";
- pinctrl-0 = <&irblaster_pins>;
- interrupts = <0 198 1>;
- status = "disabled";
- };
sd_emmc_c: emmc@ffe07000 {
status = "disabled";
clocks = <&clkc CLKID_I2C>;
clock-names = "clk_i2c";
};
+
+ irblaster: meson-irblaster@c0 {
+ compatible = "amlogic, aml_irblaster";
+ reg = <0xc0 0xc>,
+ <0x40 0x4>;
+ #irblaster-cells = <2>;
+ status = "disabled";
+ };
};
periphs: periphs@c8834000 {
function = "ee_cec";
};
};
+
+ irblaster_pins:irblaster_pin {
+ mux {
+ groups = "ir_out_ao7";
+ function = "ir_out";
+ };
+ };
+
+ irblaster_pins1:irblaster_pin1 {
+ mux {
+ groups = "ir_out_ao9";
+ function = "ir_out";
+ };
+ };
}; /* end of pinctrl_aobus*/
&pinctrl_periphs {
clocks = <&clkc CLKID_I2C>;
clock-names = "clk_i2c";
};
+
+ irblaster: meson-irblaster@c0 {
+ compatible = "amlogic, aml_irblaster";
+ reg = <0xc0 0xc>,
+ <0x40 0x4>;
+ #irblaster-cells = <2>;
+ status = "disabled";
+ };
};
periphs: periphs@c8834000 {
function = "ee_cec";
};
};
+
+ irblaster_pins:irblaster_pin {
+ mux {
+ groups = "ir_out_ao7";
+ function = "ir_out";
+ };
+ };
+
+ irblaster_pins1:irblaster_pin1 {
+ mux {
+ groups = "ir_out_ao9";
+ function = "ir_out";
+ };
+ };
}; /* end of pinctrl_aobus*/
&pinctrl_periphs {
max_frame_time = <200>;
};
- meson_irblaster: irblaster@14c {
+ irblaster: meson-irblaster@14c {
compatible = "amlogic, meson_irblaster";
reg = <0x14c 0x10>,
<0x40 0x4>;
- interrupts = <0 198 1>;
+ #irblaster-cells = <2>;
+ interrupts = <GIC_SPI 198 IRQ_TYPE_EDGE_RISING>;
status = "disabled";
};
status = "disabled";
};
+ irblaster: meson-irblaster@c0 {
+ compatible = "amlogic, aml_irblaster";
+ reg = <0xc0 0xc>,
+ <0x40 0x4>;
+ #irblaster-cells = <2>;
+ status = "disabled";
+ };
+
remote:rc@0580 {
compatible = "amlogic, aml_remote";
dev_name = "meson-remote";
function = "gpio_aobus";
};
};
+
+ irblaster_pins:irblaster_pin {
+ mux {
+ groups = "remote_out_ao2";
+ function = "ir_out";
+ };
+ };
+
+ irblaster_pins1:irblaster_pin1 {
+ mux {
+ groups = "remote_out_ao6";
+ function = "ir_out";
+ };
+ };
};
&pinctrl_periphs {
status = "disabled";
};
- meson-irblaster {
- compatible = "amlogic, am_irblaster";
- dev_name = "meson-irblaster";
- status = "disable";
- pinctrl-names = "default";
- pinctrl-0 = <&irblaster_pins>;
+ irblaster: meson-irblaster@c0 {
+ compatible = "amlogic, aml_irblaster";
+ reg = <0xc0 0xc>,
+ <0x40 0x4>;
+ #irblaster-cells = <2>;
+ status = "disabled";
};
};
};
+ irblaster_pins1:irblaster_pin1 {
+ mux {
+ groups = "remote_out_ao6";
+ function = "ir_out";
+ };
+ };
+
pwmleds_pins:pwmleds {
mux {
CONFIG_AMLOGIC_WIFI=y
CONFIG_AMLOGIC_BT_DEVICE=y
CONFIG_AMLOGIC_PCIE=y
-CONFIG_AMLOGIC_IRBLASTER=y
+CONFIG_AMLOGIC_IRBLASTER_CORE=y
+CONFIG_AMLOGIC_IRBLASTER_MESON=y
+CONFIG_AMLOGIC_IRBLASTER_PROTOCOL=y
CONFIG_AMLOGIC_IIO=y
CONFIG_AMLOGIC_SARADC=y
CONFIG_AMLOGIC_DDR_TOOL=y
pinctrl-0 = <&b_uart_pins>;
};
- meson-irblaster {
- compatible = "amlogic, am_irblaster";
- dev_name = "meson-irblaster";
- status = "disable";
- pinctrl-names = "default";
- pinctrl-0 = <&irblaster_pins>;
- };
-
vpu {
compatible = "amlogic, vpu-axg";
dev_name = "vpu";
pinctrl-0 = <&b_uart_pins>;
};
- meson-irblaster {
- compatible = "amlogic, am_irblaster";
- dev_name = "meson-irblaster";
- status = "disable";
- pinctrl-names = "default";
- pinctrl-0 = <&irblaster_pins>;
- };
-
vpu {
compatible = "amlogic, vpu-axg";
dev_name = "vpu";
pinctrl-0 = <&b_uart_pins>;
};
- meson-irblaster {
- compatible = "amlogic, am_irblaster";
- dev_name = "meson-irblaster";
- status = "disable";
- pinctrl-names = "default";
- pinctrl-0 = <&irblaster_pins>;
- };
-
/* Sound iomap */
aml_snd_iomap {
compatible = "amlogic, snd-iomap";
clock-names = "clk_i2c";
clock-frequency = <100000>;
};
+
+ irblaster: meson-irblaster@c0 {
+ compatible = "amlogic, aml_irblaster";
+ reg = <0x0 0xc0 0x0 0xc>,
+ <0x0 0x40 0x0 0x4>;
+ #irblaster-cells = <2>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&irblaster_pins>;
+ status = "disabled";
+ };
};/* end of aobus */
periphs: periphs@ff634400 {
};
};
- irblaster: meson-irblaster {
- compatible = "amlogic, meson_irblaster";
- reg = <0x0 0xff8000c0 0x0 0x10>,
- <0x0 0xff800040 0x0 0x4>;
- pinctrl-names = "default";
- pinctrl-0 = <&irblaster_pins>;
- interrupts = <0 198 1>;
- status = "disabled";
- };
-
saradc:saradc {
compatible = "amlogic,meson-axg-saradc";
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&ao_b_uart_pins>;
};
+
+ irblaster: meson-irblaster@14c {
+ compatible = "amlogic, meson_irblaster";
+ reg = <0x0 0x14c 0x0 0x10>,
+ <0x0 0x40 0x0 0x4>;
+ #irblaster-cells = <2>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&irblaster_pins>;
+ interrupts = <GIC_SPI 198 IRQ_TYPE_EDGE_RISING>;
+ status = "disabled";
+ };
};/* end of aobus */
periphs: periphs@ff634400 {
clocks = <&clkc CLKID_VPU_CLKC_MUX>;
clock-names = "vpu_clkc";
};
- irblaster: meson-irblaster {
- compatible = "amlogic, meson_irblaster";
- reg = <0x0 0xff80014c 0x0 0x10>,
- <0x0 0xff800040 0x0 0x4>;
- pinctrl-names = "default";
- pinctrl-0 = <&irblaster_pins>;
- interrupts = <0 198 1>;
- status = "okay";
- };
sd_emmc_c: emmc@ffe07000 {
status = "disabled";
pinctrl-names = "default";
pinctrl-0 = <&ao_b_uart_pins>;
};
+
+ irblaster: meson-irblaster@14c {
+ compatible = "amlogic, meson_irblaster";
+ reg = <0x0 0x14c 0x0 0x10>,
+ <0x0 0x40 0x0 0x4>;
+ #irblaster-cells = <2>;
+ interrupts = <GIC_SPI 198 IRQ_TYPE_EDGE_RISING>;
+ status = "disabled";
+ };
};/* end of aobus */
periphs: periphs@ff634400 {
clocks = <&clkc CLKID_VPU_CLKC_MUX>;
clock-names = "vpu_clkc";
};
- irblaster: meson-irblaster {
- compatible = "amlogic, meson_irblaster";
- reg = <0x0 0xff80014c 0x0 0x10>,
- <0x0 0xff800040 0x0 0x4>;
- pinctrl-names = "default";
- pinctrl-0 = <&irblaster_pins>;
- interrupts = <0 198 1>;
- status = "disabled";
- };
sd_emmc_c: emmc@ffe07000 {
status = "disabled";
clocks = <&clkc CLKID_I2C>;
clock-names = "clk_i2c";
};
+
+ irblaster: meson-irblaster@c0 {
+ compatible = "amlogic, aml_irblaster";
+ reg = <0x0 0xc0 0x0 0xc>,
+ <0x0 0x40 0x0 0x4>;
+ #irblaster-cells = <2>;
+ status = "disabled";
+ };
};
periphs: periphs@c8834000 {
};
};
+ irblaster_pins:irblaster_pin {
+ mux {
+ groups = "ir_out_ao7";
+ function = "ir_out";
+ };
+ };
+
+ irblaster_pins1:irblaster_pin1 {
+ mux {
+ groups = "ir_out_ao9";
+ function = "ir_out";
+ };
+ };
}; /* end of pinctrl_aobus*/
&pinctrl_periphs {
clocks = <&clkc CLKID_I2C>;
clock-names = "clk_i2c";
};
+
+ irblaster: meson-irblaster@c0 {
+ compatible = "amlogic, aml_irblaster";
+ reg = <0x0 0xc0 0x0 0xc>,
+ <0x0 0x40 0x0 0x4>;
+ #irblaster-cells = <2>;
+ status = "disabled";
+ };
};
periphs: periphs@c8834000 {
function = "ee_cec";
};
};
+
+ irblaster_pins:irblaster_pin {
+ mux {
+ groups = "ir_out_ao7";
+ function = "ir_out";
+ };
+ };
+
+ irblaster_pins1:irblaster_pin1 {
+ mux {
+ groups = "ir_out_ao9";
+ function = "ir_out";
+ };
+ };
}; /* end of pinctrl_aobus*/
&pinctrl_periphs {
max_frame_time = <200>;
};
- meson_irblaster: irblaster@14c {
+ irblaster: meson-irblaster@14c {
compatible = "amlogic, meson_irblaster";
reg = <0x0 0x14c 0x0 0x10>,
<0x0 0x40 0x0 0x4>;
- interrupts = <0 198 1>;
+ #irblaster-cells = <2>;
+ interrupts = <GIC_SPI 198 IRQ_TYPE_EDGE_RISING>;
status = "disabled";
};
status = "disabled";
};
+ irblaster: meson-irblaster@c0 {
+ compatible = "amlogic, aml_irblaster";
+ reg = <0x0 0xc0 0x0 0xc>,
+ <0x0 0x40 0x0 0x4>;
+ #irblaster-cells = <2>;
+ status = "disabled";
+ };
+
remote:rc@0580 {
compatible = "amlogic, aml_remote";
dev_name = "meson-remote";
function = "gpio_aobus";
};
};
+
+ irblaster_pins:irblaster_pin {
+ mux {
+ groups = "remote_out_ao2";
+ function = "ir_out";
+ };
+ };
+
+ irblaster_pins1:irblaster_pin1 {
+ mux {
+ groups = "remote_out_ao6";
+ function = "ir_out";
+ };
+ };
};
&pinctrl_periphs {
status = "disabled";
};
- meson-irblaster {
- compatible = "amlogic, am_irblaster";
- dev_name = "meson-irblaster";
- status = "disable";
- pinctrl-names = "default";
- pinctrl-0 = <&irblaster_pins>;
+ irblaster: meson-irblaster@c0 {
+ compatible = "amlogic, aml_irblaster";
+ reg = <0x0 0xc0 0x0 0xc>,
+ <0x0 0x40 0x0 0x4>;
+ #irblaster-cells = <2>;
+ status = "disabled";
};
-
remote: rc@8040 {
compatible = "amlogic, aml_remote";
dev_name = "meson-remote";
};
};
+ irblaster_pins1:irblaster_pin1 {
+ mux {
+ groups = "remote_out_ao6";
+ function = "ir_out";
+ };
+ };
+
pwmleds_pins:pwmleds {
mux {
CONFIG_AMLOGIC_WIFI=y
CONFIG_AMLOGIC_BT_DEVICE=y
CONFIG_AMLOGIC_PCIE=y
-CONFIG_AMLOGIC_IRBLASTER=y
+CONFIG_AMLOGIC_IRBLASTER_CORE=y
+CONFIG_AMLOGIC_IRBLASTER_MESON=y
+CONFIG_AMLOGIC_IRBLASTER_PROTOCOL=y
CONFIG_AMLOGIC_IIO=y
CONFIG_AMLOGIC_SARADC=y
CONFIG_AMLOGIC_DDR_TOOL=y
obj-$(CONFIG_AMLOGIC_PCIE) += pci/
-obj-$(CONFIG_AMLOGIC_IRBLASTER) += irblaster/
+obj-$(CONFIG_AMLOGIC_IRBLASTER_CORE) += irblaster/
obj-$(CONFIG_AMLOGIC_IIO) += iio/
#
-# Input core configuration
+# Amlogic IRBLASTER
#
-config AMLOGIC_IRBLASTER
- bool "Amlogic irblaster device surport"
+
+menuconfig AMLOGIC_IRBLASTER_CORE
+ tristate "Amlogic IRBLASTER Support"
default n
help
- Say Y here if you want to use the amlogic irblaster.
+ This is the core code of amlogic irblaster. Say Y you
+ can use the core api to implement the irblaster controller driver
+ and use the api in the core to control the irblaster.
+ This option alone add core code.
+
+if AMLOGIC_IRBLASTER_CORE
+config AMLOGIC_IRBLASTER_MESON
+ tristate "Amlogic Meson SoC irblaster driver"
+ default n
+ help
+ This enable irblaster support for soc mesom (irblaster communication
+ controller) available in Amlogic Meson SoCs.
+ If you want to use meson irblaster interface,
+ say Y here.If you are not sure, say N.
+config AMLOGIC_IRBLASTER_PROTOCOL
+ tristate "Add Amlogic Meson irblaster encode"
+ default n
+ help
+ This enables encode mode support for the irblaster (communication
+ controller) available in Amlogic Meson SoCs.
+ If you want to use meson irblaster interface,
+ say Y here.If you are not sure, say N.
+endif
-#
-# Makefile for the remote control drivers
-#
-
-# Each configuration option enables a list of files.
-obj-$(CONFIG_AMLOGIC_IRBLASTER) += irblaster.o meson-irblaster.o
+obj-$(CONFIG_AMLOGIC_IRBLASTER_CORE) += core.o sysfs.o
+obj-$(CONFIG_AMLOGIC_IRBLASTER_MESON) += irblaster-meson.o aml-irblaster.o
+obj-$(CONFIG_AMLOGIC_IRBLASTER_PROTOCOL) += encoder.o irblaster-nec-encoder.o \
+ irblaster-rca-encoder.o
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * drivers/amlogic/irblaster/core.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/list.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/atomic.h>
+#include <linux/amlogic/irblaster.h>
+
+void irblaster_chip_data_clear(struct irblaster_chip *chip)
+{
+ chip->buffer = NULL;
+ chip->buffer_len = 0;
+ chip->sum_time = 0;
+}
+
+/**
+ * irblaster_send() - send raw level data
+ * @chip: irblaster controller
+ * @data: raw level data (us)
+ * @len: raw len
+ */
+int irblaster_send(struct irblaster_chip *chip, unsigned int *data,
+ unsigned int len)
+{
+ unsigned int sum_time = 0;
+ int err, i;
+
+ if (!chip || (len % 2 == 1) || len == 0 || len > MAX_PLUSE) {
+ pr_err("%s(): parameter error\n", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < len; i++)
+ sum_time += data[i];
+
+ chip->buffer = data;
+ chip->buffer_len = len;
+ chip->sum_time = sum_time;
+
+ if (chip->ops->send) {
+ err = chip->ops->send(chip, data, len);
+ if (err)
+ return err;
+ } else {
+ pr_err("%s(): irblaster func %s not found\n",
+ __func__, __func__);
+ return -EINVAL;
+ }
+
+ irblaster_chip_data_clear(chip);
+
+ return 0;
+}
+
+/**
+ * irblaster_set_freq() - set irblaster freq
+ * @chip: irblaster controller
+ * @freq: irblaster freq (HZ)
+ */
+int irblaster_set_freq(struct irblaster_chip *chip, unsigned int freq)
+{
+ int ret;
+
+ if (!chip || freq <= 0)
+ return -EINVAL;
+
+ if (chip->ops->set_freq) {
+ ret = chip->ops->set_freq(chip, freq);
+ if (ret)
+ return -EINVAL;
+ }
+
+ chip->state.freq = freq;
+
+ return 0;
+}
+
+/**
+ * irblaster_get_freq() - get irblaster freq
+ * @chip: irblaster controller
+ */
+unsigned int irblaster_get_freq(struct irblaster_chip *chip)
+{
+ unsigned int freq;
+
+ if (!chip)
+ return -EINVAL;
+
+ if (chip->ops->get_freq) {
+ freq = chip->ops->get_freq(chip);
+ if (freq == 0)
+ return -EINVAL;
+ } else {
+ freq = chip->state.freq;
+ }
+
+ return freq;
+}
+
+/**
+ * irblaster_set_duty() - set irblaster duty
+ * @chip: irblaster controller
+ * @duty: irblaster duty
+ */
+int irblaster_set_duty(struct irblaster_chip *chip, unsigned int duty)
+{
+ int ret;
+
+ if (!chip || duty <= 0 || duty > 100)
+ return -EINVAL;
+
+ if (chip->ops->set_duty) {
+ ret = chip->ops->set_duty(chip, duty);
+ if (ret)
+ return -EINVAL;
+ }
+
+ chip->state.duty = duty;
+
+ return 0;
+}
+
+/**
+ * irblaster_set_duty() - set irblaster duty
+ * @chip: irblaster controller
+ * @duty: irblaster duty
+ */
+unsigned int irblaster_get_duty(struct irblaster_chip *chip)
+{
+ unsigned int duty;
+
+ if (chip->ops->get_duty) {
+ duty = chip->ops->get_duty(chip);
+ if (duty == 0)
+ return -EINVAL;
+ } else {
+ duty = chip->state.duty;
+ }
+
+ return duty;
+}
+
+/**
+ * irblasterchip_remove() - remove a irblaster Controller
+ * @chip: the irblaster chip to remove
+ * @Returns: 0 on success or a negative error code on failure.
+ */
+int irblasterchip_remove(struct irblaster_chip *chip)
+{
+ mutex_lock(&irblaster_lock);
+ list_del_init(&chip->list);
+
+ if (chip->dev)
+ of_node_put(chip->dev->of_node);
+
+ irblasterchip_sysfs_unexport(chip);
+ mutex_unlock(&irblaster_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(irblasterchip_remove);
+
+static bool irblaster_ops_check(struct irblaster_ops *ops)
+{
+ /* These one interfaces are the most basic of the irblaster */
+ if (ops->send)
+ return true;
+
+ return false;
+}
+
+/**
+ * irblasterchip_add() - register a new irblaster Controller
+ * @chip: the irblaster chip to add
+ * @Returns: 0 on success or a negative error code on failure.
+ */
+int irblasterchip_add(struct irblaster_chip *chip)
+{
+ if (!chip || !chip->dev || !chip->ops)
+ return -EINVAL;
+
+ if (!irblaster_ops_check(chip->ops))
+ return -EINVAL;
+
+ mutex_lock(&irblaster_lock);
+ atomic_set(&chip->request, IRBLASTER_EXPORTED);
+ INIT_LIST_HEAD(&chip->list);
+ list_add(&chip->list, &irblaster_chips);
+ irblasterchip_sysfs_export(chip);
+ mutex_unlock(&irblaster_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(irblasterchip_add);
+
+/**
+ * irblaster_put() - release a irblaster controller
+ * @chip: irblaster controller
+ */
+void irblaster_put(struct irblaster_chip *chip)
+{
+ if (!chip)
+ return;
+
+ mutex_lock(&irblaster_lock);
+ atomic_set(&chip->request, IRBLASTER_EXPORTED);
+ irblaster_chip_data_clear(chip);
+ irblasterchip_sysfs_export(chip);
+ mutex_unlock(&irblaster_lock);
+}
+EXPORT_SYMBOL_GPL(irblaster_put);
+
+static struct irblaster_chip *of_node_to_irblasterchip(struct device_node *np)
+{
+ struct irblaster_chip *chip;
+
+ mutex_lock(&irblaster_lock);
+ list_for_each_entry(chip, &irblaster_chips, list)
+ if (chip->dev && chip->dev->of_node == np) {
+ mutex_unlock(&irblaster_lock);
+ if (atomic_read(&chip->request) == IRBLASTER_REQUESTED)
+ return ERR_PTR(-EPROBE_DEFER);
+ return chip;
+ }
+
+ mutex_unlock(&irblaster_lock);
+ return ERR_PTR(-EPROBE_DEFER);
+}
+
+static int irblaster_set_default_state(struct irblaster_chip *pc,
+ const struct of_phandle_args *args)
+{
+ int ret;
+
+ if (pc->of_irblaster_n_cells < 2 ||
+ args->args[0] <= 0 || args->args[1] > 100)
+ return -EINVAL;
+
+ pc->state.freq = args->args[0];
+ pc->state.duty = args->args[1];
+
+ ret = irblaster_set_freq(pc, pc->state.freq);
+ if (ret)
+ return -EINVAL;
+
+ ret = irblaster_set_duty(pc, pc->state.duty);
+ if (ret)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * of_irblaster_get() - request a irblaster via the irblaster framework
+ * @np: device node to get the irblaster from
+ * @con_id: consumer name
+ *
+ * Returns the irblaster controller parsed from the phandle and index
+ * specified in the "irblaster-config" property of a device tree node
+ * or a negative error-code on failure. Values parsed from the device
+ * tree are stored in the returned irblaster device object.
+ *
+ * Returns: A pointer to the requested irblaster controller or an ERR_PTR()
+ * -encoded error code on failure.
+ */
+struct irblaster_chip *of_irblaster_get(struct device_node *np,
+ const char *con_id)
+{
+ struct of_phandle_args args;
+ struct irblaster_chip *pc;
+ int err, index = 0;
+
+ err = of_parse_phandle_with_args(np, "irblaster-config",
+ "#irblaster-cells", index,
+ &args);
+ if (err) {
+ pr_err("%s(): can't parse \"irblaster-config\" property\n",
+ __func__);
+ return ERR_PTR(err);
+ }
+
+ pc = of_node_to_irblasterchip(args.np);
+ if (IS_ERR(pc)) {
+ pr_err("%s(): irblaster chip not found\n", __func__);
+ pc = ERR_PTR(-EINVAL);
+ goto put;
+ }
+
+ if (args.args_count != pc->of_irblaster_n_cells) {
+ pr_err("%s: wrong #irblaster-cells for %s\n", np->full_name,
+ args.np->full_name);
+ pc = ERR_PTR(-EINVAL);
+ goto put;
+ }
+
+ err = irblaster_set_default_state(pc, &args);
+ if (err < 0) {
+ pr_err("%s(): irblaster get state fail\n", __func__);
+ pc = ERR_PTR(-EINVAL);
+ goto put;
+ }
+
+ atomic_set(&pc->request, IRBLASTER_REQUESTED);
+ irblasterchip_sysfs_unexport(pc);
+put:
+ of_node_put(args.np);
+
+ return pc;
+}
+EXPORT_SYMBOL_GPL(of_irblaster_get);
+
+static void devm_irblaster_release(struct device *dev, void *res)
+{
+ irblaster_put(*(struct irblaster_chip **)res);
+}
+
+/**
+ * devm_of_irblaster_get() - resource managed of_irblaster_get()
+ * @dev: device for irblaster consumer
+ * @np: device node to get the irblaster from
+ * @con_id: consumer name
+ *
+ * This function performs like of_irblaster_get() but the acquired irblaster
+ * device will automatically be released on driver detach.
+ *
+ * Returns: A pointer to the requested irblaster device or an ERR_PTR()-encoded
+ * error code on failure.
+ */
+struct irblaster_chip *devm_of_irblaster_get(struct device *dev,
+ struct device_node *np,
+ const char *con_id)
+{
+ struct irblaster_chip **dr, *chip;
+
+ dr = devres_alloc(devm_irblaster_release, sizeof(*dr), GFP_KERNEL);
+ if (!dr)
+ return ERR_PTR(-ENOMEM);
+
+ chip = of_irblaster_get(np, con_id);
+ if (!IS_ERR(chip)) {
+ *dr = chip;
+ devres_add(dev, dr);
+ } else {
+ devres_free(dr);
+ }
+
+ return chip;
+}
+EXPORT_SYMBOL_GPL(devm_of_irblaster_get);
--- /dev/null
+/*
+ * drivers/amlogic/irblaster/encoder.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/list.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/amlogic/irblaster.h>
+#include <linux/amlogic/irblaster_encoder.h>
+
+static int irblaster_raw_gen_pulse_space(unsigned int *data,
+ unsigned int *max,
+ unsigned int pulse_width,
+ unsigned int space_width,
+ unsigned int len)
+{
+ if (!*max)
+ return -ENOBUFS;
+ data[len - *max] = pulse_width;
+ if (!--*max)
+ return -ENOBUFS;
+ data[len - *max] = space_width;
+ --*max;
+
+ return 0;
+}
+
+/**
+ * irblaster_raw_gen() - Encode data to raw events with pulse-length modulation.
+ * @data: Pointer to data
+ * @timings: Pulse distance modulation timings.
+ * @raw: Data bits to encode.
+ * @Returns: buff len on success.
+ *
+ * Encodes the @n least significant bits of @data using space-distance
+ * modulation with the timing characteristics described by @timings, writing up
+ * to data using the *data pointer.
+ */
+int irblaster_raw_gen(unsigned int *data,
+ const struct irblaster_raw_timings *timings,
+ u32 raw)
+{
+ int i, ret, max;
+ int len = timings->data_size;
+ unsigned int space;
+
+ max = len;
+ if (timings->header_pulse) {
+ ret = irblaster_raw_gen_pulse_space(data, &max,
+ timings->header_pulse,
+ timings->header_space,
+ len);
+ if (ret)
+ return ret;
+ }
+
+ if (timings->msb_first) {
+ for (i = timings->raw_nbits - 1; i >= 0; --i) {
+ space = timings->bit_space[(raw >> i) & 1];
+ ret = irblaster_raw_gen_pulse_space(data, &max,
+ timings->bit_pulse,
+ space, len);
+ if (ret)
+ return ret;
+ }
+ } else {
+ for (i = 0; i < timings->raw_nbits; ++i, raw >>= 1) {
+ space = timings->bit_space[raw & 1];
+ ret = irblaster_raw_gen_pulse_space(data, &max,
+ timings->bit_pulse,
+ space, len);
+ if (ret)
+ return ret;
+ }
+ }
+
+ ret = irblaster_raw_gen_pulse_space(data, &max,
+ timings->trailer_pulse,
+ timings->trailer_space,
+ len);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+unsigned int protocol_show_select(struct irblaster_chip *chip, char *buf)
+{
+ struct irblaster_raw_handler *protocol;
+ unsigned int len = 0;
+
+ mutex_lock(&irblaster_raw_handler_lock);
+ list_for_each_entry(protocol, &irblaster_raw_handler_list, list) {
+ if (chip->protocol &&
+ chip->state.protocol == protocol->protocol)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "[%s] ",
+ protocol->name);
+ else
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%s ",
+ protocol->name);
+ }
+
+ len += scnprintf(len + buf, PAGE_SIZE - len, "\n");
+ mutex_unlock(&irblaster_raw_handler_lock);
+
+ return len;
+}
+
+unsigned int protocol_store_select(const char *buf)
+{
+ struct irblaster_raw_handler *protocol;
+
+ mutex_lock(&irblaster_raw_handler_lock);
+ list_for_each_entry(protocol, &irblaster_raw_handler_list, list) {
+ if (sysfs_streq(buf, protocol->name)) {
+ mutex_unlock(&irblaster_raw_handler_lock);
+ return protocol->protocol;
+ }
+ }
+ mutex_unlock(&irblaster_raw_handler_lock);
+
+ return 0;
+}
+
+/**
+ * irblaster_raw_handler_register()
+ * - register a new raw_handle
+ * @ir_raw_handler: the raw_handle to add
+ * @Returns: 0 on success
+ */
+int irblaster_raw_handler_register(struct irblaster_raw_handler *ir_raw_handler)
+{
+ mutex_lock(&irblaster_raw_handler_lock);
+ list_add_tail(&ir_raw_handler->list, &irblaster_raw_handler_list);
+ mutex_unlock(&irblaster_raw_handler_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(irblaster_raw_handler_register);
+
+/**
+ * irblaster_raw_handler_unregister()
+ * - unregister a raw_handle
+ * @ir_raw_handler: the raw_handle to remove
+ */
+void irblaster_raw_handler_unregister(struct irblaster_raw_handler
+ *ir_raw_handler)
+{
+ mutex_lock(&irblaster_raw_handler_lock);
+ list_del(&ir_raw_handler->list);
+ mutex_unlock(&irblaster_raw_handler_lock);
+}
+EXPORT_SYMBOL(irblaster_raw_handler_unregister);
+
+/**
+ * irblaster_send_key() - send key with addr and commmand
+ * @chip: irblaster controller
+ * @addr: remote control ID
+ * @commmand: key
+ */
+int irblaster_send_key(struct irblaster_chip *chip, unsigned int addr,
+ unsigned int commmand)
+{
+ int ret;
+ unsigned int *data;
+
+ if (!chip)
+ return -EINVAL;
+
+ if (chip->ops->send_key) {
+ ret = chip->ops->send_key(chip, addr, commmand);
+ if (ret) {
+ pr_err("%s(): irblaster_send fail\n",
+ __func__);
+ return -EINVAL;
+ }
+ } else {
+ data = kzalloc(sizeof(uint32_t) * MAX_PLUSE, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (chip->protocol->encode) {
+ ret = chip->protocol->encode(chip->state.protocol,
+ addr, commmand, data);
+ if (ret <= 0) {
+ pr_err("%s(): irblaster encode fail\n",
+ __func__);
+ goto err;
+ }
+ } else {
+ pr_err("%s(): irblaster func %s not found\n",
+ __func__, __func__);
+ goto err;
+ }
+
+ ret = irblaster_send(chip, data,
+ chip->protocol->timing->data_size);
+ if (ret) {
+ pr_err("%s(): irblaster_send fail\n", __func__);
+ goto err;
+ }
+
+ kfree(data);
+ }
+
+ return 0;
+err:
+ kfree(data);
+ return -EINVAL;
+}
+
+/**
+ * irblaster_set_protocol() - set irblaster protocol
+ * @chip: irblaster controller
+ * @ir_protocol: irblaster protocol
+ */
+int irblaster_set_protocol(struct irblaster_chip *chip,
+ enum irblaster_protocol ir_protocol)
+{
+ struct irblaster_raw_handler *protocol;
+
+ if (!chip || ir_protocol < 0 || ir_protocol >= IRBLASTER_PROTOCOL_MAX)
+ return -EINVAL;
+
+ mutex_lock(&irblaster_raw_handler_lock);
+
+ list_for_each_entry(protocol, &irblaster_raw_handler_list, list)
+ if (protocol->protocol == ir_protocol) {
+ chip->state.protocol = ir_protocol;
+ chip->protocol = protocol;
+ mutex_unlock(&irblaster_raw_handler_lock);
+ return 0;
+ }
+
+ mutex_unlock(&irblaster_raw_handler_lock);
+ pr_err("%s(): irblaster protocol is not found\n", __func__);
+
+ return -EINVAL;
+}
+
+/**
+ * irblaster_get_protocol() - get irblaster protocol
+ * @chip: irblaster controller
+ */
+enum irblaster_protocol irblaster_get_protocol(struct irblaster_chip *chip)
+{
+ if (!chip)
+ return -EINVAL;
+
+ return chip->state.protocol;
+}
--- /dev/null
+/*
+ * drivers/amlogic/irblaster/irblaster-meson.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/of_device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/of_address.h>
+#include <linux/interrupt.h>
+#include <linux/of_irq.h>
+#include <linux/amlogic/irblaster.h>
+#include <linux/amlogic/irblaster_consumer.h>
+#include <linux/amlogic/irblaster_encoder.h>
+
+/* 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)
+
+#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
+#define DEFAULT_IRBLASTER_PROTOCOL IRBLASTER_PROTOCOL_NEC
+#endif
+
+#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
+
+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;
+};
+
+static void meson_irblaster_tasklet(unsigned long data);
+DECLARE_TASKLET_DISABLED(irblaster_tasklet, meson_irblaster_tasklet, 0);
+
+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 = 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 = 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) & 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);
+
+ /*
+ * 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 void send_all_data(struct meson_irblaster_dev *dev)
+{
+ int i;
+ unsigned int *pdata = NULL;
+ unsigned long flags;
+
+ pdata = &dev->buffer[dev->count];
+ spin_lock_irqsave(&dev->irblaster_lock, flags);
+ for (i = 0; (i < 120) && (dev->count < dev->buffer_size);) {
+ 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;
+ unsigned int cycle;
+ struct meson_irblaster_dev *irblaster_dev = to_meson_irblaster(chip);
+
+ init_completion(&irblaster_dev->blaster_completion);
+ irblaster_dev->buffer = data;
+ irblaster_dev->buffer_size = len;
+ irblaster_dev->count = 0;
+
+ for (i = 0; i < irblaster_dev->buffer_size; i++)
+ sum_time = sum_time + data[i];
+
+ /*
+ * 1. set mod_high_count = 13
+ * 2. set mod_low_count = 13
+ * 3. 60khz-8us, 38k-13us
+ */
+ cycle = 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);
+
+ send_all_data(irblaster_dev);
+ ret = wait_for_completion_interruptible_timeout
+ (&irblaster_dev->blaster_completion,
+ msecs_to_jiffies(sum_time / 1000));
+ if (!ret) {
+ pr_err("failed to send all data ret = %d\n", ret);
+ return -ETIMEDOUT;
+ }
+
+ 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;
+
+ /*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;
+ }
+
+ 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;
+#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_tasklet.data = (unsigned long)irblaster_dev;
+ tasklet_enable(&irblaster_tasklet);
+
+ /*initial blaster*/
+ blaster_initialize(irblaster_dev);
+
+ return 0;
+}
+
+static int 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);
+
+ return 0;
+}
+
+static const struct of_device_id irblaster_dt_match[] = {
+ {
+ .compatible = "amlogic, meson_irblaster",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, irblaster_dt_match);
+
+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,
+ },
+};
+
+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");
--- /dev/null
+/*
+ * drivers/amlogic/irblaster/irblaster-nec-encoder.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/amlogic/irblaster_encoder.h>
+
+#define NEC_NBITS 32
+#define NEC_UNIT 562 /* ns */
+#define NEC_HEADER_PULSE 9000
+#define NEC_HEADER_SPACE 4500
+#define NEC_BIT_PULSE 560
+#define NEC_BIT_0_SPACE 560
+#define NEC_BIT_1_SPACE 1690
+#define NEC_TRAILER_PULSE 560
+#define NEC_TRAILER_SPACE 5600 /* even longer in reality */
+
+static struct irblaster_raw_timings irblaster_nec_timings = {
+ .header_pulse = NEC_HEADER_PULSE,
+ .header_space = NEC_HEADER_SPACE,
+ .bit_pulse = NEC_BIT_PULSE,
+ .bit_space[0] = NEC_BIT_0_SPACE,
+ .bit_space[1] = NEC_BIT_1_SPACE,
+ .trailer_pulse = NEC_TRAILER_PULSE,
+ .trailer_space = NEC_TRAILER_SPACE,
+ .msb_first = 0,
+ .raw_nbits = NEC_NBITS,
+ .data_size = (NEC_NBITS + 2) * 2,
+};
+
+static u32 irblaster_nec32_scancode_to_raw(enum irblaster_protocol protocol,
+ unsigned int addrs,
+ unsigned int commmand)
+{
+ unsigned int addr = 0, addr_inv, data, data_inv;
+
+ data = commmand & 0xff;
+ addr_inv = addr & 0xff;
+ addr = addrs & 0xff;
+ data_inv = data & 0xff;
+
+ return data_inv << 24 |
+ data << 16 |
+ addr_inv << 8 |
+ addr;
+}
+
+static u32 irblaster_necx_scancode_to_raw(enum irblaster_protocol protocol,
+ unsigned int addrs,
+ unsigned int commmand)
+{
+ unsigned int addr, addr_inv, data, data_inv;
+
+ data = commmand & 0xff;
+ addr = addrs & 0xff;
+ addr_inv = addr & 0xff;
+ data_inv = data ^ 0xff;
+
+ return data_inv << 24 |
+ data << 16 |
+ addr_inv << 8 |
+ addr;
+}
+
+static u32 irblaster_nec_scancode_to_raw(enum irblaster_protocol protocol,
+ unsigned int addrs,
+ unsigned int commmand)
+{
+ unsigned int addr, addr_inv, data, data_inv;
+
+ data = commmand & 0xff;
+ addr = addrs & 0xff;
+ addr_inv = addr ^ 0xff;
+ data_inv = data ^ 0xff;
+
+ return data_inv << 24 |
+ data << 16 |
+ addr_inv << 8 |
+ addr;
+}
+
+int irblaster_nec_encode(enum irblaster_protocol protocol,
+ unsigned int addr,
+ unsigned int commmand,
+ unsigned int *data)
+{
+ u32 raw, ret;
+
+ if (protocol >= IRBLASTER_PROTOCOL_MAX)
+ return -ENODEV;
+
+ /* Convert a NEC scancode to raw NEC data */
+ switch (protocol) {
+ case IRBLASTER_PROTOCOL_NEC:
+ raw = irblaster_nec_scancode_to_raw(protocol, addr, commmand);
+ break;
+ case IRBLASTER_PROTOCOL_NECX:
+ raw = irblaster_necx_scancode_to_raw(protocol, addr, commmand);
+ break;
+ case IRBLASTER_PROTOCOL_NEC32:
+ raw = irblaster_nec32_scancode_to_raw(protocol, addr, commmand);
+ break;
+ default:
+ raw = irblaster_nec_scancode_to_raw(protocol, addr, commmand);
+ break;
+ }
+
+ /* Modulate the raw data using a pulse distance modulation */
+ ret = irblaster_raw_gen(data, &irblaster_nec_timings, raw);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static struct irblaster_raw_handler irblaster_nec_handler[] = {
+ {
+ .name = "NEC",
+ .protocol = IRBLASTER_PROTOCOL_NEC,
+ .encode = irblaster_nec_encode,
+ .freq = 38000,
+ .duty = 50,
+ .timing = &irblaster_nec_timings,
+ },
+ {
+ .name = "NECX",
+ .protocol = IRBLASTER_PROTOCOL_NECX,
+ .encode = irblaster_nec_encode,
+ .freq = 38000,
+ .duty = 50,
+ .timing = &irblaster_nec_timings,
+ },
+ {
+ .name = "NEC32",
+ .protocol = IRBLASTER_PROTOCOL_NEC32,
+ .encode = irblaster_nec_encode,
+ .freq = 38000,
+ .duty = 50,
+ .timing = &irblaster_nec_timings,
+ }
+};
+
+static int __init irblaster_nec_decode_init(void)
+{
+ int i;
+
+ for (i = 0; i < sizeof(irblaster_nec_handler) /
+ sizeof(struct irblaster_raw_handler); i++)
+ irblaster_raw_handler_register(irblaster_nec_handler + i);
+
+ return 0;
+}
+
+static void __exit irblaster_nec_decode_exit(void)
+{
+ int i;
+
+ for (i = 0; i < sizeof(irblaster_nec_handler) /
+ sizeof(struct irblaster_raw_handler); i++)
+ irblaster_raw_handler_unregister(irblaster_nec_handler + i);
+}
+
+fs_initcall(irblaster_nec_decode_init);
+module_exit(irblaster_nec_decode_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("NEC IR protocol decoder");
--- /dev/null
+/*
+ * drivers/amlogic/irblaster/irblaster-rca-encoder.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/amlogic/irblaster_encoder.h>
+
+#define RCA_NBITS 24
+#define RCA_UNIT 500 /* ns */
+#define RCA_HEADER_PULSE 4000
+#define RCA_HEADER_SPACE 4000
+#define RCA_BIT_PULSE 500
+#define RCA_BIT_0_SPACE 1000
+#define RCA_BIT_1_SPACE 2000
+#define RCA_TRAILER_PULSE 500
+#define RCA_TRAILER_SPACE 5600 /* even longer in reality */
+
+static struct irblaster_raw_timings irblaster_rca_timings = {
+ .header_pulse = RCA_HEADER_PULSE,
+ .header_space = RCA_HEADER_SPACE,
+ .bit_pulse = RCA_BIT_PULSE,
+ .bit_space[0] = RCA_BIT_0_SPACE,
+ .bit_space[1] = RCA_BIT_1_SPACE,
+ .trailer_pulse = RCA_TRAILER_PULSE,
+ .trailer_space = RCA_TRAILER_SPACE,
+ .msb_first = 1,
+ .raw_nbits = RCA_NBITS,
+ .data_size = (RCA_NBITS + 2) * 2,
+};
+
+static u32 irblaster_rca_scancode_to_raw(enum irblaster_protocol protocol,
+ unsigned int addrs,
+ unsigned int commmand)
+{
+ unsigned int addr, addr_inv, data, data_inv;
+
+ data = commmand & 0xff;
+ addr = addrs & 0x0f;
+ addr_inv = addr ^ 0x0f;
+ data_inv = data ^ 0xff;
+
+ return addr << 20 |
+ data << 12 |
+ addr_inv << 8 |
+ data_inv;
+}
+
+int irblaster_rca_encode(enum irblaster_protocol protocol, unsigned int addr,
+ unsigned int commmand, unsigned int *data)
+{
+ u32 raw, ret;
+
+ /* Convert a RCA scancode to raw rca data */
+ raw = irblaster_rca_scancode_to_raw(protocol, addr, commmand);
+
+ /* Modulate the raw data using a pulse distance modulation */
+ ret = irblaster_raw_gen(data, &irblaster_rca_timings, raw);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static struct irblaster_raw_handler irblaster_rca_handler = {
+ .name = "RCA",
+ .protocol = IRBLASTER_PROTOCOL_RCA,
+ .encode = irblaster_rca_encode,
+ .freq = 38000,
+ .duty = 50,
+ .timing = &irblaster_rca_timings,
+};
+
+static int __init irblaster_rca_decode_init(void)
+{
+ return irblaster_raw_handler_register(&irblaster_rca_handler);
+}
+
+static void __exit irblaster_rca_decode_exit(void)
+{
+ irblaster_raw_handler_unregister(&irblaster_rca_handler);
+}
+
+fs_initcall(irblaster_rca_decode_init);
+module_exit(irblaster_rca_decode_exit);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("RCA IR protocol decoder");
--- /dev/null
+/*
+ * drivers/amlogic/irblaster/sysfs.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/device.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/kdev_t.h>
+#include <linux/amlogic/irblaster.h>
+#include <linux/amlogic/irblaster_consumer.h>
+#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
+#include <linux/amlogic/irblaster_encoder.h>
+#endif
+static ssize_t send_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct irblaster_chip *chip = dev_get_drvdata(dev);
+ int i = 0, j = 0, m = 0;
+ int val, ret;
+ char tone[PS_SIZE];
+ unsigned int *buffer;
+
+ buffer = kzalloc(sizeof(uint32_t) * MAX_PLUSE, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ mutex_lock(&chip->sys_lock);
+ while (buf[i] != '\0') {
+ if (buf[i] == 's') {
+ tone[j] = '\0';
+ ret = kstrtoint(tone, 10, &val);
+ if (ret) {
+ pr_err("Invalid tone\n");
+ mutex_unlock(&chip->sys_lock);
+ return ret;
+ }
+ buffer[m] = val * 10;
+ j = 0;
+ i++;
+ m++;
+ if (m >= MAX_PLUSE)
+ break;
+ continue;
+ }
+ tone[j] = buf[i];
+ i++;
+ j++;
+ if (j >= PS_SIZE) {
+ pr_err("send timing value is out of range\n");
+ mutex_unlock(&chip->sys_lock);
+ kfree(buffer);
+ return -ENOMEM;
+ }
+ }
+
+ ret = irblaster_send(chip, buffer, m);
+ if (ret)
+ pr_err("send raw data fail\n");
+
+ irblaster_chip_data_clear(chip);
+ mutex_unlock(&chip->sys_lock);
+ kfree(buffer);
+
+ return ret ? : count;
+}
+
+#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
+static ssize_t send_key_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct irblaster_chip *chip = dev_get_drvdata(dev);
+ unsigned int addr, command;
+ int ret;
+
+ ret = sscanf(buf, "%x %x", &addr, &command);
+ if (ret != 2) {
+ pr_err("Can't parse addr and command,usage:[addr command]\n");
+ return -EINVAL;
+ }
+ mutex_lock(&chip->sys_lock);
+ ret = irblaster_send_key(chip, addr, command);
+ if (ret)
+ pr_err("send key fail\n");
+
+ mutex_unlock(&chip->sys_lock);
+
+ return ret ? : count;
+}
+#endif
+
+static ssize_t carrier_freq_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct irblaster_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", chip->state.freq);
+}
+
+static ssize_t carrier_freq_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct irblaster_chip *chip = dev_get_drvdata(dev);
+ int ret = 0, val;
+
+ ret = kstrtoint(buf, 10, &val);
+ if (ret) {
+ pr_err("Invalid input for carrier_freq\n");
+ return ret;
+ }
+
+ mutex_lock(&chip->sys_lock);
+ ret = irblaster_set_freq(chip, val);
+ if (ret)
+ pr_err("set freq fail\n");
+
+ mutex_unlock(&chip->sys_lock);
+
+ return ret ? : count;
+}
+
+static ssize_t duty_cycle_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct irblaster_chip *chip = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", chip->state.duty);
+}
+
+static ssize_t duty_cycle_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct irblaster_chip *chip = dev_get_drvdata(dev);
+ int ret = 0, val;
+
+ ret = kstrtoint(buf, 10, &val);
+ if (ret) {
+ pr_err("Invalid input for duty_cycle\n");
+ return ret;
+ }
+
+ mutex_lock(&chip->sys_lock);
+ ret = irblaster_set_duty(chip, val);
+ if (ret)
+ pr_err("set duty fail\n");
+
+ mutex_unlock(&chip->sys_lock);
+
+ return ret ? : count;
+}
+
+#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
+static ssize_t protocol_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct irblaster_chip *chip = dev_get_drvdata(dev);
+ unsigned int len;
+
+ len = protocol_show_select(chip, buf);
+
+ return len;
+}
+
+static ssize_t protocol_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct irblaster_chip *chip = dev_get_drvdata(dev);
+ unsigned int ret, protocol;
+
+ protocol = protocol_store_select(buf);
+ if (protocol >= IRBLASTER_PROTOCOL_MAX)
+ pr_err("protocol is not found\n");
+
+ ret = irblaster_set_protocol(chip, protocol);
+ if (ret)
+ pr_err("set protocol fail\n");
+
+ return ret ? : count;
+}
+#endif
+
+//static DEVICE_ATTR(debug, 0644, show_debug, store_debug);
+static DEVICE_ATTR_WO(send);
+#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
+static DEVICE_ATTR_WO(send_key);
+#endif
+static DEVICE_ATTR_RW(carrier_freq);
+static DEVICE_ATTR_RW(duty_cycle);
+#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
+static DEVICE_ATTR_RW(protocol);
+#endif
+
+static struct attribute *irblaster_chip_attrs[] = {
+// &dev_attr_debug.attr,
+ &dev_attr_send.attr,
+#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
+ &dev_attr_send_key.attr,
+#endif
+ &dev_attr_carrier_freq.attr,
+ &dev_attr_duty_cycle.attr,
+#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
+ &dev_attr_protocol.attr,
+#endif
+ NULL,
+};
+ATTRIBUTE_GROUPS(irblaster_chip);
+
+static struct class irblaster_class = {
+ .name = "irblaster",
+ .owner = THIS_MODULE,
+ .dev_groups = irblaster_chip_groups,
+};
+
+static int irblasterchip_sysfs_match(struct device *parent, const void *data)
+{
+ return dev_get_drvdata(parent) == data;
+}
+
+void irblasterchip_sysfs_export(struct irblaster_chip *chip)
+{
+ struct device *parent;
+
+ /*
+ * If device_create() fails the irblaster_chip is still usable by
+ * the kernel its just not exported.
+ */
+ parent = device_create(&irblaster_class, chip->dev, MKDEV(0, 0), chip,
+ "irblaster%d", chip->base);
+ if (IS_ERR(parent)) {
+ dev_warn(chip->dev,
+ "device_create failed for irblaster_chip sysfs export\n");
+ }
+
+ mutex_init(&chip->sys_lock);
+}
+
+void irblasterchip_sysfs_unexport(struct irblaster_chip *chip)
+{
+ struct device *parent;
+
+ parent = class_find_device(&irblaster_class, NULL, chip,
+ irblasterchip_sysfs_match);
+ if (parent) {
+ /* for class_find_device() */
+ put_device(parent);
+ device_unregister(parent);
+ }
+}
+
+static int __init irblaster_sysfs_init(void)
+{
+ return class_register(&irblaster_class);
+}
+subsys_initcall(irblaster_sysfs_init);
--- /dev/null
+/*
+ * include/linux/amlogic/irblaster.h
+ *
+ * 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.
+ *
+ */
+
+#ifndef __LINUX_IRBLASTER_H
+#define __LINUX_IRBLASTER_H
+
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
+#include <linux/amlogic/irblaster_encoder.h>
+#endif
+
+#define MAX_PLUSE 256
+#define PS_SIZE 10
+
+struct irblaster_chip;
+
+static DEFINE_MUTEX(irblaster_lock);
+static LIST_HEAD(irblaster_chips);
+
+/**
+ * enum irblaster_idle - Whether the controller is occupied
+ * @IRBLASTER_EXPORTED: Controlled by the sysfs sys/class/irblaster
+ * @IRBLASTER_REQUESTED: Controlled by consumer driver
+ */
+enum irblaster_idle {
+ IRBLASTER_EXPORTED,
+ IRBLASTER_REQUESTED
+};
+
+/**
+ * struct irblaster_ops - irblaster controller operations
+ * @send: send raw level data
+ * @send_key: send key according to the protocol
+ * @set_freq: set irblaster freq
+ * @get_freq: get irblaster freq
+ * @set_duty: set irblaster duty
+ * @get_duty: get irblaster duty
+ */
+struct irblaster_ops {
+ int (*send)(struct irblaster_chip *chip,
+ unsigned int *data, unsigned int len);
+#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
+ int (*send_key)(struct irblaster_chip *chip,
+ unsigned int addr, int commmand);
+ /* int (*set_protocol)(struct irblaster_chip *chip, */
+ /* enum irblaster_protocol protocol);*/
+#endif
+ int (*set_freq)(struct irblaster_chip *chip, unsigned int freq);
+ unsigned int (*get_freq)(struct irblaster_chip *chip);
+ int (*set_duty)(struct irblaster_chip *chip, unsigned int duty);
+ unsigned int (*get_duty)(struct irblaster_chip *chip);
+};
+
+struct irblaster_state {
+ unsigned int freq;
+ unsigned int duty;
+ int enabled;
+ int idle;
+#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
+ enum irblaster_protocol protocol;
+#endif
+};
+
+/**
+ * struct irblaster_chip - abstract a irblaster controller
+ * @dev: device providing the irblaster
+ * @list: list node for internal use
+ * @ops: callbacks for this irblaster controller
+ * @base: number of first irblaster controlled by this chip
+ * @of_irblaster_n_cells: number of cells expected in the device tree
+ * irblaster specifier
+ * @state: irblaster controller status
+ * @buffer: data
+ * @buffer_len: data len
+ * @sum_time: total time
+ * @request: whether the controller is occupied
+ */
+struct irblaster_chip {
+ struct device *dev;
+ struct list_head list;
+ struct irblaster_ops *ops;
+#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
+ struct irblaster_raw_handler *protocol;
+#endif
+ struct irblaster_state state;
+ struct mutex sys_lock; /* use to sysfs */
+ unsigned int base;
+ unsigned int of_irblaster_n_cells;
+ /* unsigned int buffer[MAX_PLUSE]; */
+ unsigned int *buffer;
+ unsigned int buffer_len;
+ unsigned int sum_time;
+ atomic_t request;
+};
+
+/* irblaster sysfs APIs */
+void irblasterchip_sysfs_export(struct irblaster_chip *chip);
+void irblasterchip_sysfs_unexport(struct irblaster_chip *chip);
+
+/* irblaster provider APIs */
+int irblasterchip_add(struct irblaster_chip *chip);
+int irblasterchip_remove(struct irblaster_chip *chip);
+void irblaster_chip_data_clear(struct irblaster_chip *chip);
+
+#endif /* __LINUX_IRBLASTER_H */
--- /dev/null
+/*
+ * include/linux/amlogic/irblaster_consumer.h
+ *
+ * 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.
+ *
+ */
+
+#ifndef __LINUX_IRBLASTER_CONSUMER_H
+#define __LINUX_IRBLASTER_CONSUMER_H
+
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
+#include <linux/amlogic/irblaster_encoder.h>
+#endif
+
+struct irblaster_chip;
+
+/* irblaster user APIs */
+int irblaster_send(struct irblaster_chip *chip,
+ unsigned int *data,
+ unsigned int len);
+int irblaster_set_freq(struct irblaster_chip *chip,
+ unsigned int freq);
+unsigned int irblaster_get_freq(struct irblaster_chip *chip);
+int irblaster_set_duty(struct irblaster_chip *chip, unsigned int duty);
+unsigned int irblaster_get_duty(struct irblaster_chip *chip);
+struct irblaster_chip *of_irblaster_get(struct device_node *np,
+ const char *con_id);
+struct irblaster_chip *devm_of_irblaster_get(struct device *dev,
+ struct device_node *np,
+ const char *con_id);
+void irblaster_put(struct irblaster_chip *chip);
+
+#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL
+int irblaster_send_key(struct irblaster_chip *chip,
+ unsigned int addr,
+ unsigned int commmand);
+int irblaster_set_protocol(struct irblaster_chip *chip,
+ enum irblaster_protocol ir_protocol);
+enum irblaster_protocol irblaster_get_protocol(struct irblaster_chip *chip);
+#endif
+
+#endif /* __LINUX_IRBLASTER_CONSUMER_H */
--- /dev/null
+/*
+ * include/linux/amlogic/irblaster_encoder.h
+ *
+ * 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.
+ *
+ */
+
+#ifndef __LINUX_ENCODER_H
+#define __LINUX_ENCODER_H
+
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+
+struct irblaster_chip;
+int irblaster_send(struct irblaster_chip *chip, unsigned int *data,
+ unsigned int len);
+
+static DEFINE_MUTEX(irblaster_raw_handler_lock);
+static LIST_HEAD(irblaster_raw_handler_list);
+
+/**
+ * enum irblaster_protocol - protocol of irblaster
+ * add a new protocol here
+ * @IRBLASTER_PROTOCOL_MAX: Maximum number of support protocol
+ * please add a new protocol before this
+ */
+enum irblaster_protocol {
+ IRBLASTER_PROTOCOL_NEC,
+ IRBLASTER_PROTOCOL_NECX,
+ IRBLASTER_PROTOCOL_NEC32,
+ IRBLASTER_PROTOCOL_RCA,
+ IRBLASTER_PROTOCOL_MAX
+};
+
+#define IRBLASTER_PROTOCOL_BIT_NEC BIT_ULL(IRBLASTER_PROTOCOL_NEC)
+#define IRBLASTER_PROTOCOL_BIT_NECX BIT_ULL(IRBLASTER_PROTOCOL_NECX)
+#define IRBLASTER_PROTOCOL_BIT_NEC32 BIT_ULL(IRBLASTER_PROTOCOL_NEC32)
+#define IRBLASTER_PROTOCOL_BIT_RCA BIT_ULL(IRBLASTER_PROTOCOL_RCA)
+
+/**
+ * struct irblaster_raw_timings - pulse-length modulation timings
+ * @header_pulse: duration of header pulse in ns (0 for none)
+ * @bit_space: duration of bit space in ns
+ * @bit_pulse: duration of bit pulse (for logic 0 and 1) in ns
+ * @trailer_space: duration of trailer space in ns
+ * @msb_first: 1 if most significant bit is sent first
+ * @raw_nbits: raw bit len
+ * @data_size: the total length of the array
+ */
+struct irblaster_raw_timings {
+ unsigned int header_pulse;
+ unsigned int header_space;
+ unsigned int bit_pulse;
+ unsigned int bit_space[2];
+ unsigned int trailer_pulse;
+ unsigned int trailer_space;
+ unsigned int msb_first:1;
+ unsigned int raw_nbits;
+ unsigned int data_size;
+};
+
+struct irblaster_raw_handler {
+ struct list_head list;
+ char *name;
+ int protocol; /* which are handled by this handler */
+ int (*encode)(enum irblaster_protocol protocol, unsigned int addr,
+ unsigned int commmand, unsigned int *data);
+ struct irblaster_raw_timings *timing;
+ struct mutex encode_lock; /* use to function encode */
+ u32 freq;
+ u32 duty;
+};
+
+/* irblaster encode APIs */
+int irblaster_raw_handler_register(struct irblaster_raw_handler
+ *ir_raw_handler);
+void irblaster_raw_handler_unregister(struct irblaster_raw_handler
+ *ir_raw_handler);
+int irblaster_raw_gen(unsigned int *data,
+ const struct irblaster_raw_timings *timings,
+ u32 raw);
+
+unsigned int protocol_store_select(const char *buf);
+unsigned int protocol_show_select(struct irblaster_chip *chip, char *buf);
+
+#endif /* __LINUX_ENCODER_H */