From 849e41995754241bb33126dfccff4ea8df0a880e Mon Sep 17 00:00:00 2001 From: Bichao Zheng Date: Fri, 1 Mar 2019 14:33:02 +0800 Subject: [PATCH] irblaster: meson: refactored irblaster driver [1/1] 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 --- MAINTAINERS | 13 +- arch/arm/boot/dts/amlogic/axg_s400_v03gva.dts | 8 - arch/arm/boot/dts/amlogic/axg_s420_v03gva.dts | 8 - arch/arm/boot/dts/amlogic/mesonaxg.dtsi | 20 +- arch/arm/boot/dts/amlogic/mesong12a.dtsi | 20 +- arch/arm/boot/dts/amlogic/mesong12b.dtsi | 18 +- arch/arm/boot/dts/amlogic/mesongxl.dtsi | 22 ++ arch/arm/boot/dts/amlogic/mesongxm.dtsi | 22 ++ arch/arm/boot/dts/amlogic/mesontl1.dtsi | 5 +- arch/arm/boot/dts/amlogic/mesontxl.dtsi | 22 ++ arch/arm/boot/dts/amlogic/mesontxlx.dtsi | 19 +- arch/arm/configs/meson64_a32_defconfig | 4 +- arch/arm64/boot/dts/amlogic/axg_s400_v03gva.dts | 8 - arch/arm64/boot/dts/amlogic/axg_s410.dts | 8 - arch/arm64/boot/dts/amlogic/axg_s420_v03gva.dts | 8 - arch/arm64/boot/dts/amlogic/mesonaxg.dtsi | 20 +- arch/arm64/boot/dts/amlogic/mesong12a.dtsi | 20 +- arch/arm64/boot/dts/amlogic/mesong12b.dtsi | 18 +- arch/arm64/boot/dts/amlogic/mesongxl.dtsi | 21 ++ arch/arm64/boot/dts/amlogic/mesongxm.dtsi | 22 ++ arch/arm64/boot/dts/amlogic/mesontl1.dtsi | 5 +- arch/arm64/boot/dts/amlogic/mesontxl.dtsi | 22 ++ arch/arm64/boot/dts/amlogic/mesontxlx.dtsi | 20 +- arch/arm64/configs/meson64_defconfig | 4 +- drivers/amlogic/Makefile | 2 +- drivers/amlogic/irblaster/Kconfig | 31 +- drivers/amlogic/irblaster/Makefile | 10 +- drivers/amlogic/irblaster/aml-irblaster.c | 430 ++++++++++++++++++++++ drivers/amlogic/irblaster/core.c | 366 ++++++++++++++++++ drivers/amlogic/irblaster/encoder.c | 267 ++++++++++++++ drivers/amlogic/irblaster/irblaster-meson.c | 415 +++++++++++++++++++++ drivers/amlogic/irblaster/irblaster-nec-encoder.c | 180 +++++++++ drivers/amlogic/irblaster/irblaster-rca-encoder.c | 99 +++++ drivers/amlogic/irblaster/sysfs.c | 275 ++++++++++++++ include/linux/amlogic/irblaster.h | 121 ++++++ include/linux/amlogic/irblaster_consumer.h | 55 +++ include/linux/amlogic/irblaster_encoder.h | 97 +++++ 37 files changed, 2569 insertions(+), 136 deletions(-) create mode 100644 drivers/amlogic/irblaster/aml-irblaster.c create mode 100644 drivers/amlogic/irblaster/core.c create mode 100644 drivers/amlogic/irblaster/encoder.c create mode 100644 drivers/amlogic/irblaster/irblaster-meson.c create mode 100644 drivers/amlogic/irblaster/irblaster-nec-encoder.c create mode 100644 drivers/amlogic/irblaster/irblaster-rca-encoder.c create mode 100644 drivers/amlogic/irblaster/sysfs.c create mode 100644 include/linux/amlogic/irblaster.h create mode 100644 include/linux/amlogic/irblaster_consumer.h create mode 100644 include/linux/amlogic/irblaster_encoder.h diff --git a/MAINTAINERS b/MAINTAINERS index 6eec6f7..9d2fd35 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14053,13 +14053,6 @@ AMLOGIC AXG ADD AO CLK M: Yun Cai F: drivers/amlogic/clk/axg/axg_ao.c -AMLOGIC Irblaster driver -M: Zan Peng -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 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 -F: drivers/amlogic/irblaster/meson-irblaster.c -F: drivers/amlogic/irblaster/meson-irblaster.h +M: Bichao.Zheng +F: drivers/amlogic/irblaster/* +F: include/linux/amlogic/irblaster* AMLOGIC THERMAL DRIVER M: Huan Biao diff --git a/arch/arm/boot/dts/amlogic/axg_s400_v03gva.dts b/arch/arm/boot/dts/amlogic/axg_s400_v03gva.dts index 38895bc..6969131 100644 --- a/arch/arm/boot/dts/amlogic/axg_s400_v03gva.dts +++ b/arch/arm/boot/dts/amlogic/axg_s400_v03gva.dts @@ -421,14 +421,6 @@ 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"; diff --git a/arch/arm/boot/dts/amlogic/axg_s420_v03gva.dts b/arch/arm/boot/dts/amlogic/axg_s420_v03gva.dts index 594190f..f49e3e4 100644 --- a/arch/arm/boot/dts/amlogic/axg_s420_v03gva.dts +++ b/arch/arm/boot/dts/amlogic/axg_s420_v03gva.dts @@ -316,14 +316,6 @@ 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"; diff --git a/arch/arm/boot/dts/amlogic/mesonaxg.dtsi b/arch/arm/boot/dts/amlogic/mesonaxg.dtsi index eb3516b..1d0e163 100644 --- a/arch/arm/boot/dts/amlogic/mesonaxg.dtsi +++ b/arch/arm/boot/dts/amlogic/mesonaxg.dtsi @@ -468,6 +468,16 @@ 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 { @@ -743,16 +753,6 @@ }; }; - 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"; diff --git a/arch/arm/boot/dts/amlogic/mesong12a.dtsi b/arch/arm/boot/dts/amlogic/mesong12a.dtsi index 2a8a386..d6bef18 100644 --- a/arch/arm/boot/dts/amlogic/mesong12a.dtsi +++ b/arch/arm/boot/dts/amlogic/mesong12a.dtsi @@ -746,6 +746,17 @@ 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 = ; + status = "disabled"; + }; };/* end of aobus */ periphs: periphs@ff634400 { @@ -1323,15 +1334,6 @@ 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"; diff --git a/arch/arm/boot/dts/amlogic/mesong12b.dtsi b/arch/arm/boot/dts/amlogic/mesong12b.dtsi index 07e019d..f913a51 100644 --- a/arch/arm/boot/dts/amlogic/mesong12b.dtsi +++ b/arch/arm/boot/dts/amlogic/mesong12b.dtsi @@ -802,6 +802,15 @@ 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 = ; + status = "disabled"; + }; };/* end of aobus */ periphs: periphs@ff634400 { @@ -1424,15 +1433,6 @@ 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"; diff --git a/arch/arm/boot/dts/amlogic/mesongxl.dtsi b/arch/arm/boot/dts/amlogic/mesongxl.dtsi index b099b57..b1a9543 100644 --- a/arch/arm/boot/dts/amlogic/mesongxl.dtsi +++ b/arch/arm/boot/dts/amlogic/mesongxl.dtsi @@ -522,6 +522,14 @@ 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 { @@ -634,6 +642,20 @@ 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 { diff --git a/arch/arm/boot/dts/amlogic/mesongxm.dtsi b/arch/arm/boot/dts/amlogic/mesongxm.dtsi index a6156ce..6e585a4 100644 --- a/arch/arm/boot/dts/amlogic/mesongxm.dtsi +++ b/arch/arm/boot/dts/amlogic/mesongxm.dtsi @@ -629,6 +629,14 @@ 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 { @@ -734,6 +742,20 @@ 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 { diff --git a/arch/arm/boot/dts/amlogic/mesontl1.dtsi b/arch/arm/boot/dts/amlogic/mesontl1.dtsi index 7c7b802..4ab7e08 100644 --- a/arch/arm/boot/dts/amlogic/mesontl1.dtsi +++ b/arch/arm/boot/dts/amlogic/mesontl1.dtsi @@ -897,11 +897,12 @@ 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 = ; status = "disabled"; }; diff --git a/arch/arm/boot/dts/amlogic/mesontxl.dtsi b/arch/arm/boot/dts/amlogic/mesontxl.dtsi index c2e82ab..c6fdc49 100644 --- a/arch/arm/boot/dts/amlogic/mesontxl.dtsi +++ b/arch/arm/boot/dts/amlogic/mesontxl.dtsi @@ -645,6 +645,14 @@ 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"; @@ -1186,6 +1194,20 @@ 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 { diff --git a/arch/arm/boot/dts/amlogic/mesontxlx.dtsi b/arch/arm/boot/dts/amlogic/mesontxlx.dtsi index 6399685..3426ea1 100644 --- a/arch/arm/boot/dts/amlogic/mesontxlx.dtsi +++ b/arch/arm/boot/dts/amlogic/mesontxlx.dtsi @@ -840,12 +840,12 @@ 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"; }; @@ -1030,6 +1030,13 @@ }; }; + irblaster_pins1:irblaster_pin1 { + mux { + groups = "remote_out_ao6"; + function = "ir_out"; + }; + }; + pwmleds_pins:pwmleds { mux { diff --git a/arch/arm/configs/meson64_a32_defconfig b/arch/arm/configs/meson64_a32_defconfig index f40ccea..10c4c29 100644 --- a/arch/arm/configs/meson64_a32_defconfig +++ b/arch/arm/configs/meson64_a32_defconfig @@ -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 diff --git a/arch/arm64/boot/dts/amlogic/axg_s400_v03gva.dts b/arch/arm64/boot/dts/amlogic/axg_s400_v03gva.dts index f805271..a2e2f41 100644 --- a/arch/arm64/boot/dts/amlogic/axg_s400_v03gva.dts +++ b/arch/arm64/boot/dts/amlogic/axg_s400_v03gva.dts @@ -421,14 +421,6 @@ 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"; diff --git a/arch/arm64/boot/dts/amlogic/axg_s410.dts b/arch/arm64/boot/dts/amlogic/axg_s410.dts index e33be01..0e16ced 100644 --- a/arch/arm64/boot/dts/amlogic/axg_s410.dts +++ b/arch/arm64/boot/dts/amlogic/axg_s410.dts @@ -414,14 +414,6 @@ 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"; diff --git a/arch/arm64/boot/dts/amlogic/axg_s420_v03gva.dts b/arch/arm64/boot/dts/amlogic/axg_s420_v03gva.dts index 6f3198e..08207f9 100644 --- a/arch/arm64/boot/dts/amlogic/axg_s420_v03gva.dts +++ b/arch/arm64/boot/dts/amlogic/axg_s420_v03gva.dts @@ -316,14 +316,6 @@ 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"; diff --git a/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi b/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi index b3cd366..27d1dbf 100644 --- a/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi @@ -474,6 +474,16 @@ 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 { @@ -749,16 +759,6 @@ }; }; - 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"; diff --git a/arch/arm64/boot/dts/amlogic/mesong12a.dtsi b/arch/arm64/boot/dts/amlogic/mesong12a.dtsi index 2257e6d..6a9dc7b 100644 --- a/arch/arm64/boot/dts/amlogic/mesong12a.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesong12a.dtsi @@ -746,6 +746,17 @@ 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 = ; + status = "disabled"; + }; };/* end of aobus */ periphs: periphs@ff634400 { @@ -1323,15 +1334,6 @@ 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"; diff --git a/arch/arm64/boot/dts/amlogic/mesong12b.dtsi b/arch/arm64/boot/dts/amlogic/mesong12b.dtsi index afbf50e..a979075 100644 --- a/arch/arm64/boot/dts/amlogic/mesong12b.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesong12b.dtsi @@ -802,6 +802,15 @@ 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 = ; + status = "disabled"; + }; };/* end of aobus */ periphs: periphs@ff634400 { @@ -1424,15 +1433,6 @@ 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"; diff --git a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi index 389db76..0287b0c 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi @@ -509,6 +509,14 @@ 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 { @@ -622,6 +630,19 @@ }; }; + 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 { diff --git a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi index 049541f..44a390a 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi @@ -629,6 +629,14 @@ 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 { @@ -734,6 +742,20 @@ 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 { diff --git a/arch/arm64/boot/dts/amlogic/mesontl1.dtsi b/arch/arm64/boot/dts/amlogic/mesontl1.dtsi index c2adbd6..e2bbeb5 100644 --- a/arch/arm64/boot/dts/amlogic/mesontl1.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesontl1.dtsi @@ -878,11 +878,12 @@ 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 = ; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/amlogic/mesontxl.dtsi b/arch/arm64/boot/dts/amlogic/mesontxl.dtsi index 55092f2..ebdeac0 100644 --- a/arch/arm64/boot/dts/amlogic/mesontxl.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesontxl.dtsi @@ -645,6 +645,14 @@ 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"; @@ -1186,6 +1194,20 @@ 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 { diff --git a/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi b/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi index d22214f..2ff87a5 100644 --- a/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi @@ -840,15 +840,14 @@ 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"; @@ -1030,6 +1029,13 @@ }; }; + irblaster_pins1:irblaster_pin1 { + mux { + groups = "remote_out_ao6"; + function = "ir_out"; + }; + }; + pwmleds_pins:pwmleds { mux { diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index bb52895..bbbd04e 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -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 diff --git a/drivers/amlogic/Makefile b/drivers/amlogic/Makefile index f060cfbb..0d79615 100644 --- a/drivers/amlogic/Makefile +++ b/drivers/amlogic/Makefile @@ -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/ diff --git a/drivers/amlogic/irblaster/Kconfig b/drivers/amlogic/irblaster/Kconfig index 6c35ae8..6e35faa 100644 --- a/drivers/amlogic/irblaster/Kconfig +++ b/drivers/amlogic/irblaster/Kconfig @@ -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 diff --git a/drivers/amlogic/irblaster/Makefile b/drivers/amlogic/irblaster/Makefile index c406f7a..22c05b0 100644 --- a/drivers/amlogic/irblaster/Makefile +++ b/drivers/amlogic/irblaster/Makefile @@ -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 index 0000000..79c7d27 --- /dev/null +++ b/drivers/amlogic/irblaster/aml-irblaster.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..dfc5b78 --- /dev/null +++ b/drivers/amlogic/irblaster/core.c @@ -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 +#include +#include +#include +#include +#include +#include + +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 index 0000000..757fa18 --- /dev/null +++ b/drivers/amlogic/irblaster/encoder.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +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 index 0000000..41a7f52 --- /dev/null +++ b/drivers/amlogic/irblaster/irblaster-meson.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Amlogic AO_IR_BLASTER_ADDR0 bits */ +#define BLASTER_BUSY BIT(26) +#define BLASTER_FIFO_FULL BIT(25) +#define BLASTER_FIFO_EMPTY BIT(24) +#define BLASTER_FIFO_LEVEL (0xff << 16) +#define BLASTER_MODULATOR_TB_SYSTEM_CLOCK (0x0 << 12) +#define BLASTER_MODULATOR_TB_XTAL3_TICK (0x1 << 12) +#define BLASTER_MODULATOR_TB_1US_TICK (0x2 << 12) +#define BLASTER_MODULATOR_TB_10US_TICK (0x3 << 12) +#define BLASTER_SLOW_CLOCK_DIV (0xff << 4) +#define BLASTER_SLOW_CLOCK_MODE BIT(3) +#define BLASTER_INIT_HIGH BIT(2) +#define BLASTER_INIT_LOW BIT(1) +#define BLASTER_ENABLE BIT(0) + +/* Amlogic AO_IR_BLASTER_ADDR1 bits */ +#define BLASTER_MODULATION_LOW_COUNT(c) ((c) << 16) +#define BLASTER_MODULATION_HIGH_COUNT(c) ((c) << 0) + +/* Amlogic AO_IR_BLASTER_ADDR2 bits */ +#define BLASTER_WRITE_FIFO BIT(16) +#define BLASTER_MODULATION_ENABLE BIT(12) +#define BLASTER_TIMEBASE_1US (0x0 << 10) +#define BLASTER_TIMEBASE_10US (0x1 << 10) +#define BLASTER_TIMEBASE_100US (0x2 << 10) +#define BLASTER_TIMEBASE_MODULATION_CLOCK (0x3 << 10) + +/* Amlogic AO_IR_BLASTER_ADDR3 bits */ +#define BLASTER_FIFO_THD_PENDING BIT(16) +#define BLASTER_FIFO_IRQ_ENABLE BIT(8) +#define BLASTER_FIFO_IRQ_THRESHOLD(c) (((c) & 0xff) << 0) + +#define DEFAULT_CARRIER_FREQ (38000) +#define DEFAULT_DUTY_CYCLE (50) + +#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 index 0000000..107383f --- /dev/null +++ b/drivers/amlogic/irblaster/irblaster-nec-encoder.c @@ -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 +#include + +#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 index 0000000..21068b6 --- /dev/null +++ b/drivers/amlogic/irblaster/irblaster-rca-encoder.c @@ -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 +#include + +#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 index 0000000..650be64 --- /dev/null +++ b/drivers/amlogic/irblaster/sysfs.c @@ -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 +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL +#include +#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 index 0000000..b9cbf43 --- /dev/null +++ b/include/linux/amlogic/irblaster.h @@ -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 +#include +#include +#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL +#include +#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 index 0000000..48f6533 --- /dev/null +++ b/include/linux/amlogic/irblaster_consumer.h @@ -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 +#include +#include +#ifdef CONFIG_AMLOGIC_IRBLASTER_PROTOCOL +#include +#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 index 0000000..db0642f --- /dev/null +++ b/include/linux/amlogic/irblaster_encoder.h @@ -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 +#include +#include + +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 */ -- 2.7.4