8 Commits

38 changed files with 7489 additions and 0 deletions

View File

@@ -0,0 +1,159 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/imx/fsl,mxc-epdc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Freescale i.MX6 EPDC
maintainers:
- Andreas Kemnade <andreas@kemnade.info>
description: |
The EPDC is a controller for handling electronic paper displays found in
i.MX6 SoCs.
properties:
compatible:
enum:
- fsl,imx6sl-epdc
- fsl,imx6sll-epdc
reg:
maxItems: 1
clocks:
items:
- description: Bus clock
- description: Pixel clock
clock-names:
items:
- const: axi
- const: pix
interrupts:
maxItems: 1
vscan-holdoff:
$ref: /schemas/types.yaml#/definitions/uint32
maxItems: 1
sdoed-width:
$ref: /schemas/types.yaml#/definitions/uint32
maxItems: 1
sdoed-delay:
$ref: /schemas/types.yaml#/definitions/uint32
maxItems: 1
sdoez-width:
$ref: /schemas/types.yaml#/definitions/uint32
maxItems: 1
sdoez-delay:
$ref: /schemas/types.yaml#/definitions/uint32
maxItems: 1
gdclk-hp-offs:
$ref: /schemas/types.yaml#/definitions/uint32
maxItems: 1
gdsp-offs:
$ref: /schemas/types.yaml#/definitions/uint32
maxItems: 1
gdoe-offs:
$ref: /schemas/types.yaml#/definitions/uint32
maxItems: 1
gdclk-offs:
$ref: /schemas/types.yaml#/definitions/uint32
maxItems: 1
num-ce:
$ref: /schemas/types.yaml#/definitions/uint32
maxItems: 1
timing:
$ref: /display/panel/panel-timing.yaml#
DISPLAY-supply:
description:
A couple of +/- voltages automatically powered on in a defintive order
VCOM-supply:
description: compensation voltage
V3P3-supply:
description: V3P3 supply
epd-thermal-zone:
description:
Zone to get temperature of the EPD from, practically ambient temperature.
required:
- compatible
- reg
- clocks
- clock-names
- interrupts
- vscan-holdoff
- sdoed-width
- sdoed-delay
- sdoez-width
- sdoez-delay
- gdclk-hp-offs
- gdsp-offs
- gdoe-offs
- gdclk-offs
- num-ce
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/imx6sl-clock.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
epdc: epdc@20f4000 {
compatible = "fsl,imx6sl-epdc";
reg = <0x020f4000 0x4000>;
interrupts = <0 97 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_EPDC_AXI>, <&clks IMX6SL_CLK_EPDC_PIX>;
clock-names = "axi", "pix";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_epdc0>;
V3P3-supply = <&V3P3_reg>;
VCOM-supply = <&VCOM_reg>;
DISPLAY-supply = <&DISPLAY_reg>;
epd-thermal-zone = "epd-thermal";
vscan-holdoff = <4>;
sdoed-width = <10>;
sdoed-delay = <20>;
sdoez-width = <10>;
sdoez-delay = <20>;
gdclk-hp-offs = <562>;
gdsp-offs = <662>;
gdoe-offs = <0>;
gdclk-offs = <225>;
num-ce = <3>;
status = "okay";
timing {
clock-frequency = <80000000>;
hactive = <1448>;
hback-porch = <16>;
hfront-porch = <102>;
hsync-len = <28>;
vactive = <1072>;
vback-porch = <4>;
vfront-porch = <4>;
vsync-len = <2>;
};
};
...

View File

