irblaster: meson: refactored irblaster driver [1/1]
authorBichao Zheng <bichao.zheng@amlogic.com>
Fri, 1 Mar 2019 06:33:02 +0000 (14:33 +0800)
committerJianxin Pan <jianxin.pan@amlogic.com>
Mon, 18 Mar 2019 03:08:43 +0000 (19:08 -0800)
PD#SWPL-1856

Problem:
refactored irblaster code

Solution:
1. Refactor the code according to the core, provider, and consumer
   frameworks.
2. Provide software encode to let irblaster work according to different
   protocols
3. Provide a unified consumer interface to allow other consumer drivers
   to use irblaster.

Verify:
test pass on g12a_u200_v1

Change-Id: Ifd841ef0ed741b7fd721defc25691744ea2103f0
Signed-off-by: Bichao Zheng <bichao.zheng@amlogic.com>
37 files changed:
MAINTAINERS
arch/arm/boot/dts/amlogic/axg_s400_v03gva.dts
arch/arm/boot/dts/amlogic/axg_s420_v03gva.dts
arch/arm/boot/dts/amlogic/mesonaxg.dtsi
arch/arm/boot/dts/amlogic/mesong12a.dtsi
arch/arm/boot/dts/amlogic/mesong12b.dtsi
arch/arm/boot/dts/amlogic/mesongxl.dtsi
arch/arm/boot/dts/amlogic/mesongxm.dtsi
arch/arm/boot/dts/amlogic/mesontl1.dtsi
arch/arm/boot/dts/amlogic/mesontxl.dtsi
arch/arm/boot/dts/amlogic/mesontxlx.dtsi
arch/arm/configs/meson64_a32_defconfig
arch/arm64/boot/dts/amlogic/axg_s400_v03gva.dts
arch/arm64/boot/dts/amlogic/axg_s410.dts
arch/arm64/boot/dts/amlogic/axg_s420_v03gva.dts
arch/arm64/boot/dts/amlogic/mesonaxg.dtsi
arch/arm64/boot/dts/amlogic/mesong12a.dtsi
arch/arm64/boot/dts/amlogic/mesong12b.dtsi
arch/arm64/boot/dts/amlogic/mesongxl.dtsi
arch/arm64/boot/dts/amlogic/mesongxm.dtsi
arch/arm64/boot/dts/amlogic/mesontl1.dtsi
arch/arm64/boot/dts/amlogic/mesontxl.dtsi
arch/arm64/boot/dts/amlogic/mesontxlx.dtsi
arch/arm64/configs/meson64_defconfig
drivers/amlogic/Makefile
drivers/amlogic/irblaster/Kconfig
drivers/amlogic/irblaster/Makefile
drivers/amlogic/irblaster/aml-irblaster.c [new file with mode: 0644]
drivers/amlogic/irblaster/core.c [new file with mode: 0644]
drivers/amlogic/irblaster/encoder.c [new file with mode: 0644]
drivers/amlogic/irblaster/irblaster-meson.c [new file with mode: 0644]
drivers/amlogic/irblaster/irblaster-nec-encoder.c [new file with mode: 0644]
drivers/amlogic/irblaster/irblaster-rca-encoder.c [new file with mode: 0644]
drivers/amlogic/irblaster/sysfs.c [new file with mode: 0644]
include/linux/amlogic/irblaster.h [new file with mode: 0644]
include/linux/amlogic/irblaster_consumer.h [new file with mode: 0644]
include/linux/amlogic/irblaster_encoder.h [new file with mode: 0644]

index 6eec6f7..9d2fd35 100644 (file)
@@ -14053,13 +14053,6 @@ AMLOGIC AXG ADD AO CLK
 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
@@ -14415,9 +14408,9 @@ F:      drivers/amlogic/cpufreq/meson-cpufreq.c
 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>
index 38895bc..6969131 100644 (file)
                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";
index 594190f..f49e3e4 100644 (file)
                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";
index eb3516b..1d0e163 100644 (file)
                                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";
index 2a8a386..d6bef18 100644 (file)
                                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";
index 07e019d..f913a51 100644 (file)
                                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";
