diff --git a/MAINTAINERS b/MAINTAINERS index 5fcd9917cdf1..85a81814e1f2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14060,13 +14060,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 @@ -14422,9 +14415,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 38895bcc911d..6969131e354c 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 594190f2d64b..f49e3e48a097 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 15a0de94ab31..afc6d9c11ca5 100644 --- a/arch/arm/boot/dts/amlogic/mesonaxg.dtsi +++ b/arch/arm/boot/dts/amlogic/mesonaxg.dtsi @@ -466,6 +466,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 { @@ -741,16 +751,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 c304dfd5a86d..b8aeaeec1e8d 100644 --- a/arch/arm/boot/dts/amlogic/mesong12a.dtsi +++ b/arch/arm/boot/dts/amlogic/mesong12a.dtsi @@ -744,6 +744,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 { @@ -1327,15 +1338,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 f3eaf402f1ff..b13e3f39a8df 100644 --- a/arch/arm/boot/dts/amlogic/mesong12b.dtsi +++ b/arch/arm/boot/dts/amlogic/mesong12b.dtsi @@ -810,6 +810,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 { @@ -1438,15 +1447,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 a8cda637f231..419d03bbd5f5 100644 --- a/arch/arm/boot/dts/amlogic/mesongxl.dtsi +++ b/arch/arm/boot/dts/amlogic/mesongxl.dtsi @@ -520,6 +520,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 { @@ -632,6 +640,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 e336565a37d6..9c74f5a7953d 100644 --- a/arch/arm/boot/dts/amlogic/mesongxm.dtsi +++ b/arch/arm/boot/dts/amlogic/mesongxm.dtsi @@ -627,6 +627,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 { @@ -732,6 +740,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 b57049c7122f..f9ec2f20488a 100644 --- a/arch/arm/boot/dts/amlogic/mesontl1.dtsi +++ b/arch/arm/boot/dts/amlogic/mesontl1.dtsi @@ -879,11 +879,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/mesontxlx.dtsi b/arch/arm/boot/dts/amlogic/mesontxlx.dtsi index 417cedf1e252..8c7a8c066e3e 100644 --- a/arch/arm/boot/dts/amlogic/mesontxlx.dtsi +++ b/arch/arm/boot/dts/amlogic/mesontxlx.dtsi @@ -838,12 +838,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"; }; @@ -1028,6 +1028,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 948702e23717..f07a46f44ceb 100644 --- a/arch/arm/configs/meson64_a32_defconfig +++ b/arch/arm/configs/meson64_a32_defconfig @@ -363,7 +363,9 @@ CONFIG_AMLOGIC_WIFI=y CONFIG_AMLOGIC_BT_DEVICE=y CONFIG_AMLOGIC_POWER=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 f805271bbd23..a2e2f418e02b 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 new file mode 100644 index 000000000000..0e16cedf2cd1 --- /dev/null +++ b/arch/arm64/boot/dts/amlogic/axg_s410.dts @@ -0,0 +1,1421 @@ +/* + * arch/arm64/boot/dts/amlogic/axg_s410.dts + * + * 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. + * + */ + +/dts-v1/; + +#include "mesonaxg.dtsi" +//#include "mesonaxg_s400-panel.dtsi" +/ { + model = "Amlogic"; + amlogic-dt-id = "axg_s410_1g"; + compatible = "amlogic, axg"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + aliases { + serial0 = &uart_AO; + serial1 = &uart_A; + serial2 = &uart_B; + }; + + memory@00000000 { + device_type = "memory"; + linux,usable-memory = <0x0 0x000000 0x0 0x8000000>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + /* global autoconfigured region for contiguous allocations */ + + secmon_reserved:linux,secmon { + compatible = "shared-dma-pool"; + reusable; + size = <0x0 0x400000>; + alignment = <0x0 0x400000>; + alloc-ranges = <0x0 0x05000000 0x0 0x400000>; + }; + + secos_reserved:linux,secos { + status = "disable"; + compatible = "amlogic, aml_secos_memory"; + reg = <0x0 0x05300000 0x0 0x2000000>; + no-map; + }; + fb_reserved:linux,meson-fb { + //compatible = "amlogic, fb-memory"; + //reg = <0x0 0x3e000000 0x0 0x1f00000>; + compatible = "shared-dma-pool"; + reusable; + size = <0x0 0x2000000>; + alignment = <0x0 0x400000>; + alloc-ranges = <0x0 0x3e000000 0x0 0x2000000>; + }; + }; + mtd_nand { + compatible = "amlogic, aml_mtd_nand"; + dev_name = "mtdnand"; + status = "okay"; + reg = <0x0 0xFFE07800 0x0 0x200>; + interrupts = < 0 34 1 >; + pinctrl-names = "nand_rb_mod","nand_norb_mod", "nand_cs_only"; + pinctrl-0 = <&all_nand_pins>; + pinctrl-1 = <&all_nand_pins>; + pinctrl-2 = <&nand_cs_pins>; + device_id = <0>; + + /*fip/tpl configurations, must be same + * with uboot if bl_mode was set as 1 + * bl_mode: 0 compact mode; 1 descrete mode + * if bl_mode was set as 1, fip configeration will work + */ + bl_mode = <1>; + /*copy count of fip*/ + fip_copies = <4>; + /*size of each fip copy */ + fip_size = <0x200000>; + nand_clk_ctrl = <0xFFE07000>; + plat-names = "bootloader","nandnormal"; + plat-num = <2>; + plat-part-0 = <&bootloader>; + plat-part-1 = <&nandnormal>; + bootloader: bootloader{ + enable_pad ="ce0"; + busy_pad = "rb0"; + timming_mode = "mode5"; + bch_mode = "bch8_1k"; + t_rea = <20>; + t_rhoh = <15>; + chip_num = <1>; + part_num = <0>; + rb_detect = <1>; + }; + nandnormal: nandnormal{ + enable_pad ="ce0"; + busy_pad = "rb0"; + timming_mode = "mode5"; + bch_mode = "bch8_1k"; + plane_mode = "twoplane"; + t_rea = <20>; + t_rhoh = <15>; + chip_num = <2>; + part_num = <3>; + partition = <&nand_partitions>; + rb_detect = <1>; + }; + nand_partitions:nand_partition{ + /* + * if bl_mode is 1, tpl size was generate by + * fip_copies * fip_size which + * will not skip bad when calculating + * the partition size; + * + * if bl_mode is 0, + * tpl partition must be comment out. + */ + tpl{ + offset=<0x0 0x0>; + size=<0x0 0x0>; + }; + misc{ + offset=<0x0 0x0>; + size=<0x0 0x200000>; + }; + recovery{ + offset=<0x0 0x0>; + size=<0x0 0x1000000>; + }; + boot{ + offset=<0x0 0x0>; + size=<0x0 0xF00000>; + }; + system{ + offset=<0x0 0x0>; + size=<0x0 0x3C00000>; + }; + data{ + offset=<0xffffffff 0xffffffff>; + size=<0x0 0x0>; + }; + }; + }; + + + ethmac: ethernet@0xff3f0000 { + compatible = "amlogic, gxbb-eth-dwmac"; + reg = <0x0 0xff3f0000 0x0 0x10000 + 0x0 0xff634540 0x0 0x8>; + interrupts = <0 8 1>; + pinctrl-names = "external_eth_pins"; + pinctrl-0 = <&external_eth_pins>; + mc_val_internal_phy = <0x1800>; + mc_val_external_phy = <0x1621>; + interrupt-names = "macirq"; + clocks = <&clkc CLKID_ETH_CORE>; + clock-names = "ethclk81"; + internal_phy=<0>; + }; + + aml_sensor0: aml-sensor@0 { + compatible = "amlogic, aml-thermal"; + device_name = "thermal"; + #thermal-sensor-cells = <1>; + cooling_devices { + cpufreq_cool_cluster0 { + min_state = <1000000>; + dyn_coeff = <140>; + cluster_id = <0>; + node_name = "cpufreq_cool0"; + device_type = "cpufreq"; + }; + cpucore_cool_cluster0 { + min_state = <1>; + dyn_coeff = <0>; + cluster_id = <0>; + node_name = "cpucore_cool0"; + device_type = "cpucore"; + }; + }; + cpufreq_cool0:cpufreq_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + cpucore_cool0:cpucore_cool0 { + #cooling-cells = <2>; /* min followed by max */ + }; + }; + thermal-zones { + soc_thermal { + polling-delay = <1000>; + polling-delay-passive = <100>; + sustainable-power = <1050>; + + thermal-sensors = <&aml_sensor0 3>; + + trips { + switch_on: trip-point@0 { + temperature = <70000>; + hysteresis = <1000>; + type = "passive"; + }; + control: trip-point@1 { + temperature = <80000>; + hysteresis = <1000>; + type = "passive"; + }; + hot: trip-point@2 { + temperature = <85000>; + hysteresis = <5000>; + type = "hot"; + }; + critical: trip-point@3 { + temperature = <260000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + cpufreq_cooling_map { + trip = <&control>; + cooling-device = <&cpufreq_cool0 0 4>; + contribution = <1024>; + }; + cpucore_cooling_map { + trip = <&control>; + cooling-device = <&cpucore_cool0 0 3>; + contribution = <1024>; + }; + }; + }; + }; + + dwc3: dwc3@ff500000 { + compatible = "synopsys, dwc3"; + status = "okay"; + reg = <0x0 0xff500000 0x0 0x100000>; + interrupts = <0 30 4>; + usb-phy = <&usb2_phy>, <&usb3_phy>; + cpu-type = "gxl"; + clock-src = "usb3.0"; + clocks = <&clkc CLKID_USB_GENERAL>; + clock-names = "dwc_general"; + }; + + usb2_phy: usb2phy@ffe09000 { + compatible = "amlogic, amlogic-new-usb2"; + status = "okay"; + portnum = <4>; + reg = <0x0 0xffe09000 0x0 0x80 + 0x0 0xffd01008 0x0 0x4>; + }; + + usb3_phy: usb3phy@ffe09080 { + compatible = "amlogic, amlogic-new-usb3"; + status = "okay"; + portnum = <0>; + reg = <0x0 0xffe09080 0x0 0x20>; + interrupts = <0 16 4>; + otg = <1>; + gpio-vbus-power = "GPIOAO_5"; + gpios = <&gpio_ao GPIOAO_5 GPIO_ACTIVE_HIGH>; + }; + + dwc2_a { + compatible = "amlogic, dwc2"; + device_name = "dwc2_a"; + reg = <0x0 0xff400000 0x0 0x40000>; + status = "okay"; + interrupts = <0 31 4>; + pl-periph-id = <0>; /** lm name */ + clock-src = "usb0"; /** clock src */ + port-id = <0>; /** ref to mach/usb.h */ + port-type = <2>; /** 0: otg, 1: host, 2: slave */ + port-speed = <0>; /** 0: default, high, 1: full */ + port-config = <0>; /** 0: default */ + /*0:default,1:single,2:incr,3:incr4,4:incr8,5:incr16,6:disable*/ + port-dma = <0>; + port-id-mode = <0>; /** 0: hardware, 1: sw_host, 2: sw_slave*/ + usb-fifo = <728>; + cpu-type = "gxl"; + /** 0: normal, 1: otg+dwc3 host only, 2: otg+dwc3 device only*/ + controller-type = <3>; + phy-reg = <0xffe09000>; + phy-reg-size = <0xa0>; + clocks = <&clkc CLKID_USB_GENERAL + &clkc CLKID_USB1_TO_DDR + &clkc CLKID_USB1>; + clock-names = "usb_general", + "usb1", + "usb1_to_ddr"; + }; + + pcie_A: pcieA@f9800000 { + compatible = "amlogic, amlogic-pcie", "snps,dw-pcie"; + reg = <0x0 0xf9800000 0x0 0x400000 + 0x0 0xff646000 0x0 0x2000 + 0x0 0xf9f00000 0x0 0x100000 + 0x0 PCIE_PHY_REG 0x0 PCIE_PHY_SIZE + 0x0 PCIE_RESET_REG 0x0 PCIE_RESET_SIZE>; + reg-names = "elbi", "cfg", "config", "phy", "reset"; + reset-gpio = <&gpio GPIOX_19 GPIO_ACTIVE_HIGH>; + interrupts = <0 177 0>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &gic GIC_SPI 179 IRQ_TYPE_EDGE_RISING>; + bus-range = <0x0 0xff>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x82000000 0 0 0x0 0xf9c00000 0 0x00300000>; + /* non-prefetchable memory */ + num-lanes = <1>; + pcie-num = <1>; + + clocks = <&clkc CLKID_USB_GENERAL + &clkc CLKID_PCIE_PLL + &clkc CLKID_MIPI_ENABLE_GATE + &clkc CLKID_MIPI_BANDGAP_GATE + &clkc CLKID_PCIE_A + &clkc CLKID_PCIE_CML_EN0>; + clock-names = "pcie_general", + "pcie_refpll", + "pcie_mipi_enable_gate", + "pcie_mipi_bandgap_gate", + "pcie", + "port"; + /*reset-gpio-type 0:Shared pad(no reset)1:OD pad2:Normal pad*/ + gpio-type = <2>; + status = "disable"; + }; + + pcie_B: pcieB@fa000000 { + compatible = "amlogic, amlogic-pcie", "snps,dw-pcie"; + reg = <0x0 0xfa000000 0x0 0x400000 + 0x0 0xff648000 0x0 0x2000 + 0x0 0xfa400000 0x0 0x100000 + 0x0 PCIE_PHY_REG 0x0 PCIE_PHY_SIZE + 0x0 PCIE_RESET_REG 0x0 PCIE_RESET_SIZE>; + reg-names = "elbi", "cfg", "config", "phy", "reset"; + reset-gpio = <&gpio GPIOZ_10 GPIO_ACTIVE_HIGH>; + interrupts = <0 167 0>; + #interrupt-cells = <1>; + bus-range = <0x0 0xff>; + #address-cells = <3>; + #size-cells = <2>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &gic GIC_SPI 169 IRQ_TYPE_EDGE_RISING>; + device_type = "pci"; + ranges = <0x81000000 0 0 0 0xfa500000 0x0 0x10000 + /* downstream I/O */ + 0x82000000 0 0xfa510000 0x0 0xfa510000 0 0x002f0000>; + /* non-prefetchable memory */ + num-lanes = <1>; + pcie-num = <2>; + + clocks = <&clkc CLKID_USB_GENERAL + &clkc CLKID_PCIE_PLL + &clkc CLKID_MIPI_ENABLE_GATE + &clkc CLKID_MIPI_BANDGAP_GATE + &clkc CLKID_PCIE_B + &clkc CLKID_PCIE_CML_EN1>; + clock-names = "pcie_general", + "pcie_refpll", + "pcie_mipi_enable_gate", + "pcie_mipi_bandgap_gate", + "pcie", + "port"; + /*reset-gpio-type 0:Shared pad(no reset)1:OD pad2:Normal pad*/ + gpio-type = <1>; + status = "disable"; + }; + + + uart_A: serial@ffd24000 { + compatible = "amlogic, meson-uart"; + reg = <0x0 0xffd24000 0x0 0x18>; + interrupts = <0 26 1>; + status = "okay"; + clocks = <&xtal + &clkc CLKID_UART0>; + clock-names = "clk_uart", + "clk_gate"; + fifosize = < 128 >; + pinctrl-names = "default"; + pinctrl-0 = <&a_uart_pins>; + }; + + uart_B: serial@ffd23000 { + compatible = "amlogic, meson-uart"; + reg = <0x0 0xffd23000 0x0 0x18>; + interrupts = <0 75 1>; + status = "okay"; + clocks = <&xtal + &clkc CLKID_UART1>; + clock-names = "clk_uart", + "clk_gate"; + fifosize = < 64 >; + pinctrl-names = "default"; + pinctrl-0 = <&b_uart_pins>; + }; + + vpu { + compatible = "amlogic, vpu-axg"; + dev_name = "vpu"; + status = "okay"; + clocks = <&clkc CLKID_VAPB_MUX>, + <&clkc CLKID_VPU_INTR>, + <&clkc CLKID_VPU_P0_COMP>, + <&clkc CLKID_VPU_P1_COMP>, + <&clkc CLKID_VPU_MUX>; + clock-names = "vapb_clk", + "vpu_intr_gate", + "vpu_clk0", + "vpu_clk1", + "vpu_clk"; + clk_level = <3>; + /* 0: 100.0M 1: 166.7M 2: 200.0M 3: 250.0M */ + }; + + vout { + compatible = "amlogic, vout"; + dev_name = "vout"; + status = "disable"; + }; + + /* Audio Related start */ + /* Sound iomap */ + aml_snd_iomap { + compatible = "amlogic, snd-iomap"; + status = "okay"; + #address-cells=<2>; + #size-cells=<2>; + ranges; + pdm_bus { + reg = <0x0 0xFF632000 0x0 0x2000>; + }; + audiobus_base { + reg = <0x0 0xFF642000 0x0 0x2000>; + }; + audiolocker_base { + reg = <0x0 0xFF64A000 0x0 0x2000>; + }; + }; + pdm_codec:dummy{ + #sound-dai-cells = <0>; + compatible = "amlogic, pdm_dummy_codec"; + status = "okay"; + }; + dummy_codec:dummy{ + #sound-dai-cells = <0>; + compatible = "amlogic, aml_dummy_codec"; + status = "okay"; + }; + + auge_sound { + compatible = "amlogic, axg-sound-card"; + aml-audio-card,name = "AML-AUGESOUND"; + + aml-audio-card,loopback = <&aml_loopback>; + + aml-audio-card,dai-link@0 { + format = "i2s"; + mclk-fs = <256>; + //continuous-clock; + //bitclock-inversion; + //frame-inversion; + bitclock-master = <&aml_tdma>; + frame-master = <&aml_tdma>; + suffix-name = "alsaPORT-pcm"; + cpu{ + sound-dai = <&aml_tdma>; + dai-tdm-slot-tx-mask = <1 1>; + dai-tdm-slot-rx-mask = <1 1>; + dai-tdm-slot-num = <2>; + dai-tdm-slot-width = <32>; + system-clock-frequency = <12288000>; + }; + codec { + sound-dai = <&dummy_codec &dummy_codec>; + }; + }; + + aml-audio-card,dai-link@1 { + format = "i2s"; + mclk-fs = <256>; + //continuous-clock; + //bitclock-inversion; + //frame-inversion; + bitclock-master = <&tdmbcodec>; + frame-master = <&tdmbcodec>; + suffix-name = "alsaPORT-i2sCapture"; + cpu { + sound-dai = <&aml_tdmb>; + dai-tdm-slot-tx-mask = <1 1>; + dai-tdm-slot-rx-mask = <1 1>; + dai-tdm-slot-num = <2>; + dai-tdm-slot-width = <32>; + system-clock-frequency = <12288000>; + }; + tdmbcodec:codec { + /* + * prefix-names = "3101_A", "3101_B", + * "3101_C", "3101_D"; + * sound-dai = <&tlv320adc3101_32 + * &tlv320adc3101_30 + * &tlv320adc3101_34 + * &tlv320adc3101_36>; + */ + sound-dai = <&dummy_codec>; + }; + }; + + aml-audio-card,dai-link@2 { + format = "i2s"; + mclk-fs = <256>; + //continuous-clock; + //bitclock-inversion; + //frame-inversion; + //bitclock-master = <&aml_tdmc>; + //frame-master = <&aml_tdmc>; + suffix-name = "alsaPORT-i2sPlayback"; + cpu { + sound-dai = <&aml_tdmc>; + dai-tdm-slot-tx-mask = <1 1>; + dai-tdm-slot-rx-mask = <1 1>; + dai-tdm-slot-num = <2>; + dai-tdm-slot-width = <32>; + system-clock-frequency = <12288000>; + }; + codec { + prefix-names = "5707_A", "5707_B"; + sound-dai = <&tas5707_36 &tas5707_3a + &dummy_codec>; + }; + }; + + aml-audio-card,dai-link@3 { + mclk-fs = <256>; + cpu { + sound-dai = <&aml_pdm>; + }; + codec { + /* + * enable external loopback + * and tlv320adc3101 as loopback + */ + /*sound-dai = <&pdm_codec &tlv320adc3101_32>;*/ + /* + * enable internal loopback + * or disable loopback + */ + sound-dai = <&pdm_codec>; + }; + }; + + aml-audio-card,dai-link@4 { + mclk-fs = <128>; + suffix-name = "alsaPORT-spdif"; + cpu { + sound-dai = <&aml_spdif>; + system-clock-frequency = <6144000>; + }; + codec { + sound-dai = <&dummy_codec>; + }; + }; + }; + audiolocker: locker { + compatible = "amlogic, audiolocker"; + clocks = <&clkaudio CLKID_AUDIO_LOCKER_OUT + &clkaudio CLKID_AUDIO_LOCKER_IN + &clkaudio CLKID_AUDIO_MCLK_D + &clkaudio CLKID_AUDIO_MCLK_E + &clkc CLKID_MPLL1 + &clkc CLKID_MPLL2>; + clock-names = "lock_out", "lock_in", "out_src", + "in_src", "out_calc", "in_ref"; + interrupts = ; + interrupt-names = "irq"; + frequency = <49000000>; /* pll */ + dividor = <49>; /* locker's parent */ + status = "okay"; + }; + /* Audio Related end */ + + bt-dev{ + compatible = "amlogic, bt-dev"; + dev_name = "bt-dev"; + status = "disable"; + gpio_reset = <&gpio GPIOX_21 GPIO_ACTIVE_HIGH>; + }; + + wifi{ + compatible = "amlogic, aml_wifi"; + dev_name = "aml_wifi"; + status = "disable"; + interrupt_pin = <&gpio GPIOX_6 GPIO_ACTIVE_HIGH>; + interrupts = < 0 67 4>; + irq_trigger_type = "GPIO_IRQ_LOW"; + power_on_pin2 = <&gpio GPIOX_16 GPIO_ACTIVE_HIGH>; + power_on_pin = <&gpio GPIOX_7 GPIO_ACTIVE_HIGH>; + dhd_static_buf; //if use bcm wifi, config dhd_static_buf + pinctrl-names = "default"; + pinctrl-0 = <&wifi_32k_pins>; + pwm_config = <&wifi_pwm_conf>; + }; + + wifi_pwm_conf:wifi_pwm_conf{ + pwm_channel1_conf { + pwms = <&pwm_ab MESON_PWM_0 30040 0>; + duty-cycle = <15020>; + times = <10>; + }; + pwm_channel2_conf { + pwms = <&pwm_ab MESON_PWM_2 30030 0>; + duty-cycle = <15015>; + times = <12>; + }; + }; + + sd_emmc_c: emmc@ffe07000 { + status = "disabled"; + compatible = "amlogic, meson-mmc-axg"; + reg = <0x0 0xffe07000 0x0 0x2000>; + interrupts = <0 218 1>; + pinctrl-names = "emmc_clk_cmd_pins", "emmc_all_pins"; + pinctrl-0 = <&emmc_clk_cmd_pins>; + pinctrl-1 = <&emmc_conf_pull_up &emmc_conf_pull_done>; + clocks = <&clkc CLKID_SD_EMMC_C>, + <&clkc CLKID_SD_EMMC_C_P0_COMP>, + <&clkc CLKID_FCLK_DIV2>, + <&clkc CLKID_FCLK_DIV5>, + <&xtal>; + clock-names = "core", "clkin0", "clkin1", "clkin2", "xtal"; + + bus-width = <8>; + cap-sd-highspeed; + cap-mmc-highspeed; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + + max-frequency = <200000000>; + non-removable; + disable-wp; + emmc { + pinname = "emmc"; + ocr_avail = <0x200080>; /**VDD voltage 3.3 ~ 3.4 */ + caps = "MMC_CAP_8_BIT_DATA", + "MMC_CAP_MMC_HIGHSPEED", + "MMC_CAP_SD_HIGHSPEED", + "MMC_CAP_NONREMOVABLE", + "MMC_CAP_1_8V_DDR", + "MMC_CAP_HW_RESET", + "MMC_CAP_ERASE", + "MMC_CAP_CMD23"; + caps2 = "MMC_CAP2_HS200", "MMC_CAP2_HS400"; + f_min = <400000>; + f_max = <200000000>; + max_req_size = <0x20000>; /**128KB*/ + gpio_dat3 = <&gpio BOOT_3 GPIO_ACTIVE_HIGH>; + tx_delay = <8>; + hw_reset = <&gpio BOOT_9 GPIO_ACTIVE_HIGH>; + card_type = <1>; + /* 1:mmc card(include eMMC), + * 2:sd card(include tSD) + */ + }; + }; + + sd_emmc_b:sdio@ffe05000 { + status = "okay"; + compatible = "amlogic, meson-mmc-axg"; + reg = <0x0 0xffe05000 0x0 0x2000>; + interrupts = <0 217 4>; + pinctrl-names = "sdio_clk_cmd_pins", "sdio_all_pins"; + pinctrl-0 = <&sdio_clk_cmd_pins>; + pinctrl-1 = <&sdio_all_pins>; + clocks = <&clkc CLKID_SD_EMMC_B>, + <&clkc CLKID_SD_EMMC_B_P0_COMP>, + <&clkc CLKID_FCLK_DIV2>, + <&clkc CLKID_FCLK_DIV5>, + <&xtal>; + clock-names = "core", "clkin0", "clkin1", "clkin2", "xtal"; + + bus-width = <4>; + cap-sd-highspeed; + cap-mmc-highspeed; + max-frequency = <100000000>; + non-removable; + disable-wp; + sdio { + pinname = "sdio"; + ocr_avail = <0x200080>; /**VDD voltage 3.3 ~ 3.4 */ + caps = "MMC_CAP_4_BIT_DATA", + "MMC_CAP_MMC_HIGHSPEED", + "MMC_CAP_SD_HIGHSPEED", + "MMC_CAP_NONREMOVABLE", + "MMC_CAP_UHS_SDR12", + "MMC_CAP_UHS_SDR25", + "MMC_CAP_UHS_SDR50", + "MMC_CAP_UHS_SDR104", + "MMC_PM_KEEP_POWER", + "MMC_CAP_SDIO_IRQ"; + f_min = <400000>; + f_max = <200000000>; + max_req_size = <0x20000>; /**128KB*/ + card_type = <3>; + /* 3:sdio device(ie:sdio-wifi), + * 4:SD combo (IO+mem) card + */ + }; + }; + + partitions: partitions{ + parts = <11>; + part-0 = <&logo>; + part-1 = <&recovery>; + part-2 = <&rsv>; + part-3 = <&tee>; + part-4 = <&crypt>; + part-5 = <&misc>; + part-6 = <&instaboot>; + part-7 = <&boot>; + part-8 = <&system>; + part-9 = <&cache>; + part-10 = <&data>; + + logo:logo{ + pname = "logo"; + size = <0x0 0x2000000>; + mask = <1>; + }; + recovery:recovery{ + pname = "recovery"; + size = <0x0 0x2000000>; + mask = <1>; + }; + rsv:rsv{ + pname = "rsv"; + size = <0x0 0x800000>; + mask = <1>; + }; + tee:tee{ + pname = "tee"; + size = <0x0 0x800000>; + mask = <1>; + }; + crypt:crypt{ + pname = "crypt"; + size = <0x0 0x2000000>; + mask = <1>; + }; + misc:misc{ + pname = "misc"; + size = <0x0 0x2000000>; + mask = <1>; + }; + instaboot:instaboot{ + pname = "instaboot"; + size = <0x0 0x400000>; + mask = <1>; + }; + boot:boot + { + pname = "boot"; + size = <0x0 0x2000000>; + mask = <1>; + }; + system:system + { + pname = "system"; + size = <0x0 0x80000000>; + mask = <1>; + }; + cache:cache + { + pname = "cache"; + size = <0x0 0x20000000>; + mask = <2>; + }; + data:data + { + pname = "data"; + size = <0xffffffff 0xffffffff>; + mask = <4>; + }; + }; + + meson-fb { + compatible = "amlogic, meson-fb"; + memory-region = <&fb_reserved>; + dev_name = "meson-fb"; + status = "disable"; + interrupts = <0 3 1 + 0 89 1>; + interrupt-names = "viu-vsync", "rdma"; + mem_size = <0x00300000 0x1800000 0x00000000>; + /* uboot logo,fb0/fb1 memory size */ + display_mode_default = "1080p60hz"; + scale_mode = <0>; + /** 0:VPU free scale 1:OSD free scale 2:OSD super scale */ + display_size_default = <768 1024 768 2048 32>; + /*768*1024*4*2 = 0x600000*/ + mem_alloc = <1>; + logo_addr = "0x3e000000"; + pxp_mode = <0>; /** 0:normal mode 1:pxp mode */ + }; + + ge2d { + compatible = "amlogic, ge2d"; + dev_name = "ge2d"; + status = "disable"; + interrupts = <0 150 1>; + interrupt-names = "ge2d"; + clocks = <&clkc CLKID_VAPB_MUX>, + <&clkc CLKID_GE2D_GATE>, + <&clkc CLKID_G2D>; + clock-names = "clk_vapb_0", + "clk_ge2d", + "clk_ge2d_gate"; + reg = <0x0 0xff940000 0x0 0x10000>; + }; + + adc_keypad { + compatible = "amlogic, adc_keypad"; + status = "disable"; + key_name = "power", "vol-", "vol+", "wifi", "<<", ">>"; + key_num = <6>; + io-channels = <&saradc SARADC_CH0>; + io-channel-names = "key-chan-0"; + key_chan = ; + key_code = <116 114 115 139 105 106>; + key_val = <0 143 266 389 512 635>; //val=voltage/1800mV*1023 + key_tolerance = <40 40 40 40 40 40>; + }; + + unifykey{ + compatible = "amlogic, unifykey"; + status = "disable"; + + unifykey-num = <6>; + unifykey-index-0 = <&keysn_0>; + unifykey-index-1 = <&keysn_1>; + unifykey-index-2 = <&keysn_2>; + unifykey-index-3 = <&keysn_3>; + unifykey-index-4 = <&keysn_4>; + unifykey-index-5 = <&keysn_5>; + + keysn_0: key_0{ + key-name = "usid"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_1:key_1{ + key-name = "mac"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_2:key_2{ + key-name = "secure_boot_set"; + key-device = "efuse"; + key-permit = "write"; + }; + keysn_3:key_3{ + key-name = "mac_bt"; + key-device = "normal"; + key-permit = "read","write","del"; + key-type = "mac"; + }; + keysn_4:key_4{ + key-name = "mac_wifi"; + key-device = "normal"; + key-permit = "read","write","del"; + key-type = "mac"; + }; + keysn_5:key_5{ + key-name = "deviceid"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + };//End unifykey + +}; /* end of / */ +&efuse { + status = "ok"; +}; + +&pwm_ab { + status = "disable"; +}; + +/* Audio Related start */ +/* for spk board */ +&i2c1 { + status = "okay"; + pinctrl-names="default"; + pinctrl-0=<&b_i2c_master>; + + tlv320adc3101_32: tlv320adc3101_32@32 { + compatible = "ti,tlv320adc3101"; + #sound-dai-cells = <0>; + reg = <0x19>; + differential_pair = <1>; + status = "okay"; + }; + + tas5707_36: tas5707_36@36 { + compatible = "ti,tas5707"; + #sound-dai-cells = <0>; + reg = <0x1b>; + status = "okay"; + reset_pin = <&gpio_ao GPIOAO_4 0>; + }; + + tas5707_3a: tas5707_3a@3a { + compatible = "ti,tas5707"; + #sound-dai-cells = <0>; + reg = <0x1d>; + status = "okay"; + }; +}; + +/* for mic board */ +&i2c_AO { + status = "okay"; + pinctrl-names="default"; + pinctrl-0=<&ao_i2c_master_pin2>; + + aml_pca9557: aml_pca9557@0x1f { + compatible = "aml, ledring"; + reg = <0x1f>; + mode = <0>; /*0: 6-led 1: 4key+2led */ + key_num = <4>; + led_dev_name = "aml_ledring"; + key_dev_name = "aml_pca_key"; + key_name = "mute", "pause", "vol+", "vol-"; + key_value = <200 201 202 203>; + status = "okay"; + }; + + tlv320adc3101_30: tlv320adc3101_30@30 { + compatible = "ti,tlv320adc3101"; + #sound-dai-cells = <0>; + reg = <0x18>; + status = "disable"; + }; + tlv320adc3101_34: tlv320adc3101_34@30 { + compatible = "ti,tlv320adc3101"; + #sound-dai-cells = <0>; + reg = <0x1a>; + status = "disable"; + }; + tlv320adc3101_36: tlv320adc3101_36@30 { + compatible = "ti,tlv320adc3101"; + #sound-dai-cells = <0>; + reg = <0x1b>; + status = "disable"; + }; + es7243_10: es7243_10@10 { + compatible = "MicArray_0"; + #sound-dai-cells = <0>; + reg = <0x10>; + status = "disable"; + }; + es7243_12: es7243_12@12 { + compatible = "MicArray_1"; + #sound-dai-cells = <0>; + reg = <0x12>; + status = "disable"; + }; + es7243_13: es7243_13@13 { + compatible = "MicArray_2"; + #sound-dai-cells = <0>; + reg = <0x13>; + status = "disable"; + }; + is31fl3236a: is31f3236a@0x78 { + compatible = "issi,is31fl3236"; + reg = <0x3c>; + status = "disable"; + led1_r { + label="LED1_R"; + reg_offset = <24>; + }; + led1_g { + label="LED1_G"; + reg_offset = <23>; + }; + led1_b { + label="LED1_B"; + reg_offset = <22>; + }; + led2_r { + label="LED2_R"; + reg_offset = <21>; + }; + led2_g { + label="LED2_G"; + reg_offset = <20>; + }; + led2_b { + label="LED2_B"; + reg_offset = <19>; + }; + led3_r { + label="LED3_R"; + reg_offset = <18>; + }; + led3_g { + label="LED3_G"; + reg_offset = <17>; + }; + led3_b { + label="LED3_B"; + reg_offset = <16>; + }; + led4_r { + label="LED4_R"; + reg_offset = <15>; + }; + led4_g { + label="LED4_G"; + reg_offset = <14>; + }; + led4_b { + label="LED4_B"; + reg_offset = <13>; + }; + led5_r { + label="LED5_R"; + reg_offset = <36>; + }; + led5_g { + label="LED5_G"; + reg_offset = <35>; + }; + led5_b { + label="LED5_B"; + reg_offset = <34>; + }; + led6_r { + label="LED6_R"; + reg_offset = <33>; + }; + led6_g { + label="LED6_G"; + reg_offset = <32>; + }; + led6_b { + label="LED6_B"; + reg_offset = <31>; + }; + led7_r { + label="LED7_R"; + reg_offset = <30>; + }; + led7_g { + label="LED7_G"; + reg_offset = <29>; + }; + led7_b { + label="LED7_B"; + reg_offset = <28>; + }; + led8_r { + label="LED8_R"; + reg_offset = <27>; + }; + led8_g { + label="LED8_G"; + reg_offset = <26>; + }; + led8_b { + label="LED8_B"; + reg_offset = <25>; + }; + }; +}; + +&audiobus { + aml_tdma: tdma { + compatible = "amlogic, axg-snd-tdma"; + #sound-dai-cells = <0>; + dai-tdm-lane-slot-mask-in = <1 1>; + dai-tdm-lane-slot-mask-out = <0 0>; + dai-tdm-clk-sel = <0>; + clocks = <&clkaudio CLKID_AUDIO_MCLK_A + &clkc CLKID_MPLL0>; + clock-names = "mclk", "clk_srcpll"; + pinctrl-names = "tdm_pins"; + pinctrl-0 = <&tdmout_a &tdmin_a>; + }; + + aml_tdmb: tdmb { + compatible = "amlogic, axg-snd-tdmb"; + #sound-dai-cells = <0>; + dai-tdm-lane-slot-mask-in = <1 1 1 1>; + dai-tdm-lane-slot-mask-out = <0 0 0 0>; + dai-tdm-clk-sel = <1>; + clocks = <&clkaudio CLKID_AUDIO_MCLK_B + &clkc CLKID_MPLL1>; + clock-names = "mclk", "clk_srcpll"; + pinctrl-names = "tdm_pins"; + pinctrl-0 = <&tdmin_b>; + }; + + aml_tdmc: tdmc { + compatible = "amlogic, axg-snd-tdmc"; + #sound-dai-cells = <0>; + dai-tdm-lane-slot-mask-in = <0 0 0 0>; + dai-tdm-lane-slot-mask-out = <1 1 1 1>; + dai-tdm-clk-sel = <2>; + clocks = <&clkaudio CLKID_AUDIO_MCLK_C + &clkc CLKID_MPLL2>; + clock-names = "mclk", "clk_srcpll"; + pinctrl-names = "tdm_pins"; + pinctrl-0 = <&tdmc_mclk &tdmout_c>; + }; + + aml_spdif: spdif { + compatible = "amlogic, axg-snd-spdif"; + #sound-dai-cells = <0>; + clocks = <&clkc CLKID_MPLL0 + &clkc CLKID_FCLK_DIV4 + &clkaudio CLKID_AUDIO_SPDIFIN + &clkaudio CLKID_AUDIO_SPDIFOUT + &clkaudio CLKID_AUDIO_SPDIFIN_CTRL + &clkaudio CLKID_AUDIO_SPDIFOUT_CTRL>; + clock-names = "sysclk", "fixed_clk", "gate_spdifin", + "gate_spdifout", "clk_spdifin", "clk_spdifout"; + interrupts = + ; + + interrupt-names = "irq_spdifin"; + pinctrl-names = "spdif_pins"; + pinctrl-0 = <&spdifout &spdifin>; + + /* + * whether do asrc for pcm. + * if raw data, asrc is disabled automatically + * 0: "Disable", + * 1: "Enable:32K", + * 2: "Enable:44K", + * 3: "Enable:48K", + * 4: "Enable:88K", + * 5: "Enable:96K", + * 6: "Enable:176K", + * 7: "Enable:192K", + */ + auto_asrc = <3>; + status = "okay"; + }; + aml_pdm: pdm { + compatible = "amlogic, axg-snd-pdm"; + #sound-dai-cells = <0>; + clocks = <&clkaudio CLKID_AUDIO_PDM + &clkc CLKID_FCLK_DIV3 + &clkc CLKID_MPLL3 + &clkaudio CLKID_AUDIO_PDMIN0 + &clkaudio CLKID_AUDIO_PDMIN1>; + clock-names = "gate", + "sysclk_srcpll", + "dclk_srcpll", + "pdm_dclk", + "pdm_sysclk"; + pinctrl-names = "pdm_pins"; + pinctrl-0 = <&pdmin>; + filter_mode = <1>; /* mode 0~4, defalut:1 */ + status = "okay"; + }; + + aml_loopback: loopback { + compatible = "amlogic, snd-loopback"; + + /* + * external loopback clock config + * enable clk while pdm record data + */ + /*clocks = <&clkc CLKID_MPLL1>;*/ + /*clock-names = "datalb_mpll";*/ + + /* + * 0: out rate = in data rate; + * 1: out rate = loopback data rate; + */ + lb_mode = <0>; + + /* datain src + * 0: tdmin_a; + * 1: tdmin_b; + * 2: tdmin_c; + * 3: spdifin; + * 4: pdmin; + */ + datain_src = <4>; + datain_chnum = <8>; + datain_chmask = <0x7f>; + + /* tdmin_lb src + * 0: tdmoutA + * 1: tdmoutB + * 2: tdmoutC + * 3: PAD_tdminA + * 4: PAD_tdminB + * 5: PAD_tdminC + */ + + /*if tdmin_lb >= 3, use external loopback*/ + datalb_src = <2>; + datalb_chnum = <2>; + /*config which data pin as loopback*/ + /*datalb-lane-mask-in = <0 0 0 1>;*/ + datalb_chmask = <0x1>; + + + status = "okay"; + }; + + audioresample: resample { + compatible = "amlogic, axg-resample"; + clocks = <&clkc CLKID_MPLL3 + &clkaudio CLKID_AUDIO_MCLK_F + &clkaudio CLKID_AUDIO_RESAMPLE_CTRL>; + clock-names = "resample_pll", "resample_src", "resample_clk"; + /*same with toddr_src + * TDMIN_A, + * TDMIN_B, + * TDMIN_C, + * SPDIFIN, + * PDMIN, + * NONE, + * TDMIN_LB, + * LOOPBACK, + */ + resample_module = <3>; + status = "okay"; + }; + aml_pwrdet: pwrdet { + compatible = "amlogic, axg-power-detect"; + + interrupts = ; + interrupt-names = "pwrdet_irq"; + + /* pwrdet source sel + * 7: loopback; + * 6: tdmin_lb; + * 5: reserved; + * 4: pdmin; + * 3: spdifin; + * 2: tdmin_c; + * 1: tdmin_b; + * 0: tdmin_a; + */ + pwrdet_src = <4>; + + hi_th = <0x70000>; + lo_th = <0x16000>; + + status = "disabled"; + }; +}; /* end of audiobus */ + +&pinctrl_periphs { + tdmout_a: tdmout_a { + mux { + groups = "tdma_sclk", + "tdma_fs"; + function = "tdma_out"; + }; + }; + + tdmout_a_data: tdmout_a_data { + mux { + groups = "tdma_dout1_x15"; + function = "tdma_out"; + }; + }; + + tdmin_a: tdmin_a { + mux { + groups = "tdma_din0"; + function = "tdma_in"; + }; + }; + + tdmb_mclk: tdmb_mclk { + mux { + groups = "mclk_b"; + function = "mclk_b"; + }; + }; + + tdmout_b: tdmout_b { + mux { + groups = "tdmb_sclk", + "tdmb_fs"; + function = "tdmb_out"; + }; + }; + // tdmin and tdmout are the same pins. can't use at same time + tdmin_b:tdmin_b { + mux { + groups = + "tdmb_slv_sclk", + "tdmb_slv_fs", + "tdmb_din0", + "tdmb_din1", + "tdmb_din2_a12", + "tdmb_din3_a13"; + function = "tdmb_in"; + }; + }; + + tdmc_mclk: tdmc_mclk { + mux { + groups = "mclk_a"; + function = "mclk_a"; + }; + }; + + tdmout_c:tdmout_c { + mux { + groups = "tdmc_sclk", + "tdmc_fs", + "tdmc_dout0", + "tdmc_dout1", + "tdmc_dout2_a6", + "tdmc_dout3_a7"; + function = "tdmc_out"; + }; + }; + + + tdmin_c:tdmin_c { + mux { + groups = "tdmc_din1"; + function = "tdmc_in"; + }; + }; + + spdifout: spidfout { + mux { + groups = "spdif_out_a20"; + function = "spdif_out"; + }; + }; + + spdifin: spidfin { + mux { + groups = "spdif_in_a19"; + function = "spdif_in"; + }; + }; + + pdmin: pdmin { + mux { + groups = "pdm_dclk_a14", + "pdm_din0", + "pdm_din1", + "pdm_din2", + "pdm_din3"; + function = "pdm"; + }; + }; + + bl_pwm_on_pins: bl_pwm_on_pin { + mux { + groups = "pwm_b_z"; + function = "pwm_b"; + }; + }; +}; /* end of pinctrl_periphs */ +/* Audio Related End */ + +&aobus{ + uart_AO: serial@3000 { + compatible = "amlogic, meson-uart"; + reg = <0x0 0x3000 0x0 0x18>; + interrupts = <0 193 1>; + status = "okay"; + clocks = <&xtal>; + clock-names = "clk_uart"; + xtal_tick_en = <1>; + fifosize = < 64 >; + pinctrl-names = "default"; + pinctrl-0 = <&ao_uart_pins>; + support-sysrq = <0>; /* 0 not support , 1 support */ + }; + + uart_AO_B: serial@4000 { + compatible = "amlogic, meson-uart"; + reg = <0x0 0x4000 0x0 0x18>; + interrupts = <0 197 1>; + status = "disable"; + clocks = <&xtal>; + clock-names = "clk_uart"; + fifosize = < 64 >; + pinctrl-names = "default"; + pinctrl-0 = <&ao_b_uart_pins>; + }; +}; + +&audio_data{ + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/amlogic/axg_s420_v03gva.dts b/arch/arm64/boot/dts/amlogic/axg_s420_v03gva.dts index 6f3198ecf3f0..08207f9e8c83 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 15d6e3785075..ecc71cff7fef 100644 --- a/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesonaxg.dtsi @@ -467,6 +467,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 { @@ -742,16 +752,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 ac2df4a76968..f1d13fdf7c34 100644 --- a/arch/arm64/boot/dts/amlogic/mesong12a.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesong12a.dtsi @@ -754,6 +754,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 { @@ -1337,15 +1348,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 5b92ba775776..86098ac55816 100644 --- a/arch/arm64/boot/dts/amlogic/mesong12b.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesong12b.dtsi @@ -820,6 +820,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 { @@ -1448,15 +1457,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 e464b6bed8a7..aa7b9950261b 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi @@ -507,6 +507,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 { @@ -620,6 +628,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 be68834f1e45..24ba0cc5069a 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi @@ -627,6 +627,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 { @@ -732,6 +740,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/mesontm2.dtsi b/arch/arm64/boot/dts/amlogic/mesontm2.dtsi index 7eeb51c079dd..fd55efc15ec5 100644 --- a/arch/arm64/boot/dts/amlogic/mesontm2.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesontm2.dtsi @@ -900,11 +900,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/mesontxlx.dtsi b/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi index 8b78e147be1c..0b4a3af42277 100644 --- a/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesontxlx.dtsi @@ -838,15 +838,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"; @@ -1028,6 +1027,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 62100c08089c..9fb51faf6dde 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -358,7 +358,9 @@ CONFIG_AMLOGIC_WIFI=y CONFIG_AMLOGIC_BT_DEVICE=y CONFIG_AMLOGIC_POWER=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 4ef2059bbd30..69d054f2b64b 100644 --- a/drivers/amlogic/Makefile +++ b/drivers/amlogic/Makefile @@ -101,7 +101,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 6c35ae80350a..6e35faa39216 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 c406f7a50d1b..22c05b0257b6 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 000000000000..79c7d27b2fb5 --- /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 000000000000..dfc5b7897ff5 --- /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 000000000000..757fa18f8fa8 --- /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 000000000000..41a7f526bce6 --- /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 000000000000..107383ff3729 --- /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 000000000000..21068b608ca0 --- /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 000000000000..650be648b476 --- /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 000000000000..b9cbf432ef5e --- /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 000000000000..48f65330f247 --- /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 000000000000..db0642f0fac4 --- /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 */