@@ -73,6 +73,38 @@
}; };
}; };
&epdc {
safe-ticks-turnoff-ep3v3=<800>;
V3P3-supply = <&V3P3_reg>;
VCOM-supply = <&VCOM_reg>;
DISPLAY-supply = <&DISPLAY_reg>;
epd-thermal-zone = "epd-thermal";
vscan-holdoff = <4>;
sdoed-width = <10>;
sdoed-delay = <20>;
sdoez-width = <10>;
sdoez-delay = <20>;
gdclk-hp-offs = <562>;
gdsp-offs = <662>;
gdoe-offs = <0>;
gdclk-offs = <225>;
num-ce = <3>;
status = "okay";
timing {
clock-frequency = <80000000>;
hactive = <1448>;
hback-porch = <16>;
hfront-porch = <102>;
hsync-len = <28>;
vactive = <1072>;
vback-porch = <4>;
vfront-porch = <4>;
vsync-len = <2>;
};
};
&i2c1 { &i2c1 {
clock-frequency = <100000>; clock-frequency = <100000>;

View File

@@ -112,6 +112,40 @@
}; };
}; };
&epdc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_epdc0>;
safe-ticks-turnoff-ep3v3 = <800>;
V3P3-supply = <&V3P3_reg>;
VCOM-supply = <&VCOM_reg>;
DISPLAY-supply = <&DISPLAY_reg>;
epd-thermal-zone = "epd-thermal";
vscan-holdoff = <4>;
sdoed-width = <10>;
sdoed-delay = <20>;
sdoez-width = <10>;
sdoez-delay = <20>;
gdclk-hp-offs = <562>;
gdsp-offs = <662>;
gdoe-offs = <0>;
gdclk-offs = <225>;
num-ce = <3>;
status = "okay";
timing {
clock-frequency = <80000000>;
hactive = <1448>;
hback-porch = <16>;
hfront-porch = <102>;
hsync-len = <28>;
vactive = <1072>;
vback-porch = <4>;
vfront-porch = <4>;
vsync-len = <2>;
};
};
&i2c1 { &i2c1 {
pinctrl-names = "default","sleep"; pinctrl-names = "default","sleep";
pinctrl-0 = <&pinctrl_i2c1>; pinctrl-0 = <&pinctrl_i2c1>;
@@ -337,6 +371,36 @@
>; >;
}; };
pinctrl_epdc0: epdcgrp0 {
fsl,pins = <
MX6SL_PAD_EPDC_D0__EPDC_DATA00 0x4100b1
MX6SL_PAD_EPDC_D1__EPDC_DATA01 0x4100b1
MX6SL_PAD_EPDC_D2__EPDC_DATA02 0x4100b1
MX6SL_PAD_EPDC_D3__EPDC_DATA03 0x4100b1
MX6SL_PAD_EPDC_D4__EPDC_DATA04 0x4100b1
MX6SL_PAD_EPDC_D5__EPDC_DATA05 0x4100b1
MX6SL_PAD_EPDC_D6__EPDC_DATA06 0x4100b1
MX6SL_PAD_EPDC_D7__EPDC_DATA07 0x4100b1
MX6SL_PAD_EPDC_D8__EPDC_DATA08 0x4100b1
MX6SL_PAD_EPDC_D9__EPDC_DATA09 0x4100b1
MX6SL_PAD_EPDC_D10__EPDC_DATA10 0x4100b1
MX6SL_PAD_EPDC_D11__EPDC_DATA11 0x4100b1
MX6SL_PAD_EPDC_D12__EPDC_DATA12 0x4100b1
MX6SL_PAD_EPDC_D13__EPDC_DATA13 0x4100b1
MX6SL_PAD_EPDC_D14__EPDC_DATA14 0x4100b1
MX6SL_PAD_EPDC_D15__EPDC_DATA15 0x4100b1
MX6SL_PAD_EPDC_SDCLK__EPDC_SDCLK_P 0x4100b1
MX6SL_PAD_EPDC_SDLE__EPDC_SDLE 0x4100b1
MX6SL_PAD_EPDC_SDOE__EPDC_SDOE 0x4100b1
MX6SL_PAD_EPDC_SDSHR__EPDC_SDSHR 0x4100b1
MX6SL_PAD_EPDC_SDCE0__EPDC_SDCE0 0x4100b1
MX6SL_PAD_EPDC_GDCLK__EPDC_GDCLK 0x4100b1
MX6SL_PAD_EPDC_GDOE__EPDC_GDOE 0x4100b1
MX6SL_PAD_EPDC_GDRL__EPDC_GDRL 0x4100b1
MX6SL_PAD_EPDC_GDSP__EPDC_GDSP 0x4100b1
>;
};
pinctrl_hog: hoggrp { pinctrl_hog: hoggrp {
fsl,pins = < fsl,pins = <
MX6SL_PAD_LCD_DAT0__GPIO2_IO20 0x79 MX6SL_PAD_LCD_DAT0__GPIO2_IO20 0x79

View File

@@ -26,6 +26,11 @@
compatible = "kobo,tolino-shine3", "fsl,imx6sl"; compatible = "kobo,tolino-shine3", "fsl,imx6sl";
}; };
&epdc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_epdc0>;
};
&gpio_keys { &gpio_keys {
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio_keys>; pinctrl-0 = <&pinctrl_gpio_keys>;
@@ -59,6 +64,36 @@
>; >;
}; };
pinctrl_epdc0: epdcgrp0 {
fsl,pins = <
MX6SL_PAD_EPDC_D0__EPDC_DATA00 0x4100b1
MX6SL_PAD_EPDC_D1__EPDC_DATA01 0x4100b1
MX6SL_PAD_EPDC_D2__EPDC_DATA02 0x4100b1
MX6SL_PAD_EPDC_D3__EPDC_DATA03 0x4100b1
MX6SL_PAD_EPDC_D4__EPDC_DATA04 0x4100b1
MX6SL_PAD_EPDC_D5__EPDC_DATA05 0x4100b1
MX6SL_PAD_EPDC_D6__EPDC_DATA06 0x4100b1
MX6SL_PAD_EPDC_D7__EPDC_DATA07 0x4100b1
MX6SL_PAD_EPDC_D8__EPDC_DATA08 0x4100b1
MX6SL_PAD_EPDC_D9__EPDC_DATA09 0x4100b1
MX6SL_PAD_EPDC_D10__EPDC_DATA10 0x4100b1
MX6SL_PAD_EPDC_D11__EPDC_DATA11 0x4100b1
MX6SL_PAD_EPDC_D12__EPDC_DATA12 0x4100b1
MX6SL_PAD_EPDC_D13__EPDC_DATA13 0x4100b1
MX6SL_PAD_EPDC_D14__EPDC_DATA14 0x4100b1
MX6SL_PAD_EPDC_D15__EPDC_DATA15 0x4100b1
MX6SL_PAD_EPDC_SDCLK__EPDC_SDCLK_P 0x4100b1
MX6SL_PAD_EPDC_SDLE__EPDC_SDLE 0x4100b1
MX6SL_PAD_EPDC_SDOE__EPDC_SDOE 0x4100b1
MX6SL_PAD_EPDC_SDSHR__EPDC_SDSHR 0x4100b1
MX6SL_PAD_EPDC_SDCE0__EPDC_SDCE0 0x4100b1
MX6SL_PAD_EPDC_GDCLK__EPDC_GDCLK 0x4100b1
MX6SL_PAD_EPDC_GDOE__EPDC_GDOE 0x4100b1
MX6SL_PAD_EPDC_GDRL__EPDC_GDRL 0x4100b1
MX6SL_PAD_EPDC_GDSP__EPDC_GDSP 0x4100b1
>;
};
pinctrl_gpio_keys: gpio-keysgrp { pinctrl_gpio_keys: gpio-keysgrp {
fsl,pins = < fsl,pins = <
MX6SL_PAD_SD1_DAT1__GPIO5_IO08 0x17059 /* PWR_SW */ MX6SL_PAD_SD1_DAT1__GPIO5_IO08 0x17059 /* PWR_SW */

View File

@@ -771,8 +771,11 @@
}; };
epdc: epdc@20f4000 { epdc: epdc@20f4000 {
compatible = "fsl,imx6sl-epdc", "fsl,imx6dl-epdc";
reg = <0x020f4000 0x4000>; reg = <0x020f4000 0x4000>;
interrupts = <0 97 IRQ_TYPE_LEVEL_HIGH>; interrupts = <0 97 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SL_CLK_EPDC_AXI>, <&clks IMX6SL_CLK_EPDC_PIX>;
clock-names = "epdc_axi", "epdc_pix";
}; };
lcdif: lcdif@20f8000 { lcdif: lcdif@20f8000 {

View File

@@ -36,6 +36,11 @@
soc-supply = <&dcdc1_reg>; soc-supply = <&dcdc1_reg>;
}; };
&epdc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_epdc0>;
};
&gpio_keys { &gpio_keys {
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio_keys>; pinctrl-0 = <&pinctrl_gpio_keys>;
@@ -69,6 +74,36 @@
>; >;
}; };
pinctrl_epdc0: epdcgrp0 {
fsl,pins = <
MX6SLL_PAD_EPDC_DATA00__EPDC_DATA00 0x4100b1
MX6SLL_PAD_EPDC_DATA01__EPDC_DATA01 0x4100b1
MX6SLL_PAD_EPDC_DATA02__EPDC_DATA02 0x4100b1
MX6SLL_PAD_EPDC_DATA03__EPDC_DATA03 0x4100b1
MX6SLL_PAD_EPDC_DATA04__EPDC_DATA04 0x4100b1
MX6SLL_PAD_EPDC_DATA05__EPDC_DATA05 0x4100b1
MX6SLL_PAD_EPDC_DATA06__EPDC_DATA06 0x4100b1
MX6SLL_PAD_EPDC_DATA07__EPDC_DATA07 0x4100b1
MX6SLL_PAD_EPDC_DATA08__EPDC_DATA08 0x4100b1
MX6SLL_PAD_EPDC_DATA09__EPDC_DATA09 0x4100b1
MX6SLL_PAD_EPDC_DATA10__EPDC_DATA10 0x4100b1
MX6SLL_PAD_EPDC_DATA11__EPDC_DATA11 0x4100b1
MX6SLL_PAD_EPDC_DATA12__EPDC_DATA12 0x4100b1
MX6SLL_PAD_EPDC_DATA13__EPDC_DATA13 0x4100b1
MX6SLL_PAD_EPDC_DATA14__EPDC_DATA14 0x4100b1
MX6SLL_PAD_EPDC_DATA15__EPDC_DATA15 0x4100b1
MX6SLL_PAD_EPDC_SDCLK__EPDC_SDCLK_P 0x4100b1
MX6SLL_PAD_EPDC_SDLE__EPDC_SDLE 0x4100b1
MX6SLL_PAD_EPDC_SDOE__EPDC_SDOE 0x4100b1
MX6SLL_PAD_EPDC_SDSHR__EPDC_SDSHR 0x4100b1
MX6SLL_PAD_EPDC_SDCE0__EPDC_SDCE0 0x4100b1
MX6SLL_PAD_EPDC_GDCLK__EPDC_GDCLK 0x4100b1
MX6SLL_PAD_EPDC_GDOE__EPDC_GDOE 0x4100b1
MX6SLL_PAD_EPDC_GDRL__EPDC_GDRL 0x4100b1
MX6SLL_PAD_EPDC_GDSP__EPDC_GDSP 0x4100b1
>;
};
pinctrl_gpio_keys: gpio-keysgrp { pinctrl_gpio_keys: gpio-keysgrp {
fsl,pins = < fsl,pins = <
MX6SLL_PAD_SD1_DATA1__GPIO5_IO08 0x17059 /* PWR_SW */ MX6SLL_PAD_SD1_DATA1__GPIO5_IO08 0x17059 /* PWR_SW */

View File

@@ -656,6 +656,15 @@
clock-names = "axi"; clock-names = "axi";
}; };
epdc: epdc@20f4000 {
compatible = "fsl,imx6sll-epdc", "fsl,imx7d-epdc";
reg = <0x020f4000 0x4000>;
interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6SLL_CLK_EPDC_AXI>, <&clks IMX6SLL_CLK_EPDC_PIX>;
clock-names = "epdc_axi", "epdc_pix";
status = "disabled";
};
lcdif: lcd-controller@20f8000 { lcdif: lcd-controller@20f8000 {
compatible = "fsl,imx6sll-lcdif", "fsl,imx28-lcdif"; compatible = "fsl,imx6sll-lcdif", "fsl,imx28-lcdif";
reg = <0x020f8000 0x4000>; reg = <0x020f8000 0x4000>;

View File

@@ -0,0 +1,494 @@
CONFIG_SYSVIPC=y
CONFIG_NO_HZ_IDLE=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_BPF_SYSCALL=y
CONFIG_PREEMPT_VOLUNTARY=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=18
CONFIG_CGROUPS=y
CONFIG_CGROUP_BPF=y
CONFIG_NAMESPACES=y
CONFIG_RELAY=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_EXPERT=y
CONFIG_PERF_EVENTS=y
CONFIG_KEXEC=y
CONFIG_ARCH_MULTI_V6=y
CONFIG_ARCH_MXC=y
CONFIG_SOC_IMX31=y
CONFIG_SOC_IMX35=y
CONFIG_SOC_IMX50=y
CONFIG_SOC_IMX51=y
CONFIG_SOC_IMX53=y
CONFIG_SOC_IMX6Q=y
CONFIG_SOC_IMX6SL=y
CONFIG_SOC_IMX6SLL=y
CONFIG_SOC_IMX6SX=y
CONFIG_SOC_IMX6UL=y
CONFIG_SOC_IMX7D=y
CONFIG_SOC_IMX7ULP=y
CONFIG_SOC_VF610=y
CONFIG_SMP=y
CONFIG_ARM_PSCI=y
CONFIG_HIGHMEM=y
CONFIG_ARCH_FORCE_MAX_ORDER=13
CONFIG_CMDLINE="noinitrd console=ttymxc0,115200"
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
CONFIG_CPUFREQ_DT=y
CONFIG_ARM_IMX6Q_CPUFREQ=y
CONFIG_ARM_IMX_CPUFREQ_DT=y
CONFIG_CPU_IDLE=y
CONFIG_ARM_CPUIDLE=y
CONFIG_ARM_PSCI_CPUIDLE=y
CONFIG_VFP=y
CONFIG_NEON=y
CONFIG_PM_DEBUG=y
CONFIG_PM_TEST_SUSPEND=y
CONFIG_KPROBES=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y
CONFIG_MODULE_SRCVERSION_ALL=y
CONFIG_BINFMT_MISC=m
# CONFIG_COMPAT_BRK is not set
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_INET=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
CONFIG_NETFILTER=y
CONFIG_CAN=y
CONFIG_BT=y
CONFIG_BT_BNEP=m
CONFIG_BT_HCIUART=y
CONFIG_BT_HCIUART_LL=y
CONFIG_BT_NXPUART=m
CONFIG_CFG80211=y
CONFIG_CFG80211_WEXT=y
CONFIG_MAC80211=y
CONFIG_RFKILL=y
CONFIG_RFKILL_INPUT=y
CONFIG_PCI=y
CONFIG_PCI_MSI=y
CONFIG_PCI_IMX6_HOST=y
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_STANDALONE is not set
CONFIG_EXTRA_FIRMWARE="ath6k/AR6003/hw2.1.1/bdata.SD31.bin ath6k/AR6003/hw2.1.1/athwlan.bin ath6k/AR6003/hw2.1.1/otp.bin ath6k/AR6003/hw2.1.1/data.patch.bin"
CONFIG_IMX_WEIM=y
CONFIG_CONNECTOR=y
CONFIG_MTD=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_CFI=y
CONFIG_MTD_JEDECPROBE=y
CONFIG_MTD_CFI_INTELEXT=y
CONFIG_MTD_CFI_AMDSTD=y
CONFIG_MTD_CFI_STAA=y
CONFIG_MTD_PHYSMAP=y
CONFIG_MTD_PHYSMAP_OF=y
CONFIG_MTD_DATAFLASH=y
CONFIG_MTD_SST25L=y
CONFIG_MTD_RAW_NAND=y
CONFIG_MTD_NAND_GPMI_NAND=y
CONFIG_MTD_NAND_VF610_NFC=y
CONFIG_MTD_NAND_MXC=y
CONFIG_MTD_SPI_NOR=y
CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_FASTMAP=y
CONFIG_MTD_UBI_BLOCK=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=65536
CONFIG_EEPROM_AT24=y
CONFIG_EEPROM_AT25=y
# CONFIG_SCSI_PROC_FS is not set
CONFIG_BLK_DEV_SD=y
# CONFIG_BLK_DEV_BSG is not set
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SCAN_ASYNC=y
CONFIG_ATA=y
CONFIG_SATA_AHCI_PLATFORM=y
CONFIG_AHCI_IMX=y
CONFIG_PATA_IMX=y
CONFIG_NETDEVICES=y
# CONFIG_NET_VENDOR_BROADCOM is not set
CONFIG_CS89x0_PLATFORM=y
# CONFIG_NET_VENDOR_FARADAY is not set
# CONFIG_NET_VENDOR_INTEL is not set
# CONFIG_NET_VENDOR_MARVELL is not set
# CONFIG_NET_VENDOR_MICREL is not set
# CONFIG_NET_VENDOR_MICROCHIP is not set
# CONFIG_NET_VENDOR_NATSEMI is not set
CONFIG_QCA7000_SPI=m
# CONFIG_NET_VENDOR_SEEQ is not set
CONFIG_SMC91X=y
CONFIG_SMSC911X=y
# CONFIG_NET_VENDOR_STMICRO is not set
CONFIG_MICREL_PHY=y
CONFIG_AT803X_PHY=y
CONFIG_DP83867_PHY=y
CONFIG_CAN_FLEXCAN=y
CONFIG_USB_PEGASUS=m
CONFIG_USB_RTL8150=m
CONFIG_USB_RTL8152=y
CONFIG_USB_LAN78XX=y
CONFIG_USB_USBNET=y
CONFIG_USB_NET_CDC_EEM=m
CONFIG_USB_NET_SMSC95XX=y
CONFIG_USB_NET_MCS7830=y
CONFIG_ATH6KL=y
CONFIG_ATH6KL_SDIO=y
CONFIG_ATH10K=m
CONFIG_ATH10K_SDIO=m
CONFIG_BRCMFMAC=m
CONFIG_MWIFIEX=m
CONFIG_MWIFIEX_SDIO=m
CONFIG_MWIFIEX_PCIE=m
CONFIG_WL12XX=m
CONFIG_WL18XX=m
CONFIG_WLCORE_SDIO=m
CONFIG_INPUT_EVDEV=y
CONFIG_KEYBOARD_GPIO=y
CONFIG_KEYBOARD_SNVS_PWRKEY=y
CONFIG_KEYBOARD_IMX=y
CONFIG_MOUSE_PS2=m
CONFIG_MOUSE_PS2_ELANTECH=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_ADS7846=y
CONFIG_TOUCHSCREEN_AD7879=y
CONFIG_TOUCHSCREEN_AD7879_I2C=y
CONFIG_TOUCHSCREEN_ATMEL_MXT=y
CONFIG_TOUCHSCREEN_CYTTSP_CORE=m
CONFIG_TOUCHSCREEN_CYTTSP_I2C=m
CONFIG_TOUCHSCREEN_CYTTSP4_CORE=m
CONFIG_TOUCHSCREEN_CYTTSP4_I2C=m
CONFIG_TOUCHSCREEN_CYTTSP5=y
CONFIG_TOUCHSCREEN_DA9052=y
CONFIG_TOUCHSCREEN_EGALAX=y
CONFIG_TOUCHSCREEN_GOODIX=y
CONFIG_TOUCHSCREEN_ILI210X=y
CONFIG_TOUCHSCREEN_MAX11801=y
CONFIG_TOUCHSCREEN_IMX6UL_TSC=y
CONFIG_TOUCHSCREEN_EDT_FT5X06=y
CONFIG_TOUCHSCREEN_MC13783=y
CONFIG_TOUCHSCREEN_TSC2004=y
CONFIG_TOUCHSCREEN_TSC2007=y
CONFIG_TOUCHSCREEN_STMPE=y
CONFIG_TOUCHSCREEN_SX8654=y
CONFIG_TOUCHSCREEN_COLIBRI_VF50=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_MMA8450=y
CONFIG_INPUT_GPIO_BEEPER=m
CONFIG_INPUT_UINPUT=m
CONFIG_SERIO_SERPORT=m
# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_IMX=y
CONFIG_SERIAL_IMX_CONSOLE=y
CONFIG_SERIAL_FSL_LPUART=y
CONFIG_SERIAL_FSL_LPUART_CONSOLE=y
CONFIG_SERIAL_DEV_BUS=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_MUX_GPIO=y
# CONFIG_I2C_HELPER_AUTO is not set
CONFIG_I2C_ALGOPCF=m
CONFIG_I2C_ALGOPCA=m
CONFIG_I2C_GPIO=y
CONFIG_I2C_IMX=y
CONFIG_SPI=y
CONFIG_SPI_FSL_QUADSPI=y
CONFIG_SPI_GPIO=y
CONFIG_SPI_IMX=y
CONFIG_SPI_FSL_DSPI=y
CONFIG_PINCTRL_IMX8MM=y
CONFIG_PINCTRL_IMX8MN=y
CONFIG_PINCTRL_IMX8MP=y
CONFIG_PINCTRL_IMX8MQ=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_MXC=y
CONFIG_GPIO_SIOX=m
CONFIG_GPIO_MAX732X=y
CONFIG_GPIO_PCA953X=y
CONFIG_GPIO_PCA953X_IRQ=y
CONFIG_GPIO_PCF857X=y
CONFIG_GPIO_BD71815=y
CONFIG_GPIO_STMPE=y
CONFIG_GPIO_74X164=y
CONFIG_W1=m
CONFIG_W1_MASTER_DS2482=m
CONFIG_W1_SLAVE_THERM=m
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_SYSCON=y
CONFIG_POWER_RESET_SYSCON_POWEROFF=y
CONFIG_RN5T618_POWER=m
CONFIG_SENSORS_MC13783_ADC=y
CONFIG_SENSORS_GPIO_FAN=y
CONFIG_SENSORS_IIO_HWMON=y
CONFIG_SENSORS_JC42=m
CONFIG_SENSORS_LM75=m
CONFIG_SENSORS_PWM_FAN=y
CONFIG_SENSORS_SY7636A=y
CONFIG_THERMAL_STATISTICS=y
CONFIG_CPU_THERMAL=y
CONFIG_IMX_THERMAL=y
CONFIG_WATCHDOG=y
CONFIG_WATCHDOG_SYSFS=y
CONFIG_DA9063_WATCHDOG=m
CONFIG_DA9062_WATCHDOG=y
CONFIG_RN5T618_WATCHDOG=y
CONFIG_IMX2_WDT=y
CONFIG_IMX7ULP_WDT=y
CONFIG_MFD_DA9052_I2C=y
CONFIG_MFD_DA9062=y
CONFIG_MFD_DA9063=y
CONFIG_MFD_MC13XXX_SPI=y
CONFIG_MFD_MC13XXX_I2C=y
CONFIG_MFD_SY7636A=y
CONFIG_MFD_RN5T618=y
CONFIG_MFD_STMPE=y
CONFIG_MFD_ROHM_BD71828=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_ANATOP=y
CONFIG_REGULATOR_BD71815=y
CONFIG_REGULATOR_BD71828=y
CONFIG_REGULATOR_DA9052=y
CONFIG_REGULATOR_DA9062=y
CONFIG_REGULATOR_DA9063=y
CONFIG_REGULATOR_GPIO=y
CONFIG_REGULATOR_LTC3676=y
CONFIG_REGULATOR_MAX77696=m
CONFIG_REGULATOR_MC13783=y
CONFIG_REGULATOR_MC13892=y
CONFIG_REGULATOR_PFUZE100=y
CONFIG_REGULATOR_RN5T618=y
CONFIG_REGULATOR_SY7636A=y
CONFIG_RC_CORE=y
CONFIG_RC_DEVICES=y
CONFIG_IR_GPIO_CIR=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_USB_SUPPORT=y
CONFIG_USB_VIDEO_CLASS=m
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_V4L_MEM2MEM_DRIVERS=y
CONFIG_VIDEO_MUX=y
CONFIG_VIDEO_CODA=m
CONFIG_VIDEO_IMX_PXP=y
CONFIG_VIDEO_OV2680=m
CONFIG_VIDEO_OV5640=m
CONFIG_VIDEO_OV5645=m
CONFIG_VIDEO_ADV7180=m
CONFIG_IMX_IPUV3_CORE=y
CONFIG_DRM=y
CONFIG_DRM_MSM=y
CONFIG_DRM_PANEL_LVDS=y
CONFIG_DRM_PANEL_SEIKO_43WVF1G=y
CONFIG_DRM_PANEL_EDP=y
CONFIG_DRM_PANEL_SIMPLE=y
CONFIG_DRM_DISPLAY_CONNECTOR=y
CONFIG_DRM_I2C_NXP_TDA998X=y
CONFIG_DRM_LVDS_CODEC=m
CONFIG_DRM_SII902X=y
CONFIG_DRM_TI_TFP410=y
CONFIG_DRM_DW_HDMI_AHB_AUDIO=m
CONFIG_DRM_DW_HDMI_CEC=y
CONFIG_DRM_IMX=y
CONFIG_DRM_IMX_PARALLEL_DISPLAY=y
CONFIG_DRM_IMX_TVE=y
CONFIG_DRM_IMX_LDB=y
CONFIG_DRM_IMX_HDMI=y
CONFIG_DRM_ETNAVIV=y
CONFIG_DRM_MXSFB=y
CONFIG_DRM_MXC_EPDC=m
CONFIG_FB=y
CONFIG_FB_MODE_HELPERS=y
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_LCD_L4F00242T03=y
CONFIG_LCD_PLATFORM=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_BACKLIGHT_PWM=y
CONFIG_BACKLIGHT_MAX77696=m
CONFIG_BACKLIGHT_GPIO=y
CONFIG_LOGO=y
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_USB_AUDIO=m
CONFIG_SND_SOC=y
CONFIG_SND_SOC_FSL_ASRC=y
CONFIG_SND_IMX_SOC=y
CONFIG_SND_SOC_EUKREA_TLV320=y
CONFIG_SND_SOC_IMX_ES8328=y
CONFIG_SND_SOC_IMX_SGTL5000=y
CONFIG_SND_SOC_FSL_ASOC_CARD=y
CONFIG_SND_SOC_AC97_CODEC=y
CONFIG_SND_SOC_CS42XX8_I2C=y
CONFIG_SND_SOC_SPDIF=y
CONFIG_SND_SOC_TLV320AIC3X_I2C=y
CONFIG_SND_SOC_WM8960=y
CONFIG_SND_SOC_WM8962=y
CONFIG_SND_SIMPLE_CARD=y
CONFIG_HID_MULTITOUCH=y
CONFIG_HID_WACOM=y
CONFIG_I2C_HID_OF=y
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_ACM=m
CONFIG_USB_STORAGE=y
CONFIG_USB_CHIPIDEA=y
CONFIG_USB_CHIPIDEA_UDC=y
CONFIG_USB_CHIPIDEA_HOST=y
CONFIG_USB_SERIAL=m
CONFIG_USB_SERIAL_GENERIC=y
CONFIG_USB_SERIAL_FTDI_SIO=m
CONFIG_USB_SERIAL_OPTION=m
CONFIG_USB_TEST=m
CONFIG_USB_EHSET_TEST_FIXTURE=m
CONFIG_USB_ONBOARD_DEV=y
CONFIG_NOP_USB_XCEIV=y
CONFIG_USB_MXS_PHY=y
CONFIG_USB_GADGET=y
CONFIG_USB_CONFIGFS=y
CONFIG_USB_CONFIGFS_SERIAL=y
CONFIG_USB_CONFIGFS_ACM=y
CONFIG_USB_CONFIGFS_OBEX=y
CONFIG_USB_CONFIGFS_NCM=y
CONFIG_USB_CONFIGFS_ECM=y
CONFIG_USB_CONFIGFS_ECM_SUBSET=y
CONFIG_USB_CONFIGFS_RNDIS=y
CONFIG_USB_CONFIGFS_EEM=y
CONFIG_USB_CONFIGFS_MASS_STORAGE=y
CONFIG_USB_CONFIGFS_F_LB_SS=y
CONFIG_USB_CONFIGFS_F_FS=y
CONFIG_USB_CONFIGFS_F_UAC1=y
CONFIG_USB_CONFIGFS_F_UAC2=y
CONFIG_USB_CONFIGFS_F_MIDI=y
CONFIG_USB_CONFIGFS_F_HID=y
CONFIG_USB_CONFIGFS_F_UVC=y
CONFIG_USB_CONFIGFS_F_PRINTER=y
CONFIG_USB_ZERO=m
CONFIG_USB_AUDIO=m
CONFIG_USB_ETH=m
CONFIG_USB_G_NCM=m
CONFIG_USB_GADGETFS=m
CONFIG_USB_FUNCTIONFS=m
CONFIG_USB_MASS_STORAGE=m
CONFIG_USB_G_SERIAL=m
CONFIG_MMC=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_ESDHC_IMX=y
CONFIG_LEDS_GPIO=y
CONFIG_LEDS_PWM=y
CONFIG_LEDS_TRIGGER_TIMER=y
CONFIG_LEDS_TRIGGER_ONESHOT=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_LEDS_TRIGGER_BACKLIGHT=y
CONFIG_LEDS_TRIGGER_GPIO=y
CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_INTF_DEV_UIE_EMUL=y
CONFIG_RTC_DRV_DS1307=y
CONFIG_RTC_DRV_ISL1208=y
CONFIG_RTC_DRV_PCF8523=y
CONFIG_RTC_DRV_PCF8563=y
CONFIG_RTC_DRV_M41T80=y
CONFIG_RTC_DRV_BD70528=y
CONFIG_RTC_DRV_RC5T619=y
CONFIG_RTC_DRV_RV3029C2=y
CONFIG_RTC_DRV_DA9063=y
CONFIG_RTC_DRV_MC13XXX=y
CONFIG_RTC_DRV_MXC=y
CONFIG_RTC_DRV_MXC_V2=y
CONFIG_RTC_DRV_SNVS=y
CONFIG_DMADEVICES=y
CONFIG_FSL_EDMA=y
CONFIG_IMX_SDMA=m
CONFIG_MXS_DMA=y
CONFIG_DMATEST=m
CONFIG_STAGING=y
CONFIG_STAGING_MEDIA=y
CONFIG_VIDEO_IMX_MEDIA=y
CONFIG_COMMON_CLK_PWM=y
CONFIG_COMMON_CLK_BD718XX=y
CONFIG_CLK_IMX8MM=y
CONFIG_CLK_IMX8MN=y
CONFIG_CLK_IMX8MP=y
CONFIG_CLK_IMX8MQ=y
CONFIG_SOC_IMX8M=y
CONFIG_EXTCON_USB_GPIO=y
CONFIG_IIO=y
CONFIG_IIO_ST_ACCEL_3AXIS=m
CONFIG_MMA8452=y
CONFIG_IMX7D_ADC=y
CONFIG_RN5T618_ADC=y
CONFIG_STMPE_ADC=y
CONFIG_VF610_ADC=y
CONFIG_SENSORS_ISL29018=y
CONFIG_MAG3110=y
CONFIG_MPL3115=y
CONFIG_PWM=y
CONFIG_PWM_FSL_FTM=y
CONFIG_PWM_IMX27=y
CONFIG_PWM_IMX_TPM=y
CONFIG_NVMEM_IMX_OCOTP=y
CONFIG_NVMEM_SNVS_LPGPR=y
CONFIG_NVMEM_VF610_OCOTP=y
CONFIG_TEE=y
CONFIG_OPTEE=y
CONFIG_MUX_MMIO=y
CONFIG_SIOX=m
CONFIG_SIOX_BUS_GPIO=m
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT2_FS_POSIX_ACL=y
CONFIG_EXT2_FS_SECURITY=y
CONFIG_EXT3_FS=y
CONFIG_EXT3_FS_POSIX_ACL=y
CONFIG_EXT3_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
CONFIG_AUTOFS_FS=y
CONFIG_FUSE_FS=y
CONFIG_ISO9660_FS=m
CONFIG_JOLIET=y
CONFIG_ZISOFS=y
CONFIG_UDF_FS=m
CONFIG_MSDOS_FS=m
CONFIG_VFAT_FS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_JFFS2_FS=y
CONFIG_UBIFS_FS=y
CONFIG_NFS_FS=y
CONFIG_NFS_V3_ACL=y
CONFIG_NFS_V4=y
CONFIG_NFS_V4_1=y
CONFIG_NFS_V4_2=y
CONFIG_ROOT_NFS=y
CONFIG_NLS_DEFAULT="cp437"
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ASCII=y
CONFIG_NLS_ISO8859_1=y
CONFIG_NLS_ISO8859_15=m
CONFIG_NLS_UTF8=y
CONFIG_SECURITYFS=y
CONFIG_CRYPTO_DEV_FSL_CAAM=y
CONFIG_CRYPTO_DEV_SAHARA=y
CONFIG_CRYPTO_DEV_MXS_DCP=y
CONFIG_CMA_SIZE_MBYTES=64
CONFIG_FONTS=y
CONFIG_FONT_8x8=y
CONFIG_FONT_8x16=y
CONFIG_PRINTK_TIME=y
# CONFIG_DEBUG_BUGVERBOSE is not set
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_FS=y
# CONFIG_SLUB_DEBUG is not set
# CONFIG_FTRACE is not set

View File

@@ -460,6 +460,8 @@ source "drivers/gpu/drm/sprd/Kconfig"
source "drivers/gpu/drm/imagination/Kconfig" source "drivers/gpu/drm/imagination/Kconfig"
source "drivers/gpu/drm/mxc-epdc/Kconfig"
config DRM_HYPERV config DRM_HYPERV
tristate "DRM Support for Hyper-V synthetic video device" tristate "DRM Support for Hyper-V synthetic video device"
depends on DRM && PCI && MMU && HYPERV depends on DRM && PCI && MMU && HYPERV

View File

@@ -216,6 +216,7 @@ obj-$(CONFIG_DRM_PANTHOR) += panthor/
obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/ obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
obj-$(CONFIG_DRM_MCDE) += mcde/ obj-$(CONFIG_DRM_MCDE) += mcde/
obj-$(CONFIG_DRM_TIDSS) += tidss/ obj-$(CONFIG_DRM_TIDSS) += tidss/
obj-$(CONFIG_DRM_MXC_EPDC) += mxc-epdc/
obj-y += xlnx/ obj-y += xlnx/
obj-y += gud/ obj-y += gud/
obj-$(CONFIG_DRM_HYPERV) += hyperv/ obj-$(CONFIG_DRM_HYPERV) += hyperv/

View File

@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0
config DRM_MXC_EPDC
tristate "i.MX EPD Controller"
depends on DRM && OF
depends on (COMPILE_TEST || ARCH_MXC)
select DRM_KMS_HELPER
select DRM_KMS_CMA_HELPER
select DRM_TTM_HELPER
select DRM_CLIENT_SELECTION
select DMA_CMA if HAVE_DMA_CONTIGUOUS
select CMA if HAVE_DMA_CONTIGUOUS
select VIDEOMODE_HELPERS
help
Choose this option if you have an i.MX system with an EPDC.
If M is selected this module will be called mxc_epdc_drm.

View File

@@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
mxc_epdc_drm-y := mxc_epdc_drv.o epdc_hw.o epdc_update.o epdc_waveform.o
obj-$(CONFIG_DRM_MXC_EPDC) += mxc_epdc_drm.o

View File

@@ -0,0 +1,497 @@
// SPDX-License-Identifier: GPL-2.0+
// Copyright (C) 2020 Andreas Kemnade
//
/*
* based on the EPDC framebuffer driver
* Copyright (C) 2010-2016 Freescale Semiconductor, Inc.
* Copyright 2017 NXP
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include "mxc_epdc.h"
#include "epdc_regs.h"
#include "epdc_hw.h"
/* TODO: should probably be untangled */
#include "epdc_update.h"
#include "epdc_waveform.h"
void mxc_epdc_powerup(struct mxc_epdc *priv)
{
int ret = 0;
mutex_lock(&priv->power_mutex);
/*
* If power down request is pending, clear
* powering_down to cancel the request.
*/
if (priv->powering_down)
priv->powering_down = false;
if (priv->powered) {
mutex_unlock(&priv->power_mutex);
return;
}
dev_dbg(priv->drm.dev, "EPDC Powerup\n");
priv->updates_active = true;
/* Enable the v3p3 regulator */
ret = regulator_enable(priv->v3p3_regulator);
if (IS_ERR((void *)ret)) {
dev_err(priv->drm.dev,
"Unable to enable V3P3 regulator. err = 0x%x\n",
ret);
mutex_unlock(&priv->power_mutex);
return;
}
usleep_range(1000, 2000);
pm_runtime_get_sync(priv->drm.dev);
/* Enable clocks to EPDC */
clk_prepare_enable(priv->epdc_clk_axi);
clk_prepare_enable(priv->epdc_clk_pix);
epdc_write(priv, EPDC_CTRL_CLEAR, EPDC_CTRL_CLKGATE);
/* Enable power to the EPD panel */
ret = regulator_enable(priv->display_regulator);
if (IS_ERR((void *)ret)) {
dev_err(priv->drm.dev,
"Unable to enable DISPLAY regulator. err = 0x%x\n",
ret);
mutex_unlock(&priv->power_mutex);
return;
}
ret = regulator_enable(priv->vcom_regulator);
if (IS_ERR((void *)ret)) {
dev_err(priv->drm.dev,
"Unable to enable VCOM regulator. err = 0x%x\n",
ret);
mutex_unlock(&priv->power_mutex);
return;
}
priv->powered = true;
mutex_unlock(&priv->power_mutex);
}
void mxc_epdc_powerdown(struct mxc_epdc *priv)
{
mutex_lock(&priv->power_mutex);
/* If powering_down has been cleared, a powerup
* request is pre-empting this powerdown request.
*/
if (!priv->powering_down
|| (!priv->powered)) {
mutex_unlock(&priv->power_mutex);
return;
}
dev_dbg(priv->drm.dev, "EPDC Powerdown\n");
/* Disable power to the EPD panel */
regulator_disable(priv->vcom_regulator);
regulator_disable(priv->display_regulator);
/* Disable clocks to EPDC */
epdc_write(priv, EPDC_CTRL_SET, EPDC_CTRL_CLKGATE);
clk_disable_unprepare(priv->epdc_clk_pix);
clk_disable_unprepare(priv->epdc_clk_axi);
pm_runtime_put_sync_suspend(priv->drm.dev);
/* turn off the V3p3 */
regulator_disable(priv->v3p3_regulator);
priv->powered = false;
priv->powering_down = false;
if (priv->wait_for_powerdown) {
priv->wait_for_powerdown = false;
complete(&priv->powerdown_compl);
}
mutex_unlock(&priv->power_mutex);
}
static void epdc_set_horizontal_timing(struct mxc_epdc *priv, u32 horiz_start,
u32 horiz_end,
u32 hsync_width, u32 hsync_line_length)
{
u32 reg_val =
((hsync_width << EPDC_TCE_HSCAN1_LINE_SYNC_WIDTH_OFFSET) &
EPDC_TCE_HSCAN1_LINE_SYNC_WIDTH_MASK)
| ((hsync_line_length << EPDC_TCE_HSCAN1_LINE_SYNC_OFFSET) &
EPDC_TCE_HSCAN1_LINE_SYNC_MASK);
epdc_write(priv, EPDC_TCE_HSCAN1, reg_val);
reg_val =
((horiz_start << EPDC_TCE_HSCAN2_LINE_BEGIN_OFFSET) &
EPDC_TCE_HSCAN2_LINE_BEGIN_MASK)
| ((horiz_end << EPDC_TCE_HSCAN2_LINE_END_OFFSET) &
EPDC_TCE_HSCAN2_LINE_END_MASK);
epdc_write(priv, EPDC_TCE_HSCAN2, reg_val);
}
static void epdc_set_vertical_timing(struct mxc_epdc *priv,
u32 vert_start,
u32 vert_end,
u32 vsync_width)
{
u32 reg_val =
((vert_start << EPDC_TCE_VSCAN_FRAME_BEGIN_OFFSET) &
EPDC_TCE_VSCAN_FRAME_BEGIN_MASK)
| ((vert_end << EPDC_TCE_VSCAN_FRAME_END_OFFSET) &
EPDC_TCE_VSCAN_FRAME_END_MASK)
| ((vsync_width << EPDC_TCE_VSCAN_FRAME_SYNC_OFFSET) &
EPDC_TCE_VSCAN_FRAME_SYNC_MASK);
epdc_write(priv, EPDC_TCE_VSCAN, reg_val);
}
static inline void epdc_set_screen_res(struct mxc_epdc *priv,
u32 width, u32 height)
{
u32 val = (height << EPDC_RES_VERTICAL_OFFSET) | width;
epdc_write(priv, EPDC_RES, val);
}
void epdc_init_settings(struct mxc_epdc *priv, struct drm_display_mode *m)
{
u32 reg_val;
int num_ce;
int i;
/* Enable clocks to access EPDC regs */
clk_prepare_enable(priv->epdc_clk_axi);
clk_prepare_enable(priv->epdc_clk_pix);
/* Reset */
epdc_write(priv, EPDC_CTRL_SET, EPDC_CTRL_SFTRST);
while (!(epdc_read(priv, EPDC_CTRL) & EPDC_CTRL_CLKGATE))
;
epdc_write(priv, EPDC_CTRL_CLEAR, EPDC_CTRL_SFTRST);
/* Enable clock gating (clear to enable) */
epdc_write(priv, EPDC_CTRL_CLEAR, EPDC_CTRL_CLKGATE);
while (epdc_read(priv, EPDC_CTRL) & (EPDC_CTRL_SFTRST | EPDC_CTRL_CLKGATE))
;
/* EPDC_CTRL */
reg_val = epdc_read(priv, EPDC_CTRL);
reg_val &= ~EPDC_CTRL_UPD_DATA_SWIZZLE_MASK;
reg_val |= EPDC_CTRL_UPD_DATA_SWIZZLE_NO_SWAP;
reg_val &= ~EPDC_CTRL_LUT_DATA_SWIZZLE_MASK;
reg_val |= EPDC_CTRL_LUT_DATA_SWIZZLE_NO_SWAP;
epdc_write(priv, EPDC_CTRL_SET, reg_val);
/* EPDC_FORMAT - 2bit TFT and buf_pix_fmt Buf pixel format */
reg_val = EPDC_FORMAT_TFT_PIXEL_FORMAT_2BIT
| priv->buf_pix_fmt
| ((0x0 << EPDC_FORMAT_DEFAULT_TFT_PIXEL_OFFSET) &
EPDC_FORMAT_DEFAULT_TFT_PIXEL_MASK);
epdc_write(priv, EPDC_FORMAT, reg_val);
if (priv->rev >= 30) {
if (priv->buf_pix_fmt == EPDC_FORMAT_BUF_PIXEL_FORMAT_P5N) {
/* 110 00101 0101 0100*/
epdc_write(priv, EPDC_WB_FIELD2, 0xc554);
/* 101 00000 0000 0100*/
epdc_write(priv, EPDC_WB_FIELD1, 0xa004);
} else {
/* 110 00101 0101 0100*/
epdc_write(priv, EPDC_WB_FIELD2, 0xc443);
/* 101 00000 0000 0100*/
epdc_write(priv, EPDC_WB_FIELD1, 0xa003);
}
}
/* EPDC_FIFOCTRL (disabled) */
reg_val =
((100 << EPDC_FIFOCTRL_FIFO_INIT_LEVEL_OFFSET) &
EPDC_FIFOCTRL_FIFO_INIT_LEVEL_MASK)
| ((200 << EPDC_FIFOCTRL_FIFO_H_LEVEL_OFFSET) &
EPDC_FIFOCTRL_FIFO_H_LEVEL_MASK)
| ((100 << EPDC_FIFOCTRL_FIFO_L_LEVEL_OFFSET) &
EPDC_FIFOCTRL_FIFO_L_LEVEL_MASK);
epdc_write(priv, EPDC_FIFOCTRL, reg_val);
/* EPDC_TEMP - Use default temp to get index */
epdc_write(priv, EPDC_TEMP,
mxc_epdc_fb_get_temp_index(priv, TEMP_USE_AMBIENT));
/* EPDC_RES */
epdc_set_screen_res(priv, m->hdisplay, m->vdisplay);
/* EPDC_AUTOWV_LUT */
/* Initialize all auto-wavefrom look-up values to 2 - GC16 */
for (i = 0; i < 8; i++)
epdc_write(priv, EPDC_AUTOWV_LUT,
(2 << EPDC_AUTOWV_LUT_DATA_OFFSET) |
(i << EPDC_AUTOWV_LUT_ADDR_OFFSET));
/*
* EPDC_TCE_CTRL
* VSCAN_HOLDOFF = 4
* VCOM_MODE = MANUAL
* VCOM_VAL = 0
* DDR_MODE = DISABLED
* LVDS_MODE_CE = DISABLED
* LVDS_MODE = DISABLED
* DUAL_SCAN = DISABLED
* SDDO_WIDTH = 8bit
* PIXELS_PER_SDCLK = 4
*/
reg_val =
((priv->imx_mode.vscan_holdoff << EPDC_TCE_CTRL_VSCAN_HOLDOFF_OFFSET) &
EPDC_TCE_CTRL_VSCAN_HOLDOFF_MASK)
| EPDC_TCE_CTRL_PIXELS_PER_SDCLK_4;
epdc_write(priv, EPDC_TCE_CTRL, reg_val);
/* EPDC_TCE_HSCAN */
epdc_set_horizontal_timing(priv, m->hsync_start - m->hdisplay,
m->htotal - m->hsync_end,
m->hsync_end - m->hsync_start,
m->hsync_end - m->hsync_start);
/* EPDC_TCE_VSCAN */
epdc_set_vertical_timing(priv, m->vsync_start - m->vdisplay,
m->vtotal - m->vsync_end,
m->vsync_end - m->vsync_start);
/* EPDC_TCE_OE */
reg_val =
((priv->imx_mode.sdoed_width << EPDC_TCE_OE_SDOED_WIDTH_OFFSET) &
EPDC_TCE_OE_SDOED_WIDTH_MASK)
| ((priv->imx_mode.sdoed_delay << EPDC_TCE_OE_SDOED_DLY_OFFSET) &
EPDC_TCE_OE_SDOED_DLY_MASK)
| ((priv->imx_mode.sdoez_width << EPDC_TCE_OE_SDOEZ_WIDTH_OFFSET) &
EPDC_TCE_OE_SDOEZ_WIDTH_MASK)
| ((priv->imx_mode.sdoez_delay << EPDC_TCE_OE_SDOEZ_DLY_OFFSET) &
EPDC_TCE_OE_SDOEZ_DLY_MASK);
epdc_write(priv, EPDC_TCE_OE, reg_val);
/* EPDC_TCE_TIMING1 */
epdc_write(priv, EPDC_TCE_TIMING1, 0x0);
/* EPDC_TCE_TIMING2 */
reg_val =
((priv->imx_mode.gdclk_hp_offs << EPDC_TCE_TIMING2_GDCLK_HP_OFFSET) &
EPDC_TCE_TIMING2_GDCLK_HP_MASK)
| ((priv->imx_mode.gdsp_offs << EPDC_TCE_TIMING2_GDSP_OFFSET_OFFSET) &
EPDC_TCE_TIMING2_GDSP_OFFSET_MASK);
epdc_write(priv, EPDC_TCE_TIMING2, reg_val);
/* EPDC_TCE_TIMING3 */
reg_val =
((priv->imx_mode.gdoe_offs << EPDC_TCE_TIMING3_GDOE_OFFSET_OFFSET) &
EPDC_TCE_TIMING3_GDOE_OFFSET_MASK)
| ((priv->imx_mode.gdclk_offs << EPDC_TCE_TIMING3_GDCLK_OFFSET_OFFSET) &
EPDC_TCE_TIMING3_GDCLK_OFFSET_MASK);
epdc_write(priv, EPDC_TCE_TIMING3, reg_val);
/*
* EPDC_TCE_SDCFG
* SDCLK_HOLD = 1
* SDSHR = 1
* NUM_CE = 1
* SDDO_REFORMAT = FLIP_PIXELS
* SDDO_INVERT = DISABLED
* PIXELS_PER_CE = display horizontal resolution
*/
num_ce = priv->imx_mode.num_ce;
if (num_ce == 0)
num_ce = 1;
reg_val = EPDC_TCE_SDCFG_SDCLK_HOLD | EPDC_TCE_SDCFG_SDSHR
| ((num_ce << EPDC_TCE_SDCFG_NUM_CE_OFFSET) &
EPDC_TCE_SDCFG_NUM_CE_MASK)
| EPDC_TCE_SDCFG_SDDO_REFORMAT_FLIP_PIXELS
| ((priv->epdc_mem_width/num_ce << EPDC_TCE_SDCFG_PIXELS_PER_CE_OFFSET) &
EPDC_TCE_SDCFG_PIXELS_PER_CE_MASK);
epdc_write(priv, EPDC_TCE_SDCFG, reg_val);
/*
* EPDC_TCE_GDCFG
* GDRL = 1
* GDOE_MODE = 0;
* GDSP_MODE = 0;
*/
reg_val = EPDC_TCE_SDCFG_GDRL;
epdc_write(priv, EPDC_TCE_GDCFG, reg_val);
/*
* EPDC_TCE_POLARITY
* SDCE_POL = ACTIVE LOW
* SDLE_POL = ACTIVE HIGH
* SDOE_POL = ACTIVE HIGH
* GDOE_POL = ACTIVE HIGH
* GDSP_POL = ACTIVE LOW
*/
reg_val = EPDC_TCE_POLARITY_SDLE_POL_ACTIVE_HIGH
| EPDC_TCE_POLARITY_SDOE_POL_ACTIVE_HIGH
| EPDC_TCE_POLARITY_GDOE_POL_ACTIVE_HIGH;
epdc_write(priv, EPDC_TCE_POLARITY, reg_val);
/* EPDC_IRQ_MASK */
epdc_write(priv, EPDC_IRQ_MASK, EPDC_IRQ_TCE_UNDERRUN_IRQ);
/*
* EPDC_GPIO
* PWRCOM = ?
* PWRCTRL = ?
* BDR = ?
*/
reg_val = ((0 << EPDC_GPIO_PWRCTRL_OFFSET) & EPDC_GPIO_PWRCTRL_MASK)
| ((0 << EPDC_GPIO_BDR_OFFSET) & EPDC_GPIO_BDR_MASK);
epdc_write(priv, EPDC_GPIO, reg_val);
epdc_write(priv, EPDC_WVADDR, priv->waveform_buffer_phys);
epdc_write(priv, EPDC_WB_ADDR, priv->working_buffer_phys);
if (priv->rev >= 30)
epdc_write(priv, EPDC_WB_ADDR_TCE_V3,
priv->working_buffer_phys);
else
epdc_write(priv, EPDC_WB_ADDR_TCE,
priv->working_buffer_phys);
/* Disable clock */
clk_disable_unprepare(priv->epdc_clk_axi);
clk_disable_unprepare(priv->epdc_clk_pix);
}
void mxc_epdc_init_sequence(struct mxc_epdc *priv, struct drm_display_mode *m)
{
/* Initialize EPDC, passing pointer to EPDC registers */
struct clk *epdc_parent;
unsigned long rounded_parent_rate, epdc_pix_rate,
rounded_pix_clk, target_pix_clk;
/* Enable pix clk for EPDC */
clk_prepare_enable(priv->epdc_clk_axi);
target_pix_clk = m->clock * 1000;
rounded_pix_clk = clk_round_rate(priv->epdc_clk_pix, target_pix_clk);
if (((rounded_pix_clk >= target_pix_clk + target_pix_clk/100) ||
(rounded_pix_clk <= target_pix_clk - target_pix_clk/100))) {
/* Can't get close enough without changing parent clk */
epdc_parent = clk_get_parent(priv->epdc_clk_pix);
rounded_parent_rate = clk_round_rate(epdc_parent, target_pix_clk);
epdc_pix_rate = target_pix_clk;
while (epdc_pix_rate < rounded_parent_rate)
epdc_pix_rate *= 2;
clk_set_rate(epdc_parent, epdc_pix_rate);
rounded_pix_clk = clk_round_rate(priv->epdc_clk_pix, target_pix_clk);
if (((rounded_pix_clk >= target_pix_clk + target_pix_clk/100) ||
(rounded_pix_clk <= target_pix_clk - target_pix_clk/100)))
/* Still can't get a good clock, provide warning */
dev_err(priv->drm.dev, "Unable to get an accurate EPDC pix clk"
"desired = %lu, actual = %lu\n", target_pix_clk,
rounded_pix_clk);
}
clk_set_rate(priv->epdc_clk_pix, rounded_pix_clk);
clk_prepare_enable(priv->epdc_clk_pix);
epdc_init_settings(priv, m);
priv->in_init = true;
mxc_epdc_powerup(priv);
mxc_epdc_draw_mode0(priv);
/* Force power down event */
priv->powering_down = true;
mxc_epdc_powerdown(priv);
priv->updates_active = false;
/* Disable clocks */
clk_disable_unprepare(priv->epdc_clk_axi);
clk_disable_unprepare(priv->epdc_clk_pix);
priv->hw_ready = true;
priv->hw_initializing = false;
}
int mxc_epdc_init_hw(struct mxc_epdc *priv)
{
struct pinctrl *pinctrl;
const char *thermal = NULL;
u32 val;
/* get pmic regulators */
priv->display_regulator = devm_regulator_get(priv->drm.dev, "DISPLAY");
if (IS_ERR(priv->display_regulator))
return dev_err_probe(priv->drm.dev, PTR_ERR(priv->display_regulator),
"Unable to get display PMIC regulator\n");
priv->vcom_regulator = devm_regulator_get(priv->drm.dev, "VCOM");
if (IS_ERR(priv->vcom_regulator))
return dev_err_probe(priv->drm.dev, PTR_ERR(priv->vcom_regulator),
"Unable to get VCOM regulator\n");
priv->v3p3_regulator = devm_regulator_get(priv->drm.dev, "V3P3");
if (IS_ERR(priv->v3p3_regulator))
return dev_err_probe(priv->drm.dev, PTR_ERR(priv->v3p3_regulator),
"Unable to get V3P3 regulator\n");
of_property_read_string(priv->drm.dev->of_node,
"epd-thermal-zone", &thermal);
if (thermal) {
priv->thermal = thermal_zone_get_zone_by_name(thermal);
if (IS_ERR(priv->thermal))
return dev_err_probe(priv->drm.dev, PTR_ERR(priv->thermal),
"unable to get thermal");
}
priv->iobase = devm_platform_get_and_ioremap_resource(to_platform_device(priv->drm.dev),
0, NULL);
if (priv->iobase == NULL)
return -ENOMEM;
// priv->auto_mode = AUTO_UPDATE_MODE_REGION_MODE;
priv->epdc_clk_axi = devm_clk_get(priv->drm.dev, "epdc_axi");
if (IS_ERR(priv->epdc_clk_axi))
return dev_err_probe(priv->drm.dev, PTR_ERR(priv->epdc_clk_axi),
"Unable to get EPDC AXI clk\n");
priv->epdc_clk_pix = devm_clk_get(priv->drm.dev, "epdc_pix");
if (IS_ERR(priv->epdc_clk_pix))
return dev_err_probe(priv->drm.dev, PTR_ERR(priv->epdc_clk_pix),
"Unable to get EPDC pix clk\n");
clk_prepare_enable(priv->epdc_clk_axi);
val = epdc_read(priv, EPDC_VERSION);
clk_disable_unprepare(priv->epdc_clk_axi);
priv->rev = ((val & EPDC_VERSION_MAJOR_MASK) >>
EPDC_VERSION_MAJOR_OFFSET) * 10
+ ((val & EPDC_VERSION_MINOR_MASK) >>
EPDC_VERSION_MINOR_OFFSET);
dev_dbg(priv->drm.dev, "EPDC version = %d\n", priv->rev);
/* Initialize EPDC pins */
pinctrl = devm_pinctrl_get_select_default(priv->drm.dev);
if (IS_ERR(pinctrl)) {
dev_err(priv->drm.dev, "can't get/select pinctrl\n");
return PTR_ERR(pinctrl);
}
mutex_init(&priv->power_mutex);
return 0;
}

View File

@@ -0,0 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/* Copyright (C) 2020 Andreas Kemnade */
void mxc_epdc_init_sequence(struct mxc_epdc *priv, struct drm_display_mode *m);
int mxc_epdc_init_hw(struct mxc_epdc *priv);
void mxc_epdc_powerup(struct mxc_epdc *priv);
void mxc_epdc_powerdown(struct mxc_epdc *priv);

View File

@@ -0,0 +1,442 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/* Copyright (C) 2010-2013 Freescale Semiconductor, Inc. */
#ifndef __EPDC_REGS_INCLUDED__
#define __EPDC_REGS_INCLUDED__
/*************************************
* Register addresses
**************************************/
#define EPDC_CTRL 0x000
#define EPDC_CTRL_SET 0x004
#define EPDC_CTRL_CLEAR 0x008
#define EPDC_CTRL_TOGGLE 0x00C
#define EPDC_WB_ADDR_TCE_V3 0x010
#define EPDC_WVADDR 0x020
#define EPDC_WB_ADDR 0x030
#define EPDC_RES 0x040
#define EPDC_FORMAT 0x050
#define EPDC_FORMAT_SET 0x054
#define EPDC_FORMAT_CLEAR 0x058
#define EPDC_FORMAT_TOGGLE 0x05C
#define EPDC_WB_FIELD0 0x060
#define EPDC_WB_FIELD0_SET 0x064
#define EPDC_WB_FIELD0_CLEAR 0x068
#define EPDC_WB_FIELD0_TOGGLE 0x06C
#define EPDC_WB_FIELD1 0x070
#define EPDC_WB_FIELD1_SET 0x074
#define EPDC_WB_FIELD1_CLEAR 0x078
#define EPDC_WB_FIELD1_TOGGLE 0x07C
#define EPDC_WB_FIELD2 0x080
#define EPDC_WB_FIELD2_SET 0x084
#define EPDC_WB_FIELD2_CLEAR 0x088
#define EPDC_WB_FIELD2_TOGGLE 0x08C
#define EPDC_WB_FIELD3 0x090
#define EPDC_WB_FIELD3_SET 0x094
#define EPDC_WB_FIELD3_CLEAR 0x098
#define EPDC_WB_FIELD3_TOGGLE 0x09C
#define EPDC_FIFOCTRL 0x0A0
#define EPDC_FIFOCTRL_SET 0x0A4
#define EPDC_FIFOCTRL_CLEAR 0x0A8
#define EPDC_FIFOCTRL_TOGGLE 0x0AC
#define EPDC_UPD_ADDR 0x100
#define EPDC_UPD_STRIDE 0x110
#define EPDC_UPD_CORD 0x120
#define EPDC_UPD_SIZE 0x140
#define EPDC_UPD_CTRL 0x160
#define EPDC_UPD_FIXED 0x180
#define EPDC_TEMP 0x1A0
#define EPDC_AUTOWV_LUT 0x1C0
#define EPDC_TCE_CTRL 0x200
#define EPDC_TCE_SDCFG 0x220
#define EPDC_TCE_GDCFG 0x240
#define EPDC_TCE_HSCAN1 0x260
#define EPDC_TCE_HSCAN2 0x280
#define EPDC_TCE_VSCAN 0x2A0
#define EPDC_TCE_OE 0x2C0
#define EPDC_TCE_POLARITY 0x2E0
#define EPDC_TCE_TIMING1 0x300
#define EPDC_TCE_TIMING2 0x310
#define EPDC_TCE_TIMING3 0x320
#define EPDC_PIGEON_CTRL0 0x380
#define EPDC_PIGEON_CTRL1 0x390
#define EPDC_IRQ_MASK1 0x3C0
#define EPDC_IRQ_MASK1_SET 0x3C4
#define EPDC_IRQ_MASK1_CLEAR 0x3C8
#define EPDC_IRQ_MASK1_TOGGLE 0x3CC
#define EPDC_IRQ_MASK2 0x3D0
#define EPDC_IRQ_MASK2_SET 0x3D4
#define EPDC_IRQ_MASK2_CLEAR 0x3D8
#define EPDC_IRQ_MASK2_TOGGLE 0x3DC
#define EPDC_IRQ1 0x3E0
#define EPDC_IRQ1_SET 0x3E4
#define EPDC_IRQ1_CLEAR 0x3E8
#define EPDC_IRQ1_TOGGLE 0x3EC
#define EPDC_IRQ2 0x3F0
#define EPDC_IRQ2_SET 0x3F4
#define EPDC_IRQ2_CLEAR 0x3F8
#define EPDC_IRQ2_TOGGLE 0x3FC
#define EPDC_IRQ_MASK 0x400
#define EPDC_IRQ_MASK_SET 0x404
#define EPDC_IRQ_MASK_CLEAR 0x408
#define EPDC_IRQ_MASK_TOGGLE 0x40C
#define EPDC_IRQ 0x420
#define EPDC_IRQ_SET 0x424
#define EPDC_IRQ_CLEAR 0x428
#define EPDC_IRQ_TOGGLE 0x42C
#define EPDC_STATUS_LUTS 0x440
#define EPDC_STATUS_LUTS_SET 0x444
#define EPDC_STATUS_LUTS_CLEAR 0x448
#define EPDC_STATUS_LUTS_TOGGLE 0x44C
#define EPDC_STATUS_LUTS2 0x450
#define EPDC_STATUS_LUTS2_SET 0x454
#define EPDC_STATUS_LUTS2_CLEAR 0x458
#define EPDC_STATUS_LUTS2_TOGGLE 0x45C
#define EPDC_STATUS_NEXTLUT 0x460
#define EPDC_STATUS_COL 0x480
#define EPDC_STATUS_COL2 0x490
#define EPDC_STATUS 0x4A0
#define EPDC_STATUS_SET 0x4A4
#define EPDC_STATUS_CLEAR 0x4A8
#define EPDC_STATUS_TOGGLE 0x4AC
#define EPDC_UPD_COL_CORD 0x4C0
#define EPDC_UPD_COL_SIZE 0x4E0
#define EPDC_DEBUG 0x500
#define EPDC_DEBUG_LUT 0x530
#define EPDC_HIST1_PARAM 0x600
#define EPDC_HIST2_PARAM 0x610
#define EPDC_HIST4_PARAM 0x620
#define EPDC_HIST8_PARAM0 0x630
#define EPDC_HIST8_PARAM1 0x640
#define EPDC_HIST16_PARAM0 0x650
#define EPDC_HIST16_PARAM1 0x660
#define EPDC_HIST16_PARAM2 0x670
#define EPDC_HIST16_PARAM3 0x680
#define EPDC_GPIO 0x700
#define EPDC_VERSION 0x7F0
#define EPDC_PIGEON_0_0 0x800
#define EPDC_PIGEON_0_1 0x810
#define EPDC_PIGEON_0_2 0x820
#define EPDC_PIGEON_1_0 0x840
#define EPDC_PIGEON_1_1 0x850
#define EPDC_PIGEON_1_2 0x860
#define EPDC_PIGEON_2_0 0x880
#define EPDC_PIGEON_2_1 0x890
#define EPDC_PIGEON_2_2 0x8A0
#define EPDC_PIGEON_3_0 0x8C0
#define EPDC_PIGEON_3_1 0x8D0
#define EPDC_PIGEON_3_2 0x8E0
#define EPDC_PIGEON_4_0 0x900
#define EPDC_PIGEON_4_1 0x910
#define EPDC_PIGEON_4_2 0x920
#define EPDC_PIGEON_5_0 0x940
#define EPDC_PIGEON_5_1 0x950
#define EPDC_PIGEON_5_2 0x960
#define EPDC_PIGEON_6_0 0x980
#define EPDC_PIGEON_6_1 0x990
#define EPDC_PIGEON_6_2 0x9A0
#define EPDC_PIGEON_7_0 0x9C0
#define EPDC_PIGEON_7_1 0x9D0
#define EPDC_PIGEON_7_2 0x9E0
#define EPDC_PIGEON_8_0 0xA00
#define EPDC_PIGEON_8_1 0xA10
#define EPDC_PIGEON_8_2 0xA20
#define EPDC_PIGEON_9_0 0xA40
#define EPDC_PIGEON_9_1 0xA50
#define EPDC_PIGEON_9_2 0xA60
#define EPDC_PIGEON_10_0 0xA80
#define EPDC_PIGEON_10_1 0xA90
#define EPDC_PIGEON_10_2 0xAA0
#define EPDC_PIGEON_11_0 0xAC0
#define EPDC_PIGEON_11_1 0xAD0
#define EPDC_PIGEON_11_2 0xAE0
#define EPDC_PIGEON_12_0 0xB00
#define EPDC_PIGEON_12_1 0xB10
#define EPDC_PIGEON_12_2 0xB20
#define EPDC_PIGEON_13_0 0xB40
#define EPDC_PIGEON_13_1 0xB50
#define EPDC_PIGEON_13_2 0xB60
#define EPDC_PIGEON_14_0 0xB80
#define EPDC_PIGEON_14_1 0xB90
#define EPDC_PIGEON_14_2 0xBA0
#define EPDC_PIGEON_15_0 0xBC0
#define EPDC_PIGEON_15_1 0xBD0
#define EPDC_PIGEON_15_2 0xBE0
#define EPDC_WB_ADDR_TCE 0xC10
/*
* Register field definitions
*/
enum {
/* EPDC_CTRL field values */
EPDC_CTRL_SFTRST = 0x80000000,
EPDC_CTRL_CLKGATE = 0x40000000,
EPDC_CTRL_SRAM_POWERDOWN = 0x100,
EPDC_CTRL_UPD_DATA_SWIZZLE_MASK = 0xC0,
EPDC_CTRL_UPD_DATA_SWIZZLE_NO_SWAP = 0,
EPDC_CTRL_UPD_DATA_SWIZZLE_ALL_BYTES_SWAP = 0x40,
EPDC_CTRL_UPD_DATA_SWIZZLE_HWD_SWAP = 0x80,
EPDC_CTRL_UPD_DATA_SWIZZLE_HWD_BYTE_SWAP = 0xC0,
EPDC_CTRL_LUT_DATA_SWIZZLE_MASK = 0x30,
EPDC_CTRL_LUT_DATA_SWIZZLE_NO_SWAP = 0,
EPDC_CTRL_LUT_DATA_SWIZZLE_ALL_BYTES_SWAP = 0x10,
EPDC_CTRL_LUT_DATA_SWIZZLE_HWD_SWAP = 0x20,
EPDC_CTRL_LUT_DATA_SWIZZLE_HWD_BYTE_SWAP = 0x30,
EPDC_CTRL_BURST_LEN_8_8 = 0x1,
EPDC_CTRL_BURST_LEN_8_16 = 0,
/* EPDC_RES field values */
EPDC_RES_VERTICAL_MASK = 0x1FFF0000,
EPDC_RES_VERTICAL_OFFSET = 16,
EPDC_RES_HORIZONTAL_MASK = 0x1FFF,
EPDC_RES_HORIZONTAL_OFFSET = 0,
/* EPDC_FORMAT field values */
EPDC_FORMAT_BUF_PIXEL_SCALE_ROUND = 0x1000000,
EPDC_FORMAT_DEFAULT_TFT_PIXEL_MASK = 0xFF0000,
EPDC_FORMAT_DEFAULT_TFT_PIXEL_OFFSET = 16,
EPDC_FORMAT_BUF_PIXEL_FORMAT_MASK = 0x700,
EPDC_FORMAT_BUF_PIXEL_FORMAT_P2N = 0x200,
EPDC_FORMAT_BUF_PIXEL_FORMAT_P3N = 0x300,
EPDC_FORMAT_BUF_PIXEL_FORMAT_P4N = 0x400,
EPDC_FORMAT_BUF_PIXEL_FORMAT_P5N = 0x500,
EPDC_FORMAT_TFT_PIXEL_FORMAT_2BIT = 0x0,
EPDC_FORMAT_TFT_PIXEL_FORMAT_2BIT_VCOM = 0x1,
EPDC_FORMAT_TFT_PIXEL_FORMAT_4BIT = 0x2,
EPDC_FORMAT_TFT_PIXEL_FORMAT_4BIT_VCOM = 0x3,
/* EPDC_FIFOCTRL field values */
EPDC_FIFOCTRL_ENABLE_PRIORITY = 0x80000000,
EPDC_FIFOCTRL_FIFO_INIT_LEVEL_MASK = 0xFF0000,
EPDC_FIFOCTRL_FIFO_INIT_LEVEL_OFFSET = 16,
EPDC_FIFOCTRL_FIFO_H_LEVEL_MASK = 0xFF00,
EPDC_FIFOCTRL_FIFO_H_LEVEL_OFFSET = 8,
EPDC_FIFOCTRL_FIFO_L_LEVEL_MASK = 0xFF,
EPDC_FIFOCTRL_FIFO_L_LEVEL_OFFSET = 0,
/* EPDC_UPD_CORD field values */
EPDC_UPD_CORD_YCORD_MASK = 0x1FFF0000,
EPDC_UPD_CORD_YCORD_OFFSET = 16,
EPDC_UPD_CORD_XCORD_MASK = 0x1FFF,
EPDC_UPD_CORD_XCORD_OFFSET = 0,
/* EPDC_UPD_SIZE field values */
EPDC_UPD_SIZE_HEIGHT_MASK = 0x1FFF0000,
EPDC_UPD_SIZE_HEIGHT_OFFSET = 16,
EPDC_UPD_SIZE_WIDTH_MASK = 0x1FFF,
EPDC_UPD_SIZE_WIDTH_OFFSET = 0,
/* EPDC_UPD_CTRL field values */
EPDC_UPD_CTRL_USE_FIXED = 0x80000000,
EPDC_UPD_CTRL_LUT_SEL_MASK = 0x3F0000,
EPDC_UPD_CTRL_LUT_SEL_OFFSET = 16,
EPDC_UPD_CTRL_WAVEFORM_MODE_MASK = 0xFF00,
EPDC_UPD_CTRL_WAVEFORM_MODE_OFFSET = 8,
EPDC_UPD_CTRL_AUTOWV_PAUSE = 0x8,
EPDC_UPD_CTRL_AUTOWV = 0x4,
EPDC_UPD_CTRL_DRY_RUN = 0x2,
EPDC_UPD_CTRL_UPDATE_MODE_FULL = 0x1,
/* EPDC_UPD_FIXED field values */
EPDC_UPD_FIXED_FIXNP_EN = 0x80000000,
EPDC_UPD_FIXED_FIXCP_EN = 0x40000000,
EPDC_UPD_FIXED_FIXNP_MASK = 0xFF00,
EPDC_UPD_FIXED_FIXNP_OFFSET = 8,
EPDC_UPD_FIXED_FIXCP_MASK = 0xFF,
EPDC_UPD_FIXED_FIXCP_OFFSET = 0,
/* EPDC_AUTOWV_LUT field values */
EPDC_AUTOWV_LUT_DATA_MASK = 0xFF0000,
EPDC_AUTOWV_LUT_DATA_OFFSET = 16,
EPDC_AUTOWV_LUT_ADDR_MASK = 0xFF,
EPDC_AUTOWV_LUT_ADDR_OFFSET = 0,
/* EPDC_TCE_CTRL field values */
EPDC_TCE_CTRL_VSCAN_HOLDOFF_MASK = 0x1FF0000,
EPDC_TCE_CTRL_VSCAN_HOLDOFF_OFFSET = 16,
EPDC_TCE_CTRL_VCOM_VAL_MASK = 0xC00,
EPDC_TCE_CTRL_VCOM_VAL_OFFSET = 10,
EPDC_TCE_CTRL_VCOM_MODE_AUTO = 0x200,
EPDC_TCE_CTRL_VCOM_MODE_MANUAL = 0x000,
EPDC_TCE_CTRL_DDR_MODE_ENABLE = 0x100,
EPDC_TCE_CTRL_LVDS_MODE_CE_ENABLE = 0x80,
EPDC_TCE_CTRL_LVDS_MODE_ENABLE = 0x40,
EPDC_TCE_CTRL_SCAN_DIR_1_UP = 0x20,
EPDC_TCE_CTRL_SCAN_DIR_0_UP = 0x10,
EPDC_TCE_CTRL_DUAL_SCAN_ENABLE = 0x8,
EPDC_TCE_CTRL_SDDO_WIDTH_16BIT = 0x4,
EPDC_TCE_CTRL_PIXELS_PER_SDCLK_2 = 1,
EPDC_TCE_CTRL_PIXELS_PER_SDCLK_4 = 2,
EPDC_TCE_CTRL_PIXELS_PER_SDCLK_8 = 3,
/* EPDC_TCE_SDCFG field values */
EPDC_TCE_SDCFG_SDCLK_HOLD = 0x200000,
EPDC_TCE_SDCFG_SDSHR = 0x100000,
EPDC_TCE_SDCFG_NUM_CE_MASK = 0xF0000,
EPDC_TCE_SDCFG_NUM_CE_OFFSET = 16,
EPDC_TCE_SDCFG_SDDO_REFORMAT_STANDARD = 0,
EPDC_TCE_SDCFG_SDDO_REFORMAT_FLIP_PIXELS = 0x4000,
EPDC_TCE_SDCFG_SDDO_INVERT_ENABLE = 0x2000,
EPDC_TCE_SDCFG_PIXELS_PER_CE_MASK = 0x1FFF,
EPDC_TCE_SDCFG_PIXELS_PER_CE_OFFSET = 0,
/* EPDC_TCE_GDCFG field values */
EPDC_TCE_SDCFG_GDRL = 0x10,
EPDC_TCE_SDCFG_GDOE_MODE_DELAYED_GDCLK = 0x2,
EPDC_TCE_SDCFG_GDSP_MODE_FRAME_SYNC = 0x1,
EPDC_TCE_SDCFG_GDSP_MODE_ONE_LINE = 0x0,
/* EPDC_TCE_HSCAN1 field values */
EPDC_TCE_HSCAN1_LINE_SYNC_WIDTH_MASK = 0xFFF0000,
EPDC_TCE_HSCAN1_LINE_SYNC_WIDTH_OFFSET = 16,
EPDC_TCE_HSCAN1_LINE_SYNC_MASK = 0xFFF,
EPDC_TCE_HSCAN1_LINE_SYNC_OFFSET = 0,
/* EPDC_TCE_HSCAN2 field values */
EPDC_TCE_HSCAN2_LINE_END_MASK = 0xFFF0000,
EPDC_TCE_HSCAN2_LINE_END_OFFSET = 16,
EPDC_TCE_HSCAN2_LINE_BEGIN_MASK = 0xFFF,
EPDC_TCE_HSCAN2_LINE_BEGIN_OFFSET = 0,
/* EPDC_TCE_VSCAN field values */
EPDC_TCE_VSCAN_FRAME_END_MASK = 0xFF0000,
EPDC_TCE_VSCAN_FRAME_END_OFFSET = 16,
EPDC_TCE_VSCAN_FRAME_BEGIN_MASK = 0xFF00,
EPDC_TCE_VSCAN_FRAME_BEGIN_OFFSET = 8,
EPDC_TCE_VSCAN_FRAME_SYNC_MASK = 0xFF,
EPDC_TCE_VSCAN_FRAME_SYNC_OFFSET = 0,
/* EPDC_TCE_OE field values */
EPDC_TCE_OE_SDOED_WIDTH_MASK = 0xFF000000,
EPDC_TCE_OE_SDOED_WIDTH_OFFSET = 24,
EPDC_TCE_OE_SDOED_DLY_MASK = 0xFF0000,
EPDC_TCE_OE_SDOED_DLY_OFFSET = 16,
EPDC_TCE_OE_SDOEZ_WIDTH_MASK = 0xFF00,
EPDC_TCE_OE_SDOEZ_WIDTH_OFFSET = 8,
EPDC_TCE_OE_SDOEZ_DLY_MASK = 0xFF,
EPDC_TCE_OE_SDOEZ_DLY_OFFSET = 0,
/* EPDC_TCE_POLARITY field values */
EPDC_TCE_POLARITY_GDSP_POL_ACTIVE_HIGH = 0x10,
EPDC_TCE_POLARITY_GDOE_POL_ACTIVE_HIGH = 0x8,
EPDC_TCE_POLARITY_SDOE_POL_ACTIVE_HIGH = 0x4,
EPDC_TCE_POLARITY_SDLE_POL_ACTIVE_HIGH = 0x2,
EPDC_TCE_POLARITY_SDCE_POL_ACTIVE_HIGH = 0x1,
/* EPDC_TCE_TIMING1 field values */
EPDC_TCE_TIMING1_SDLE_SHIFT_NONE = 0x00,
EPDC_TCE_TIMING1_SDLE_SHIFT_1 = 0x10,
EPDC_TCE_TIMING1_SDLE_SHIFT_2 = 0x20,
EPDC_TCE_TIMING1_SDLE_SHIFT_3 = 0x30,
EPDC_TCE_TIMING1_SDCLK_INVERT = 0x8,
EPDC_TCE_TIMING1_SDCLK_SHIFT_NONE = 0,
EPDC_TCE_TIMING1_SDCLK_SHIFT_1CYCLE = 1,
EPDC_TCE_TIMING1_SDCLK_SHIFT_2CYCLES = 2,
EPDC_TCE_TIMING1_SDCLK_SHIFT_3CYCLES = 3,
/* EPDC_TCE_TIMING2 field values */
EPDC_TCE_TIMING2_GDCLK_HP_MASK = 0xFFFF0000,
EPDC_TCE_TIMING2_GDCLK_HP_OFFSET = 16,
EPDC_TCE_TIMING2_GDSP_OFFSET_MASK = 0xFFFF,
EPDC_TCE_TIMING2_GDSP_OFFSET_OFFSET = 0,
/* EPDC_TCE_TIMING3 field values */
EPDC_TCE_TIMING3_GDOE_OFFSET_MASK = 0xFFFF0000,
EPDC_TCE_TIMING3_GDOE_OFFSET_OFFSET = 16,
EPDC_TCE_TIMING3_GDCLK_OFFSET_MASK = 0xFFFF,
EPDC_TCE_TIMING3_GDCLK_OFFSET_OFFSET = 0,
/* EPDC_IRQ_MASK/EPDC_IRQ field values */
EPDC_IRQ_WB_CMPLT_IRQ = 0x10000,
EPDC_IRQ_LUT_COL_IRQ = 0x20000,
EPDC_IRQ_TCE_UNDERRUN_IRQ = 0x40000,
EPDC_IRQ_FRAME_END_IRQ = 0x80000,
EPDC_IRQ_BUS_ERROR_IRQ = 0x100000,
EPDC_IRQ_TCE_IDLE_IRQ = 0x200000,
EPDC_IRQ_UPD_DONE_IRQ = 0x400000,
EPDC_IRQ_PWR_IRQ = 0x800000,
/* EPDC_STATUS_NEXTLUT field values */
EPDC_STATUS_NEXTLUT_NEXT_LUT_VALID = 0x100,
EPDC_STATUS_NEXTLUT_NEXT_LUT_MASK = 0x3F,
EPDC_STATUS_NEXTLUT_NEXT_LUT_OFFSET = 0,
/* EPDC_STATUS field values */
EPDC_STATUS_HISTOGRAM_CP_MASK = 0x1F0000,
EPDC_STATUS_HISTOGRAM_CP_OFFSET = 16,
EPDC_STATUS_HISTOGRAM_NP_MASK = 0x1F00,
EPDC_STATUS_HISTOGRAM_NP_OFFSET = 8,
EPDC_STATUS_UPD_VOID = 0x8,
EPDC_STATUS_LUTS_UNDERRUN = 0x4,
EPDC_STATUS_LUTS_BUSY = 0x2,
EPDC_STATUS_WB_BUSY = 0x1,
/* EPDC_UPD_COL_CORD field values */
EPDC_UPD_COL_CORD_YCORD_MASK = 0x1FFF0000,
EPDC_UPD_COL_CORD_YCORD_OFFSET = 16,
EPDC_UPD_COL_CORD_XCORD_MASK = 0x1FFF,
EPDC_UPD_COL_CORD_XCORD_OFFSET = 0,
/* EPDC_UPD_COL_SIZE field values */
EPDC_UPD_COL_SIZE_HEIGHT_MASK = 0x1FFF0000,
EPDC_UPD_COL_SIZE_HEIGHT_OFFSET = 16,
EPDC_UPD_COL_SIZE_WIDTH_MASK = 0x1FFF,
EPDC_UPD_COL_SIZE_WIDTH_OFFSET = 0,
/* EPDC_DEBUG field values */
EPDC_DEBUG_UNDERRUN_RECOVER = 0x2,
EPDC_DEBUG_COLLISION_OFF = 0x1,
/* EPDC_HISTx_PARAM field values */
EPDC_HIST_PARAM_VALUE0_MASK = 0x1F,
EPDC_HIST_PARAM_VALUE0_OFFSET = 0,
EPDC_HIST_PARAM_VALUE1_MASK = 0x1F00,
EPDC_HIST_PARAM_VALUE1_OFFSET = 8,
EPDC_HIST_PARAM_VALUE2_MASK = 0x1F0000,
EPDC_HIST_PARAM_VALUE2_OFFSET = 16,
EPDC_HIST_PARAM_VALUE3_MASK = 0x1F000000,
EPDC_HIST_PARAM_VALUE3_OFFSET = 24,
EPDC_HIST_PARAM_VALUE4_MASK = 0x1F,
EPDC_HIST_PARAM_VALUE4_OFFSET = 0,
EPDC_HIST_PARAM_VALUE5_MASK = 0x1F00,
EPDC_HIST_PARAM_VALUE5_OFFSET = 8,
EPDC_HIST_PARAM_VALUE6_MASK = 0x1F0000,
EPDC_HIST_PARAM_VALUE6_OFFSET = 16,
EPDC_HIST_PARAM_VALUE7_MASK = 0x1F000000,
EPDC_HIST_PARAM_VALUE7_OFFSET = 24,
EPDC_HIST_PARAM_VALUE8_MASK = 0x1F,
EPDC_HIST_PARAM_VALUE8_OFFSET = 0,
EPDC_HIST_PARAM_VALUE9_MASK = 0x1F00,
EPDC_HIST_PARAM_VALUE9_OFFSET = 8,
EPDC_HIST_PARAM_VALUE10_MASK = 0x1F0000,
EPDC_HIST_PARAM_VALUE10_OFFSET = 16,
EPDC_HIST_PARAM_VALUE11_MASK = 0x1F000000,
EPDC_HIST_PARAM_VALUE11_OFFSET = 24,
EPDC_HIST_PARAM_VALUE12_MASK = 0x1F,
EPDC_HIST_PARAM_VALUE12_OFFSET = 0,
EPDC_HIST_PARAM_VALUE13_MASK = 0x1F00,
EPDC_HIST_PARAM_VALUE13_OFFSET = 8,
EPDC_HIST_PARAM_VALUE14_MASK = 0x1F0000,
EPDC_HIST_PARAM_VALUE14_OFFSET = 16,
EPDC_HIST_PARAM_VALUE15_MASK = 0x1F000000,
EPDC_HIST_PARAM_VALUE15_OFFSET = 24,
/* EPDC_GPIO field values */
EPDC_GPIO_PWRCOM = 0x40,
EPDC_GPIO_PWRCTRL_MASK = 0x3C,
EPDC_GPIO_PWRCTRL_OFFSET = 2,
EPDC_GPIO_BDR_MASK = 0x3,
EPDC_GPIO_BDR_OFFSET = 0,
/* EPDC_VERSION field values */
EPDC_VERSION_MAJOR_MASK = 0xFF000000,
EPDC_VERSION_MAJOR_OFFSET = 24,
EPDC_VERSION_MINOR_MASK = 0xFF0000,
EPDC_VERSION_MINOR_OFFSET = 16,
EPDC_VERSION_STEP_MASK = 0xFFFF,
EPDC_VERSION_STEP_OFFSET = 0,
};
#endif /* __EPDC_REGS_INCLUDED__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/* Copyright (C) 2020 Andreas Kemnade */
int mxc_epdc_send_single_update(struct drm_rect *clip, int pitch,
void *vaddr,
struct mxc_epdc *priv);
void mxc_epdc_draw_mode0(struct mxc_epdc *priv);
int mxc_epdc_init_update(struct mxc_epdc *priv);
void mxc_epdc_flush_updates(struct mxc_epdc *priv);

View File

@@ -0,0 +1,185 @@
// SPDX-License-Identifier: GPL-2.0+
// Copyright (C) 2020 Andreas Kemnade
//
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include "mxc_epdc.h"
#define DEFAULT_TEMP_INDEX 0
#define DEFAULT_TEMP 20 /* room temp in deg Celsius */
struct waveform_data_header {
unsigned int wi0;
unsigned int wi1;
unsigned int wi2;
unsigned int wi3;
unsigned int wi4;
unsigned int wi5;
unsigned int wi6;
unsigned int xwia:24;
unsigned int cs1:8;
unsigned int wmta:24;
unsigned int fvsn:8;
unsigned int luts:8;
unsigned int mc:8;
unsigned int trc:8;
unsigned int reserved0_0:8;
unsigned int eb:8;
unsigned int sb:8;
unsigned int reserved0_1:8;
unsigned int reserved0_2:8;
unsigned int reserved0_3:8;
unsigned int reserved0_4:8;
unsigned int reserved0_5:8;
unsigned int cs2:8;
};
struct mxcfb_waveform_data_file {
struct waveform_data_header wdh;
u32 *data; /* Temperature Range Table + Waveform Data */
};
void mxc_epdc_set_update_waveform(struct mxc_epdc *priv,
struct mxcfb_waveform_modes *wv_modes)
{
u32 val;
/* Configure the auto-waveform look-up table based on waveform modes */
/* Entry 1 = DU, 2 = GC4, 3 = GC8, etc. */
val = (wv_modes->mode_du << EPDC_AUTOWV_LUT_DATA_OFFSET) |
(0 << EPDC_AUTOWV_LUT_ADDR_OFFSET);
epdc_write(priv, EPDC_AUTOWV_LUT, val);
val = (wv_modes->mode_du << EPDC_AUTOWV_LUT_DATA_OFFSET) |
(1 << EPDC_AUTOWV_LUT_ADDR_OFFSET);
epdc_write(priv, EPDC_AUTOWV_LUT, val);
val = (wv_modes->mode_gc4 << EPDC_AUTOWV_LUT_DATA_OFFSET) |
(2 << EPDC_AUTOWV_LUT_ADDR_OFFSET);
epdc_write(priv, EPDC_AUTOWV_LUT, val);
val = (wv_modes->mode_gc8 << EPDC_AUTOWV_LUT_DATA_OFFSET) |
(3 << EPDC_AUTOWV_LUT_ADDR_OFFSET);
epdc_write(priv, EPDC_AUTOWV_LUT, val);
val = (wv_modes->mode_gc16 << EPDC_AUTOWV_LUT_DATA_OFFSET) |
(4 << EPDC_AUTOWV_LUT_ADDR_OFFSET);
epdc_write(priv, EPDC_AUTOWV_LUT, val);
val = (wv_modes->mode_gc32 << EPDC_AUTOWV_LUT_DATA_OFFSET) |
(5 << EPDC_AUTOWV_LUT_ADDR_OFFSET);
epdc_write(priv, EPDC_AUTOWV_LUT, val);
}
int mxc_epdc_fb_get_temp_index(struct mxc_epdc *priv, int temp)
{
int i;
int index = -1;
if (temp == TEMP_USE_AMBIENT) {
if (priv->thermal) {
if (thermal_zone_get_temp(priv->thermal, &temp)) {
dev_err(priv->drm.dev,
"reading temperature failed");
return DEFAULT_TEMP_INDEX;
}
temp /= 1000;
} else
temp = DEFAULT_TEMP;
}
if (priv->trt_entries == 0) {
dev_err(priv->drm.dev,
"No TRT exists...using default temp index\n");
return DEFAULT_TEMP_INDEX;
}
/* Search temperature ranges for a match */
for (i = 0; i < priv->trt_entries - 1; i++) {
if ((temp >= priv->temp_range_bounds[i])
&& (temp < priv->temp_range_bounds[i+1])) {
index = i;
break;
}
}
if (index < 0) {
dev_err(priv->drm.dev,
"No TRT index match...using lowest/highest\n");
if (temp < priv->temp_range_bounds[0]) {
dev_dbg(priv->drm.dev, "temperature < minimum range\n");
return 0;
}
if (temp >= priv->temp_range_bounds[priv->trt_entries-1]) {
dev_dbg(priv->drm.dev, "temperature >= maximum range\n");
return priv->trt_entries-1;
}
return DEFAULT_TEMP_INDEX;
}
dev_dbg(priv->drm.dev, "Using temperature index %d\n", index);
return index;
}
int mxc_epdc_prepare_waveform(struct mxc_epdc *priv,
const u8 *data, size_t size)
{
int ret;
const struct mxcfb_waveform_data_file *wv_file;
int wv_data_offs;
int i;
priv->wv_modes.mode_init = 0;
priv->wv_modes.mode_du = 1;
priv->wv_modes.mode_gc4 = 3;
priv->wv_modes.mode_gc8 = 2;
priv->wv_modes.mode_gc16 = 2;
priv->wv_modes.mode_gc32 = 2;
priv->wv_modes_update = true;
wv_file = (struct mxcfb_waveform_data_file *)data;
/* Get size and allocate temperature range table */
priv->trt_entries = wv_file->wdh.trc + 1;
priv->temp_range_bounds = devm_kzalloc(priv->drm.dev, priv->trt_entries, GFP_KERNEL);
for (i = 0; i < priv->trt_entries; i++)
dev_dbg(priv->drm.dev, "trt entry #%d = 0x%x\n", i, *((u8 *)&wv_file->data + i));
/* Copy TRT data */
memcpy(priv->temp_range_bounds, &wv_file->data, priv->trt_entries);
/* Set default temperature index using TRT and room temp */
priv->temp_index = mxc_epdc_fb_get_temp_index(priv, DEFAULT_TEMP);
/* Get offset and size for waveform data */
wv_data_offs = sizeof(wv_file->wdh) + priv->trt_entries + 1;
priv->waveform_buffer_size = size - wv_data_offs;
/* Allocate memory for waveform data */
priv->waveform_buffer_virt = dmam_alloc_coherent(priv->drm.dev,
priv->waveform_buffer_size,
&priv->waveform_buffer_phys,
GFP_DMA | GFP_KERNEL);
if (priv->waveform_buffer_virt == NULL) {
dev_err(priv->drm.dev, "Can't allocate mem for waveform!\n");
return -ENOMEM;
}
memcpy(priv->waveform_buffer_virt, (u8 *)(data) + wv_data_offs,
priv->waveform_buffer_size);
/* Read field to determine if 4-bit or 5-bit mode */
if ((wv_file->wdh.luts & 0xC) == 0x4)
priv->buf_pix_fmt = EPDC_FORMAT_BUF_PIXEL_FORMAT_P5N;
else
priv->buf_pix_fmt = EPDC_FORMAT_BUF_PIXEL_FORMAT_P4N;
dev_info(priv->drm.dev, "EPDC pix format: %x\n",
priv->buf_pix_fmt);
return 0;
}

View File

@@ -0,0 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/* Copyright (C) 2020 Andreas Kemnade */
int mxc_epdc_fb_get_temp_index(struct mxc_epdc *priv, int temp);
int mxc_epdc_prepare_waveform(struct mxc_epdc *priv,
const u8 *waveform, size_t size);
void mxc_epdc_set_update_waveform(struct mxc_epdc *priv,
struct mxcfb_waveform_modes *wv_modes);

View File

@@ -0,0 +1,153 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/* Copyright (C) 2020 Andreas Kemnade */
#include <video/display_timing.h>
#include <video/of_display_timing.h>
#include <video/videomode.h>
#include <drm/drm_drv.h>
#include <drm/drm_connector.h>
#include <drm/drm_simple_kms_helper.h>
#include <linux/thermal.h>
#include "epdc_regs.h"
#define GRAYSCALE_8BIT 0x1
#define GRAYSCALE_8BIT_INVERTED 0x2
#define GRAYSCALE_4BIT 0x3
#define GRAYSCALE_4BIT_INVERTED 0x4
#define AUTO_UPDATE_MODE_REGION_MODE 0
#define AUTO_UPDATE_MODE_AUTOMATIC_MODE 1
#define UPDATE_SCHEME_SNAPSHOT 0
#define UPDATE_SCHEME_QUEUE 1
#define UPDATE_SCHEME_QUEUE_AND_MERGE 2
#define UPDATE_MODE_PARTIAL 0x0
#define UPDATE_MODE_FULL 0x1
#define WAVEFORM_MODE_GLR16 4
#define WAVEFORM_MODE_GLD16 5
#define WAVEFORM_MODE_AUTO 257
#define TEMP_USE_AMBIENT 0x1000
#define FB_POWERDOWN_DISABLE -1
struct mxcfb_update_data {
struct drm_rect update_region;
u32 waveform_mode;
u32 update_mode;
int temp;
int dither_mode;
int quant_bit;
};
struct mxcfb_waveform_modes {
int mode_init;
int mode_du;
int mode_gc4;
int mode_gc8;
int mode_gc16;
int mode_gc32;
};
struct imx_epdc_fb_mode {
u32 vscan_holdoff;
u32 sdoed_width;
u32 sdoed_delay;
u32 sdoez_width;
u32 sdoez_delay;
u32 gdclk_hp_offs;
u32 gdsp_offs;
u32 gdoe_offs;
u32 gdclk_offs;
u32 num_ce;
};
struct clk;
struct regulator;
struct mxc_epdc {
struct drm_device drm;
struct drm_simple_display_pipe pipe;
struct drm_connector connector;
struct display_timing timing;
struct imx_epdc_fb_mode imx_mode;
void __iomem *iobase;
struct completion powerdown_compl;
struct clk *epdc_clk_axi;
struct clk *epdc_clk_pix;
struct regulator *display_regulator;
struct regulator *vcom_regulator;
struct regulator *v3p3_regulator;
struct thermal_zone_device *thermal;
int rev;
dma_addr_t epdc_mem_phys;
void *epdc_mem_virt;
int epdc_mem_width;
int epdc_mem_height;
u32 *working_buffer_virt;
dma_addr_t working_buffer_phys;
u32 working_buffer_size;
/* waveform related stuff */
int trt_entries;
int temp_index;
u8 *temp_range_bounds;
int buf_pix_fmt;
struct mxcfb_waveform_modes wv_modes;
bool wv_modes_update;
u32 *waveform_buffer_virt;
dma_addr_t waveform_buffer_phys;
u32 waveform_buffer_size;
struct mutex power_mutex;
bool powered;
bool powering_down;
bool updates_active;
int wait_for_powerdown;
int pwrdown_delay;
/* elements related to EPDC updates */
int num_luts;
int max_num_updates;
#warning take of init of in_init hw_ready, hw_initializing
bool in_init;
bool hw_ready;
bool hw_initializing;
bool waiting_for_idle;
int order_cnt;
struct list_head upd_pending_list;
struct list_head upd_buf_queue;
struct list_head upd_buf_free_list;
struct list_head upd_buf_collision_list;
struct update_data_list *cur_update;
struct mutex queue_mutex;
int epdc_irq;
struct list_head full_marker_list;
u32 *lut_update_order;
u64 epdc_colliding_luts;
u64 luts_complete_wb;
struct completion updates_done;
struct delayed_work epdc_done_work;
struct workqueue_struct *epdc_submit_workqueue;
struct work_struct epdc_submit_work;
struct workqueue_struct *epdc_intr_workqueue;
struct work_struct epdc_intr_work;
bool waiting_for_wb;
bool waiting_for_lut;
struct completion update_res_free;
};
static inline u32 epdc_read(struct mxc_epdc *priv, u32 reg)
{
return readl(priv->iobase + reg);
}
static inline void epdc_write(struct mxc_epdc *priv, u32 reg, u32 data)
{
writel(data, priv->iobase + reg);
}

View File

@@ -0,0 +1,368 @@
// SPDX-License-Identifier: GPL-2.0+
// Copyright (C) 2020 Andreas Kemnade
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/dma-mapping.h>
#include <linux/firmware.h>
#include <linux/fs.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/clients/drm_client_setup.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fbdev_ttm.h>
#include <drm/drm_file.h>
#include <drm/drm_format_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_panel.h>
#include <drm/drm_prime.h>
#include <drm/drm_probe_helper.h>
#include "mxc_epdc.h"
#include "epdc_hw.h"
#include "epdc_update.h"
#include "epdc_waveform.h"
#define DRIVER_NAME "mxc_epdc"
#define DRIVER_DESC "IMX EPDC"
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
#define DRIVER_PATCHLEVEL 0
#define to_mxc_epdc(x) container_of(x, struct mxc_epdc, drm)
static const struct drm_mode_config_funcs mxc_epdc_mode_config_funcs = {
.fb_create = drm_gem_fb_create_with_dirty,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
static struct mxc_epdc *
drm_pipe_to_mxc_epdc(struct drm_simple_display_pipe *pipe)
{
return container_of(pipe, struct mxc_epdc, pipe);
}
static struct mxc_epdc *
drm_connector_to_mxc_epdc(struct drm_connector *connector)
{
return container_of(connector, struct mxc_epdc, connector);
}
static void mxc_epdc_setup_mode_config(struct drm_device *drm)
{
drm_mode_config_init(drm);
drm->mode_config.min_width = 0;
drm->mode_config.min_height = 0;
/*
* Maximum update buffer image width due to v2.0 and v2.1 errata
* ERR005313.
*/
drm->mode_config.max_width = 2047;
drm->mode_config.max_height = 2048;
drm->mode_config.funcs = &mxc_epdc_mode_config_funcs;
}
DEFINE_DRM_GEM_DMA_FOPS(fops);
static int mxc_epdc_get_modes(struct drm_connector *connector)
{
struct mxc_epdc *priv = drm_connector_to_mxc_epdc(connector);
struct drm_display_mode *mode;
struct videomode vm;
videomode_from_timing(&priv->timing, &vm);
mode = drm_mode_create(connector->dev);
if (!mode) {
dev_err(priv->drm.dev, "failed to add mode\n");
return 0;
}
drm_display_mode_from_videomode(&vm, mode);
mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
return 1;
}
static const struct
drm_connector_helper_funcs mxc_epdc_connector_helper_funcs = {
.get_modes = mxc_epdc_get_modes,
};
static const struct drm_connector_funcs mxc_epdc_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
int mxc_epdc_output(struct drm_device *drm)
{
struct mxc_epdc *priv = to_mxc_epdc(drm);
int ret;
priv->connector.dpms = DRM_MODE_DPMS_OFF;
priv->connector.polled = 0;
drm_connector_helper_add(&priv->connector,
&mxc_epdc_connector_helper_funcs);
ret = drm_connector_init(drm, &priv->connector,
&mxc_epdc_connector_funcs,
DRM_MODE_CONNECTOR_Unknown);
if (ret)
return ret;
ret = of_property_read_u32(drm->dev->of_node, "vscan-holdoff",
&priv->imx_mode.vscan_holdoff);
if (ret)
return ret;
ret = of_property_read_u32(drm->dev->of_node, "sdoed-width",
&priv->imx_mode.sdoed_width);
if (ret)
return ret;
ret = of_property_read_u32(drm->dev->of_node, "sdoed-delay",
&priv->imx_mode.sdoed_delay);
if (ret)
return ret;
ret = of_property_read_u32(drm->dev->of_node, "sdoez-width",
&priv->imx_mode.sdoez_width);
if (ret)
return ret;
ret = of_property_read_u32(drm->dev->of_node, "sdoez-delay",
&priv->imx_mode.sdoez_delay);
if (ret)
return ret;
ret = of_property_read_u32(drm->dev->of_node, "gdclk-hp-offs",
&priv->imx_mode.gdclk_hp_offs);
if (ret)
return ret;
ret = of_property_read_u32(drm->dev->of_node, "gdsp-offs",
&priv->imx_mode.gdsp_offs);
if (ret)
return ret;
ret = of_property_read_u32(drm->dev->of_node, "gdoe-offs",
&priv->imx_mode.gdoe_offs);
if (ret)
return ret;
ret = of_property_read_u32(drm->dev->of_node, "gdclk-offs",
&priv->imx_mode.gdclk_offs);
if (ret)
return ret;
ret = of_property_read_u32(drm->dev->of_node, "num-ce",
&priv->imx_mode.num_ce);
if (ret)
return ret;
ret = of_get_display_timing(drm->dev->of_node, "timing", &priv->timing);
if (ret)
return ret;
return 0;
}
static void mxc_epdc_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state,
struct drm_plane_state *plane_state)
{
struct mxc_epdc *priv = drm_pipe_to_mxc_epdc(pipe);
struct drm_display_mode *m = &pipe->crtc.state->adjusted_mode;
dev_info(priv->drm.dev, "Mode: %d x %d\n", m->hdisplay, m->vdisplay);
priv->epdc_mem_width = m->hdisplay;
priv->epdc_mem_height = m->vdisplay;
priv->epdc_mem_virt = dma_alloc_wc(priv->drm.dev,
m->hdisplay * m->vdisplay,
&priv->epdc_mem_phys, GFP_DMA | GFP_KERNEL);
priv->working_buffer_size = m->hdisplay * m->vdisplay * 2;
priv->working_buffer_virt =
dma_alloc_coherent(priv->drm.dev,
priv->working_buffer_size,
&priv->working_buffer_phys,
GFP_DMA | GFP_KERNEL);
if (priv->working_buffer_virt && priv->epdc_mem_virt)
mxc_epdc_init_sequence(priv, m);
}
static void mxc_epdc_pipe_disable(struct drm_simple_display_pipe *pipe)
{
struct mxc_epdc *priv = drm_pipe_to_mxc_epdc(pipe);
dev_dbg(priv->drm.dev, "pipe disable\n");
mxc_epdc_flush_updates(priv);
if (priv->epdc_mem_virt) {
dma_free_wc(priv->drm.dev, priv->epdc_mem_width * priv->epdc_mem_height,
priv->epdc_mem_virt, priv->epdc_mem_phys);
priv->epdc_mem_virt = NULL;
}
if (priv->working_buffer_virt) {
dma_free_wc(priv->drm.dev, priv->working_buffer_size,
priv->working_buffer_virt,
priv->working_buffer_phys);
priv->working_buffer_virt = NULL;
}
}
static void mxc_epdc_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state)
{
struct mxc_epdc *priv = drm_pipe_to_mxc_epdc(pipe);
struct drm_gem_dma_object *gem;
struct drm_atomic_helper_damage_iter iter;
struct drm_rect clip;
dev_dbg(priv->drm.dev, "pipe update\n");
if (!old_state->fb) {
dev_dbg(priv->drm.dev, "no fb, nothing to update\n");
return;
}
if (priv->epdc_mem_virt == NULL)
return;
gem = drm_fb_dma_get_gem_obj(pipe->plane.state->fb, 0);
drm_atomic_helper_damage_iter_init(&iter, old_state, pipe->plane.state);
drm_atomic_for_each_plane_damage(&iter, &clip) {
dev_dbg(priv->drm.dev, "damaged: %d,%d-%d,%d\n",
clip.x1, clip.y1, clip.x2, clip.y2);
mxc_epdc_send_single_update(&clip, old_state->fb->pitches[0],
gem->vaddr, priv);
}
return;
}
static const struct drm_simple_display_pipe_funcs mxc_epdc_funcs = {
.enable = mxc_epdc_pipe_enable,
.disable = mxc_epdc_pipe_disable,
.update = mxc_epdc_pipe_update,
};
static const uint32_t mxc_epdc_formats[] = {
DRM_FORMAT_XRGB8888,
};
static struct drm_driver mxc_epdc_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
.fops = &fops,
.dumb_create = drm_gem_dma_dumb_create,
.gem_prime_import_sg_table = drm_gem_dma_prime_import_sg_table,
DRM_FBDEV_TTM_DRIVER_OPS,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
};
static int mxc_epdc_probe(struct platform_device *pdev)
{
struct mxc_epdc *priv;
const struct firmware *firmware;
int ret;
priv = devm_drm_dev_alloc(&pdev->dev, &mxc_epdc_driver, struct mxc_epdc, drm);
if (IS_ERR(priv))
return PTR_ERR(priv);
platform_set_drvdata(pdev, priv);
ret = mxc_epdc_init_hw(priv);
if (ret)
return ret;
ret = mxc_epdc_init_update(priv);
if (ret)
return ret;
ret = request_firmware(&firmware, "imx/epdc/epdc.fw", priv->drm.dev);
if (ret)
return ret;
ret = mxc_epdc_prepare_waveform(priv, firmware->data, firmware->size);
release_firmware(firmware);
if (ret)
return ret;
mxc_epdc_setup_mode_config(&priv->drm);
ret = mxc_epdc_output(&priv->drm);
if (ret)
return ret;
drm_simple_display_pipe_init(&priv->drm, &priv->pipe, &mxc_epdc_funcs,
mxc_epdc_formats,
ARRAY_SIZE(mxc_epdc_formats),
NULL,
&priv->connector);
drm_plane_enable_fb_damage_clips(&priv->pipe.plane);
drm_mode_config_reset(&priv->drm);
ret = drm_dev_register(&priv->drm, 0);
drm_client_setup(&priv->drm, NULL);
return 0;
}
static void mxc_epdc_remove(struct platform_device *pdev)
{
struct mxc_epdc *priv = platform_get_drvdata(pdev);
drm_dev_unregister(&priv->drm);
drm_kms_helper_poll_fini(&priv->drm);
drm_mode_config_cleanup(&priv->drm);
}
static const struct of_device_id imx_epdc_dt_ids[] = {
{ .compatible = "fsl,imx6sl-epdc", },
{ .compatible = "fsl,imx6sll-epdc", },
{ .compatible = "fsl,imx6ull-epdc", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_epdc_dt_ids);
static struct platform_driver pdev = {
.driver = {
.name = "mxc_epdc",
.of_match_table = of_match_ptr(imx_epdc_dt_ids),
},
.probe = mxc_epdc_probe,
.remove = mxc_epdc_remove,
};
module_platform_driver(pdev);
MODULE_DESCRIPTION("IMX EPDC driver");
MODULE_LICENSE("GPL");

View File

@@ -267,6 +267,36 @@ config TOUCHSCREEN_CYTTSP_SPI
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called cyttsp_spi. module will be called cyttsp_spi.
config TOUCHSCREEN_CYTTSP4_CORE
tristate "Cypress TrueTouch Gen4 Touchscreen Driver"
help
Core driver for Cypress TrueTouch(tm) Standard Product
Generation4 touchscreen controllers.
Say Y here if you have a Cypress Gen4 touchscreen.
If unsure, say N.
To compile this driver as a module, choose M here.
config TOUCHSCREEN_CYTTSP4_I2C
tristate "support I2C bus connection"
depends on TOUCHSCREEN_CYTTSP4_CORE && I2C
help
Say Y here if the touchscreen is connected via I2C bus.
To compile this driver as a module, choose M here: the
module will be called cyttsp4_i2c.
config TOUCHSCREEN_CYTTSP4_SPI
tristate "support SPI bus connection"
depends on TOUCHSCREEN_CYTTSP4_CORE && SPI_MASTER
help
Say Y here if the touchscreen is connected via SPI bus.
To compile this driver as a module, choose M here: the
module will be called cyttsp4_spi.
config TOUCHSCREEN_CYTTSP5 config TOUCHSCREEN_CYTTSP5
tristate "Cypress TrueTouch Gen5 Touchscreen Driver" tristate "Cypress TrueTouch Gen5 Touchscreen Driver"
depends on I2C depends on I2C

View File

@@ -27,6 +27,9 @@ obj-$(CONFIG_TOUCHSCREEN_CY8CTMA140) += cy8ctma140.o
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_CORE) += cyttsp4_core.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_I2C) += cyttsp4_i2c.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_SPI) += cyttsp4_spi.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP5) += cyttsp5.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP5) += cyttsp5.o
obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,446 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cyttsp4_core.h
* Cypress TrueTouch(TM) Standard Product V4 Core driver module.
* For use with Cypress Txx4xx parts.
* Supported parts include:
* TMA4XX
* TMA1036
*
* Copyright (C) 2012 Cypress Semiconductor
*
* Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
*/
#ifndef _LINUX_CYTTSP4_CORE_H
#define _LINUX_CYTTSP4_CORE_H
#include <linux/device.h>
#include <linux/err.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/limits.h>
#include <linux/module.h>
#include <linux/stringify.h>
#include <linux/types.h>
#include <linux/platform_data/cyttsp4.h>
#include <linux/of_gpio.h>
#define CY_REG_BASE 0x00
#define CY_POST_CODEL_WDG_RST 0x01
#define CY_POST_CODEL_CFG_DATA_CRC_FAIL 0x02
#define CY_POST_CODEL_PANEL_TEST_FAIL 0x04
#define CY_NUM_BTN_PER_REG 4
/* touch record system information offset masks and shifts */
#define CY_BYTE_OFS_MASK 0x1F
#define CY_BOFS_MASK 0xE0
#define CY_BOFS_SHIFT 5
#define CY_TMA1036_TCH_REC_SIZE 6
#define CY_TMA4XX_TCH_REC_SIZE 9
#define CY_TMA1036_MAX_TCH 0x0E
#define CY_TMA4XX_MAX_TCH 0x1E
#define CY_NORMAL_ORIGIN 0 /* upper, left corner */
#define CY_INVERT_ORIGIN 1 /* lower, right corner */
/* helpers */
#define GET_NUM_TOUCHES(x) ((x) & 0x1F)
#define IS_LARGE_AREA(x) ((x) & 0x20)
#define IS_BAD_PKT(x) ((x) & 0x20)
#define IS_BOOTLOADER(hst_mode, reset_detect) \
((hst_mode) & 0x01 || (reset_detect) != 0)
#define IS_TMO(t) ((t) == 0)
enum cyttsp_cmd_bits {
CY_CMD_COMPLETE = (1 << 6),
};
/* Timeout in ms. */
#define CY_WATCHDOG_TIMEOUT 1000
#define CY_MAX_PRINT_SIZE 512
#ifdef VERBOSE_DEBUG
#define CY_MAX_PRBUF_SIZE PIPE_BUF
#define CY_PR_TRUNCATED " truncated..."
#endif
enum cyttsp4_ic_grpnum {
CY_IC_GRPNUM_RESERVED,
CY_IC_GRPNUM_CMD_REGS,
CY_IC_GRPNUM_TCH_REP,
CY_IC_GRPNUM_DATA_REC,
CY_IC_GRPNUM_TEST_REC,
CY_IC_GRPNUM_PCFG_REC,
CY_IC_GRPNUM_TCH_PARM_VAL,
CY_IC_GRPNUM_TCH_PARM_SIZE,
CY_IC_GRPNUM_RESERVED1,
CY_IC_GRPNUM_RESERVED2,
CY_IC_GRPNUM_OPCFG_REC,
CY_IC_GRPNUM_DDATA_REC,
CY_IC_GRPNUM_MDATA_REC,
CY_IC_GRPNUM_TEST_REGS,
CY_IC_GRPNUM_BTN_KEYS,
CY_IC_GRPNUM_TTHE_REGS,
CY_IC_GRPNUM_NUM
};
enum cyttsp4_int_state {
CY_INT_NONE,
CY_INT_IGNORE = (1 << 0),
CY_INT_MODE_CHANGE = (1 << 1),
CY_INT_EXEC_CMD = (1 << 2),
CY_INT_AWAKE = (1 << 3),
};
enum cyttsp4_mode {
CY_MODE_UNKNOWN,
CY_MODE_BOOTLOADER = (1 << 1),
CY_MODE_OPERATIONAL = (1 << 2),
CY_MODE_SYSINFO = (1 << 3),
CY_MODE_CAT = (1 << 4),
CY_MODE_STARTUP = (1 << 5),
CY_MODE_LOADER = (1 << 6),
CY_MODE_CHANGE_MODE = (1 << 7),
CY_MODE_CHANGED = (1 << 8),
CY_MODE_CMD_COMPLETE = (1 << 9),
};
enum cyttsp4_sleep_state {
SS_SLEEP_OFF,
SS_SLEEP_ON,
SS_SLEEPING,
SS_WAKING,
};
enum cyttsp4_startup_state {
STARTUP_NONE,
STARTUP_QUEUED,
STARTUP_RUNNING,
};
#define CY_NUM_REVCTRL 8
struct cyttsp4_cydata {
u8 ttpidh;
u8 ttpidl;
u8 fw_ver_major;
u8 fw_ver_minor;
u8 revctrl[CY_NUM_REVCTRL];
u8 blver_major;
u8 blver_minor;
u8 jtag_si_id3;
u8 jtag_si_id2;
u8 jtag_si_id1;
u8 jtag_si_id0;
u8 mfgid_sz;
u8 cyito_idh;
u8 cyito_idl;
u8 cyito_verh;
u8 cyito_verl;
u8 ttsp_ver_major;
u8 ttsp_ver_minor;
u8 device_info;
u8 mfg_id[];
} __packed;
struct cyttsp4_test {
u8 post_codeh;
u8 post_codel;
} __packed;
struct cyttsp4_pcfg {
u8 electrodes_x;
u8 electrodes_y;
u8 len_xh;
u8 len_xl;
u8 len_yh;
u8 len_yl;
u8 res_xh;
u8 res_xl;
u8 res_yh;
u8 res_yl;
u8 max_zh;
u8 max_zl;
u8 panel_info0;
} __packed;
struct cyttsp4_tch_rec_params {
u8 loc;
u8 size;
} __packed;
#define CY_NUM_TCH_FIELDS 7
#define CY_NUM_EXT_TCH_FIELDS 3
struct cyttsp4_opcfg {
u8 cmd_ofs;
u8 rep_ofs;
u8 rep_szh;
u8 rep_szl;
u8 num_btns;
u8 tt_stat_ofs;
u8 obj_cfg0;
u8 max_tchs;
u8 tch_rec_size;
struct cyttsp4_tch_rec_params tch_rec_old[CY_NUM_TCH_FIELDS];
u8 btn_rec_size; /* btn record size (in bytes) */
u8 btn_diff_ofs; /* btn data loc, diff counts */
u8 btn_diff_size; /* btn size of diff counts (in bits) */
struct cyttsp4_tch_rec_params tch_rec_new[CY_NUM_EXT_TCH_FIELDS];
} __packed;
struct cyttsp4_sysinfo_ptr {
struct cyttsp4_cydata *cydata;
struct cyttsp4_test *test;
struct cyttsp4_pcfg *pcfg;
struct cyttsp4_opcfg *opcfg;
struct cyttsp4_ddata *ddata;
struct cyttsp4_mdata *mdata;
} __packed;
struct cyttsp4_sysinfo_data {
u8 hst_mode;
u8 reserved;
u8 map_szh;
u8 map_szl;
u8 cydata_ofsh;
u8 cydata_ofsl;
u8 test_ofsh;
u8 test_ofsl;
u8 pcfg_ofsh;
u8 pcfg_ofsl;
u8 opcfg_ofsh;
u8 opcfg_ofsl;
u8 ddata_ofsh;
u8 ddata_ofsl;
u8 mdata_ofsh;
u8 mdata_ofsl;
} __packed;
enum cyttsp4_tch_abs { /* for ordering within the extracted touch data array */
CY_TCH_X, /* X */
CY_TCH_Y, /* Y */
CY_TCH_P, /* P (Z) */
CY_TCH_T, /* TOUCH ID */
CY_TCH_E, /* EVENT ID */
CY_TCH_O, /* OBJECT ID */
CY_TCH_W, /* SIZE */
CY_TCH_MAJ, /* TOUCH_MAJOR */
CY_TCH_MIN, /* TOUCH_MINOR */
CY_TCH_OR, /* ORIENTATION */
CY_TCH_NUM_ABS
};
struct cyttsp4_touch {
int abs[CY_TCH_NUM_ABS];
};
struct cyttsp4_tch_abs_params {
size_t ofs; /* abs byte offset */
size_t size; /* size in bits */
size_t max; /* max value */
size_t bofs; /* bit offset */
};
struct cyttsp4_sysinfo_ofs {
size_t chip_type;
size_t cmd_ofs;
size_t rep_ofs;
size_t rep_sz;
size_t num_btns;
size_t num_btn_regs; /* ceil(num_btns/4) */
size_t tt_stat_ofs;
size_t tch_rec_size;
size_t obj_cfg0;
size_t max_tchs;
size_t mode_size;
size_t data_size;
size_t map_sz;
size_t max_x;
size_t x_origin; /* left or right corner */
size_t max_y;
size_t y_origin; /* upper or lower corner */
size_t max_p;
size_t cydata_ofs;
size_t test_ofs;
size_t pcfg_ofs;
size_t opcfg_ofs;
size_t ddata_ofs;
size_t mdata_ofs;
size_t cydata_size;
size_t test_size;
size_t pcfg_size;
size_t opcfg_size;
size_t ddata_size;
size_t mdata_size;
size_t btn_keys_size;
struct cyttsp4_tch_abs_params tch_abs[CY_TCH_NUM_ABS];
size_t btn_rec_size; /* btn record size (in bytes) */
size_t btn_diff_ofs;/* btn data loc ,diff counts, (Op-Mode byte ofs) */
size_t btn_diff_size;/* btn size of diff counts (in bits) */
};
enum cyttsp4_btn_state {
CY_BTN_RELEASED,
CY_BTN_PRESSED,
CY_BTN_NUM_STATE
};
struct cyttsp4_btn {
bool enabled;
int state; /* CY_BTN_PRESSED, CY_BTN_RELEASED */
int key_code;
};
struct cyttsp4_sysinfo {
bool ready;
struct cyttsp4_sysinfo_data si_data;
struct cyttsp4_sysinfo_ptr si_ptrs;
struct cyttsp4_sysinfo_ofs si_ofs;
struct cyttsp4_btn *btn; /* button states */
u8 *btn_rec_data; /* button diff count data */
u8 *xy_mode; /* operational mode and status regs */
u8 *xy_data; /* operational touch regs */
};
struct cyttsp4_mt_data {
struct cyttsp4_mt_platform_data *pdata;
struct cyttsp4_sysinfo *si;
struct input_dev *input;
struct mutex report_lock;
bool is_suspended;
char phys[NAME_MAX];
int num_prv_tch;
};
struct cyttsp4 {
struct device *dev;
struct mutex system_lock;
struct mutex adap_lock;
enum cyttsp4_mode mode;
enum cyttsp4_sleep_state sleep_state;
enum cyttsp4_startup_state startup_state;
int int_status;
wait_queue_head_t wait_q;
int irq;
struct work_struct startup_work;
struct work_struct watchdog_work;
struct timer_list watchdog_timer;
struct cyttsp4_sysinfo sysinfo;
void *exclusive_dev;
int exclusive_waits;
atomic_t ignore_irq;
bool invalid_touch_app;
struct cyttsp4_mt_data md;
struct cyttsp4_platform_data *pdata;
struct cyttsp4_core_platform_data *cpdata;
const struct cyttsp4_bus_ops *bus_ops;
u8 *xfer_buf;
#ifdef VERBOSE_DEBUG
u8 pr_buf[CY_MAX_PRBUF_SIZE];
#endif
};
struct cyttsp4_bus_ops {
u16 bustype;
int (*write)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length,
const void *values);
int (*read)(struct device *dev, u8 *xfer_buf, u16 addr, u8 length,
void *values);
};
enum cyttsp4_hst_mode_bits {
CY_HST_TOGGLE = (1 << 7),
CY_HST_MODE_CHANGE = (1 << 3),
CY_HST_MODE = (7 << 4),
CY_HST_OPERATE = (0 << 4),
CY_HST_SYSINFO = (1 << 4),
CY_HST_CAT = (2 << 4),
CY_HST_LOWPOW = (1 << 2),
CY_HST_SLEEP = (1 << 1),
CY_HST_RESET = (1 << 0),
};
/* abs settings */
#define CY_IGNORE_VALUE 0xFFFF
/* abs signal capabilities offsets in the frameworks array */
enum cyttsp4_sig_caps {
CY_SIGNAL_OST,
CY_MIN_OST,
CY_MAX_OST,
CY_FUZZ_OST,
CY_FLAT_OST,
CY_NUM_ABS_SET /* number of signal capability fields */
};
/* abs axis signal offsets in the framworks array */
enum cyttsp4_sig_ost {
CY_ABS_X_OST,
CY_ABS_Y_OST,
CY_ABS_P_OST,
CY_ABS_W_OST,
CY_ABS_ID_OST,
CY_ABS_MAJ_OST,
CY_ABS_MIN_OST,
CY_ABS_OR_OST,
CY_NUM_ABS_OST /* number of abs signals */
};
enum cyttsp4_flags {
CY_FLAG_NONE = 0x00,
CY_FLAG_HOVER = 0x04,
CY_FLAG_FLIP = 0x08,
CY_FLAG_INV_X = 0x10,
CY_FLAG_INV_Y = 0x20,
CY_FLAG_VKEYS = 0x40,
};
enum cyttsp4_object_id {
CY_OBJ_STANDARD_FINGER,
CY_OBJ_LARGE_OBJECT,
CY_OBJ_STYLUS,
CY_OBJ_HOVER,
};
enum cyttsp4_event_id {
CY_EV_NO_EVENT,
CY_EV_TOUCHDOWN,
CY_EV_MOVE, /* significant displacement (> act dist) */
CY_EV_LIFTOFF, /* record reports last position */
};
/* x-axis resolution of panel in pixels */
#define CY_PCFG_RESOLUTION_X_MASK 0x7F
/* y-axis resolution of panel in pixels */
#define CY_PCFG_RESOLUTION_Y_MASK 0x7F
/* x-axis, 0:origin is on left side of panel, 1: right */
#define CY_PCFG_ORIGIN_X_MASK 0x80
/* y-axis, 0:origin is on top side of panel, 1: bottom */
#define CY_PCFG_ORIGIN_Y_MASK 0x80
static inline int cyttsp4_adap_read(struct cyttsp4 *ts, u16 addr, int size,
void *buf)
{
return ts->bus_ops->read(ts->dev, ts->xfer_buf, addr, size, buf);
}
static inline int cyttsp4_adap_write(struct cyttsp4 *ts, u16 addr, int size,
const void *buf)
{
return ts->bus_ops->write(ts->dev, ts->xfer_buf, addr, size, buf);
}
extern struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops,
struct device *dev, u16 irq, size_t xfer_buf_size);
extern int cyttsp4_remove(struct cyttsp4 *ts);
extern const struct dev_pm_ops cyttsp4_pm_ops;
#endif /* _LINUX_CYTTSP4_CORE_H */