index b099b57..b1a9543 100644 (file)
                                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 {
index a6156ce..6e585a4 100644 (file)
                                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 {
index 7c7b802..4ab7e08 100644 (file)
                                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";
                        };
 
index c2e82ab..c6fdc49 100644 (file)
                                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 {
index 6399685..3426ea1 100644 (file)
                                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 {
index f40ccea..10c4c29 100644 (file)
@@ -362,7 +362,9 @@ CONFIG_AMLOGIC_ESM=y
 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
index f805271..a2e2f41 100644 (file)
                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";
index e33be01..0e16ced 100644 (file)
                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";
index 6f3198e..08207f9 100644 (file)
                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";
index b3cd366..27d1dbf 100644 (file)
                                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";
index 2257e6d..6a9dc7b 100644 (file)
                                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";
index afbf50e..a979075 100644 (file)
                                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";
index 389db76..0287b0c 100644 (file)
                                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 {
index 049541f..44a390a 100644 (file)
                                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 {
index c2adbd6..e2bbeb5 100644 (file)
                                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";
                        };
 
index 55092f2..ebdeac0 100644 (file)
                                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 {
index d22214f..2ff87a5 100644 (file)
                                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 {
index bb52895..bbbd04e 100644 (file)
@@ -357,7 +357,9 @@ CONFIG_AMLOGIC_ESM=y
 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
index f060cfb..0d79615 100644 (file)
@@ -108,7 +108,7 @@ obj-$(CONFIG_AMLOGIC_POWER)         += power/
 
 obj-$(CONFIG_AMLOGIC_PCIE)      += pci/
 
-obj-$(CONFIG_AMLOGIC_IRBLASTER) += irblaster/
+obj-$(CONFIG_AMLOGIC_IRBLASTER_CORE) += irblaster/
 
 obj-$(CONFIG_AMLOGIC_IIO)       += iio/
 
index 6c35ae8..6e35faa 100644 (file)
@@ -1,11 +1,34 @@
 #
-# 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
index c406f7a..22c05b0 100644 (file)
@@ -1,6 +1,4 @@
-#
-# 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
diff --git a/drivers/amlogic/irblaster/aml-irblaster.c b/drivers/amlogic/irblaster/aml-irblaster.c
new file mode 100644 (file)
index 0000000..79c7d27
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ * 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");
diff --git a/drivers/amlogic/irblaster/core.c b/drivers/amlogic/irblaster/core.c
new file mode 100644 (file)
index 0000000..dfc5b78
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * 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);
diff --git a/drivers/amlogic/irblaster/encoder.c b/drivers/amlogic/irblaster/encoder.c
new file mode 100644 (file)
index 0000000..757fa18
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * 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;
+}
diff --git a/drivers/amlogic/irblaster/irblaster-meson.c b/drivers/amlogic/irblaster/irblaster-meson.c
new file mode 100644 (file)
index 0000000..41a7f52
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+ * 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");
diff --git a/drivers/amlogic/irblaster/irblaster-nec-encoder.c b/drivers/amlogic/irblaster/irblaster-nec-encoder.c
new file mode 100644 (file)
index 0000000..107383f
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * 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");
diff --git a/drivers/amlogic/irblaster/irblaster-rca-encoder.c b/drivers/amlogic/irblaster/irblaster-rca-encoder.c
new file mode 100644 (file)
index 0000000..21068b6
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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");
diff --git a/drivers/amlogic/irblaster/sysfs.c b/drivers/amlogic/irblaster/sysfs.c
new file mode 100644 (file)
index 0000000..650be64
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * 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);
diff --git a/include/linux/amlogic/irblaster.h b/include/linux/amlogic/irblaster.h
new file mode 100644 (file)
index 0000000..b9cbf43
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * 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 */
diff --git a/include/linux/amlogic/irblaster_consumer.h b/include/linux/amlogic/irblaster_consumer.h
new file mode 100644 (file)
index 0000000..48f6533
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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 */
diff --git a/include/linux/amlogic/irblaster_encoder.h b/include/linux/amlogic/irblaster_encoder.h
new file mode 100644 (file)
index 0000000..db0642f
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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 */