View File

@@ -0,0 +1,178 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* cyttsp_i2c.c
* Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver.
* For use with Cypress Txx4xx parts.
* Supported parts include:
* TMA4XX
* TMA1036
*
* Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
* Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
* Copyright (C) 2013 Cypress Semiconductor
*
* Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
*/
#include "cyttsp4_core.h"
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/delay.h>
#define CYTTSP4_I2C_DATA_SIZE (3 * 256)
static int cyttsp4_i2c_read_block_data(struct device *dev, u8 *xfer_buf,
u16 addr, u8 length, void *values)
{
struct i2c_client *client = to_i2c_client(dev);
int rc;
xfer_buf[0] = addr;
rc = i2c_master_send(client, xfer_buf, 1);
if (rc < 0)
return rc;
rc = i2c_master_recv(client, values, length);
return rc < 0 ? rc : 0;
}
static int cyttsp4_i2c_write_block_data(struct device *dev, u8 *xfer_buf,
u16 addr, u8 length, const void *values)
{
struct i2c_client *client = to_i2c_client(dev);
xfer_buf[0] = addr;
memcpy(&xfer_buf[1], values, length);
return i2c_master_send(client, xfer_buf, length + 1) < 0 ? -EIO : 0;
}
static const struct cyttsp4_bus_ops cyttsp4_i2c_bus_ops = {
.bustype = BUS_I2C,
.write = cyttsp4_i2c_write_block_data,
.read = cyttsp4_i2c_read_block_data,
};
static const int16_t cyttsp4_abs[] = {
ABS_MT_POSITION_X, 0, 758, 0, 0,
ABS_MT_POSITION_Y, 0, 1024, 0, 0,
ABS_MT_PRESSURE, 0, 255, 0, 0,
ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0,
ABS_MT_TRACKING_ID, 0, 15, 0, 0,
CY_IGNORE_VALUE, 0, 255, 0, 0,
CY_IGNORE_VALUE, 0, 255, 0, 0,
CY_IGNORE_VALUE, -128, 127, 0, 0,
};
static struct touch_framework cyttsp4_framework = {
.abs = cyttsp4_abs,
.size = ARRAY_SIZE(cyttsp4_abs),
.enable_vkeys = 0,
};
static int cyttsp4_xres(struct cyttsp4_core_platform_data *pdata,
struct device *dev)
{
int rst_gpio = pdata->rst_gpio;
gpio_set_value(rst_gpio, 1);
msleep(20);
gpio_set_value(rst_gpio, 0);
msleep(40);
gpio_set_value(rst_gpio, 1);
msleep(20);
return 0;
}
static int cyttsp4_i2c_probe(struct i2c_client *client)
{
struct cyttsp4_platform_data *pdata;
struct cyttsp4_core_platform_data *core_pdata;
struct cyttsp4_mt_platform_data *mt_pdata;
struct gpio_desc *irq_desc, *rst_desc;
struct cyttsp4 *ts;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "I2C functionality not Supported\n");
return -EIO;
}
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
core_pdata = devm_kzalloc(&client->dev, sizeof(*core_pdata), GFP_KERNEL);
mt_pdata = devm_kzalloc(&client->dev, sizeof(*mt_pdata), GFP_KERNEL);
if (!pdata || !core_pdata || !mt_pdata)
return -ENOMEM;
irq_desc = devm_gpiod_get(&client->dev, "irq", GPIOD_IN);
if (IS_ERR(irq_desc))
return PTR_ERR(irq_desc);
rst_desc = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(rst_desc))
return PTR_ERR(rst_desc);
core_pdata->irq_gpio = desc_to_gpio(irq_desc);
core_pdata->rst_gpio = desc_to_gpio(rst_desc);
mt_pdata->inp_dev_name = CYTTSP4_MT_NAME;
mt_pdata->frmwrk = &cyttsp4_framework;
pdata->core_pdata = core_pdata;
pdata->mt_pdata = mt_pdata;
client->dev.platform_data = pdata;
/* Reset the touch controller */
gpiod_set_value(rst_desc, 1);
msleep(20);
gpiod_set_value(rst_desc, 0);
msleep(40);
gpiod_set_value(rst_desc, 1);
msleep(20);
core_pdata->xres = cyttsp4_xres;
ts = cyttsp4_probe(&cyttsp4_i2c_bus_ops, &client->dev, client->irq,
CYTTSP4_I2C_DATA_SIZE);
return PTR_ERR_OR_ZERO(ts);
}
static void cyttsp4_i2c_remove(struct i2c_client *client)
{
struct cyttsp4 *ts = i2c_get_clientdata(client);
cyttsp4_remove(ts);
}
#define CYTTSP4_I2C_NAME "cyttsp4_i2c_adapter"
static const struct i2c_device_id cyttsp4_i2c_id[] = {
{ CYTTSP4_I2C_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, cyttsp4_i2c_id);
static const struct of_device_id cyttsp4_of_match[] = {
{ .compatible = "cypress,cyttsp4-i2c", },
{ }
};
MODULE_DEVICE_TABLE(of, cyttsp4_of_match);
static struct i2c_driver cyttsp4_i2c_driver = {
.driver = {
.name = CYTTSP4_I2C_NAME,
.of_match_table = cyttsp4_of_match,
.pm = &cyttsp4_pm_ops,
},
.probe = cyttsp4_i2c_probe,
.remove = cyttsp4_i2c_remove,
.id_table = cyttsp4_i2c_id,
};
module_i2c_driver(cyttsp4_i2c_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver");
MODULE_AUTHOR("Cypress");

View File

@@ -0,0 +1,187 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Source for:
* Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver.
* For use with Cypress Txx4xx parts.
* Supported parts include:
* TMA4XX
* TMA1036
*
* Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
* Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
* Copyright (C) 2013 Cypress Semiconductor
*
* Contact Cypress Semiconductor at www.cypress.com <ttdrivers@cypress.com>
*/
#include "cyttsp4_core.h"
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/spi/spi.h>
#define CY_SPI_WR_OP 0x00 /* r/~w */
#define CY_SPI_RD_OP 0x01
#define CY_SPI_BITS_PER_WORD 8
#define CY_SPI_A8_BIT 0x02
#define CY_SPI_WR_HEADER_BYTES 2
#define CY_SPI_RD_HEADER_BYTES 1
#define CY_SPI_CMD_BYTES 2
#define CY_SPI_SYNC_BYTE 0
#define CY_SPI_SYNC_ACK 0x62 /* from TRM *A protocol */
#define CY_SPI_DATA_SIZE (2 * 256)
#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE)
static int cyttsp_spi_xfer(struct device *dev, u8 *xfer_buf,
u8 op, u16 reg, u8 *buf, int length)
{
struct spi_device *spi = to_spi_device(dev);
struct spi_message msg;
struct spi_transfer xfer[2];
u8 *wr_buf = &xfer_buf[0];
u8 rd_buf[CY_SPI_CMD_BYTES];
int retval;
int i;
if (length > CY_SPI_DATA_SIZE) {
dev_err(dev, "%s: length %d is too big.\n",
__func__, length);
return -EINVAL;
}
memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE);
memset(rd_buf, 0, CY_SPI_CMD_BYTES);
wr_buf[0] = op + (((reg >> 8) & 0x1) ? CY_SPI_A8_BIT : 0);
if (op == CY_SPI_WR_OP) {
wr_buf[1] = reg & 0xFF;
if (length > 0)
memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length);
}
memset(xfer, 0, sizeof(xfer));
spi_message_init(&msg);
/*
We set both TX and RX buffers because Cypress TTSP
requires full duplex operation.
*/
xfer[0].tx_buf = wr_buf;
xfer[0].rx_buf = rd_buf;
switch (op) {
case CY_SPI_WR_OP:
xfer[0].len = length + CY_SPI_CMD_BYTES;
spi_message_add_tail(&xfer[0], &msg);
break;
case CY_SPI_RD_OP:
xfer[0].len = CY_SPI_RD_HEADER_BYTES;
spi_message_add_tail(&xfer[0], &msg);
xfer[1].rx_buf = buf;
xfer[1].len = length;
spi_message_add_tail(&xfer[1], &msg);
break;
default:
dev_err(dev, "%s: bad operation code=%d\n", __func__, op);
return -EINVAL;
}
retval = spi_sync(spi, &msg);
if (retval < 0) {
dev_dbg(dev, "%s: spi_sync() error %d, len=%d, op=%d\n",
__func__, retval, xfer[1].len, op);
/*
* do not return here since was a bad ACK sequence
* let the following ACK check handle any errors and
* allow silent retries
*/
}
if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK) {
dev_dbg(dev, "%s: operation %d failed\n", __func__, op);
for (i = 0; i < CY_SPI_CMD_BYTES; i++)
dev_dbg(dev, "%s: test rd_buf[%d]:0x%02x\n",
__func__, i, rd_buf[i]);
for (i = 0; i < length; i++)
dev_dbg(dev, "%s: test buf[%d]:0x%02x\n",
__func__, i, buf[i]);
return -EIO;
}
return 0;
}
static int cyttsp_spi_read_block_data(struct device *dev, u8 *xfer_buf,
u16 addr, u8 length, void *data)
{
int rc;
rc = cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, NULL, 0);
if (rc)
return rc;
else
return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_RD_OP, addr, data,
length);
}
static int cyttsp_spi_write_block_data(struct device *dev, u8 *xfer_buf,
u16 addr, u8 length, const void *data)
{
return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, (void *)data,
length);
}
static const struct cyttsp4_bus_ops cyttsp_spi_bus_ops = {
.bustype = BUS_SPI,
.write = cyttsp_spi_write_block_data,
.read = cyttsp_spi_read_block_data,
};
static int cyttsp4_spi_probe(struct spi_device *spi)
{
struct cyttsp4 *ts;
int error;
/* Set up SPI*/
spi->bits_per_word = CY_SPI_BITS_PER_WORD;
spi->mode = SPI_MODE_0;
error = spi_setup(spi);
if (error < 0) {
dev_err(&spi->dev, "%s: SPI setup error %d\n",
__func__, error);
return error;
}
ts = cyttsp4_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq,
CY_SPI_DATA_BUF_SIZE);
return PTR_ERR_OR_ZERO(ts);
}
static void cyttsp4_spi_remove(struct spi_device *spi)
{
struct cyttsp4 *ts = spi_get_drvdata(spi);
cyttsp4_remove(ts);
}
static struct spi_driver cyttsp4_spi_driver = {
.driver = {
.name = CYTTSP4_SPI_NAME,
.pm = &cyttsp4_pm_ops,
},
.probe = cyttsp4_spi_probe,
.remove = cyttsp4_spi_remove,
};
module_spi_driver(cyttsp4_spi_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver");
MODULE_AUTHOR("Cypress");
MODULE_ALIAS("spi:cyttsp4");

View File

@@ -916,6 +916,20 @@ config MFD_MAX77693
additional drivers must be enabled in order to use the functionality additional drivers must be enabled in order to use the functionality
of the device. of the device.
config MFD_MAX77696
tristate "Maxim MAX77696 PMIC Support"
depends on I2C
depends on OF
select MFD_CORE
select REGMAP_I2C
help
Say Y here to add support for Maxim Semiconductor MAX77696
Power Management ICs. This is the core multifunction
driver for interacting with the device. The module name is
'max77696'. Additional drivers can be enabled in order to use
the following functionalities of the device: GPIO, regulator,
charger, LED.
config MFD_MAX77705 config MFD_MAX77705
tristate "Maxim MAX77705 PMIC Support" tristate "Maxim MAX77705 PMIC Support"
depends on I2C depends on I2C

View File

@@ -167,6 +167,7 @@ obj-$(CONFIG_MFD_MAX77620) += max77620.o
obj-$(CONFIG_MFD_MAX77650) += max77650.o obj-$(CONFIG_MFD_MAX77650) += max77650.o
obj-$(CONFIG_MFD_MAX77686) += max77686.o obj-$(CONFIG_MFD_MAX77686) += max77686.o
obj-$(CONFIG_MFD_MAX77693) += max77693.o obj-$(CONFIG_MFD_MAX77693) += max77693.o
obj-$(CONFIG_MFD_MAX77696) += max77696.o
obj-$(CONFIG_MFD_MAX77705) += max77705.o obj-$(CONFIG_MFD_MAX77705) += max77705.o
obj-$(CONFIG_MFD_MAX77714) += max77714.o obj-$(CONFIG_MFD_MAX77714) += max77714.o
obj-$(CONFIG_MFD_MAX77843) += max77843.o obj-$(CONFIG_MFD_MAX77843) += max77843.o

84
drivers/mfd/max77696.c Normal file
View File

@@ -0,0 +1,84 @@
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (C) 2026 smallsoalr
// Author: smallsolar <solarcene@protonmail.com>
//
// Core MFD driver for MAXIM 77696 power-supply.
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max77696.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
static const struct mfd_cell max77696_cells[] = {
{
.name = "max77696-regulator",
.of_compatible = "maxim,max77696-regulator",
}, {
.name = "max77696-epd",
.of_compatible = "maxim,max77696-epd",
}, {
.name = "max77696-wled",
.of_compatible = "maxim,max77696-wled",
},
};
static const struct regmap_config max77696_regmap_config = {
.name = "max77696",
.reg_bits = 8,
.val_bits = 8,
};
static int max77696_i2c_probe(struct i2c_client *client)
{
struct max77696 *chip;
unsigned int val;
int rv;
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->dev = &client->dev;
i2c_set_clientdata(client, chip);
chip->regmap = devm_regmap_init_i2c(client, &max77696_regmap_config);
if (IS_ERR(chip->regmap)) {
dev_err(chip->dev, "Unable to initialise I2C Regmap\n");
return PTR_ERR(chip->regmap);
}
rv = regmap_read(chip->regmap, MAX77696_REG_CID, &val);
if (rv) {
dev_err(chip->dev, "Unable to read Chip ID\n");
return rv;
}
dev_info(chip->dev, "Chip Detected ID: 0x%02x\n", val);
return devm_mfd_add_devices(chip->dev, PLATFORM_DEVID_NONE,
max77696_cells, ARRAY_SIZE(max77696_cells),
NULL, 0, NULL);
}
static const struct of_device_id max77696_of_match[] = {
{ .compatible = "maxim,max77696" },
{ }
};
MODULE_DEVICE_TABLE(of, max77696_of_match);
static struct i2c_driver max77696_i2c_driver = {
.driver = {
.name = "max77696",
.of_match_table = max77696_of_match,
},
.probe = max77696_i2c_probe,
};
module_i2c_driver(max77696_i2c_driver);
MODULE_DESCRIPTION("MAXIM 77696 multi-function core driver");
MODULE_AUTHOR("smallsolar <solarcene@protonmail.com>");
MODULE_LICENSE("GPL v2");

View File

@@ -747,6 +747,12 @@ config REGULATOR_MAX77693
and one current regulator 'CHARGER'. This is suitable for and one current regulator 'CHARGER'. This is suitable for
Exynos-4x12 (MAX77693) or Exynos5433 (MAX77843) SoC chips. Exynos-4x12 (MAX77693) or Exynos5433 (MAX77843) SoC chips.
config REGULATOR_MAX77696
tristate "Maxim 77696 regulator"
depends on MFD_MAX77696 || COMPILE_TEST
help
This driver controls a Maxim 77693/77843 regulators via I2C bus.
config REGULATOR_MAX77802 config REGULATOR_MAX77802
tristate "Maxim 77802 regulator" tristate "Maxim 77802 regulator"
depends on MFD_MAX77686 || COMPILE_TEST depends on MFD_MAX77686 || COMPILE_TEST

View File

@@ -89,6 +89,7 @@ obj-$(CONFIG_REGULATOR_MAX20086) += max20086-regulator.o
obj-$(CONFIG_REGULATOR_MAX20411) += max20411-regulator.o obj-$(CONFIG_REGULATOR_MAX20411) += max20411-regulator.o
obj-$(CONFIG_REGULATOR_MAX77686) += max77686-regulator.o obj-$(CONFIG_REGULATOR_MAX77686) += max77686-regulator.o
obj-$(CONFIG_REGULATOR_MAX77693) += max77693-regulator.o obj-$(CONFIG_REGULATOR_MAX77693) += max77693-regulator.o
obj-$(CONFIG_REGULATOR_MAX77696) += max77696-epd.o
obj-$(CONFIG_REGULATOR_MAX77802) += max77802-regulator.o obj-$(CONFIG_REGULATOR_MAX77802) += max77802-regulator.o
obj-$(CONFIG_REGULATOR_MAX77826) += max77826-regulator.o obj-$(CONFIG_REGULATOR_MAX77826) += max77826-regulator.o
obj-$(CONFIG_REGULATOR_MAX77857) += max77857-regulator.o obj-$(CONFIG_REGULATOR_MAX77857) += max77857-regulator.o

View File

@@ -0,0 +1,236 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* MAX77696 EPD (E-Paper Display) power supply driver
*
* Drives the EPD regulator block of the MAX77696 PMIC.
* Registers DISPLAY, VCOM and V3P3 regulators needed by
* the i.MX6SL EPDC driver.
*
* Copyright (C) 2026 smallsolar <solarcene@protonmail.com>
*/
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/mfd/max77696.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/delay.h>
enum {
MAX77696_EPD_DISPLAY,
MAX77696_EPD_VCOM,
MAX77696_EPD_V3P3,
MAX77696_EPD_NUM_REGS,
};
/*
* max77696_epd_vcom_enable - enable VCOM with fault detection and retry.
*
* EPDINT (0x62) is read-to-clear: a single read both samples and clears
* the fault bit, so no separate clear step is needed.
*
* Called by the regulator core as the .enable op for VCOM only.
* DISPLAY and V3P3 use the generic regmap enable op.
*/
static int max77696_vcom_enable(struct regulator_dev *rdev)
{
struct max77696 *chip = rdev_get_drvdata(rdev);
struct regmap *regmap = chip->regmap;
struct device *dev = chip->dev;
unsigned int val;
int i, ret;
for (i = 0; i < MAX77696_EPD_VCOM_FAULT_RETRIES; i++) {
/* Enable VCOM via the standard regmap bit */
ret = regulator_enable_regmap(rdev);
if (ret) {
dev_err(dev, "VCOM enable failed (attempt %d): %d\n",
i + 1, ret);
return ret;
}
msleep(MAX77696_EPD_VCOM_SETTLE_MS);
/*
* Read EPDINT. This is read-to-clear, so a single read
* both samples and clears the fault status.
*/
ret = regmap_read(regmap, EPDINT_REG, &val);
if (ret) {
dev_err(dev, "EPDINT read failed: %d\n", ret);
return ret;
}
if (!(val & EPDINT_VCOM_FAULT)) {
if (i > 0)
dev_info(dev,
"VCOM stable after %d attempt(s)\n",
i + 1);
return 0;
}
dev_warn(chip->dev,
"VCOM fault detected (EPDINT=0x%02x), "
"retrying (%d/%d)\n",
val, i + 1, MAX77696_EPD_VCOM_FAULT_RETRIES);
ret = regulator_disable_regmap(rdev);
if (ret) {
dev_err(dev, "VCOM disable (for retry) failed: %d\n",
ret);
return ret;
}
msleep(MAX77696_EPD_VCOM_RETRY_DELAY_MS);
}
dev_err(dev,
"VCOM failed to stabilise after %d attempts; "
"last EPDINT=0x%02x\n",
MAX77696_EPD_VCOM_FAULT_RETRIES, val);
return -EIO;
}
static const struct regulator_ops max77696_regmap_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
};
static const struct regulator_ops max77696_vcom_ops = {
.enable = max77696_vcom_enable,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
};
static const struct regulator_desc max77696_regs[] = {
[MAX77696_EPD_DISPLAY] = {
.name = "DISPLAY",
.of_match = "DISPLAY",
.regulators_node = "regulators",
.id = MAX77696_EPD_DISPLAY,
.ops = &max77696_regmap_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = EPDCNFG_REG,
.enable_mask = EPDCNFG_EPDEN,
},
[MAX77696_EPD_VCOM] = {
.name = "VCOM",
.of_match = "VCOM",
.regulators_node = "regulators",
.id = MAX77696_EPD_VCOM,
.ops = &max77696_vcom_ops, /* fault-aware enable */
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = EPDCNFG_REG,
.enable_mask = EPDCNFG_VCOMEN,
},
[MAX77696_EPD_V3P3] = {
.name = "V3P3",
.of_match = "V3P3",
.regulators_node = "regulators",
.id = MAX77696_EPD_V3P3,
.ops = &max77696_regmap_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = EPDCNFG_REG,
.enable_mask = EPDCNFG_SSHVDDH,
},
};
static int max77696_regulator_probe(struct platform_device *pdev)
{
struct max77696 *chip = dev_get_drvdata(pdev->dev.parent);
struct regulator_config config = { };
struct regulator_dev *rdev;
unsigned int vcom_val, vcomr_val, lsw4;
int i, ret;
config.dev = &pdev->dev;
config.regmap = chip->regmap;
config.driver_data = chip;
dev_info(&pdev->dev, "of_node = %pOF\n", pdev->dev.of_node);
for (i = 0; i < ARRAY_SIZE(max77696_regs); i++) {
rdev = devm_regulator_register(&pdev->dev, &max77696_regs[i], &config);
if (IS_ERR(rdev))
return dev_err_probe(chip->dev, PTR_ERR(rdev),
"register %s failed\n",
max77696_regs[i].name);
}
/*
* Set VCOM to its factory-calibrated voltage. Written unconditionally
* at probe, independent of enable state, matching the vendor driver's
* behaviour of voltage being a separate concern from enable/disable.
*/
ret = regmap_update_bits(chip->regmap, EPDVCOMR_REG, EPDVCOMR_VCOMR, EPDVCOMR_VCOMR);
if (ret)
return dev_err_probe(chip->dev, ret, "failed to set VCOMR\n");
ret = regmap_write(chip->regmap, EPDVCOM_REG, VCOM_CAL_REG_VALUE);
if (ret)
return dev_err_probe(chip->dev, ret, "failed to set VCOM calibration\n");
regmap_read(chip->regmap, EPDVCOM_REG, &vcom_val);
regmap_read(chip->regmap, EPDVCOMR_REG, &vcomr_val);
dev_info(chip->dev, "VCOM readback: EPDVCOM=0x%02x EPDVCOMR=0x%02x\n",
vcom_val, vcomr_val & EPDVCOMR_VCOMR);
/* Enable LSW4 (DISP_GATED) — gates 3.3V to panel logic on Muscat WFO.
* LSW4 = DISP_GATED-old per board-mx6sl_wario.c MAX77696_VREG_CONSUMERS.
*/
ret = regmap_update_bits(chip->regmap, LSW4_CNTRL_REG, LSW4_OUTLS, LSW4_OUTLS);
if (ret)
return dev_err_probe(chip->dev, ret, "failed to enable LSW4 (DISP_GATED)\n");
/* Verify LSW4 enable latched */
regmap_read(chip->regmap, LSW4_CNTRL_REG, &lsw4);
dev_info(chip->dev, "LSW4: 0x%02x\n", lsw4);
dev_info(chip->dev, "MAX77696 v0.8 EPD regulators registered (VCOM=%d uV)\n",
(VCOM_CAL_REG_VALUE + 0xFF) * 9985);
return 0;
}
static const struct of_device_id max77696_regulator_of_match[] = {
{ .compatible = "maxim,max77696-epd" },
{ }
};
MODULE_DEVICE_TABLE(of, max77696_regulator_of_match);
static const struct platform_device_id max77696_regulator_id[] = {
{ "max77696-epd", 0 },
{ }
};
MODULE_DEVICE_TABLE(platform, max77696_regulator_id);
static struct platform_driver max77696_regulator_driver = {
.driver = {
.name = "max77696-epd",
.of_match_table = max77696_regulator_of_match,
},
.probe = max77696_regulator_probe,
.id_table = max77696_regulator_id,
};
module_platform_driver(max77696_regulator_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MAX77696 EPD power supply driver");
MODULE_AUTHOR("smallsolar");

View File

@@ -262,6 +262,12 @@ config BACKLIGHT_DA9052
help help
Enable the Backlight Driver for DA9052-BC and DA9053-AA/Bx PMICs. Enable the Backlight Driver for DA9052-BC and DA9053-AA/Bx PMICs.
config BACKLIGHT_MAX77696
tristate "Backlight driver for MAX77696"
depends on MFD_MAX77696 || COMPILE_TEST
help
Enable driver for MAX77696 Front WLEDs (Kindle PW3).
config BACKLIGHT_MAX8925 config BACKLIGHT_MAX8925
tristate "Backlight driver for MAX8925" tristate "Backlight driver for MAX8925"
depends on MFD_MAX8925 depends on MFD_MAX8925

View File

@@ -46,6 +46,7 @@ obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o
obj-$(CONFIG_BACKLIGHT_LP8788) += lp8788_bl.o obj-$(CONFIG_BACKLIGHT_LP8788) += lp8788_bl.o
obj-$(CONFIG_BACKLIGHT_LV5207LP) += lv5207lp.o obj-$(CONFIG_BACKLIGHT_LV5207LP) += lv5207lp.o
obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o
obj-$(CONFIG_BACKLIGHT_MAX77696) += max77696-wled.o
obj-$(CONFIG_BACKLIGHT_MP3309C) += mp3309c.o obj-$(CONFIG_BACKLIGHT_MP3309C) += mp3309c.o
obj-$(CONFIG_BACKLIGHT_MT6370) += mt6370-backlight.o obj-$(CONFIG_BACKLIGHT_MT6370) += mt6370-backlight.o
obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o

View File

@@ -0,0 +1,201 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* MAX77696 WLED Backlight Driver
*
* Controls the WLED boost regulator block of the MAX77696 PMIC,
* used as the frontlight driver on the Amazon Kindle Paperwhite 3
* (Wario/Muscat platform).
*
* Register map (from vendor kernel):
* 0x6C LEDBST_CNTRL1 — bit 7 = LED1EN (boost + LED enable)
* 0x6D LED1CURRENT_1 — bits 11:4 of 12-bit current DAC (MSB)
* 0x6E LED1CURRENT_2 — bits 3:0 of 12-bit current DAC (LSB)
*
* Brightness is a 12-bit value (04095). The stock driver uses
* DUET_FL_LEVEL12_MID = 240 as the default brightness on Muscat WFO.
*
* Copyright (C) 2026 smallsolar <solarcene@protonmail.com>
*/
#include <linux/backlight.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/mfd/max77696.h>
#include <linux/platform_device.h>
/*
* Default brightness on probe: DUET_FL_LEVEL12_MID from vendor
* frontlight.h. Matches the stock kernel's initial setting on
* Muscat WFO (KPW3). Set to 0 to start with backlight off.
*/
#define MAX77696_BL_DEFAULT_BRIGHTNESS 240
struct max77696_bl {
struct regmap *regmap;
struct device *dev;
int brightness;
};
/*
* max77696_bl_set - write brightness to hardware.
*
* Brightness is a 12-bit value split across two registers:
* LED1CURRENT_1 = bits 11:4 (upper 8 bits)
* LED1CURRENT_2 = bits 3:0 (lower 4 bits)
*
* At brightness=0, disable the boost converter (LED1EN=0).
* At brightness>0, write current registers then enable (LED1EN=1).
* This matches the sequence in the vendor max77696_bl_set().
*/
static int max77696_bl_set(struct max77696_bl *bl, int brightness)
{
int ret;
brightness = clamp(brightness,
MAX77696_BL_MIN_BRIGHTNESS,
MAX77696_BL_MAX_BRIGHTNESS);
if (brightness == MAX77696_BL_MIN_BRIGHTNESS) {
/* Disable boost converter */
ret = regmap_update_bits(bl->regmap, LEDBST_CNTRL1_REG,
LEDBST_CNTRL1_LED1EN, 0);
if (ret)
return ret;
bl->brightness = 0;
return 0;
}
/* Write 12-bit brightness: MSB in LED1CURRENT_1, LSB in LED1CURRENT_2 */
ret = regmap_write(bl->regmap, LED1CURRENT_1_REG,
(brightness >> 4) & 0xFF);
if (ret)
return ret;
ret = regmap_write(bl->regmap, LED1CURRENT_2_REG,
brightness & 0x0F);
if (ret)
return ret;
/* Enable boost converter */
ret = regmap_update_bits(bl->regmap, LEDBST_CNTRL1_REG,
LEDBST_CNTRL1_LED1EN, LEDBST_CNTRL1_LED1EN);
if (ret)
return ret;
bl->brightness = brightness;
return 0;
}
static int max77696_bl_update_status(struct backlight_device *bd)
{
struct max77696_bl *bl = bl_get_data(bd);
int brightness = backlight_get_brightness(bd);
return max77696_bl_set(bl, brightness);
}
static int max77696_bl_get_brightness(struct backlight_device *bd)
{
struct max77696_bl *bl = bl_get_data(bd);
unsigned int msb, lsb, enabled;
int ret;
ret = regmap_read(bl->regmap, LEDBST_CNTRL1_REG, &enabled);
if (ret)
return ret;
if (!(enabled & LEDBST_CNTRL1_LED1EN))
return 0;
ret = regmap_read(bl->regmap, LED1CURRENT_1_REG, &msb);
if (ret)
return ret;
ret = regmap_read(bl->regmap, LED1CURRENT_2_REG, &lsb);
if (ret)
return ret;
return (msb << 4) | (lsb & 0x0F);
}
static const struct backlight_ops max77696_bl_ops = {
.options = BL_CORE_SUSPENDRESUME,
.update_status = max77696_bl_update_status,
.get_brightness = max77696_bl_get_brightness,
};
static int max77696_bl_probe(struct platform_device *pdev)
{
struct max77696 *chip = dev_get_drvdata(pdev->dev.parent);
struct device *dev = &pdev->dev;
struct max77696_bl *bl;
struct backlight_device *bd;
struct backlight_properties props = {
.type = BACKLIGHT_RAW,
.max_brightness = MAX77696_BL_MAX_BRIGHTNESS,
.brightness = MAX77696_BL_DEFAULT_BRIGHTNESS,
};
u32 brightness;
int ret;
bl = devm_kzalloc(dev, sizeof(*bl), GFP_KERNEL);
if (!bl)
return -ENOMEM;
bl->regmap = chip->regmap;
bl->dev = dev;
/* Allow DT override of default brightness */
if (!of_property_read_u32(dev->of_node, "default-brightness", &brightness))
props.brightness = clamp_t(u32, brightness,
MAX77696_BL_MIN_BRIGHTNESS,
MAX77696_BL_MAX_BRIGHTNESS);
bd = devm_backlight_device_register(dev, "max77696-wled", dev, bl,
&max77696_bl_ops, &props);
if (IS_ERR(bd))
return dev_err_probe(dev, PTR_ERR(bd),
"backlight device register failed\n");
platform_set_drvdata(pdev, bd);
/* Apply initial brightness */
ret = max77696_bl_set(bl, props.brightness);
if (ret)
return dev_err_probe(dev, ret,
"failed to set initial brightness\n");
dev_info(dev, "MAX77696 WLED backlight registered (brightness=%d/%d)\n",
props.brightness, MAX77696_BL_MAX_BRIGHTNESS);
return 0;
}
static const struct of_device_id max77696_bl_of_match[] = {
{ .compatible = "maxim,max77696-wled" },
{ }
};
MODULE_DEVICE_TABLE(of, max77696_bl_of_match);
static const struct platform_device_id max77696_bl_id[] = {
{ "max77696-wled", 0 },
{ }
};
MODULE_DEVICE_TABLE(platform, max77696_bl_id);
static struct platform_driver max77696_bl_driver = {
.driver = {
.name = "max77696-wled",
.of_match_table = max77696_bl_of_match,
},
.probe = max77696_bl_probe,
.id_table = max77696_bl_id,
};
module_platform_driver(max77696_bl_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MAX77696 WLED Backlight Driver");
MODULE_AUTHOR("smallsolar");

View File

@@ -0,0 +1,87 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2026 smallsolar
* Author: smallsolar
*
* Common definitions for MAXIM 77696 charger/power-supply.
*/
#ifndef MAX77696_H
#define MAX77696_H
#include <linux/bits.h>
struct max77696 {
struct regmap *regmap;
struct device *dev;
};
#define MAX77696_REG_CID 0x0
#define EPDCNFG_REG 0x60
#define EPDCNFG_EPDEN BIT(7)
#define EPDCNFG_VCOMEN BIT(6)
#define EPDCNFG_SSHVDDH BIT(1)
#define EPDVCOM_REG 0x64
#define EPDVCOMR_REG 0xB4
#define EPDVCOMR_VCOMR BIT(0)
/*
* EPDINT (0x62) — interrupt/fault status register.
* Bit 6 (0x40): VCOM fault. Set when VCOM rail fails to reach
* target voltage. Cleared on read (read-to-clear behaviour confirmed
* from stock kernel: max77696_epd_isr() does a single regmap_read
* of EPDINT and acts on the result without a separate clear step).
*
* The stock driver retries up to MAX77696_EPD_VCOM_FAULT_RETRIES
* times with a fixed inter-retry delay before giving up.
*/
#define EPDINT_REG 0x62
#define EPDINT_VCOM_FAULT BIT(6)
#define MAX77696_EPD_VCOM_FAULT_RETRIES 5
#define MAX77696_EPD_VCOM_SETTLE_MS 100
#define MAX77696_EPD_VCOM_RETRY_DELAY_MS 100
/*
* VCOM is a factory-calibrated, per-panel value (Amazon's stock driver
* reads it from a dedicated SPI NOR flash chip on the panel module —
* see panel_flash_spi / panel_get_vcom_str() in the vendor 3.0.35 kernel,
* drivers/video/mxc/mxc_epdc_fb_lab126.c). Mainline has no driver for
* that SPI flash yet, so this value is hardcoded from a one-time read
* via the stock firmware's sysfs:
* /sys/devices/platform/imx-i2c.0/i2c-0/0-003c/max77696-epd.0/vcom_microvolt
* = 2476280 (µV magnitude; VCOM is a negative bias, per
* reg_data[MAX77696_EPD_ID_EPDVCOM].neg_sign in the vendor
* drivers/regulator/max77696-epd.c).
*
* Register encoding (from vendor max77696_epd_vcom_set_voltage()):
* step_uV = 9985, value = round(target_uV / step_uV)
* if (value >= 0xFF) { value -= 0xFF; EPDVCOMR.VCOMR = 1 }
* else EPDVCOMR.VCOMR = 0
* EPDVCOM = (u8)value
*
* 2476280 / 9985 = 248 exactly (248 * 9985 = 2476280, no rounding loss).
* 248 < 0xFF, so VCOMR stays 0 and only EPDVCOM needs writing.
*
* THIS VALUE IS SPECIFIC TO THE PANEL THIS WAS CALIBRATED AGAINST.
* Do not reuse on a different physical unit without re-reading its own
* factory calibration first.
*/
#define VCOM_CAL_REG_VALUE 0x0B
#define LSW4_CNTRL_REG 0x5F
#define LSW4_OUTLS BIT(0)
/* WLED register addresses */
#define LEDBST_CNTRL1_REG 0x6C
#define LEDBST_CNTRL1_LED1EN BIT(7) /* boost + LED enable */
#define LED1CURRENT_1_REG 0x6D /* bits 11:4 of brightness (MSB) */
#define LED1CURRENT_2_REG 0x6E /* bits 3:0 of brightness (LSB) */
#define MAX77696_BL_MAX_BRIGHTNESS 0xFFF /* 12-bit DAC */
#define MAX77696_BL_MIN_BRIGHTNESS 0
#endif /* MAX77696_H */

View File

@@ -0,0 +1,62 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Header file for:
* Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers.
* For use with Cypress Txx3xx parts.
* Supported parts include:
* CY8CTST341
* CY8CTMA340
*
* Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
* Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
*
* Contact Cypress Semiconductor at www.cypress.com (kev@cypress.com)
*/
#ifndef _CYTTSP4_H_
#define _CYTTSP4_H_
#define CYTTSP4_MT_NAME "cyttsp4_mt"
#define CYTTSP4_I2C_NAME "cyttsp4_i2c_adapter"
#define CYTTSP4_SPI_NAME "cyttsp4_spi_adapter"
#define CY_TOUCH_SETTINGS_MAX 32
struct touch_framework {
const uint16_t *abs;
uint8_t size;
uint8_t enable_vkeys;
} __packed;
struct cyttsp4_mt_platform_data {
struct touch_framework *frmwrk;
unsigned short flags;
char const *inp_dev_name;
};
struct touch_settings {
const uint8_t *data;
uint32_t size;
uint8_t tag;
} __packed;
struct cyttsp4_core_platform_data {
int irq_gpio;
int rst_gpio;
int level_irq_udelay;
int (*xres)(struct cyttsp4_core_platform_data *pdata,
struct device *dev);
int (*init)(struct cyttsp4_core_platform_data *pdata,
int on, struct device *dev);
int (*power)(struct cyttsp4_core_platform_data *pdata,
int on, struct device *dev, atomic_t *ignore_irq);
int (*irq_stat)(struct cyttsp4_core_platform_data *pdata,
struct device *dev);
struct touch_settings *sett[CY_TOUCH_SETTINGS_MAX];
};
struct cyttsp4_platform_data {
struct cyttsp4_core_platform_data *core_pdata;
struct cyttsp4_mt_platform_data *mt_pdata;
};
#endif /* _CYTTSP4_H_ */