diff --git a/Documentation/devicetree/bindings/display/fbdev.txt b/Documentation/devicetree/bindings/display/fbdev.txt new file mode 100644 index 00000000000000..5c572a123e6a61 --- /dev/null +++ b/Documentation/devicetree/bindings/display/fbdev.txt @@ -0,0 +1,6 @@ +General framebuffer properties: + +Optional properties for framebuffers: + - no-clear-on-probe : Do not clear the framebuffer during probe (bool) + - rotate : rotate a framebuffer over 0, 1, 2 and 3 (or 0, 90, + 180 and 270) degree's (integer) diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt index 769b512d1b3cf1..26346f9d299148 100644 --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt @@ -50,9 +50,8 @@ Required properties: * pll-0: the first video PLL * pll-1: the second video PLL - clock-names: the clock names mentioned above + - clock-output-names: the name of the output clock by the HDMI/TMDS encoder - dmas: phandles to the DMA channels used by the HDMI encoder - * ddc-tx: The channel for DDC transmission - * ddc-rx: The channel for DDC reception * audio-tx: The channel used for audio transmission - dma-names: the channel names mentioned above @@ -61,6 +60,32 @@ Required properties: first port should be the input endpoint. The second should be the output, usually to an HDMI connector. +Optional properties: + - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing + +HDMI-I2C bus driver +------------------- + +The HDMI-I2C bus controller is implemented using a reasonably common I2C block +and can be used as a regular I2C device in a board layout. The only caveat +however is that the bus timing is dependent on the TMDS clock and thus the +exact I2C bus frequency may not be achieved. + +Required properties: + - compatible: value must be one of: + * allwinner,sun4i-a10-hdmi-i2c + * allwinner,sun6i-a31-hdmi-i2c + - reg: base address and size of the memory-mapped region + - clocks: phandle to the clock feeding the HDMI-I2C bus controller + * hdmi: the HDMI/TMDS peripheral is the input clock, common on sun4i + * ddc: a dedicated DDC clock is the input clock, common on sun6i + - dmas: phandles to the DMA channels used by the HDMI-I2C bus controller + * ddc-tx: The channel for DDC transmission + * ddc-rx: The channel for DDC reception + +Optional properties: + - clock-frequency: see the common i2c binding document + TV Encoder ---------- @@ -277,16 +302,16 @@ connector { hdmi: hdmi@01c16000 { compatible = "allwinner,sun5i-a10s-hdmi"; - reg = <0x01c16000 0x1000>; + reg = <0x01c16000 0x500>; interrupts = <58>; clocks = <&ccu CLK_AHB_HDMI>, <&ccu CLK_HDMI>, <&ccu CLK_PLL_VIDEO0_2X>, <&ccu CLK_PLL_VIDEO1_2X>; clock-names = "ahb", "mod", "pll-0", "pll-1"; - dmas = <&dma SUN4I_DMA_NORMAL 16>, - <&dma SUN4I_DMA_NORMAL 16>, - <&dma SUN4I_DMA_DEDICATED 24>; - dma-names = "ddc-tx", "ddc-rx", "audio-tx"; + clock-output-names = "hdmi-tmds"; + ddc-i2c-bus = <&hdmi_i2c>; + dmas = <&dma SUN4I_DMA_DEDICATED 24>; + dma-names = "audio-tx"; ports { #address-cells = <1>; @@ -314,6 +339,19 @@ hdmi: hdmi@01c16000 { }; }; +hdmi_i2c: i2c@16500 { + compatible = "allwinner,sun7i-a20-hdmi-i2c", + "allwinner,sun4i-a10-hdmi-i2c"; + reg = <0x01c16500 0x40>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&hdmi>; + dmas = <&dma SUN4I_DMA_NORMAL 16>, + <&dma SUN4I_DMA_NORMAL 16>, + dma-names = "ddc-tx", "ddc-rx"; + status = "disabled"; +}; + tve0: tv-encoder@01c0a000 { compatible = "allwinner,sun4i-a10-tv-encoder"; reg = <0x01c0a000 0x1000>; diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt index 7f57271df2bc96..78511d54cafb88 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt @@ -13,6 +13,7 @@ Required properties: nxp,pca9555 nxp,pca9556 nxp,pca9557 + nxp,pca9570 nxp,pca9574 nxp,pca9575 nxp,pca9698 diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt index 025cf8c9324ac3..ab01900ea73ad2 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt @@ -18,16 +18,17 @@ Required properties: - compatible: "edt,edt-ft5206" or: "edt,edt-ft5306" or: "edt,edt-ft5406" + or: "edt,edt-ft5426" or: "edt,edt-ft5506" or: "focaltech,ft6236" - reg: I2C slave address of the chip (0x38) + +Optional properties: - interrupt-parent: a phandle pointing to the interrupt controller serving the interrupt for this chip - interrupts: interrupt specification for the touchdetect interrupt - -Optional properties: - reset-gpios: GPIO specification for the RESET input - wake-gpios: GPIO specification for the WAKE input @@ -44,6 +45,7 @@ Optional properties: - offset: allows setting the edge compensation in the range from 0 to 31. + - poll-interval: Poll interval time in milliseconds - touchscreen-size-x : See touchscreen.txt - touchscreen-size-y : See touchscreen.txt - touchscreen-fuzz-x : See touchscreen.txt diff --git a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt index 537643e86f6186..3420a816543bde 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt @@ -1,10 +1,14 @@ General Touchscreen Properties: Optional properties for Touchscreens: - - touchscreen-size-x : horizontal resolution of touchscreen - (in pixels) - - touchscreen-size-y : vertical resolution of touchscreen - (in pixels) + - touchscreen-min-size-x : minimal reported horizontal resolution of + touchscreen (in pixels) + - touchscreen-min-size-y : minimal reported vertical resolution of + touchscreen (in pixels) + - touchscreen-max-size-x : maximal reported horizontal resolution of + touchscreen (in pixels) + - touchscreen-max-size-y : maximal reported vertical resolution of touchscreen + touchscreen (in pixels) - touchscreen-max-pressure : maximum reported pressure (arbitrary range dependent on the controller) - touchscreen-fuzz-x : horizontal noise value of the absolute input @@ -25,6 +29,8 @@ Optional properties for Touchscreens: - touchscreen-y-mm : vertical length in mm of the touchscreen Deprecated properties for Touchscreens: + - touchscreen-size-x : deprecated name for touchscreen-size-x + - touchscreen-size-y : deprecated name for touchscreen-size-y - x-size : deprecated name for touchscreen-size-x - y-size : deprecated name for touchscreen-size-y - moving-threshold : deprecated name for a combination of diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt index 9455503b029931..582dcc2a3b289c 100644 --- a/Documentation/devicetree/bindings/mfd/axp20x.txt +++ b/Documentation/devicetree/bindings/mfd/axp20x.txt @@ -33,6 +33,14 @@ Required properties: - interrupt-controller: The PMIC has its own internal IRQs - #interrupt-cells: Should be set to 1 +Supported common regulator properties, see regulator.txt for more information: +- regulator-ramp-delay: sets the ramp up delay in uV/us + AXP20x/DCDC2: 1600, 800 + AXP20x/LDO3: 1600, 800 +- regulator-soft-start: enable the output at the lowest possible voltage and + only then set the desired voltage + AXP20x/LDO3 + Optional properties: - x-powers,dcdc-freq: defines the work frequency of DC-DC in KHz AXP152/20X: range: 750-1875, Default: 1.5 MHz diff --git a/Documentation/devicetree/bindings/net/rfkill-gpio.txt b/Documentation/devicetree/bindings/net/rfkill-gpio.txt new file mode 100644 index 00000000000000..391a126bb01add --- /dev/null +++ b/Documentation/devicetree/bindings/net/rfkill-gpio.txt @@ -0,0 +1,33 @@ +Linux rfkill-gpio Bindings +-------------------------- + +This binding provides support the rfkill via an gpio interface. + +Note: Only one of the gpios definitions is required. If both are set, both +will be toggled uppon rfkill invocation. + +Required properties: +- compatible Should be one of: + "linux,rfkill-gpio" +- type Shall be one of: + "wlan" + "bluetooth" + "ultrawideband" + "wimax" + "wwan" + "gps" + "fm" + "nfc" + If left undefined, the 'RFKILL_TYPE_ALL' type + shall be used. + +- shutdown-gpios Which GPIO to use for shutdown +or +- reset-gpios Which GPIO to use for reset + +Example: + phy0-kill { + compatible = "linux,rfkill-gpio"; + type = "wlan"; + shutdown-gpios = <&pio 2 17 GPIO_ACTIVE_HIGH>; + }; diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts index ba250189d07f66..376ded57095cc9 100644 --- a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts +++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts @@ -121,6 +121,12 @@ }; &hdmi { + ddc-i2c-bus = <&hdmi_i2c>; + status = "okay"; +}; + +&hdmi_i2c { + clock-frequency = <100000>; status = "okay"; }; @@ -154,6 +160,10 @@ }; }; +&lradc { + vref-supply = <®_vcc3v0>; +}; + &mmc0 { pinctrl-names = "default"; pinctrl-0 = <&mmc0_pins_a>; @@ -246,12 +256,15 @@ regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; regulator-name = "vddio-csi0"; + regulator-soft-start; + regulator-ramp-delay = <1600>; }; ®_ldo4 { - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2800000>; - regulator-name = "vddio-csi1"; + regulator-always-on; + regulator-min-microvolt = <1250000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vdd-io-pg"; }; ®_usb0_vbus { diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index a917276aac9290..d27dca82cdad7b 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -575,16 +575,16 @@ hdmi: hdmi@1c16000 { compatible = "allwinner,sun7i-a20-hdmi", "allwinner,sun5i-a10s-hdmi"; - reg = <0x01c16000 0x1000>; + reg = <0x01c16000 0x500>; interrupts = ; clocks = <&ccu CLK_AHB_HDMI0>, <&ccu CLK_HDMI>, <&ccu CLK_PLL_VIDEO0_2X>, <&ccu CLK_PLL_VIDEO1_2X>; clock-names = "ahb", "mod", "pll-0", "pll-1"; - dmas = <&dma SUN4I_DMA_NORMAL 16>, - <&dma SUN4I_DMA_NORMAL 16>, - <&dma SUN4I_DMA_DEDICATED 24>; - dma-names = "ddc-tx", "ddc-rx", "audio-tx"; + #clock-cells = <0>; + clock-output-names = "hdmi-tmds"; + dmas = <&dma SUN4I_DMA_DEDICATED 24>; + dma-names = "audio-tx"; status = "disabled"; ports { @@ -615,6 +615,19 @@ }; }; + hdmi_i2c: i2c@16500 { + compatible = "allwinner,sun7i-a20-hdmi-i2c", + "allwinner,sun4i-a10-hdmi-i2c"; + reg = <0x01c16500 0x40>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&hdmi>; + dmas = <&dma SUN4I_DMA_NORMAL 16>, + <&dma SUN4I_DMA_NORMAL 16>; + dma-names = "ddc-tx", "ddc-rx"; + status = "disabled"; + }; + spi2: spi@1c17000 { compatible = "allwinner,sun4i-a10-spi"; reg = <0x01c17000 0x1000>; diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 1b9dbf691ae7a8..1e82f956a7d40e 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -66,6 +66,7 @@ static const struct i2c_device_id pca953x_id[] = { { "pca9555", 16 | PCA953X_TYPE | PCA_INT, }, { "pca9556", 8 | PCA953X_TYPE, }, { "pca9557", 8 | PCA953X_TYPE, }, + { "pca9570", 4 | PCA957X_TYPE, }, { "pca9574", 8 | PCA957X_TYPE | PCA_INT, }, { "pca9575", 16 | PCA957X_TYPE | PCA_INT, }, { "pca9698", 40 | PCA953X_TYPE, }, @@ -931,6 +932,7 @@ static const struct of_device_id pca953x_dt_ids[] = { { .compatible = "nxp,pca9555", .data = OF_953X(16, PCA_INT), }, { .compatible = "nxp,pca9556", .data = OF_953X( 8, 0), }, { .compatible = "nxp,pca9557", .data = OF_953X( 8, 0), }, + { .compatible = "nxp,pca9570", .data = OF_957X( 4, 0), }, { .compatible = "nxp,pca9574", .data = OF_957X( 8, PCA_INT), }, { .compatible = "nxp,pca9575", .data = OF_957X(16, PCA_INT), }, { .compatible = "nxp,pca9698", .data = OF_953X(40, 0), }, diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 1f1fd3139c5b8c..c7718d0ecbf931 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1451,6 +1451,9 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len) unsigned char xfers = segment ? 3 : 2; int ret, retries = 5; + if (!adapter) + return -ENODEV; + /* * The core I2C driver will automatically retry the transfer if the * adapter reports EAGAIN. However, we find that bit-banging transfers diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig index 882d85db90539a..3eff346b593c79 100644 --- a/drivers/gpu/drm/sun4i/Kconfig +++ b/drivers/gpu/drm/sun4i/Kconfig @@ -16,18 +16,26 @@ config DRM_SUN4I if DRM_SUN4I config DRM_SUN4I_HDMI - tristate "Allwinner A10 HDMI Controller Support" - default DRM_SUN4I - help + tristate "Allwinner A10 HDMI Controller Support" + default DRM_SUN4I + help Choose this option if you have an Allwinner SoC with an HDMI controller. +config I2C_SUN4I_HDMI + tristate "Allwinner A10 HDMI I2C Support" + depends on DRM_SUN4I_HDMI + select I2C + help + Choose this option if you have an Allwinner SoC with an I2C + enabled HDMI controller and want to enable the I2C channel. + config DRM_SUN4I_HDMI_CEC - bool "Allwinner A10 HDMI CEC Support" - depends on DRM_SUN4I_HDMI - select CEC_CORE - select CEC_PIN - help + bool "Allwinner A10 HDMI CEC Support" + depends on DRM_SUN4I_HDMI + select CEC_CORE + select CEC_PIN + help Choose this option if you have an Allwinner SoC with an HDMI controller and want to use CEC. diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile index 0c2f8c7facae70..34745338824776 100644 --- a/drivers/gpu/drm/sun4i/Makefile +++ b/drivers/gpu/drm/sun4i/Makefile @@ -4,9 +4,11 @@ sun4i-backend-y += sun4i_backend.o sun4i_layer.o sun4i-drm-y += sun4i_drv.o sun4i-drm-y += sun4i_framebuffer.o -sun4i-drm-hdmi-y += sun4i_hdmi_ddc_clk.o +sun4i-hdmi-i2c-y += sun4i_hdmi_i2c_drv.o sun4i_hdmi_ddc_clk.o +sun4i-hdmi-i2c-y += sun4i_hdmi_i2c.o + +sun4i-drm-hdmi-y += sun4i_hdmi_i2c_drv.o sun4i_hdmi_ddc_clk.o sun4i-drm-hdmi-y += sun4i_hdmi_enc.o -sun4i-drm-hdmi-y += sun4i_hdmi_i2c.o sun4i-drm-hdmi-y += sun4i_hdmi_tmds_clk.o sun8i-mixer-y += sun8i_mixer.o sun8i_layer.o @@ -24,3 +26,5 @@ obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o obj-$(CONFIG_DRM_SUN4I_HDMI) += sun4i-drm-hdmi.o obj-$(CONFIG_DRM_SUN8I_MIXER) += sun8i-mixer.o + +obj-$(CONFIG_I2C_SUN4I_HDMI) += sun4i-hdmi-i2c.o diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 75c76cdd82bc6e..eb02ff48cda316 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -140,6 +140,8 @@ static int sun4i_drv_bind(struct device *dev) if (ret) goto finish_poll; + dev_set_drvdata(dev, drm); + return 0; finish_poll: diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi.h b/drivers/gpu/drm/sun4i/sun4i_hdmi.h index b685ee11623d10..88fed25855eccd 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi.h +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi.h @@ -22,7 +22,7 @@ #define SUN4I_HDMI_CTRL_ENABLE BIT(31) #define SUN4I_HDMI_IRQ_REG 0x008 -#define SUN4I_HDMI_IRQ_STA_MASK 0x73 +#define SUN4I_HDMI_IRQ_STA_MASK (GENMASK(6, 4) | GENMASK(1, 0)) #define SUN4I_HDMI_IRQ_STA_FIFO_OF BIT(1) #define SUN4I_HDMI_IRQ_STA_FIFO_UF BIT(0) @@ -68,9 +68,9 @@ #define SUN4I_HDMI_PAD_CTRL1_PWSDT BIT(17) #define SUN4I_HDMI_PAD_CTRL1_REG_DEN BIT(15) #define SUN4I_HDMI_PAD_CTRL1_REG_DENCK BIT(14) -#define SUN4I_HDMI_PAD_CTRL1_REG_EMP(n) (((n) & 7) << 10) +#define SUN4I_HDMI_PAD_CTRL1_REG_EMP(n) (((n) << 10) & GENMASK(12, 10)) #define SUN4I_HDMI_PAD_CTRL1_HALVE_CLK BIT(6) -#define SUN4I_HDMI_PAD_CTRL1_REG_AMP(n) (((n) & 7) << 3) +#define SUN4I_HDMI_PAD_CTRL1_REG_AMP(n) (((n) << 3) & GENMASK(5, 3)) /* These bits seem to invert the TMDS data channels */ #define SUN4I_HDMI_PAD_CTRL1_INVERT_R BIT(2) @@ -84,18 +84,23 @@ #define SUN4I_HDMI_PLL_CTRL_LDO1_EN BIT(28) #define SUN4I_HDMI_PLL_CTRL_LDO2_EN BIT(27) #define SUN4I_HDMI_PLL_CTRL_SDIV2 BIT(25) -#define SUN4I_HDMI_PLL_CTRL_VCO_GAIN(n) (((n) & 7) << 20) -#define SUN4I_HDMI_PLL_CTRL_S(n) (((n) & 7) << 17) -#define SUN4I_HDMI_PLL_CTRL_CP_S(n) (((n) & 0x1f) << 12) -#define SUN4I_HDMI_PLL_CTRL_CS(n) (((n) & 0xf) << 8) -#define SUN4I_HDMI_PLL_CTRL_DIV(n) (((n) & 0xf) << 4) +#define SUN4I_HDMI_PLL_CTRL_VCO_GAIN(n) (((n) << 20) & GENMASK(22, 20)) +#define SUN4I_HDMI_PLL_CTRL_S(n) (((n) << 17) & GENMASK(19, 17)) +#define SUN4I_HDMI_PLL_CTRL_CP_S(n) (((n) << 12) & GENMASK(16, 12)) +#define SUN4I_HDMI_PLL_CTRL_CS(n) (((n) << 8) & GENMASK(11, 8)) +#define SUN4I_HDMI_PLL_CTRL_DIV(n) (((n) << 4) & GENMASK(7, 4)) #define SUN4I_HDMI_PLL_CTRL_DIV_MASK GENMASK(7, 4) -#define SUN4I_HDMI_PLL_CTRL_VCO_S(n) ((n) & 0xf) +#define SUN4I_HDMI_PLL_CTRL_VCO_S(n) ((n) & GENMASK(3, 0)) #define SUN4I_HDMI_PLL_DBG0_REG 0x20c -#define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT(n) (((n) & 1) << 21) #define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_MASK BIT(21) -#define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_SHIFT 21 +#define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_OFFSET 21 +#define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT(n) \ + (((n) << SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_OFFSET) & \ + SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_MASK) +#define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_GET(n) \ + (((n) & SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_MASK) >> \ + SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_OFFSET) #define SUN4I_HDMI_CEC 0x214 #define SUN4I_HDMI_CEC_ENABLE BIT(11) @@ -108,90 +113,6 @@ #define SUN4I_HDMI_UNKNOWN_REG 0x300 #define SUN4I_HDMI_UNKNOWN_INPUT_SYNC BIT(27) -#define SUN4I_HDMI_DDC_CTRL_REG 0x500 -#define SUN4I_HDMI_DDC_CTRL_ENABLE BIT(31) -#define SUN4I_HDMI_DDC_CTRL_START_CMD BIT(30) -#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK BIT(8) -#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE (1 << 8) -#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ (0 << 8) -#define SUN4I_HDMI_DDC_CTRL_RESET BIT(0) - -#define SUN4I_HDMI_DDC_ADDR_REG 0x504 -#define SUN4I_HDMI_DDC_ADDR_SEGMENT(seg) (((seg) & 0xff) << 24) -#define SUN4I_HDMI_DDC_ADDR_EDDC(addr) (((addr) & 0xff) << 16) -#define SUN4I_HDMI_DDC_ADDR_OFFSET(off) (((off) & 0xff) << 8) -#define SUN4I_HDMI_DDC_ADDR_SLAVE(addr) ((addr) & 0xff) - -#define SUN4I_HDMI_DDC_INT_STATUS_REG 0x50c -#define SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION BIT(7) -#define SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW BIT(6) -#define SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW BIT(5) -#define SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST BIT(4) -#define SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR BIT(3) -#define SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR BIT(2) -#define SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR BIT(1) -#define SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE BIT(0) - -#define SUN4I_HDMI_DDC_FIFO_CTRL_REG 0x510 -#define SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(31) -#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES(n) (((n) & 0xf) << 4) -#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MASK GENMASK(7, 4) -#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX (BIT(4) - 1) -#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES(n) ((n) & 0xf) -#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MASK GENMASK(3, 0) -#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MAX (BIT(4) - 1) - -#define SUN4I_HDMI_DDC_FIFO_DATA_REG 0x518 - -#define SUN4I_HDMI_DDC_BYTE_COUNT_REG 0x51c -#define SUN4I_HDMI_DDC_BYTE_COUNT_MAX (BIT(10) - 1) - -#define SUN4I_HDMI_DDC_CMD_REG 0x520 -#define SUN4I_HDMI_DDC_CMD_EXPLICIT_EDDC_READ 6 -#define SUN4I_HDMI_DDC_CMD_IMPLICIT_READ 5 -#define SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE 3 - -#define SUN4I_HDMI_DDC_CLK_REG 0x528 -#define SUN4I_HDMI_DDC_CLK_M(m) (((m) & 0x7) << 3) -#define SUN4I_HDMI_DDC_CLK_N(n) ((n) & 0x7) - -#define SUN4I_HDMI_DDC_LINE_CTRL_REG 0x540 -#define SUN4I_HDMI_DDC_LINE_CTRL_SDA_ENABLE BIT(9) -#define SUN4I_HDMI_DDC_LINE_CTRL_SCL_ENABLE BIT(8) - -#define SUN4I_HDMI_DDC_FIFO_SIZE 16 - -/* A31 specific */ -#define SUN6I_HDMI_DDC_CTRL_REG 0x500 -#define SUN6I_HDMI_DDC_CTRL_RESET BIT(31) -#define SUN6I_HDMI_DDC_CTRL_START_CMD BIT(27) -#define SUN6I_HDMI_DDC_CTRL_SDA_ENABLE BIT(6) -#define SUN6I_HDMI_DDC_CTRL_SCL_ENABLE BIT(4) -#define SUN6I_HDMI_DDC_CTRL_ENABLE BIT(0) - -#define SUN6I_HDMI_DDC_CMD_REG 0x508 -#define SUN6I_HDMI_DDC_CMD_BYTE_COUNT(count) ((count) << 16) -/* command types in lower 3 bits are the same as sun4i */ - -#define SUN6I_HDMI_DDC_ADDR_REG 0x50c -#define SUN6I_HDMI_DDC_ADDR_SEGMENT(seg) (((seg) & 0xff) << 24) -#define SUN6I_HDMI_DDC_ADDR_EDDC(addr) (((addr) & 0xff) << 16) -#define SUN6I_HDMI_DDC_ADDR_OFFSET(off) (((off) & 0xff) << 8) -#define SUN6I_HDMI_DDC_ADDR_SLAVE(addr) (((addr) & 0xff) << 1) - -#define SUN6I_HDMI_DDC_INT_STATUS_REG 0x514 -#define SUN6I_HDMI_DDC_INT_STATUS_TIMEOUT BIT(8) -/* lower 8 bits are the same as sun4i */ - -#define SUN6I_HDMI_DDC_FIFO_CTRL_REG 0x518 -#define SUN6I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(15) -/* lower 9 bits are the same as sun4i */ - -#define SUN6I_HDMI_DDC_CLK_REG 0x520 -/* DDC CLK bit fields are the same, but the formula is not */ - -#define SUN6I_HDMI_DDC_FIFO_DATA_REG 0x580 - enum sun4i_hdmi_pkt_type { SUN4I_HDMI_PKT_AVI = 2, SUN4I_HDMI_PKT_END = 15, @@ -205,10 +126,6 @@ struct sun4i_hdmi_variant { u32 pad_ctrl1_init_val; u32 pll_ctrl_init_val; - struct reg_field ddc_clk_reg; - u8 ddc_clk_pre_divider; - u8 ddc_clk_m_offset; - u8 tmds_clk_div_offset; /* Register fields for I2C adapter */ @@ -252,7 +169,6 @@ struct sun4i_hdmi { struct device *dev; void __iomem *base; - struct regmap *regmap; /* Reset control */ struct reset_control *reset; @@ -260,15 +176,16 @@ struct sun4i_hdmi { /* Parent clocks */ struct clk *bus_clk; struct clk *mod_clk; - struct clk *ddc_parent_clk; struct clk *pll0_clk; struct clk *pll1_clk; /* And the clocks we create */ - struct clk *ddc_clk; struct clk *tmds_clk; + const char *tmds_clk_name; struct i2c_adapter *i2c; + /* Legacy dt i2c device */ + struct sun4i_hdmi_i2c_drv *i2c_drv; /* Regmap fields for I2C adapter */ struct regmap_field *field_ddc_en; @@ -294,8 +211,6 @@ struct sun4i_hdmi { const struct sun4i_hdmi_variant *variant; }; -int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *clk); int sun4i_tmds_create(struct sun4i_hdmi *hdmi); -int sun4i_hdmi_i2c_create(struct device *dev, struct sun4i_hdmi *hdmi); #endif /* _SUN4I_HDMI_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c index e826da34e9191f..0a902fcd58ebbd 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c @@ -10,15 +10,20 @@ * the License, or (at your option) any later version. */ +#include #include +#include +#include +#include #include +#include -#include "sun4i_hdmi.h" +#include "sun4i_hdmi_i2c_drv.h" struct sun4i_ddc { struct clk_hw hw; - struct sun4i_hdmi *hdmi; struct regmap_field *reg; + struct clk *parent_clk; u8 pre_div; u8 m_offset; }; @@ -80,8 +85,8 @@ static unsigned long sun4i_ddc_recalc_rate(struct clk_hw *hw, u8 m, n; regmap_field_read(ddc->reg, ®); - m = (reg >> 3) & 0xf; - n = reg & 0x7; + m = SUN4I_HDMI_DDC_CLK_M_GET(reg); + n = SUN4I_HDMI_DDC_CLK_N_GET(reg); return (((parent_rate / ddc->pre_div) / 10) >> n) / (m + ddc->m_offset); @@ -109,7 +114,9 @@ static const struct clk_ops sun4i_ddc_ops = { .set_rate = sun4i_ddc_set_rate, }; -int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *parent) +struct clk *sun4i_ddc_create(struct device *dev, struct regmap *regmap, + const struct sun4i_hdmi_i2c_variant *variant, + const struct clk *parent) { struct clk_init_data init; struct sun4i_ddc *ddc; @@ -117,30 +124,25 @@ int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *parent) parent_name = __clk_get_name(parent); if (!parent_name) - return -ENODEV; + return ERR_PTR(-ENODEV); - ddc = devm_kzalloc(hdmi->dev, sizeof(*ddc), GFP_KERNEL); + ddc = devm_kzalloc(dev, sizeof(*ddc), GFP_KERNEL); if (!ddc) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - ddc->reg = devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->ddc_clk_reg); + ddc->reg = devm_regmap_field_alloc(dev, regmap, variant->ddc_clk_reg); if (IS_ERR(ddc->reg)) - return PTR_ERR(ddc->reg); + return ERR_CAST(ddc->reg); - init.name = "hdmi-ddc"; + init.name = "hdmi-i2c"; init.ops = &sun4i_ddc_ops; init.parent_names = &parent_name; init.num_parents = 1; + init.flags = CLK_RECALC_NEW_RATES; - ddc->hdmi = hdmi; ddc->hw.init = &init; - ddc->pre_div = hdmi->variant->ddc_clk_pre_divider; - ddc->m_offset = hdmi->variant->ddc_clk_m_offset; + ddc->pre_div = variant->ddc_clk_pre_divider; + ddc->m_offset = variant->ddc_clk_m_offset; - hdmi->ddc_clk = devm_clk_register(hdmi->dev, &ddc->hw); - if (IS_ERR(hdmi->ddc_clk)) - return PTR_ERR(hdmi->ddc_clk); - - return 0; + return devm_clk_register(dev, &ddc->hw); } diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.h b/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.h new file mode 100644 index 00000000000000..12f86c378542b3 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2018 Olliver Schinagl + * + * Olliver Schinagl + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#ifndef _SUN4I_HDMI_DDC_CLK_H_ +#define _SUN4I_HDMI_DDC_CLK_H_ + +#include +#include +#include + +#include "sun4i_hdmi_i2c_drv.h" + +struct clk *sun4i_ddc_create(struct device *dev, struct regmap *regmap, + const struct sun4i_hdmi_i2c_variant *variant, + const struct clk *parent); + +#endif diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c index dda904ec0534cd..eb5c85abaf35fe 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c @@ -18,18 +18,23 @@ #include #include +#include #include +#include +#include #include #include +#include #include +#include #include -#include #include #include "sun4i_backend.h" #include "sun4i_crtc.h" #include "sun4i_drv.h" #include "sun4i_hdmi.h" +#include "sun4i_hdmi_i2c_drv.h" static inline struct sun4i_hdmi * drm_encoder_to_sun4i_hdmi(struct drm_encoder *encoder) @@ -92,6 +97,8 @@ static void sun4i_hdmi_disable(struct drm_encoder *encoder) val = readl(hdmi->base + SUN4I_HDMI_VID_CTRL_REG); val &= ~SUN4I_HDMI_VID_CTRL_ENABLE; writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG); + + clk_disable_unprepare(hdmi->tmds_clk); } static void sun4i_hdmi_enable(struct drm_encoder *encoder) @@ -102,6 +109,8 @@ static void sun4i_hdmi_enable(struct drm_encoder *encoder) DRM_DEBUG_DRIVER("Enabling the HDMI Output\n"); + clk_prepare_enable(hdmi->tmds_clk); + sun4i_hdmi_setup_avi_infoframes(hdmi, mode); val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI); val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END); @@ -276,6 +285,7 @@ static const struct cec_pin_ops sun4i_hdmi_cec_pin_ops = { /* Only difference from sun5i is AMP is 4 instead of 6 */ static const struct sun4i_hdmi_variant sun4i_variant = { + .has_ddc_parent_clk = true, .pad_ctrl0_init_val = SUN4I_HDMI_PAD_CTRL0_TXEN | SUN4I_HDMI_PAD_CTRL0_CKEN | SUN4I_HDMI_PAD_CTRL0_PWENG | @@ -303,30 +313,10 @@ static const struct sun4i_hdmi_variant sun4i_variant = { SUN4I_HDMI_PLL_CTRL_HV_IS_33 | SUN4I_HDMI_PLL_CTRL_BWS | SUN4I_HDMI_PLL_CTRL_PLL_EN, - - .ddc_clk_reg = REG_FIELD(SUN4I_HDMI_DDC_CLK_REG, 0, 6), - .ddc_clk_pre_divider = 2, - .ddc_clk_m_offset = 1, - - .field_ddc_en = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 31, 31), - .field_ddc_start = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 30, 30), - .field_ddc_reset = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 0, 0), - .field_ddc_addr_reg = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 31), - .field_ddc_slave_addr = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 6), - .field_ddc_int_status = REG_FIELD(SUN4I_HDMI_DDC_INT_STATUS_REG, 0, 8), - .field_ddc_fifo_clear = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 31, 31), - .field_ddc_fifo_rx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 4, 7), - .field_ddc_fifo_tx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 0, 3), - .field_ddc_byte_count = REG_FIELD(SUN4I_HDMI_DDC_BYTE_COUNT_REG, 0, 9), - .field_ddc_cmd = REG_FIELD(SUN4I_HDMI_DDC_CMD_REG, 0, 2), - .field_ddc_sda_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 9, 9), - .field_ddc_sck_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 8, 8), - - .ddc_fifo_reg = SUN4I_HDMI_DDC_FIFO_DATA_REG, - .ddc_fifo_has_dir = true, }; static const struct sun4i_hdmi_variant sun5i_variant = { + .has_ddc_parent_clk = true, .pad_ctrl0_init_val = SUN4I_HDMI_PAD_CTRL0_TXEN | SUN4I_HDMI_PAD_CTRL0_CKEN | SUN4I_HDMI_PAD_CTRL0_PWENG | @@ -354,31 +344,9 @@ static const struct sun4i_hdmi_variant sun5i_variant = { SUN4I_HDMI_PLL_CTRL_HV_IS_33 | SUN4I_HDMI_PLL_CTRL_BWS | SUN4I_HDMI_PLL_CTRL_PLL_EN, - - .ddc_clk_reg = REG_FIELD(SUN4I_HDMI_DDC_CLK_REG, 0, 6), - .ddc_clk_pre_divider = 2, - .ddc_clk_m_offset = 1, - - .field_ddc_en = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 31, 31), - .field_ddc_start = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 30, 30), - .field_ddc_reset = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 0, 0), - .field_ddc_addr_reg = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 31), - .field_ddc_slave_addr = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 6), - .field_ddc_int_status = REG_FIELD(SUN4I_HDMI_DDC_INT_STATUS_REG, 0, 8), - .field_ddc_fifo_clear = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 31, 31), - .field_ddc_fifo_rx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 4, 7), - .field_ddc_fifo_tx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 0, 3), - .field_ddc_byte_count = REG_FIELD(SUN4I_HDMI_DDC_BYTE_COUNT_REG, 0, 9), - .field_ddc_cmd = REG_FIELD(SUN4I_HDMI_DDC_CMD_REG, 0, 2), - .field_ddc_sda_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 9, 9), - .field_ddc_sck_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 8, 8), - - .ddc_fifo_reg = SUN4I_HDMI_DDC_FIFO_DATA_REG, - .ddc_fifo_has_dir = true, }; static const struct sun4i_hdmi_variant sun6i_variant = { - .has_ddc_parent_clk = true, .has_reset_control = true, .pad_ctrl0_init_val = 0xff | SUN4I_HDMI_PAD_CTRL0_TXEN | @@ -410,133 +378,31 @@ static const struct sun4i_hdmi_variant sun6i_variant = { SUN4I_HDMI_PLL_CTRL_HV_IS_33 | SUN4I_HDMI_PLL_CTRL_PLL_EN, - .ddc_clk_reg = REG_FIELD(SUN6I_HDMI_DDC_CLK_REG, 0, 6), - .ddc_clk_pre_divider = 1, - .ddc_clk_m_offset = 2, - .tmds_clk_div_offset = 1, - - .field_ddc_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 0, 0), - .field_ddc_start = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 27, 27), - .field_ddc_reset = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 31, 31), - .field_ddc_addr_reg = REG_FIELD(SUN6I_HDMI_DDC_ADDR_REG, 1, 31), - .field_ddc_slave_addr = REG_FIELD(SUN6I_HDMI_DDC_ADDR_REG, 1, 7), - .field_ddc_int_status = REG_FIELD(SUN6I_HDMI_DDC_INT_STATUS_REG, 0, 8), - .field_ddc_fifo_clear = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 18, 18), - .field_ddc_fifo_rx_thres = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 4, 7), - .field_ddc_fifo_tx_thres = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 0, 3), - .field_ddc_byte_count = REG_FIELD(SUN6I_HDMI_DDC_CMD_REG, 16, 25), - .field_ddc_cmd = REG_FIELD(SUN6I_HDMI_DDC_CMD_REG, 0, 2), - .field_ddc_sda_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 6, 6), - .field_ddc_sck_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 4, 4), - - .ddc_fifo_reg = SUN6I_HDMI_DDC_FIFO_DATA_REG, - .ddc_fifo_thres_incl = true, -}; - -static const struct regmap_config sun4i_hdmi_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .max_register = 0x580, }; static int sun4i_hdmi_bind(struct device *dev, struct device *master, void *data) { - struct platform_device *pdev = to_platform_device(dev); + struct device_node *i2c_np; + struct device_node *node = dev_of_node(dev); struct drm_device *drm = data; struct sun4i_drv *drv = drm->dev_private; struct sun4i_hdmi *hdmi; - struct resource *res; u32 reg; int ret; - hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return -ENOMEM; - dev_set_drvdata(dev, hdmi); - hdmi->dev = dev; - hdmi->drv = drv; - - hdmi->variant = of_device_get_match_data(dev); - if (!hdmi->variant) - return -EINVAL; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hdmi->base = devm_ioremap_resource(dev, res); - if (IS_ERR(hdmi->base)) { - dev_err(dev, "Couldn't map the HDMI encoder registers\n"); - return PTR_ERR(hdmi->base); + hdmi = dev_get_drvdata(dev); + if (!hdmi) { + dev_err(dev, "HDMI not initialized\n"); + return -ENODEV; } - if (hdmi->variant->has_reset_control) { - hdmi->reset = devm_reset_control_get(dev, NULL); - if (IS_ERR(hdmi->reset)) { - dev_err(dev, "Couldn't get the HDMI reset control\n"); - return PTR_ERR(hdmi->reset); - } - - ret = reset_control_deassert(hdmi->reset); - if (ret) { - dev_err(dev, "Couldn't deassert HDMI reset\n"); - return ret; - } - } + hdmi->drv = drv; - hdmi->bus_clk = devm_clk_get(dev, "ahb"); - if (IS_ERR(hdmi->bus_clk)) { - dev_err(dev, "Couldn't get the HDMI bus clock\n"); - ret = PTR_ERR(hdmi->bus_clk); - goto err_assert_reset; - } clk_prepare_enable(hdmi->bus_clk); - - hdmi->mod_clk = devm_clk_get(dev, "mod"); - if (IS_ERR(hdmi->mod_clk)) { - dev_err(dev, "Couldn't get the HDMI mod clock\n"); - ret = PTR_ERR(hdmi->mod_clk); - goto err_disable_bus_clk; - } clk_prepare_enable(hdmi->mod_clk); - hdmi->pll0_clk = devm_clk_get(dev, "pll-0"); - if (IS_ERR(hdmi->pll0_clk)) { - dev_err(dev, "Couldn't get the HDMI PLL 0 clock\n"); - ret = PTR_ERR(hdmi->pll0_clk); - goto err_disable_mod_clk; - } - - hdmi->pll1_clk = devm_clk_get(dev, "pll-1"); - if (IS_ERR(hdmi->pll1_clk)) { - dev_err(dev, "Couldn't get the HDMI PLL 1 clock\n"); - ret = PTR_ERR(hdmi->pll1_clk); - goto err_disable_mod_clk; - } - - hdmi->regmap = devm_regmap_init_mmio(dev, hdmi->base, - &sun4i_hdmi_regmap_config); - if (IS_ERR(hdmi->regmap)) { - dev_err(dev, "Couldn't create HDMI encoder regmap\n"); - return PTR_ERR(hdmi->regmap); - } - - ret = sun4i_tmds_create(hdmi); - if (ret) { - dev_err(dev, "Couldn't create the TMDS clock\n"); - goto err_disable_mod_clk; - } - - if (hdmi->variant->has_ddc_parent_clk) { - hdmi->ddc_parent_clk = devm_clk_get(dev, "ddc"); - if (IS_ERR(hdmi->ddc_parent_clk)) { - dev_err(dev, "Couldn't get the HDMI DDC clock\n"); - return PTR_ERR(hdmi->ddc_parent_clk); - } - } else { - hdmi->ddc_parent_clk = hdmi->tmds_clk; - } - writel(SUN4I_HDMI_CTRL_ENABLE, hdmi->base + SUN4I_HDMI_CTRL_REG); writel(hdmi->variant->pad_ctrl0_init_val, @@ -547,10 +413,30 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master, reg |= hdmi->variant->pll_ctrl_init_val; writel(reg, hdmi->base + SUN4I_HDMI_PLL_CTRL_REG); - ret = sun4i_hdmi_i2c_create(dev, hdmi); - if (ret) { + i2c_np = of_parse_phandle(node, "ddc-i2c-bus", 0); + if (!i2c_np) { + dev_warn(dev, "Missing ddc-i2c-bus node in %pOF\n", node); + + /* legacy devicetree's do not have the hdmi-i2c node */ + if (hdmi->variant->has_ddc_parent_clk) + hdmi->i2c_drv = sun4i_hdmi_i2c_setup(dev, hdmi->base, + hdmi->variant->has_ddc_parent_clk ? + hdmi->tmds_clk : NULL); + if (IS_ERR(hdmi->i2c_drv)) { + if (PTR_ERR(hdmi->i2c_drv) != -EPROBE_DEFER) + dev_err(dev, "Couldn't setup HDMI I2C driver\n"); + ret = PTR_ERR(hdmi->i2c_drv); + goto err_disable_clks; + } + + hdmi->i2c = &hdmi->i2c_drv->adap; + } else { + hdmi->i2c = of_find_i2c_adapter_by_node(i2c_np); + } + if (!hdmi->i2c) { dev_err(dev, "Couldn't create the HDMI I2C adapter\n"); - goto err_disable_mod_clk; + ret = -ENODEV; + goto err_disable_clks; } drm_encoder_helper_add(&hdmi->encoder, @@ -562,14 +448,12 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master, NULL); if (ret) { dev_err(dev, "Couldn't initialise the HDMI encoder\n"); - goto err_del_i2c_adapter; + return -ENODEV; } - hdmi->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm, - dev->of_node); + hdmi->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm, node); if (!hdmi->encoder.possible_crtcs) { - ret = -EPROBE_DEFER; - goto err_del_i2c_adapter; + return -EPROBE_DEFER; } #ifdef CONFIG_DRM_SUN4I_HDMI_CEC @@ -608,14 +492,9 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master, err_cleanup_connector: cec_delete_adapter(hdmi->cec_adap); drm_encoder_cleanup(&hdmi->encoder); -err_del_i2c_adapter: - i2c_del_adapter(hdmi->i2c); -err_disable_mod_clk: +err_disable_clks: clk_disable_unprepare(hdmi->mod_clk); -err_disable_bus_clk: clk_disable_unprepare(hdmi->bus_clk); -err_assert_reset: - reset_control_assert(hdmi->reset); return ret; } @@ -624,10 +503,11 @@ static void sun4i_hdmi_unbind(struct device *dev, struct device *master, { struct sun4i_hdmi *hdmi = dev_get_drvdata(dev); + if (!hdmi->i2c_drv) + put_device(&hdmi->i2c->dev); cec_unregister_adapter(hdmi->cec_adap); drm_connector_cleanup(&hdmi->connector); drm_encoder_cleanup(&hdmi->encoder); - i2c_del_adapter(hdmi->i2c); clk_disable_unprepare(hdmi->mod_clk); clk_disable_unprepare(hdmi->bus_clk); } @@ -639,12 +519,118 @@ static const struct component_ops sun4i_hdmi_ops = { static int sun4i_hdmi_probe(struct platform_device *pdev) { + struct device_node *node = dev_of_node(&pdev->dev); + struct sun4i_hdmi *hdmi; + struct resource *res; + struct device *dev = &pdev->dev; + int ret; + + hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) + return -ENOMEM; + + hdmi->dev = dev; + + hdmi->variant = of_device_get_match_data(dev); + if (!hdmi->variant) { + dev_err(dev, "hdmi_tmds_clk: couldn't find matching device\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hdmi->base = devm_ioremap_resource(dev, res); + if (IS_ERR(hdmi->base)) { + dev_err(dev, "couldn't map the HDMI encoder registers\n"); + return PTR_ERR(hdmi->base); + } + + if (hdmi->variant->has_reset_control) { + hdmi->reset = devm_reset_control_get(dev, NULL); + if (IS_ERR(hdmi->reset)) { + dev_err(dev, "couldn't get the HDMI reset control\n"); + return PTR_ERR(hdmi->reset); + } + + ret = reset_control_deassert(hdmi->reset); + if (ret) { + dev_err(dev, "couldn't deassert HDMI reset\n"); + goto err_assert_reset; + } + } + + hdmi->bus_clk = devm_clk_get(dev, "ahb"); + if (IS_ERR(hdmi->bus_clk)) { + dev_err(dev, "couldn't get the HDMI bus clock\n"); + ret = PTR_ERR(hdmi->bus_clk); + goto err_assert_reset; + } + clk_prepare_enable(hdmi->bus_clk); + + hdmi->mod_clk = devm_clk_get(dev, "mod"); + if (IS_ERR(hdmi->mod_clk)) { + dev_err(dev, "couldn't get the HDMI mod clock\n"); + ret = PTR_ERR(hdmi->mod_clk); + goto err_disable_bus_clk; + } + clk_prepare_enable(hdmi->mod_clk); + + hdmi->pll0_clk = devm_clk_get(dev, "pll-0"); + if (IS_ERR(hdmi->pll0_clk)) { + dev_err(dev, "couldn't get the HDMI PLL 0 clock\n"); + ret = PTR_ERR(hdmi->pll0_clk); + goto err_disable_mod_clk; + } + + hdmi->pll1_clk = devm_clk_get(dev, "pll-1"); + if (IS_ERR(hdmi->pll1_clk)) { + dev_err(dev, "couldn't get the HDMI PLL 1 clock\n"); + ret = PTR_ERR(hdmi->pll1_clk); + goto err_disable_mod_clk; + } + + ret = of_property_read_string(node, "clock-output-names", + &hdmi->tmds_clk_name); + if (ret) { + /* Deal with old/incomplete DTs */ + hdmi->tmds_clk_name = "hdmi-tmds"; + dev_warn(dev, "no 'clock-output-names', falling back to: %s\n", + hdmi->tmds_clk_name); + } + + ret = sun4i_tmds_create(hdmi); + if (ret) { + dev_err(dev, "couldn't create the TMDS clock\n"); + goto err_disable_mod_clk; + } + ret = of_clk_add_provider(node, of_clk_src_simple_get, hdmi->tmds_clk); + // TODO devm_of_clk_add_provider() + if (ret) { + dev_err(dev, "couldn't register the TMDS clock\n"); + goto err_disable_mod_clk; + } + + dev_set_drvdata(dev, hdmi); + return component_add(&pdev->dev, &sun4i_hdmi_ops); + +err_disable_mod_clk: + clk_disable_unprepare(hdmi->mod_clk); +err_disable_bus_clk: + clk_disable_unprepare(hdmi->bus_clk); +err_assert_reset: + reset_control_assert(hdmi->reset); + + return ret; } static int sun4i_hdmi_remove(struct platform_device *pdev) { + struct device_node *node = dev_of_node(&pdev->dev); + struct sun4i_hdmi *hdmi = dev_get_drvdata(&pdev->dev); + component_del(&pdev->dev, &sun4i_hdmi_ops); + sun4i_hdmi_i2c_fini(hdmi->i2c_drv); + of_clk_del_provider(node); return 0; } diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c index 58e9d37e8c17c0..cb7e1d0d12ff24 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c @@ -1,321 +1,148 @@ /* - * Copyright (C) 2016 Maxime Ripard - * Copyright (C) 2017 Jonathan Liu + * Copyright (C) 2018 Olliver Schinagl + * + * Olliver Schinagl + * + * SPDX-License-Identifier: GPL-2.0+ * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. */ -#include -#include -#include - -#include "sun4i_hdmi.h" - -#define SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK ( \ - SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION | \ - SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW | \ - SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW | \ - SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR | \ - SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR | \ - SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR \ -) - -/* FIFO request bit is set when FIFO level is above RX_THRESHOLD during read */ -#define RX_THRESHOLD SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX - -static int fifo_transfer(struct sun4i_hdmi *hdmi, u8 *buf, int len, bool read) -{ - /* - * 1 byte takes 9 clock cycles (8 bits + 1 ACK) = 90 us for 100 kHz - * clock. As clock rate is fixed, just round it up to 100 us. - */ - const unsigned long byte_time_ns = 100; - const u32 mask = SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK | - SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST | - SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE; - u32 reg; - /* - * If threshold is inclusive, then the FIFO may only have - * RX_THRESHOLD number of bytes, instead of RX_THRESHOLD + 1. - */ - int read_len = RX_THRESHOLD + - (hdmi->variant->ddc_fifo_thres_incl ? 0 : 1); - - /* - * Limit transfer length by FIFO threshold or FIFO size. - * For TX the threshold is for an empty FIFO. - */ - len = min_t(int, len, read ? read_len : SUN4I_HDMI_DDC_FIFO_SIZE); - - /* Wait until error, FIFO request bit set or transfer complete */ - if (regmap_field_read_poll_timeout(hdmi->field_ddc_int_status, reg, - reg & mask, len * byte_time_ns, - 100000)) - return -ETIMEDOUT; - - if (reg & SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK) - return -EIO; +#include +#include +#include +#include +#include +#include +#include + +#include "sun4i_hdmi_i2c_drv.h" + +#define SUN4I_HDMI_I2C_DRIVER_NAME "sun4i-hdmi-i2c" + +static const struct sun4i_hdmi_i2c_variant sun4i_variant = { + .ddc_clk_reg = REG_FIELD(SUN4I_HDMI_DDC_CLK_REG, 0, 6), + .ddc_clk_pre_divider = 2, + .ddc_clk_m_offset = 1, + + .field_ddc_en = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 31, 31), + .field_ddc_start = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 30, 30), + .field_ddc_reset = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 0, 0), + .field_ddc_addr_reg = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 31), + .field_ddc_slave_addr = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 6), + .field_ddc_int_status = REG_FIELD(SUN4I_HDMI_DDC_INT_STATUS_REG, 0, 8), + .field_ddc_fifo_clear = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 31, 31), + .field_ddc_fifo_rx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 4, 7), + .field_ddc_fifo_tx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 0, 3), + .field_ddc_byte_count = REG_FIELD(SUN4I_HDMI_DDC_BYTE_COUNT_REG, 0, 9), + .field_ddc_cmd = REG_FIELD(SUN4I_HDMI_DDC_CMD_REG, 0, 2), + .field_ddc_sda_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 9, 9), + .field_ddc_sck_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 8, 8), + .field_ddc_bus_busy = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG, 10, 10), + .field_ddc_sda_state = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG, 9, 9), + .field_ddc_sck_state = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG, 8, 8), + .field_ddc_sck_line_ctrl = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG, 3, 3), + .field_ddc_sck_line_ctrl_en = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG, 2, 2), + .field_ddc_sda_line_ctrl = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG, 1, 1), + .field_ddc_sda_line_ctrl_en = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG, 0, 0), + + .ddc_fifo_reg = SUN4I_HDMI_DDC_FIFO_DATA_REG, + .ddc_fifo_has_dir = true, +}; - if (read) - readsb(hdmi->base + hdmi->variant->ddc_fifo_reg, buf, len); - else - writesb(hdmi->base + hdmi->variant->ddc_fifo_reg, buf, len); +static const struct sun4i_hdmi_i2c_variant sun6i_variant = { + .ddc_clk_reg = REG_FIELD(SUN6I_HDMI_DDC_CLK_REG, 0, 6), + .ddc_clk_pre_divider = 1, + .ddc_clk_m_offset = 2, + + .field_ddc_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 0, 0), + .field_ddc_start = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 27, 27), + .field_ddc_reset = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 31, 31), + .field_ddc_addr_reg = REG_FIELD(SUN6I_HDMI_DDC_ADDR_REG, 1, 31), + .field_ddc_slave_addr = REG_FIELD(SUN6I_HDMI_DDC_ADDR_REG, 1, 7), + .field_ddc_int_status = REG_FIELD(SUN6I_HDMI_DDC_INT_STATUS_REG, 0, 8), + .field_ddc_fifo_clear = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 18, 18), + .field_ddc_fifo_rx_thres = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 4, 7), + .field_ddc_fifo_tx_thres = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 0, 3), + .field_ddc_byte_count = REG_FIELD(SUN6I_HDMI_DDC_CMD_REG, 16, 25), + .field_ddc_cmd = REG_FIELD(SUN6I_HDMI_DDC_CMD_REG, 0, 2), + .field_ddc_sda_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 6, 6), + .field_ddc_sck_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 4, 4), + .field_ddc_bus_busy = REG_FIELD(SUN6I_HDMI_DDC_EXT_REG, 10, 10), + .field_ddc_sda_state = REG_FIELD(SUN6I_HDMI_DDC_EXT_REG, 9, 9), + .field_ddc_sck_state = REG_FIELD(SUN6I_HDMI_DDC_EXT_REG, 8, 8), + .field_ddc_sck_line_ctrl = REG_FIELD(SUN6I_HDMI_DDC_EXT_REG, 3, 3), + .field_ddc_sck_line_ctrl_en = REG_FIELD(SUN6I_HDMI_DDC_EXT_REG, 2, 2), + .field_ddc_sda_line_ctrl = REG_FIELD(SUN6I_HDMI_DDC_EXT_REG, 1, 1), + .field_ddc_sda_line_ctrl_en = REG_FIELD(SUN6I_HDMI_DDC_EXT_REG, 0, 0), + + .ddc_fifo_reg = SUN6I_HDMI_DDC_FIFO_DATA_REG, + .ddc_fifo_thres_incl = true, +}; - /* Clear FIFO request bit by forcing a write to that bit */ - regmap_field_force_write(hdmi->field_ddc_int_status, - SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST); +static const struct regmap_config sun4i_hdmi_i2c_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x80, +}; - return len; -} +static const struct of_device_id sun4i_hdmi_i2c_of_table[] = { + { .compatible = "allwinner,sun4i-a10-hdmi-i2c", .data = &sun4i_variant }, + { .compatible = "allwinner,sun6i-a31-hdmi-i2c", .data = &sun6i_variant }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sun4i_hdmi_i2c_of_table); -static int xfer_msg(struct sun4i_hdmi *hdmi, struct i2c_msg *msg) +static int sun4i_hdmi_i2c_probe(struct platform_device *pdev) { - int i, len; - u32 reg; - - /* Set FIFO direction */ - if (hdmi->variant->ddc_fifo_has_dir) { - reg = readl(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG); - reg &= ~SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK; - reg |= (msg->flags & I2C_M_RD) ? - SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ : - SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE; - writel(reg, hdmi->base + SUN4I_HDMI_DDC_CTRL_REG); + struct sun4i_hdmi_i2c_drv *drv; + struct resource *res; + void __iomem *base; + + if (!pdev) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "couldn't map the HDMI-I2C registers\n"); + return PTR_ERR(base); } - /* Clear address register (not cleared by soft reset) */ - regmap_field_write(hdmi->field_ddc_addr_reg, 0); - - /* Set I2C address */ - regmap_field_write(hdmi->field_ddc_slave_addr, msg->addr); - - /* - * Set FIFO RX/TX thresholds and clear FIFO - * - * If threshold is inclusive, we can set the TX threshold to - * 0 instead of 1. - */ - regmap_field_write(hdmi->field_ddc_fifo_tx_thres, - hdmi->variant->ddc_fifo_thres_incl ? 0 : 1); - regmap_field_write(hdmi->field_ddc_fifo_rx_thres, RX_THRESHOLD); - regmap_field_write(hdmi->field_ddc_fifo_clear, 1); - if (regmap_field_read_poll_timeout(hdmi->field_ddc_fifo_clear, - reg, !reg, 100, 2000)) - return -EIO; - - /* Set transfer length */ - regmap_field_write(hdmi->field_ddc_byte_count, msg->len); - - /* Set command */ - regmap_field_write(hdmi->field_ddc_cmd, - msg->flags & I2C_M_RD ? - SUN4I_HDMI_DDC_CMD_IMPLICIT_READ : - SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE); - - /* Clear interrupt status bits by forcing a write */ - regmap_field_force_write(hdmi->field_ddc_int_status, - SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK | - SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST | - SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE); - - /* Start command */ - regmap_field_write(hdmi->field_ddc_start, 1); - - /* Transfer bytes */ - for (i = 0; i < msg->len; i += len) { - len = fifo_transfer(hdmi, msg->buf + i, msg->len - i, - msg->flags & I2C_M_RD); - if (len <= 0) - return len; + drv = sun4i_hdmi_i2c_init(&pdev->dev, base, sun4i_hdmi_i2c_of_table, + &sun4i_hdmi_i2c_regmap_config, NULL); + if (IS_ERR(drv)) { + if (PTR_ERR(drv) != -EPROBE_DEFER) + dev_err(&pdev->dev, "couldn't setup HDMI-I2C driver\n"); + return PTR_ERR(drv); } - /* Wait for command to finish */ - if (regmap_field_read_poll_timeout(hdmi->field_ddc_start, - reg, !reg, 100, 100000)) - return -EIO; - - /* Check for errors */ - regmap_field_read(hdmi->field_ddc_int_status, ®); - if ((reg & SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK) || - !(reg & SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE)) { - return -EIO; - } + platform_set_drvdata(pdev, drv); return 0; } -static int sun4i_hdmi_i2c_xfer(struct i2c_adapter *adap, - struct i2c_msg *msgs, int num) -{ - struct sun4i_hdmi *hdmi = i2c_get_adapdata(adap); - u32 reg; - int err, i, ret = num; - - for (i = 0; i < num; i++) { - if (!msgs[i].len) - return -EINVAL; - if (msgs[i].len > SUN4I_HDMI_DDC_BYTE_COUNT_MAX) - return -EINVAL; - } - - /* DDC clock needs to be enabled for the module to work */ - clk_prepare_enable(hdmi->ddc_clk); - clk_set_rate(hdmi->ddc_clk, 100000); - - /* Reset I2C controller */ - regmap_field_write(hdmi->field_ddc_en, 1); - regmap_field_write(hdmi->field_ddc_reset, 1); - if (regmap_field_read_poll_timeout(hdmi->field_ddc_reset, - reg, !reg, 100, 2000)) { - clk_disable_unprepare(hdmi->ddc_clk); - return -EIO; - } - - regmap_field_write(hdmi->field_ddc_sck_en, 1); - regmap_field_write(hdmi->field_ddc_sda_en, 1); - - for (i = 0; i < num; i++) { - err = xfer_msg(hdmi, &msgs[i]); - if (err) { - ret = err; - break; - } - } - - clk_disable_unprepare(hdmi->ddc_clk); - return ret; -} - -static u32 sun4i_hdmi_i2c_func(struct i2c_adapter *adap) -{ - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; -} - -static const struct i2c_algorithm sun4i_hdmi_i2c_algorithm = { - .master_xfer = sun4i_hdmi_i2c_xfer, - .functionality = sun4i_hdmi_i2c_func, -}; -static int sun4i_hdmi_init_regmap_fields(struct sun4i_hdmi *hdmi) +static int sun4i_hdmi_i2c_remove(struct platform_device *pdev) { - hdmi->field_ddc_en = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_en); - if (IS_ERR(hdmi->field_ddc_en)) - return PTR_ERR(hdmi->field_ddc_en); - - hdmi->field_ddc_start = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_start); - if (IS_ERR(hdmi->field_ddc_start)) - return PTR_ERR(hdmi->field_ddc_start); - - hdmi->field_ddc_reset = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_reset); - if (IS_ERR(hdmi->field_ddc_reset)) - return PTR_ERR(hdmi->field_ddc_reset); - - hdmi->field_ddc_addr_reg = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_addr_reg); - if (IS_ERR(hdmi->field_ddc_addr_reg)) - return PTR_ERR(hdmi->field_ddc_addr_reg); - - hdmi->field_ddc_slave_addr = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_slave_addr); - if (IS_ERR(hdmi->field_ddc_slave_addr)) - return PTR_ERR(hdmi->field_ddc_slave_addr); - - hdmi->field_ddc_int_mask = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_int_mask); - if (IS_ERR(hdmi->field_ddc_int_mask)) - return PTR_ERR(hdmi->field_ddc_int_mask); - - hdmi->field_ddc_int_status = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_int_status); - if (IS_ERR(hdmi->field_ddc_int_status)) - return PTR_ERR(hdmi->field_ddc_int_status); + struct sun4i_hdmi_i2c_drv *drv = platform_get_drvdata(pdev); - hdmi->field_ddc_fifo_clear = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_fifo_clear); - if (IS_ERR(hdmi->field_ddc_fifo_clear)) - return PTR_ERR(hdmi->field_ddc_fifo_clear); - - hdmi->field_ddc_fifo_rx_thres = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_fifo_rx_thres); - if (IS_ERR(hdmi->field_ddc_fifo_rx_thres)) - return PTR_ERR(hdmi->field_ddc_fifo_rx_thres); - - hdmi->field_ddc_fifo_tx_thres = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_fifo_tx_thres); - if (IS_ERR(hdmi->field_ddc_fifo_tx_thres)) - return PTR_ERR(hdmi->field_ddc_fifo_tx_thres); - - hdmi->field_ddc_byte_count = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_byte_count); - if (IS_ERR(hdmi->field_ddc_byte_count)) - return PTR_ERR(hdmi->field_ddc_byte_count); - - hdmi->field_ddc_cmd = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_cmd); - if (IS_ERR(hdmi->field_ddc_cmd)) - return PTR_ERR(hdmi->field_ddc_cmd); - - hdmi->field_ddc_sda_en = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_sda_en); - if (IS_ERR(hdmi->field_ddc_sda_en)) - return PTR_ERR(hdmi->field_ddc_sda_en); - - hdmi->field_ddc_sck_en = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_sck_en); - if (IS_ERR(hdmi->field_ddc_sck_en)) - return PTR_ERR(hdmi->field_ddc_sck_en); + sun4i_hdmi_i2c_fini(drv); return 0; } -int sun4i_hdmi_i2c_create(struct device *dev, struct sun4i_hdmi *hdmi) -{ - struct i2c_adapter *adap; - int ret = 0; - - ret = sun4i_ddc_create(hdmi, hdmi->ddc_parent_clk); - if (ret) - return ret; - - ret = sun4i_hdmi_init_regmap_fields(hdmi); - if (ret) - return ret; - - adap = devm_kzalloc(dev, sizeof(*adap), GFP_KERNEL); - if (!adap) - return -ENOMEM; - - adap->owner = THIS_MODULE; - adap->class = I2C_CLASS_DDC; - adap->algo = &sun4i_hdmi_i2c_algorithm; - strlcpy(adap->name, "sun4i_hdmi_i2c adapter", sizeof(adap->name)); - i2c_set_adapdata(adap, hdmi); - - ret = i2c_add_adapter(adap); - if (ret) - return ret; - - hdmi->i2c = adap; +static struct platform_driver sun4i_hdmi_i2c_driver = { + .probe = sun4i_hdmi_i2c_probe, + .remove = sun4i_hdmi_i2c_remove, + .driver = { + .name = SUN4I_HDMI_I2C_DRIVER_NAME, + .of_match_table = sun4i_hdmi_i2c_of_table, + }, +}; +module_platform_driver(sun4i_hdmi_i2c_driver); - return ret; -} +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Olliver Schinagl "); +MODULE_DESCRIPTION("I2C adapter driver for Allwinner sunxi HDMI I2C bus"); +MODULE_ALIAS("platform:" SUN4I_HDMI_I2C_DRIVER_NAME); diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.h b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.h new file mode 100644 index 00000000000000..a747998157203d --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2016 Maxime Ripard + * Copyright (C) 2017 Chen-Yu Tsai + * Copyirght (C) 2017 Jonathan Liu + * Copyright (C) 2017 Olliver Schinagl + * + * Chen-Yu Tsai + * Maxime Ripard + * Jonathan Liu + * Olliver Schinagl + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _SUN4I_HDMI_I2C_H_ +#define _SUN4I_HDMI_I2C_H_ + +#define SUN4I_HDMI_DDC_CTRL_REG 0x500 +#define SUN4I_HDMI_DDC_CTRL_ENABLE BIT(31) +#define SUN4I_HDMI_DDC_CTRL_START_CMD BIT(30) +#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK BIT(8) +#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE (1 << 8) +#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ (0 << 8) +#define SUN4I_HDMI_DDC_CTRL_RESET BIT(0) + +#define SUN4I_HDMI_DDC_ADDR_REG 0x504 +#define SUN4I_HDMI_DDC_ADDR_SEGMENT(seg) (((seg) << 24) & GENMASK(31, 24)) +#define SUN4I_HDMI_DDC_ADDR_EDDC(addr) (((addr) << 16) & GENMASK(23, 16)) +#define SUN4I_HDMI_DDC_ADDR_OFFSET(off) (((off) << 8) & GENMASK(15, 8)) +#define SUN4I_HDMI_DDC_ADDR_SLAVE(addr) ((addr) & GENMASK(7, 0)) + +#define SUN4I_HDMI_DDC_INT_STATUS_REG 0x50c +#define SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION BIT(7) +#define SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW BIT(6) +#define SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW BIT(5) +#define SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST BIT(4) +#define SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR BIT(3) +#define SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR BIT(2) +#define SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR BIT(1) +#define SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE BIT(0) + +#define SUN4I_HDMI_DDC_FIFO_CTRL_REG 0x510 +#define SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(31) +#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MASK GENMASK(7, 4) +#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES(n) \ + (((n) << 4) & SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MASK) +#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX (BIT(4) - 1) +#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MASK GENMASK(3, 0) +#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES(n) \ + ((n) & SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MASK) +#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MAX (BIT(4) - 1) + +#define SUN4I_HDMI_DDC_FIFO_DATA_REG 0x518 + +#define SUN4I_HDMI_DDC_BYTE_COUNT_REG 0x51c +#define SUN4I_HDMI_DDC_BYTE_COUNT_MAX (BIT(10) - 1) + +#define SUN4I_HDMI_DDC_CMD_REG 0x520 +#define SUN4I_HDMI_DDC_CMD_EXPLICIT_EDDC_READ 0x6 +#define SUN4I_HDMI_DDC_CMD_IMPLICIT_READ 0x5 +#define SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE 0x3 + +#define SUN4I_HDMI_DDC_EXT_REG 0x524 +#define SUN4I_HDMI_DDC_EXT_BUS_BUSY BIT(10) +#define SUN4I_HDMI_DDC_EXT_SDA_STATE BIT(9) +#define SUN4I_HDMI_DDC_EXT_SCK_STATE BIT(8) +#define SUN4I_HDMI_DDC_EXT_SCL_LINE_CTRL BIT(3) +#define SUN4I_HDMI_DDC_EXT_SCL_LINE_CTRL_EN BIT(2) +#define SUN4I_HDMI_DDC_EXT_SDA_LINE_CTRL BIT(1) +#define SUN4I_HDMI_DDC_EXT_SDA_LINE_CTRL_EN BIT(0) + +#define SUN4I_HDMI_DDC_CLK_REG 0x528 +#define SUN4I_HDMI_DDC_CLK_M_OFFSET 3 +#define SUN4I_HDMI_DDC_CLK_M_MASK GENMASK(7, 4) +#define SUN4I_HDMI_DDC_CLK_N_MASK GENMASK(3, 0) +#define SUN4I_HDMI_DDC_CLK_M(m) \ + (((m) << SUN4I_HDMI_DDC_CLK_M_OFFSET) & SUN4I_HDMI_DDC_CLK_M_MASK) +#define SUN4I_HDMI_DDC_CLK_N(n) \ + ((n) & SUN4I_HDMI_DDC_CLK_N_MASK) +#define SUN4I_HDMI_DDC_CLK_M_GET(reg) \ + (((reg) & SUN4I_HDMI_DDC_CLK_M_MASK) >> SUN4I_HDMI_DDC_CLK_M_OFFSET) +#define SUN4I_HDMI_DDC_CLK_N_GET(reg) \ + ((reg) & SUN4I_HDMI_DDC_CLK_N_MASK) + +#define SUN4I_HDMI_DDC_LINE_CTRL_REG 0x540 +#define SUN4I_HDMI_DDC_LINE_CTRL_SDA_ENABLE BIT(9) +#define SUN4I_HDMI_DDC_LINE_CTRL_SCL_ENABLE BIT(8) + +#define SUN4I_HDMI_DDC_FIFO_SIZE 16 + +/* A31 specific */ +#define SUN6I_HDMI_DDC_CTRL_REG 0x500 +#define SUN6I_HDMI_DDC_CTRL_RESET BIT(31) +#define SUN6I_HDMI_DDC_CTRL_START_CMD BIT(27) +#define SUN6I_HDMI_DDC_CTRL_SDA_ENABLE BIT(6) +#define SUN6I_HDMI_DDC_CTRL_SCL_ENABLE BIT(4) +#define SUN6I_HDMI_DDC_CTRL_ENABLE BIT(0) + +#define SUN6I_HDMI_DDC_EXT_REG 0x504 + +#define SUN6I_HDMI_DDC_CMD_REG 0x508 +#define SUN6I_HDMI_DDC_CMD_BYTE_COUNT(count) ((count) << 16) +/* command types in lower 3 bits are the same as sun4i */ + +#define SUN6I_HDMI_DDC_ADDR_REG 0x50c +#define SUN6I_HDMI_DDC_ADDR_SEGMENT(seg) (((seg) << 24) & GENMASK(31, 24)) +#define SUN6I_HDMI_DDC_ADDR_EDDC(addr) (((addr) << 16) & GENMASK(23, 16)) +#define SUN6I_HDMI_DDC_ADDR_OFFSET(off) (((off) << 8) & GENMASK(15, 8)) +#define SUN6I_HDMI_DDC_ADDR_SLAVE(addr) (((addr) << 1) & GENMASK(7, 1)) + +#define SUN6I_HDMI_DDC_INT_STATUS_REG 0x514 +#define SUN6I_HDMI_DDC_INT_STATUS_TIMEOUT BIT(8) +/* lower 8 bits are the same as sun4i */ + +#define SUN6I_HDMI_DDC_FIFO_CTRL_REG 0x518 +#define SUN6I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(15) +/* lower 9 bits are the same as sun4i */ + +#define SUN6I_HDMI_DDC_CLK_REG 0x520 +/* DDC CLK bit fields are the same, but the formula is not */ + +#define SUN6I_HDMI_DDC_FIFO_DATA_REG 0x580 + +#endif diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c_drv.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c_drv.c new file mode 100644 index 00000000000000..1c3f7f53560238 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c_drv.c @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2016 Maxime Ripard + * Copyright (C) 2017 Chen-Yu Tsai + * Copyright (C) 2017 Jonathan Liu + * Copyright (C) 2017 Olliver Schinagl + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sun4i_hdmi_ddc_clk.h" +#include "sun4i_hdmi_i2c_drv.h" + +#define SUN4I_HDMI_I2C_SPEED_MAX 25000000 +#define SUN4I_HDMI_I2C_SPEED_DEFAULT 100000 + +/* FIFO request bit is set when FIFO level is above RX_THRESHOLD during read */ +#define RX_THRESHOLD SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX + +static const struct sun4i_hdmi_i2c_variant sun4i_legacy_variant = { + .ddc_clk_reg = REG_FIELD(SUN4I_HDMI_DDC_CLK_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 6), + .ddc_clk_pre_divider = 4, + .ddc_clk_m_offset = 1, + + .field_ddc_en = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 31, 31), + .field_ddc_start = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 30, 30), + .field_ddc_reset = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 0), + .field_ddc_addr_reg = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 31), + .field_ddc_slave_addr = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 6), + .field_ddc_int_status = REG_FIELD(SUN4I_HDMI_DDC_INT_STATUS_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 8), + .field_ddc_fifo_clear = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 31, 31), + .field_ddc_fifo_rx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 4, 7), + .field_ddc_fifo_tx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 3), + .field_ddc_byte_count = REG_FIELD(SUN4I_HDMI_DDC_BYTE_COUNT_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 9), + .field_ddc_cmd = REG_FIELD(SUN4I_HDMI_DDC_CMD_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 2), + .field_ddc_sda_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 9, 9), + .field_ddc_sck_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 8, 8), + .field_ddc_bus_busy = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG + + SUN4I_HDMI_DDC_OFFSET, 10, 10), + .field_ddc_sda_state = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG + + SUN4I_HDMI_DDC_OFFSET, 9, 9), + .field_ddc_sck_state = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG + + SUN4I_HDMI_DDC_OFFSET, 8, 8), + .field_ddc_sck_line_ctrl = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG + + SUN4I_HDMI_DDC_OFFSET, 3, 3), + .field_ddc_sck_line_ctrl_en = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG + + SUN4I_HDMI_DDC_OFFSET, 2, 2), + .field_ddc_sda_line_ctrl = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG + + SUN4I_HDMI_DDC_OFFSET, 1, 1), + .field_ddc_sda_line_ctrl_en = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 0), + + .ddc_fifo_reg = SUN4I_HDMI_DDC_FIFO_DATA_REG + + SUN4I_HDMI_DDC_OFFSET, + .ddc_fifo_has_dir = true, +}; + +static const struct sun4i_hdmi_i2c_variant sun6i_legacy_variant = { + .parent_clk_name = "ddc", + .ddc_clk_reg = REG_FIELD(SUN6I_HDMI_DDC_CLK_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 6), + .ddc_clk_pre_divider = 1, + .ddc_clk_m_offset = 2, + + .field_ddc_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 0), + .field_ddc_start = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 27, 27), + .field_ddc_reset = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 31, 31), + .field_ddc_addr_reg = REG_FIELD(SUN6I_HDMI_DDC_ADDR_REG + + SUN4I_HDMI_DDC_OFFSET, 1, 31), + .field_ddc_slave_addr = REG_FIELD(SUN6I_HDMI_DDC_ADDR_REG + + SUN4I_HDMI_DDC_OFFSET, 1, 7), + .field_ddc_int_status = REG_FIELD(SUN6I_HDMI_DDC_INT_STATUS_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 8), + .field_ddc_fifo_clear = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 18, 18), + .field_ddc_fifo_rx_thres = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 4, 7), + .field_ddc_fifo_tx_thres = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 3), + .field_ddc_byte_count = REG_FIELD(SUN6I_HDMI_DDC_CMD_REG + + SUN4I_HDMI_DDC_OFFSET, 16, 25), + .field_ddc_cmd = REG_FIELD(SUN6I_HDMI_DDC_CMD_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 2), + .field_ddc_sda_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 6, 6), + .field_ddc_sck_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 4, 4), + .field_ddc_bus_busy = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG + + SUN4I_HDMI_DDC_OFFSET, 10, 10), + .field_ddc_sda_state = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG + + SUN4I_HDMI_DDC_OFFSET, 9, 9), + .field_ddc_sck_state = REG_FIELD(SUN4I_HDMI_DDC_EXT_REG + + SUN4I_HDMI_DDC_OFFSET, 8, 8), + .field_ddc_sck_line_ctrl = REG_FIELD(SUN6I_HDMI_DDC_EXT_REG + + SUN4I_HDMI_DDC_OFFSET, 3, 3), + .field_ddc_sck_line_ctrl_en = REG_FIELD(SUN6I_HDMI_DDC_EXT_REG + + SUN4I_HDMI_DDC_OFFSET, 2, 2), + .field_ddc_sda_line_ctrl = REG_FIELD(SUN6I_HDMI_DDC_EXT_REG + + SUN4I_HDMI_DDC_OFFSET, 1, 1), + .field_ddc_sda_line_ctrl_en = REG_FIELD(SUN6I_HDMI_DDC_EXT_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 0), + + .ddc_fifo_reg = SUN6I_HDMI_DDC_FIFO_DATA_REG + + SUN4I_HDMI_DDC_OFFSET, + .ddc_fifo_thres_incl = true, +}; + +static const struct regmap_config sun4i_hdmi_i2c_legacy_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x580, +}; + +static int fifo_transfer(struct sun4i_hdmi_i2c_drv *drv, u8 *buf, int len, bool read) +{ + const u32 mask = SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK | + SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST | + SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE; + u32 reg; + unsigned long byte_time_us; + + /* + * If threshold is inclusive, then the FIFO may only have + * RX_THRESHOLD number of bytes, instead of RX_THRESHOLD + 1. + */ + int read_len = RX_THRESHOLD + + (drv->variant->ddc_fifo_thres_incl ? 0 : 1); + + /* + * Limit transfer length by FIFO threshold or FIFO size. + * For TX the threshold is for an empty FIFO. + */ + len = min_t(int, len, read ? read_len : SUN4I_HDMI_DDC_FIFO_SIZE); + + /* + * 1 byte takes 9 clock cycles (8 bits + 1 ACK) times the number of + * bytes to be transmitted. One additional 'round-up' byte is added + * as a margin. + */ + byte_time_us = (len + 1) * 9 * clk_get_rate(drv->ddc_clk) / 10000; + + /* Wait until error, FIFO request bit set or transfer complete */ + if (regmap_field_read_poll_timeout(drv->field_ddc_int_status, reg, + reg & mask, byte_time_us, 100000)) { + dev_err(drv->dev, "DDC bus timeout after %lu us\n", byte_time_us); + return -ETIMEDOUT; + } + + if (reg & SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK) + return -EIO; + + if (read) + readsb(drv->base + drv->variant->ddc_fifo_reg, buf, len); + else + writesb(drv->base + drv->variant->ddc_fifo_reg, buf, len); + + /* Clear FIFO request bit by forcing a write to that bit */ + regmap_field_force_write(drv->field_ddc_int_status, + SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST); + + return len; +} + +static int xfer_msg(struct sun4i_hdmi_i2c_drv *drv, struct i2c_msg *msg) +{ + unsigned int bus_busy; + int i, len; + int err; + u32 reg; + + err = regmap_field_read(drv->field_ddc_bus_busy, &bus_busy); + if (err) + return err; + + if (bus_busy) { + dev_err(drv->dev, "failed to transfer data, bus busy\n"); + return -EAGAIN; + } + + /* Set FIFO direction */ + if (drv->variant->ddc_fifo_has_dir) { + reg = readl(drv->base + SUN4I_HDMI_DDC_CTRL_REG); + reg &= ~SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK; + reg |= (msg->flags & I2C_M_RD) ? + SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ : + SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE; + writel(reg, drv->base + SUN4I_HDMI_DDC_CTRL_REG); + } + + /* Clear address register (not cleared by soft reset) */ + regmap_field_write(drv->field_ddc_addr_reg, 0); + + /* Set I2C address */ + regmap_field_write(drv->field_ddc_slave_addr, msg->addr); + + /* + * Set FIFO RX/TX thresholds and clear FIFO + * + * If threshold is inclusive, we can set the TX threshold to + * 0 instead of 1. + */ + regmap_field_write(drv->field_ddc_fifo_tx_thres, + drv->variant->ddc_fifo_thres_incl ? 0 : 1); + regmap_field_write(drv->field_ddc_fifo_rx_thres, RX_THRESHOLD); + regmap_field_write(drv->field_ddc_fifo_clear, 1); + if (regmap_field_read_poll_timeout(drv->field_ddc_fifo_clear, + reg, !reg, 100, 2000)) + return -EIO; + + /* Set transfer length */ + regmap_field_write(drv->field_ddc_byte_count, msg->len); + + /* Set command */ + regmap_field_write(drv->field_ddc_cmd, + msg->flags & I2C_M_RD ? + SUN4I_HDMI_DDC_CMD_IMPLICIT_READ : + SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE); + + /* Clear interrupt status bits by forcing a write */ + regmap_field_force_write(drv->field_ddc_int_status, + SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK | + SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST | + SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE); + + /* Start command */ + regmap_field_write(drv->field_ddc_start, 1); + + /* Transfer bytes */ + for (i = 0; i < msg->len; i += len) { + len = fifo_transfer(drv, msg->buf + i, msg->len - i, + msg->flags & I2C_M_RD); + if (len <= 0) + return len; + } + + /* Wait for command to finish */ + if (regmap_field_read_poll_timeout(drv->field_ddc_start, + reg, !reg, 100, 100000)) + return -EIO; + + /* Check for errors */ + regmap_field_read(drv->field_ddc_int_status, ®); + if ((reg & SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK) || + !(reg & SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE)) { + return -EIO; + } + + return 0; +} + +static int sun4i_hdmi_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct sun4i_hdmi_i2c_drv *drv = i2c_get_adapdata(adap); + u32 reg; + int err, i, ret = num; + + for (i = 0; i < num; i++) { + if (!msgs[i].len) + return -EINVAL; + if (msgs[i].len > SUN4I_HDMI_DDC_BYTE_COUNT_MAX) + return -EINVAL; + } + + /* DDC clock needs to be enabled for the module to work */ + clk_prepare_enable(drv->ddc_clk); + err = clk_set_rate(drv->ddc_clk, drv->clock_freq); + if (err) { + dev_err(drv->dev, "unable to set HDMI-I2C clock rate\n"); + ret = err; + goto exit; + } + + /* Reset I2C controller */ + regmap_field_write(drv->field_ddc_en, 1); + regmap_field_write(drv->field_ddc_reset, 1); + if (regmap_field_read_poll_timeout(drv->field_ddc_reset, + reg, !reg, 100, 2000)) { + ret = -EIO; + goto exit; + } + + regmap_field_write(drv->field_ddc_sck_en, 1); + regmap_field_write(drv->field_ddc_sda_en, 1); + + for (i = 0; i < num; i++) { + err = xfer_msg(drv, &msgs[i]); + if (err) { + ret = err; + break; + } + } + +exit: + clk_disable_unprepare(drv->ddc_clk); + return ret; +} + +static u32 sun4i_hdmi_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm sun4i_hdmi_i2c_algorithm = { + .master_xfer = sun4i_hdmi_i2c_xfer, + .functionality = sun4i_hdmi_i2c_func, +}; + +static int sun4i_hdmi_i2c_init_regmap_fields(struct sun4i_hdmi_i2c_drv *drv) +{ + drv->field_ddc_en = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_en); + if (IS_ERR(drv->field_ddc_en)) + return PTR_ERR(drv->field_ddc_en); + + drv->field_ddc_start = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_start); + if (IS_ERR(drv->field_ddc_start)) + return PTR_ERR(drv->field_ddc_start); + + drv->field_ddc_reset = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_reset); + if (IS_ERR(drv->field_ddc_reset)) + return PTR_ERR(drv->field_ddc_reset); + + drv->field_ddc_addr_reg = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_addr_reg); + if (IS_ERR(drv->field_ddc_addr_reg)) + return PTR_ERR(drv->field_ddc_addr_reg); + + drv->field_ddc_slave_addr = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_slave_addr); + if (IS_ERR(drv->field_ddc_slave_addr)) + return PTR_ERR(drv->field_ddc_slave_addr); + + drv->field_ddc_int_mask = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_int_mask); + if (IS_ERR(drv->field_ddc_int_mask)) + return PTR_ERR(drv->field_ddc_int_mask); + + drv->field_ddc_int_status = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_int_status); + if (IS_ERR(drv->field_ddc_int_status)) + return PTR_ERR(drv->field_ddc_int_status); + + drv->field_ddc_fifo_clear = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_fifo_clear); + if (IS_ERR(drv->field_ddc_fifo_clear)) + return PTR_ERR(drv->field_ddc_fifo_clear); + + drv->field_ddc_fifo_rx_thres = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_fifo_rx_thres); + if (IS_ERR(drv->field_ddc_fifo_rx_thres)) + return PTR_ERR(drv->field_ddc_fifo_rx_thres); + + drv->field_ddc_fifo_tx_thres = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_fifo_tx_thres); + if (IS_ERR(drv->field_ddc_fifo_tx_thres)) + return PTR_ERR(drv->field_ddc_fifo_tx_thres); + + drv->field_ddc_byte_count = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_byte_count); + if (IS_ERR(drv->field_ddc_byte_count)) + return PTR_ERR(drv->field_ddc_byte_count); + + drv->field_ddc_cmd = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_cmd); + if (IS_ERR(drv->field_ddc_cmd)) + return PTR_ERR(drv->field_ddc_cmd); + + drv->field_ddc_sda_en = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_sda_en); + if (IS_ERR(drv->field_ddc_sda_en)) + return PTR_ERR(drv->field_ddc_sda_en); + + drv->field_ddc_sck_en = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_sck_en); + if (IS_ERR(drv->field_ddc_sck_en)) + return PTR_ERR(drv->field_ddc_sck_en); + + drv->field_ddc_bus_busy = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_bus_busy); + if (IS_ERR(drv->field_ddc_bus_busy)) + return PTR_ERR(drv->field_ddc_bus_busy); + + drv->field_ddc_sda_state = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_sda_state); + if (IS_ERR(drv->field_ddc_sda_state)) + return PTR_ERR(drv->field_ddc_sda_state); + + drv->field_ddc_sck_state = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_sck_state); + if (IS_ERR(drv->field_ddc_sck_state)) + return PTR_ERR(drv->field_ddc_sck_state); + + drv->field_ddc_sda_line_ctrl = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_sda_line_ctrl); + if (IS_ERR(drv->field_ddc_sda_line_ctrl)) + return PTR_ERR(drv->field_ddc_sda_line_ctrl); + + drv->field_ddc_sck_line_ctrl = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_sck_line_ctrl); + if (IS_ERR(drv->field_ddc_sck_line_ctrl)) + return PTR_ERR(drv->field_ddc_sck_line_ctrl); + + drv->field_ddc_sda_line_ctrl_en = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_sda_line_ctrl_en); + if (IS_ERR(drv->field_ddc_sda_line_ctrl_en)) + return PTR_ERR(drv->field_ddc_sda_line_ctrl_en); + + drv->field_ddc_sck_line_ctrl_en = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_sck_line_ctrl_en); + if (IS_ERR(drv->field_ddc_sck_line_ctrl_en)) + return PTR_ERR(drv->field_ddc_sck_line_ctrl_en); + + return 0; +} + +struct sun4i_hdmi_i2c_drv +*sun4i_hdmi_i2c_init(struct device *dev, void __iomem *base, + const struct of_device_id *of_id_table, + const struct regmap_config *regmap_config, + struct clk *parent_clk) +{ + struct sun4i_hdmi_i2c_drv *drv; + const struct of_device_id *of_id; + struct device_node *node = dev_of_node(dev); + int ret; + + if ((!dev) || (!base) || (!regmap_config) || (!of_id_table)) + return ERR_PTR(-ENODEV); + + drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return ERR_PTR(-ENOMEM); + + drv->dev = dev; + drv->base = base; + + of_id = of_match_device(of_id_table, drv->dev); + if (!of_id) { + dev_err(drv->dev, "missing platform data\n"); + return ERR_PTR(-ENODEV); + } + drv->variant = of_id->data; + // TODO: of_dev_get_data + + if (parent_clk) { + drv->parent_clk = parent_clk; + } else { + drv->parent_clk = devm_clk_get(drv->dev, + drv->variant->parent_clk_name); + } + if (IS_ERR(drv->parent_clk)) { + if (PTR_ERR(drv->parent_clk) != -EPROBE_DEFER) + dev_err(drv->dev, "couldn't get the HDMI-I2C clock\n"); + return ERR_CAST(drv->parent_clk); + } + + ret = of_property_read_u32(node, "clock-frequency", + &drv->clock_freq); + if (ret || (drv->clock_freq > SUN4I_HDMI_I2C_SPEED_MAX)) + drv->clock_freq = SUN4I_HDMI_I2C_SPEED_DEFAULT; + + drv->regmap = devm_regmap_init_mmio(drv->dev, drv->base, regmap_config); + if (IS_ERR(drv->regmap)) { + dev_err(drv->dev, "couldn't create HDMI-I2C regmap\n"); + return ERR_CAST(drv->regmap); + } + + ret = sun4i_hdmi_i2c_init_regmap_fields(drv); + if (ret) { + dev_err(drv->dev, "couldn't init HDMI-I2C regmap fields\n"); + return ERR_PTR(ret); + } + + drv->ddc_clk = sun4i_ddc_create(drv->dev, drv->regmap, drv->variant, + drv->parent_clk); + if (IS_ERR(drv->ddc_clk)) { + dev_err(drv->dev, "couldn't create the HDMI-I2C clock\n"); + return ERR_CAST(drv->ddc_clk); + } + + // TODO devm_of_clk_add_provider() + ret = of_clk_add_provider(node, of_clk_src_simple_get, drv->ddc_clk); + if (ret) { + dev_err(drv->dev, "couldn't register the HDMI-I2C clock\n"); + return ERR_PTR(ret); + } + + ret = clk_prepare_enable(drv->ddc_clk); + if (ret) { + dev_err(drv->dev, "unable to enable HDMI-I2C clock\n"); + return ERR_PTR(ret); + } + + i2c_set_adapdata(&drv->adap, drv); + drv->adap.dev.parent = drv->dev; + drv->adap.owner = THIS_MODULE; + drv->adap.class = I2C_CLASS_DDC; + drv->adap.algo = &sun4i_hdmi_i2c_algorithm; + drv->adap.dev.of_node = node; + strlcpy(drv->adap.name, "sun4i_hdmi_i2c adapter", sizeof(drv->adap.name)); + + clk_disable_unprepare(drv->ddc_clk); + + ret = i2c_add_adapter(&drv->adap); + if (ret) { + dev_err(drv->dev, "unable to create HDMI-I2C adapter\n"); + goto ddc_clk_err; + } + + return drv; + +ddc_clk_err: + clk_disable_unprepare(drv->ddc_clk); + + return ERR_PTR(ret); +} + +static const struct of_device_id sun4i_hdmi_i2c_legacy_of_table[] = { + { .compatible = "allwinner,sun4i-a10-hdmi", .data = &sun4i_legacy_variant }, + { .compatible = "allwinner,sun6i-a31-hdmi", .data = &sun6i_legacy_variant }, + { /* sentinel */ } +}; + +struct sun4i_hdmi_i2c_drv *sun4i_hdmi_i2c_setup(struct device *dev, + void __iomem *base, + struct clk *clk) +{ + return sun4i_hdmi_i2c_init(dev, base, sun4i_hdmi_i2c_legacy_of_table, + &sun4i_hdmi_i2c_legacy_regmap_config, clk); +} + +void sun4i_hdmi_i2c_fini(struct sun4i_hdmi_i2c_drv *drv) +{ + struct device_node *node = dev_of_node(drv->dev); + + clk_prepare_enable(drv->ddc_clk); + i2c_del_adapter(&drv->adap); + clk_disable_unprepare(drv->ddc_clk); + of_clk_del_provider(node); +} diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c_drv.h b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c_drv.h new file mode 100644 index 00000000000000..08ed3658459063 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c_drv.h @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2016 Maxime Ripard + * Copyright (C) 2017 Chen-Yu Tsai + * Copyright (C) 2017 Jonathan Liu + * Copyright (C) 2017 Olliver Schinagl + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#ifndef _SUN4I_HDMI_I2C_DRV_H_ +#define _SUN4I_HDMI_I2C_DRV_H_ + +#include +#include +#include +#include +#include +#include + +#define SUN4I_HDMI_DDC_OFFSET 0x500 + +#define SUN4I_HDMI_DDC_CTRL_REG 0x00 +#define SUN4I_HDMI_DDC_CTRL_ENABLE BIT(31) +#define SUN4I_HDMI_DDC_CTRL_START_CMD BIT(30) +#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK BIT(8) +#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE (1 << 8) +#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ (0 << 8) +#define SUN4I_HDMI_DDC_CTRL_RESET BIT(0) + +#define SUN4I_HDMI_DDC_ADDR_REG 0x04 +#define SUN4I_HDMI_DDC_ADDR_SEGMENT(seg) (((seg) << 24) & GENMASK(31, 24)) +#define SUN4I_HDMI_DDC_ADDR_EDDC(addr) (((addr) << 16) & GENMASK(23, 16)) +#define SUN4I_HDMI_DDC_ADDR_OFFSET(off) (((off) << 8) & GENMASK(15, 8)) +#define SUN4I_HDMI_DDC_ADDR_SLAVE(addr) ((addr) & GENMASK(7, 0)) + +#define SUN4I_HDMI_DDC_INT_STATUS_REG 0x0c +#define SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION BIT(7) +#define SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW BIT(6) +#define SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW BIT(5) +#define SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST BIT(4) +#define SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR BIT(3) +#define SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR BIT(2) +#define SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR BIT(1) +#define SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE BIT(0) + +#define SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK ( \ + SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION | \ + SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW | \ + SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW | \ + SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR | \ + SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR | \ + SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR \ +) + +#define SUN4I_HDMI_DDC_FIFO_CTRL_REG 0x10 +#define SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(31) +#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MASK GENMASK(7, 4) +#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES(n) \ + (((n) << 4) & SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MASK) +#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX (BIT(4) - 1) +#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MASK GENMASK(3, 0) +#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES(n) \ + ((n) & SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MASK) +#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MAX (BIT(4) - 1) + +#define SUN4I_HDMI_DDC_FIFO_DATA_REG 0x18 + +#define SUN4I_HDMI_DDC_BYTE_COUNT_REG 0x1c +#define SUN4I_HDMI_DDC_BYTE_COUNT_MAX (BIT(10) - 1) + +#define SUN4I_HDMI_DDC_CMD_REG 0x20 +#define SUN4I_HDMI_DDC_CMD_EXPLICIT_EDDC_READ 0x6 +#define SUN4I_HDMI_DDC_CMD_IMPLICIT_READ 0x5 +#define SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE 0x3 + +#define SUN4I_HDMI_DDC_EXT_REG 0x24 +#define SUN4I_HDMI_DDC_EXT_BUS_BUSY BIT(10) +#define SUN4I_HDMI_DDC_EXT_SDA_STATE BIT(9) +#define SUN4I_HDMI_DDC_EXT_SCK_STATE BIT(8) +#define SUN4I_HDMI_DDC_EXT_SCL_LINE_CTRL BIT(3) +#define SUN4I_HDMI_DDC_EXT_SCL_LINE_CTRL_EN BIT(2) +#define SUN4I_HDMI_DDC_EXT_SDA_LINE_CTRL BIT(1) +#define SUN4I_HDMI_DDC_EXT_SDA_LINE_CTRL_EN BIT(0) + +#define SUN4I_HDMI_DDC_CLK_REG 0x28 +#define SUN4I_HDMI_DDC_CLK_M_OFFSET 3 +#define SUN4I_HDMI_DDC_CLK_M_MASK GENMASK(7, 4) +#define SUN4I_HDMI_DDC_CLK_N_MASK GENMASK(3, 0) +#define SUN4I_HDMI_DDC_CLK_M(m) \ + (((m) << SUN4I_HDMI_DDC_CLK_M_OFFSET) & SUN4I_HDMI_DDC_CLK_M_MASK) +#define SUN4I_HDMI_DDC_CLK_N(n) \ + ((n) & SUN4I_HDMI_DDC_CLK_N_MASK) +#define SUN4I_HDMI_DDC_CLK_M_GET(reg) \ + (((reg) & SUN4I_HDMI_DDC_CLK_M_MASK) >> SUN4I_HDMI_DDC_CLK_M_OFFSET) +#define SUN4I_HDMI_DDC_CLK_N_GET(reg) \ + ((reg) & SUN4I_HDMI_DDC_CLK_N_MASK) + +#define SUN4I_HDMI_DDC_LINE_CTRL_REG 0x40 +#define SUN4I_HDMI_DDC_LINE_CTRL_SDA_ENABLE BIT(9) +#define SUN4I_HDMI_DDC_LINE_CTRL_SCL_ENABLE BIT(8) + +#define SUN4I_HDMI_DDC_FIFO_SIZE 16 + +/* A31 specific */ +#define SUN6I_HDMI_DDC_CTRL_REG 0x00 +#define SUN6I_HDMI_DDC_CTRL_RESET BIT(31) +#define SUN6I_HDMI_DDC_CTRL_START_CMD BIT(27) +#define SUN6I_HDMI_DDC_CTRL_SDA_ENABLE BIT(6) +#define SUN6I_HDMI_DDC_CTRL_SCL_ENABLE BIT(4) +#define SUN6I_HDMI_DDC_CTRL_ENABLE BIT(0) + +#define SUN6I_HDMI_DDC_EXT_REG 0x04 + +#define SUN6I_HDMI_DDC_CMD_REG 0x08 +#define SUN6I_HDMI_DDC_CMD_BYTE_COUNT(count) ((count) << 16) +/* command types in lower 3 bits are the same as sun4i */ + +#define SUN6I_HDMI_DDC_ADDR_REG 0x0c +#define SUN6I_HDMI_DDC_ADDR_SEGMENT(seg) (((seg) << 24) & GENMASK(31, 24)) +#define SUN6I_HDMI_DDC_ADDR_EDDC(addr) (((addr) << 16) & GENMASK(23, 16)) +#define SUN6I_HDMI_DDC_ADDR_OFFSET(off) (((off) << 8) & GENMASK(15, 8)) +#define SUN6I_HDMI_DDC_ADDR_SLAVE(addr) (((addr) << 1) & GENMASK(7, 1)) + +#define SUN6I_HDMI_DDC_INT_STATUS_REG 0x14 +#define SUN6I_HDMI_DDC_INT_STATUS_TIMEOUT BIT(8) +/* lower 8 bits are the same as sun4i */ + +#define SUN6I_HDMI_DDC_FIFO_CTRL_REG 0x18 +#define SUN6I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(15) +/* lower 9 bits are the same as sun4i */ + +#define SUN6I_HDMI_DDC_CLK_REG 0x20 +/* DDC CLK bit fields are the same, but the formula is not */ + +#define SUN6I_HDMI_DDC_FIFO_DATA_REG 0x80 + +struct sun4i_hdmi_i2c_variant { + char *parent_clk_name; + + struct reg_field ddc_clk_reg; + u8 ddc_clk_pre_divider; + u8 ddc_clk_m_offset; + + /* Register fields for I2C adapter */ + struct reg_field field_ddc_en; + struct reg_field field_ddc_start; + struct reg_field field_ddc_reset; + struct reg_field field_ddc_addr_reg; + struct reg_field field_ddc_slave_addr; + struct reg_field field_ddc_int_mask; + struct reg_field field_ddc_int_status; + struct reg_field field_ddc_fifo_clear; + struct reg_field field_ddc_fifo_rx_thres; + struct reg_field field_ddc_fifo_tx_thres; + struct reg_field field_ddc_byte_count; + struct reg_field field_ddc_cmd; + struct reg_field field_ddc_sda_en; + struct reg_field field_ddc_sck_en; + struct reg_field field_ddc_bus_busy; + struct reg_field field_ddc_sda_state; + struct reg_field field_ddc_sck_state; + struct reg_field field_ddc_sda_line_ctrl_en; + struct reg_field field_ddc_sck_line_ctrl_en; + struct reg_field field_ddc_sda_line_ctrl; + struct reg_field field_ddc_sck_line_ctrl; + + + /* DDC FIFO register offset */ + u32 ddc_fifo_reg; + + /* + * DDC FIFO threshold boundary conditions + * + * This is used to cope with the threshold boundary condition + * being slightly different on sun5i and sun6i. + * + * On sun5i the threshold is exclusive, i.e. does not include, + * the value of the threshold. ( > for RX; < for TX ) + * On sun6i the threshold is inclusive, i.e. includes, the + * value of the threshold. ( >= for RX; <= for TX ) + */ + bool ddc_fifo_thres_incl; + + bool ddc_fifo_has_dir; +}; + +struct sun4i_hdmi_i2c_drv { + struct device *dev; + + void __iomem *base; + struct regmap *regmap; + + struct clk *parent_clk; + struct clk *ddc_clk; + uint32_t clock_freq; + + struct i2c_adapter adap; + + struct regmap_field *field_ddc_en; + struct regmap_field *field_ddc_start; + struct regmap_field *field_ddc_reset; + struct regmap_field *field_ddc_addr_reg; + struct regmap_field *field_ddc_slave_addr; + struct regmap_field *field_ddc_int_mask; + struct regmap_field *field_ddc_int_status; + struct regmap_field *field_ddc_fifo_clear; + struct regmap_field *field_ddc_fifo_rx_thres; + struct regmap_field *field_ddc_fifo_tx_thres; + struct regmap_field *field_ddc_byte_count; + struct regmap_field *field_ddc_cmd; + struct regmap_field *field_ddc_sda_en; + struct regmap_field *field_ddc_sck_en; + struct regmap_field *field_ddc_bus_busy; + struct regmap_field *field_ddc_sda_state; + struct regmap_field *field_ddc_sck_state; + struct regmap_field *field_ddc_sda_line_ctrl; + struct regmap_field *field_ddc_sck_line_ctrl; + struct regmap_field *field_ddc_sda_line_ctrl_en; + struct regmap_field *field_ddc_sck_line_ctrl_en; + + + const struct sun4i_hdmi_i2c_variant *variant; +}; + +struct sun4i_hdmi_i2c_drv +*sun4i_hdmi_i2c_init(struct device *dev, void __iomem *base, + const struct of_device_id *of_id_table, + const struct regmap_config *regmap_config, + struct clk *parent_clk); + +void sun4i_hdmi_i2c_fini(struct sun4i_hdmi_i2c_drv *drv); + +struct sun4i_hdmi_i2c_drv *sun4i_hdmi_i2c_setup(struct device *dev, + void __iomem *base, + struct clk *clk); + +#endif diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c index dc332ea56f6c75..5785ffb03f972a 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c @@ -10,7 +10,13 @@ * the License, or (at your option) any later version. */ +#include #include +#include +#include +#include +#include +#include #include "sun4i_hdmi.h" @@ -21,6 +27,15 @@ struct sun4i_tmds { u8 div_offset; }; +struct sun4i_tmds_ddc { + struct device *dev; + void __iomem *base; + + struct clk_hw hw; + + spinlock_t lock; +}; + static inline struct sun4i_tmds *hw_to_tmds(struct clk_hw *hw) { return container_of(hw, struct sun4i_tmds, hw); @@ -52,7 +67,7 @@ static unsigned long sun4i_tmds_calc_divider(unsigned long rate, (rate - tmp_rate) < (rate - best_rate)) { best_rate = tmp_rate; best_m = m; - is_double = d; + is_double = true; } } } @@ -171,8 +186,7 @@ static u8 sun4i_tmds_get_parent(struct clk_hw *hw) u32 reg; reg = readl(tmds->hdmi->base + SUN4I_HDMI_PLL_DBG0_REG); - return ((reg & SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_MASK) >> - SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_SHIFT); + return SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_GET(reg); } static int sun4i_tmds_set_parent(struct clk_hw *hw, u8 index) @@ -206,6 +220,9 @@ int sun4i_tmds_create(struct sun4i_hdmi *hdmi) struct sun4i_tmds *tmds; const char *parents[2]; + if (!hdmi->tmds_clk_name) + return -ENODEV; + parents[0] = __clk_get_name(hdmi->pll0_clk); if (!parents[0]) return -ENODEV; @@ -218,7 +235,7 @@ int sun4i_tmds_create(struct sun4i_hdmi *hdmi) if (!tmds) return -ENOMEM; - init.name = "hdmi-tmds"; + init.name = hdmi->tmds_clk_name; init.ops = &sun4i_tmds_ops; init.parent_names = parents; init.num_parents = 2; diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index edca0d737750bd..7a53e008f29d4f 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -67,11 +67,22 @@ static int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period) static void pwm_beeper_off(struct pwm_beeper *beeper) { + struct pwm_state state; + if (beeper->amplifier_on) { regulator_disable(beeper->amplifier); beeper->amplifier_on = false; } + /* + * When disabling, some PWM hardware IP leaves the pin in the state it + * happens to be in. To make sure it is in the inactive state, set the + * duty cycle to 0 first. + */ + pwm_get_state(beeper->pwm, &state); + state.duty_cycle = 0; + pwm_apply_state(beeper->pwm, &state); + pwm_disable(beeper->pwm); } diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 1e18ca0d1b4e1e..dd5f2db2a260f5 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -25,20 +25,37 @@ * http://www.glyn.com/Products/Displays */ -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include +#include +#include +#include +#include +#include #include +#include +#include +#include #include #include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include + +#define NIBBLE_LOWER(x) \ + ((uint8_t)((x) & GENMASK(3, 0))) +#define NIBBLE_UPPER(x) \ + ((uint8_t)(((x) & GENMASK(7, 4)) >> 4)) #define WORK_REGISTER_THRESHOLD 0x00 #define WORK_REGISTER_REPORT_RATE 0x08 @@ -47,28 +64,78 @@ #define WORK_REGISTER_NUM_X 0x33 #define WORK_REGISTER_NUM_Y 0x34 -#define M09_REGISTER_THRESHOLD 0x80 -#define M09_REGISTER_GAIN 0x92 -#define M09_REGISTER_OFFSET 0x93 -#define M09_REGISTER_NUM_X 0x94 -#define M09_REGISTER_NUM_Y 0x95 +#define M06_TOUCH_REPORT 0x05 +#define M06_TOUCH_REPORT_LEN 4 +#define M06_TOUCH_REPORT_CRC_LEN 1 +#define M06_TOUCH_REPORT_HEADER_H 0x00 +#define M06_TOUCH_REPORT_HEADER_L 0x01 +#define M06_TOUCH_REPORT_DATALEN 0x02 +#define M06_TOUCH_REPORT_MAGIC 0xaa + +#define M09_TD_STATUS 0x02 +#define M09_TOUCH_REPORT 0x03 +#define M09_TOUCH_REPORT_LEN 6 + +#define EDT_TOUCH_XH_EVENT 0x00 +#define EDT_TOUCH_XH_MASK GENMASK(3, 0) +#define EDT_TOUCH_EVENT_DOWN 0x00 +#define EDT_TOUCH_EVENT_UP BIT(6) +#define EDT_TOUCH_EVENT_ON BIT(7) +#define EDT_TOUCH_EVENT_RESERVED \ + (EDT_TOUCH_EVENT_UP | EDT_TOUCH_EVENT_ON) +#define EDT_TOUCH_EVENT_FLAG_MASK GENMASK(7, 6) +#define EDT_TOUCH_XL 0x01 +#define EDT_TOUCH_ID_YH 0x02 +#define EDT_TOUCH_YH_MASK GENMASK(3, 0) +#define EDT_TOUCH_ID_MASK GENMASK(7, 4) +#define EDT_TOUCH_ID_OFFSET 4 +#define EDT_TOUCH_YL 0x03 + +#define M09_THRESHOLD 0x80 +#define M09_GAIN 0x92 +#define M09_OFFSET 0x93 +#define M09_NUM_X 0x94 +#define M09_NUM_Y 0x95 + +#define M09_ID_G_MODE 0xa4 +#define M09_ID_G_MODE_POLL 0x00 +#define M09_ID_G_MODE_IRQ 0x01 + +#define M09_ID_G_PMODE 0xa5 +#define M09_ID_G_PMODE_ACTIVE 0x00 +#define M09_ID_G_PMODE_MONITOR 0x01 +#define M09_ID_G_PMODE_HIBERNATE 0x03 + +#define EDT_FW_VERSION 0xa6 +#define EDT_PANEL_ID 0xa8 +#define EDT_PANEL_ID_EP0350M09 0x35 +#define EDT_PANEL_ID_EP0430M09 0x43 +#define EDT_PANEL_ID_EP0500M09 0x50 +#define EDT_PANEL_ID_EP0570M09 0x57 +#define EDT_PANEL_ID_EP0700M09 0x70 +#define EDT_PANEL_ID_EP1010ML00 0xa1 +#define SOL_PANEL_ID_GKTW50SCED1R0 0x5a + +#define EDT_CUSTOM_DATA 0xbb +#define EDT_NAME_LEN 23 + +#define M06_TOUCH_REPORT_REQ 0xf9 #define NO_REGISTER 0xff #define WORK_REGISTER_OPMODE 0x3c #define FACTORY_REGISTER_OPMODE 0x01 -#define TOUCH_EVENT_DOWN 0x00 -#define TOUCH_EVENT_UP 0x01 -#define TOUCH_EVENT_ON 0x02 -#define TOUCH_EVENT_RESERVED 0x03 - -#define EDT_NAME_LEN 23 #define EDT_SWITCH_MODE_RETRIES 10 #define EDT_SWITCH_MODE_DELAY 5 /* msec */ #define EDT_RAW_DATA_RETRIES 100 #define EDT_RAW_DATA_DELAY 1000 /* usec */ +#define EDT_TOUCH_REPORT_MAX_SIZE ((10 * M09_TOUCH_REPORT_LEN) + \ + M09_TOUCH_REPORT_LEN + \ + M06_TOUCH_REPORT + \ + M06_TOUCH_REPORT_CRC_LEN) + enum edt_ver { EDT_M06, EDT_M09, @@ -76,6 +143,11 @@ enum edt_ver { GENERIC_FT, }; +enum readout_mode { + EDT_READOUT_MODE_POLL, + EDT_READOUT_MODE_IRQ, +}; + struct edt_reg_addr { int reg_threshold; int reg_report_rate; @@ -88,6 +160,9 @@ struct edt_reg_addr { struct edt_ft5x06_ts_data { struct i2c_client *client; struct input_dev *input; +#if IS_ENABLED(CONFIG_INPUT_POLLDEV) + struct input_polled_dev *polldev; +#endif struct touchscreen_properties prop; u16 num_x; u16 num_y; @@ -151,6 +226,76 @@ static int edt_ft5x06_ts_readwrite(struct i2c_client *client, return 0; } +static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, + u8 addr, u8 value) +{ + u8 wrbuf[4]; + + switch (tsdata->version) { + case EDT_M06: + wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; + wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; + wrbuf[2] = value; + wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; + return edt_ft5x06_ts_readwrite(tsdata->client, 4, + wrbuf, 0, NULL); + case EDT_M09: + case EDT_M12: + case GENERIC_FT: + wrbuf[0] = addr; + wrbuf[1] = value; + + return edt_ft5x06_ts_readwrite(tsdata->client, 2, + wrbuf, 0, NULL); + + default: + return -EINVAL; + } +} + +static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, + u8 addr) +{ + u8 wrbuf[2], rdbuf[2]; + int error; + + switch (tsdata->version) { + case EDT_M06: + wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; + wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; + wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; + + error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, + rdbuf); + if (error) + return error; + + if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { + dev_err(&tsdata->client->dev, + "crc error: 0x%02x expected, got 0x%02x\n", + wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], + rdbuf[1]); + return -EIO; + } + break; + + case EDT_M09: + case EDT_M12: + case GENERIC_FT: + wrbuf[0] = addr; + error = edt_ft5x06_ts_readwrite(tsdata->client, 1, + wrbuf, 1, rdbuf); + if (error) + return error; + break; + + default: + return -EINVAL; + } + + return rdbuf[0]; +} + static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata, u8 *buf, int buflen) { @@ -170,35 +315,39 @@ static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata, return true; } -static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) +static void edt_ft5x06_report(struct edt_ft5x06_ts_data *tsdata) { - struct edt_ft5x06_ts_data *tsdata = dev_id; struct device *dev = &tsdata->client->dev; u8 cmd; - u8 rdbuf[63]; + u8 rdbuf[EDT_TOUCH_REPORT_MAX_SIZE]; int i, type, x, y, id; - int offset, tplen, datalen, crclen; + int offset, touch_cnt, tplen, datalen, crclen; int error; + if ((!tsdata) || (!tsdata->input)) + return; + switch (tsdata->version) { case EDT_M06: - cmd = 0xf9; /* tell the controller to send touch data */ - offset = 5; /* where the actual touch data starts */ - tplen = 4; /* data comes in so called frames */ - crclen = 1; /* length of the crc data */ + touch_cnt = tsdata->max_support_points; + cmd = M06_TOUCH_REPORT_REQ; /* tell the controller to send touch data */ + offset = M06_TOUCH_REPORT; /* where the actual touch data starts */ + tplen = M06_TOUCH_REPORT_LEN; /* data comes in so called frames */ + crclen = M06_TOUCH_REPORT_CRC_LEN; /* length of the crc data */ break; case EDT_M09: case EDT_M12: case GENERIC_FT: + touch_cnt = 0; cmd = 0x0; - offset = 3; - tplen = 6; + offset = M09_TOUCH_REPORT; + tplen = M09_TOUCH_REPORT_LEN; crclen = 0; break; default: - goto out; + return; } memset(rdbuf, 0, sizeof(rdbuf)); @@ -210,46 +359,63 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) if (error) { dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n", error); - goto out; + return; } /* M09/M12 does not send header or CRC */ if (tsdata->version == EDT_M06) { - if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || - rdbuf[2] != datalen) { + if (rdbuf[M06_TOUCH_REPORT_HEADER_H] != M06_TOUCH_REPORT_MAGIC || + rdbuf[M06_TOUCH_REPORT_HEADER_L] != M06_TOUCH_REPORT_MAGIC || + rdbuf[M06_TOUCH_REPORT_DATALEN] != datalen) { dev_err_ratelimited(dev, - "Unexpected header: %02x%02x%02x!\n", - rdbuf[0], rdbuf[1], rdbuf[2]); - goto out; + "Unexpected header: %02x%02x%02x!\n", + rdbuf[M06_TOUCH_REPORT_HEADER_H], + rdbuf[M06_TOUCH_REPORT_HEADER_L], + rdbuf[M06_TOUCH_REPORT_DATALEN]); + return; } if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, datalen)) - goto out; + return; + } else { + touch_cnt = rdbuf[M09_TD_STATUS]; } for (i = 0; i < tsdata->max_support_points; i++) { u8 *buf = &rdbuf[i * tplen + offset]; bool down; - type = buf[0] >> 6; + input_mt_slot(tsdata->input, i); + + type = buf[EDT_TOUCH_XH_EVENT] & EDT_TOUCH_EVENT_FLAG_MASK; + + down = type != EDT_TOUCH_EVENT_UP; + if (!down || i > touch_cnt) { + input_report_abs(tsdata->input, ABS_MT_TRACKING_ID, -1); + continue; + } + /* ignore Reserved events */ - if (type == TOUCH_EVENT_RESERVED) + if (type == EDT_TOUCH_EVENT_RESERVED) continue; /* M06 sometimes sends bogus coordinates in TOUCH_DOWN */ - if (tsdata->version == EDT_M06 && type == TOUCH_EVENT_DOWN) + if (tsdata->version == EDT_M06 && type == EDT_TOUCH_EVENT_DOWN) continue; - x = ((buf[0] << 8) | buf[1]) & 0x0fff; - y = ((buf[2] << 8) | buf[3]) & 0x0fff; - id = (buf[2] >> 4) & 0x0f; - down = type != TOUCH_EVENT_UP; - - input_mt_slot(tsdata->input, id); input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down); - if (!down) - continue; + id = (buf[EDT_TOUCH_ID_YH] & EDT_TOUCH_ID_MASK); + id >>= EDT_TOUCH_ID_OFFSET; + input_report_abs(tsdata->input, ABS_MT_TRACKING_ID, id); + + x = buf[EDT_TOUCH_XH_EVENT] & EDT_TOUCH_XH_MASK; + x <<= BITS_PER_BYTE; + x |= buf[EDT_TOUCH_XL]; + + y = buf[EDT_TOUCH_ID_YH] & EDT_TOUCH_YH_MASK; + y <<= BITS_PER_BYTE; + y |= buf[EDT_TOUCH_YL]; touchscreen_report_pos(tsdata->input, &tsdata->prop, x, y, true); @@ -257,79 +423,34 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) input_mt_report_pointer_emulation(tsdata->input, true); input_sync(tsdata->input); - -out: - return IRQ_HANDLED; } -static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, - u8 addr, u8 value) +static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) { - u8 wrbuf[4]; - - switch (tsdata->version) { - case EDT_M06: - wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; - wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; - wrbuf[2] = value; - wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; - return edt_ft5x06_ts_readwrite(tsdata->client, 4, - wrbuf, 0, NULL); - case EDT_M09: - case EDT_M12: - case GENERIC_FT: - wrbuf[0] = addr; - wrbuf[1] = value; + struct edt_ft5x06_ts_data *tsdata = dev_id; - return edt_ft5x06_ts_readwrite(tsdata->client, 2, - wrbuf, 0, NULL); + edt_ft5x06_report(tsdata); - default: - return -EINVAL; - } + return IRQ_HANDLED; } -static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, - u8 addr) +static void edt_ft5x06_poll(struct input_polled_dev *polldev) { - u8 wrbuf[2], rdbuf[2]; - int error; + struct edt_ft5x06_ts_data *tsdata = polldev->private; + /* Ensure display is always awake */ switch (tsdata->version) { case EDT_M06: - wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; - wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; - wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; - - error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, - rdbuf); - if (error) - return error; - - if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { - dev_err(&tsdata->client->dev, - "crc error: 0x%02x expected, got 0x%02x\n", - wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], - rdbuf[1]); - return -EIO; - } break; - - case EDT_M09: - case EDT_M12: + case EDT_M09: /* fall through */ + case EDT_M12: /* fall through */ case GENERIC_FT: - wrbuf[0] = addr; - error = edt_ft5x06_ts_readwrite(tsdata->client, 1, - wrbuf, 1, rdbuf); - if (error) - return error; + edt_ft5x06_register_write(tsdata, M09_ID_G_PMODE, + M09_ID_G_PMODE_ACTIVE); break; - - default: - return -EINVAL; } - return rdbuf[0]; + edt_ft5x06_report(tsdata); } struct edt_ft5x06_attribute { @@ -480,14 +601,11 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, /* m06, m09: range 0-31, m12: range 0-5 */ static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, - M09_REGISTER_GAIN, 0, 31); -/* m06, m09: range 0-31, m12: range 0-16 */ + M09_GAIN, 0, 31); static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, - M09_REGISTER_OFFSET, 0, 31); -/* m06: range 20 to 80, m09: range 0 to 30, m12: range 1 to 255... */ + M09_OFFSET, 0, 31); static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD, - M09_REGISTER_THRESHOLD, 0, 255); -/* m06: range 3 to 14, m12: (0x64: 100Hz) */ + M09_THRESHOLD, 0, 80); static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE, NO_REGISTER, 0, 255); @@ -649,7 +767,8 @@ DEFINE_SIMPLE_ATTRIBUTE(debugfs_mode_fops, edt_ft5x06_debugfs_mode_get, edt_ft5x06_debugfs_mode_set, "%llu\n"); static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, - char __user *buf, size_t count, loff_t *off) + char __user *buf, + size_t count, loff_t *off) { struct edt_ft5x06_ts_data *tsdata = file->private_data; struct i2c_client *client = tsdata->client; @@ -770,26 +889,46 @@ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) #endif /* CONFIG_DEBUGFS */ +static int edt_ft5x06_open(struct input_dev *dev) +{ + struct edt_ft5x06_ts_data *tsdata = input_get_drvdata(dev); + + enable_irq(tsdata->client->irq); + + return 0; +} + +static void edt_ft5x06_close(struct input_dev *dev) +{ + struct edt_ft5x06_ts_data *tsdata = input_get_drvdata(dev); + + disable_irq(tsdata->client->irq); +} + static int edt_ft5x06_ts_identify(struct i2c_client *client, - struct edt_ft5x06_ts_data *tsdata, - char *fw_version) + struct edt_ft5x06_ts_data *tsdata, + char *fw_version) { u8 rdbuf[EDT_NAME_LEN]; + u8 cmd; char *p; int error; char *model_name = tsdata->name; - /* see what we find if we assume it is a M06 * - * if we get less than EDT_NAME_LEN, we don't want - * to have garbage in there + /* + * See what we find if we assume it is a M06. + * If we get less than EDT_NAME_LEN, we don't want + * to have garbage in there. */ memset(rdbuf, 0, sizeof(rdbuf)); - error = edt_ft5x06_ts_readwrite(client, 1, "\xBB", + cmd = EDT_CUSTOM_DATA; + error = edt_ft5x06_ts_readwrite(client, sizeof(cmd), &cmd, EDT_NAME_LEN - 1, rdbuf); if (error) return error; - /* Probe content for something consistent. + /* + * Probe content for something consistent. * M06 starts with a response byte, M12 gives the data directly. * M09/Generic does not provide model number information. */ @@ -822,9 +961,10 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client, strlcpy(model_name, rdbuf, EDT_NAME_LEN); strlcpy(fw_version, p ? p : "", EDT_NAME_LEN); } else { - /* If it is not an EDT M06/M12 touchscreen, then the model + /* + * If it is not an EDT M06/M12 touchscreen, then the model * detection is a bit hairy. The different ft5x06 - * firmares around don't reliably implement the + * firmware's around don't reliably implement the * identification registers. Well, we'll take a shot. * * The main difference between generic focaltec based @@ -833,38 +973,41 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client, */ tsdata->version = GENERIC_FT; - error = edt_ft5x06_ts_readwrite(client, 1, "\xA6", + cmd = EDT_FW_VERSION; + error = edt_ft5x06_ts_readwrite(client, sizeof(cmd), &cmd, 2, rdbuf); if (error) return error; strlcpy(fw_version, rdbuf, 2); - error = edt_ft5x06_ts_readwrite(client, 1, "\xA8", + cmd = EDT_PANEL_ID; + error = edt_ft5x06_ts_readwrite(client, sizeof(cmd), &cmd, 1, rdbuf); if (error) return error; - /* This "model identification" is not exact. Unfortunately + /* + * This "model identification" is not exact. Unfortunately * not all firmwares for the ft5x06 put useful values in * the identification registers. */ switch (rdbuf[0]) { - case 0x35: /* EDT EP0350M09 */ - case 0x43: /* EDT EP0430M09 */ - case 0x50: /* EDT EP0500M09 */ - case 0x57: /* EDT EP0570M09 */ - case 0x70: /* EDT EP0700M09 */ + case EDT_PANEL_ID_EP0350M09: + case EDT_PANEL_ID_EP0430M09: + case EDT_PANEL_ID_EP0500M09: + case EDT_PANEL_ID_EP0570M09: + case EDT_PANEL_ID_EP0700M09: tsdata->version = EDT_M09; snprintf(model_name, EDT_NAME_LEN, "EP0%i%i0M09", - rdbuf[0] >> 4, rdbuf[0] & 0x0F); + NIBBLE_UPPER(rdbuf[0]), NIBBLE_LOWER(rdbuf[0])); break; - case 0xa1: /* EDT EP1010ML00 */ + case EDT_PANEL_ID_EP1010ML00: tsdata->version = EDT_M09; snprintf(model_name, EDT_NAME_LEN, "EP%i%i0ML00", - rdbuf[0] >> 4, rdbuf[0] & 0x0F); + NIBBLE_UPPER(rdbuf[0]), NIBBLE_LOWER(rdbuf[0])); break; - case 0x5a: /* Solomon Goldentek Display */ + case SOL_PANEL_ID_GKTW50SCED1R0: /* Solomon Goldentek Display */ snprintf(model_name, EDT_NAME_LEN, "GKTW50SCED1R0"); break; default: @@ -929,6 +1072,26 @@ edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) } } +static void edt_ft5x06_ts_set_readout_mode(struct edt_ft5x06_ts_data *tsdata, + enum readout_mode mode) +{ + uint8_t readout_mode; + + switch (tsdata->version) { + case EDT_M06: + break; + case EDT_M09: /* fall through */ + case EDT_M12: /* fall through */ + case GENERIC_FT: + readout_mode = (mode == EDT_READOUT_MODE_POLL) ? + M09_ID_G_MODE_POLL : + M09_ID_G_MODE_IRQ; + edt_ft5x06_register_write(tsdata, M09_ID_G_MODE, readout_mode); + break; + } +} + + static void edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) { @@ -946,66 +1109,67 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) case EDT_M09: case EDT_M12: - reg_addr->reg_threshold = M09_REGISTER_THRESHOLD; + reg_addr->reg_threshold = M09_THRESHOLD; reg_addr->reg_report_rate = NO_REGISTER; - reg_addr->reg_gain = M09_REGISTER_GAIN; - reg_addr->reg_offset = M09_REGISTER_OFFSET; - reg_addr->reg_num_x = M09_REGISTER_NUM_X; - reg_addr->reg_num_y = M09_REGISTER_NUM_Y; + reg_addr->reg_gain = M09_GAIN; + reg_addr->reg_offset = M09_OFFSET; + reg_addr->reg_num_x = M09_NUM_X; + reg_addr->reg_num_y = M09_NUM_Y; break; case GENERIC_FT: /* this is a guesswork */ - reg_addr->reg_threshold = M09_REGISTER_THRESHOLD; - reg_addr->reg_gain = M09_REGISTER_GAIN; - reg_addr->reg_offset = M09_REGISTER_OFFSET; + reg_addr->reg_threshold = M09_THRESHOLD; + reg_addr->reg_gain = M09_GAIN; + reg_addr->reg_offset = M09_OFFSET; break; } } static int edt_ft5x06_ts_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { const struct edt_i2c_chip_data *chip_data; + struct device *dev = &client->dev; struct edt_ft5x06_ts_data *tsdata; struct input_dev *input; - unsigned long irq_flags; int error; char fw_version[EDT_NAME_LEN]; - dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n"); + dev_dbg(dev, "probing for EDT FT5x06 I2C\n"); - tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL); + tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL); if (!tsdata) { - dev_err(&client->dev, "failed to allocate driver data.\n"); + dev_err(dev, "failed to allocate driver data\n"); return -ENOMEM; } - chip_data = of_device_get_match_data(&client->dev); + chip_data = of_device_get_match_data(dev); if (!chip_data) chip_data = (const struct edt_i2c_chip_data *)id->driver_data; if (!chip_data || !chip_data->max_support_points) { - dev_err(&client->dev, "invalid or missing chip data\n"); + dev_err(dev, "invalid or missing chip data\n"); return -EINVAL; } tsdata->max_support_points = chip_data->max_support_points; - tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev, + tsdata->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(tsdata->reset_gpio)) { error = PTR_ERR(tsdata->reset_gpio); - dev_err(&client->dev, - "Failed to request GPIO reset pin, error %d\n", error); + if (error != -EPROBE_DEFER) + dev_err(dev, "failed to request GPIO reset pin: %d\n", + error); return error; } - tsdata->wake_gpio = devm_gpiod_get_optional(&client->dev, - "wake", GPIOD_OUT_LOW); + tsdata->wake_gpio = devm_gpiod_get_optional(dev, "wake", GPIOD_OUT_LOW); if (IS_ERR(tsdata->wake_gpio)) { error = PTR_ERR(tsdata->wake_gpio); - dev_err(&client->dev, - "Failed to request GPIO wake pin, error %d\n", error); + if (error != -EPROBE_DEFER) + dev_err(dev, "failed to request GPIO wake pin: %d\n", + error); return error; } @@ -1020,34 +1184,83 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, msleep(300); } - input = devm_input_allocate_device(&client->dev); - if (!input) { - dev_err(&client->dev, "failed to allocate input device.\n"); - return -ENOMEM; - } - mutex_init(&tsdata->mutex); tsdata->client = client; - tsdata->input = input; tsdata->factory_mode = false; error = edt_ft5x06_ts_identify(client, tsdata, fw_version); if (error) { - dev_err(&client->dev, "touchscreen probe failed\n"); + dev_err(dev, "touchscreen probe failed\n"); return error; } edt_ft5x06_ts_set_regs(tsdata); - edt_ft5x06_ts_get_defaults(&client->dev, tsdata); + edt_ft5x06_ts_get_defaults(dev, tsdata); edt_ft5x06_ts_get_parameters(tsdata); - dev_dbg(&client->dev, - "Model \"%s\", Rev. \"%s\", %dx%d sensors\n", + dev_dbg(dev, "model \"%s\", Rev. \"%s\", %dx%d sensors\n", tsdata->name, fw_version, tsdata->num_x, tsdata->num_y); + i2c_set_clientdata(client, tsdata); + + if (client->irq) { + unsigned long irq_flags; + + irq_flags = irq_get_trigger_type(client->irq); + if (irq_flags == IRQF_TRIGGER_NONE) + irq_flags = IRQF_TRIGGER_FALLING; + irq_flags |= IRQF_ONESHOT; + + error = devm_request_threaded_irq(dev, client->irq, NULL, + edt_ft5x06_ts_isr, irq_flags, + client->name, tsdata); + if (error) { + dev_err(dev, "unable to request touchscreen IRQ\n"); + return error; + } + + disable_irq(client->irq); + + input = devm_input_allocate_device(dev); + if (!input) { + dev_err(dev, "failed to allocate input device\n"); + return -ENOMEM; + } + input->open = edt_ft5x06_open; + input->close = edt_ft5x06_close; + + edt_ft5x06_ts_set_readout_mode(tsdata, EDT_READOUT_MODE_IRQ); + } else { +#if !IS_ENABLED(CONFIG_INPUT_POLLDEV) + dev_err(dev, "no IRQ setup and built without INPUT_POLLDEV\n"); + return -ENODEV; +#else + uint32_t poll_interval; + + dev_warn(dev, "no IRQ setup, using polled input\n"); + + tsdata->polldev = devm_input_allocate_polled_device(dev); + if (!tsdata->polldev) { + dev_err(dev, "failed to allocate polldev\n"); + return -ENOMEM; + } + + if (!device_property_read_u32(dev, "poll-interval", &poll_interval)) + tsdata->polldev->poll_interval = poll_interval; + + tsdata->polldev->private = tsdata; + tsdata->polldev->poll = edt_ft5x06_poll; + input = tsdata->polldev->input; + + edt_ft5x06_ts_set_readout_mode(tsdata, EDT_READOUT_MODE_POLL); +#endif + } + input->name = tsdata->name; input->id.bustype = BUS_I2C; - input->dev.parent = &client->dev; + input->dev.parent = dev; + input_set_drvdata(input, tsdata); + tsdata->input = input; if (tsdata->version == EDT_M06 || tsdata->version == EDT_M09 || @@ -1067,40 +1280,28 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, touchscreen_parse_properties(input, true, &tsdata->prop); error = input_mt_init_slots(input, tsdata->max_support_points, - INPUT_MT_DIRECT); - if (error) { - dev_err(&client->dev, "Unable to init MT slots.\n"); - return error; - } - - i2c_set_clientdata(client, tsdata); - - irq_flags = irq_get_trigger_type(client->irq); - if (irq_flags == IRQF_TRIGGER_NONE) - irq_flags = IRQF_TRIGGER_FALLING; - irq_flags |= IRQF_ONESHOT; - - error = devm_request_threaded_irq(&client->dev, client->irq, - NULL, edt_ft5x06_ts_isr, irq_flags, - client->name, tsdata); + INPUT_MT_DIRECT); if (error) { - dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); + dev_err(dev, "unable to init MT slots\n"); return error; } - error = devm_device_add_group(&client->dev, &edt_ft5x06_attr_group); + error = devm_device_add_group(dev, &edt_ft5x06_attr_group); if (error) return error; - error = input_register_device(input); + if (client->irq) + error = input_register_device(input); + else + error = input_register_polled_device(tsdata->polldev); if (error) return error; - edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev)); - device_init_wakeup(&client->dev, 1); + edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(dev)); + device_init_wakeup(dev, 1); - dev_dbg(&client->dev, - "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n", + dev_dbg(dev, + "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d\n", client->irq, tsdata->wake_gpio ? desc_to_gpio(tsdata->wake_gpio) : -1, tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1); @@ -1154,6 +1355,7 @@ static const struct edt_i2c_chip_data edt_ft6236_data = { static const struct i2c_device_id edt_ft5x06_ts_id[] = { { .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data }, + { .name = "edt-ft5426", .driver_data = (long)&edt_ft5506_data }, { .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data }, /* Note no edt- prefix for compatibility with the ft6236.c driver */ { .name = "ft6236", .driver_data = (long)&edt_ft6236_data }, @@ -1166,6 +1368,7 @@ static const struct of_device_id edt_ft5x06_of_match[] = { { .compatible = "edt,edt-ft5206", .data = &edt_ft5x06_data }, { .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data }, { .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data }, + { .compatible = "edt,edt-ft5426", .data = &edt_ft5506_data }, { .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data }, /* Note focaltech vendor prefix for compatibility with ft6236.c */ { .compatible = "focaltech,ft6236", .data = &edt_ft6236_data }, diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c index 8d7f9c8f2771c7..c12f3c863feb3a 100644 --- a/drivers/input/touchscreen/of_touchscreen.c +++ b/drivers/input/touchscreen/of_touchscreen.c @@ -34,7 +34,7 @@ static bool touchscreen_get_prop_u32(struct device *dev, static void touchscreen_set_params(struct input_dev *dev, unsigned long axis, - int max, int fuzz) + int min, int max, int fuzz) { struct input_absinfo *absinfo; @@ -46,6 +46,7 @@ static void touchscreen_set_params(struct input_dev *dev, } absinfo = &dev->absinfo[axis]; + absinfo->minimum = min; absinfo->maximum = max; absinfo->fuzz = fuzz; } @@ -68,46 +69,54 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch, { struct device *dev = input->dev.parent; unsigned int axis; - unsigned int maximum, fuzz; - bool data_present; + unsigned int min, max, fuzz; + bool ret; input_alloc_absinfo(input); if (!input->absinfo) return; axis = multitouch ? ABS_MT_POSITION_X : ABS_X; - data_present = touchscreen_get_prop_u32(dev, "touchscreen-size-x", - input_abs_get_max(input, - axis) + 1, - &maximum) | - touchscreen_get_prop_u32(dev, "touchscreen-fuzz-x", - input_abs_get_fuzz(input, axis), - &fuzz); - if (data_present) - touchscreen_set_params(input, axis, maximum - 1, fuzz); + ret = touchscreen_get_prop_u32(dev, "touchscreen-min-size-x", + input_abs_get_min(input, axis), + &min) | + touchscreen_get_prop_u32(dev, "touchscreen-size-x", + input_abs_get_max(input, axis) + 1, + &max) | + touchscreen_get_prop_u32(dev, "touchscreen-max-size-x", + input_abs_get_max(input, axis) + 1, + &max) | + touchscreen_get_prop_u32(dev, "touchscreen-fuzz-x", + input_abs_get_fuzz(input, axis), + &fuzz); + if (ret) + touchscreen_set_params(input, axis, min, max - 1, fuzz); axis = multitouch ? ABS_MT_POSITION_Y : ABS_Y; - data_present = touchscreen_get_prop_u32(dev, "touchscreen-size-y", - input_abs_get_max(input, - axis) + 1, - &maximum) | - touchscreen_get_prop_u32(dev, "touchscreen-fuzz-y", - input_abs_get_fuzz(input, axis), - &fuzz); - if (data_present) - touchscreen_set_params(input, axis, maximum - 1, fuzz); + ret = touchscreen_get_prop_u32(dev, "touchscreen-min-size-y", + input_abs_get_min(input, axis), + &min) | + touchscreen_get_prop_u32(dev, "touchscreen-size-y", + input_abs_get_max(input, axis) + 1, + &max) | + touchscreen_get_prop_u32(dev, "touchscreen-max-size-y", + input_abs_get_max(input, axis) + 1, + &max) | + touchscreen_get_prop_u32(dev, "touchscreen-fuzz-y", + input_abs_get_fuzz(input, axis), + &fuzz); + if (ret) + touchscreen_set_params(input, axis, min, max - 1, fuzz); axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE; - data_present = touchscreen_get_prop_u32(dev, - "touchscreen-max-pressure", - input_abs_get_max(input, axis), - &maximum) | - touchscreen_get_prop_u32(dev, - "touchscreen-fuzz-pressure", - input_abs_get_fuzz(input, axis), - &fuzz); - if (data_present) - touchscreen_set_params(input, axis, maximum, fuzz); + ret = touchscreen_get_prop_u32(dev, "touchscreen-max-pressure", + input_abs_get_max(input, axis), + &max) | + touchscreen_get_prop_u32(dev, "touchscreen-fuzz-pressure", + input_abs_get_fuzz(input, axis), + &fuzz); + if (ret) + touchscreen_set_params(input, axis, 0, max, fuzz); if (!prop) return; diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index 3bf9a127181927..49cb9ba633a80f 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -26,28 +26,79 @@ */ #include -#include -#include -#include #include -#include +#include #include #include -#include +#include +#include #include #include +#include +#include -/* LED select registers determine the source that drives LED outputs */ -#define PCA963X_LED_OFF 0x0 /* LED driver off */ -#define PCA963X_LED_ON 0x1 /* LED driver on */ -#define PCA963X_LED_PWM 0x2 /* Controlled through PWM */ -#define PCA963X_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */ - -#define PCA963X_MODE2_DMBLNK 0x20 /* Enable blinking */ - -#define PCA963X_MODE1 0x00 -#define PCA963X_MODE2 0x01 -#define PCA963X_PWM_BASE 0x02 +#define PCA963X_MAX_NAME_LEN 32 + +#define PCA963X_MODE1 0x00 /* mode 1 register addr */ +#define PCA963X_MODE1_ALLCALL_ON BIT(0) /* respond to LED All Call */ +#define PCA963X_MODE1_RESPOND_SUB3 BIT(1) /* respond to Sub address 3 */ +#define PCA963X_MODE1_RESPOND_SUB2 BIT(2) /* respond to Sub address 2 */ +#define PCA963X_MODE1_RESPOND_SUB1 BIT(3) /* respond to Sub address 1 */ +#define PCA963X_MODE1_SLEEP BIT(4) /* put in low power mode */ +#define PCA963X_MODE1_AI_ROLL_PWM BIT(5) /* auto-increment only PWM's */ +#define PCA963X_MODE1_AI_ROLL_GRP BIT(6) /* AI only group-controls */ +#define PCA963X_MODE1_AI_EN BIT(7) /* enable Auto-Increment */ + +#define PCA963X_MODE2 0x01 /* mode 2 register addr */ +#define PCA963X_MODE2_OUTNE_OUTDRV BIT(0) /* outdrv determines LED state */ +#define PCA963X_MODE2_OUTNE_HIZ BIT(1) /* LED-state in Hi-Z */ +#define PCA963X_MODE2_OUTDRV_TOTEM_POLE BIT(2) /* outputs are totem-pole'd */ +#define PCA963X_MODE2_OCH_ACK BIT(3) /* out change on ACK else STOP */ +#define PCA963X_MODE2_INVRT BIT(4) /* output logic state inverted */ +#define PCA963X_MODE2_DMBLNK BIT(5) /* grp-ctrl blink else dimming */ + +#define PCA963X_PWM_ADDR(led) (0x02 + (led)) + +#define PCA9633_GRPPWM 0x06 /* group PWM duty cycle ctrl for PCA9633 */ +#define PCA9634_GRPPWM 0x0a /* group PWM duty cycle ctrl for PCA9634 */ +#define PCA9635_GRPPWM 0x12 /* group PWM duty cycle ctrl for PCA9635 */ +#define PCA9633_GRPFREQ 0x07 /* group frequency control for PCA9633 */ +#define PCA9634_GRPFREQ 0x0b /* group frequency control for PCA9634 */ +#define PCA9635_GRPFREQ 0x13 /* group frequency control for PCA9635 */ + +#define PCA9633_LEDOUT_BASE 0x08 /* LED output state 0 reg for PCA9633 */ +#define PCA9634_LEDOUT_BASE 0x0c /* LED output state 0 reg for PCA9635 */ +#define PCA9635_LEDOUT_BASE 0x14 /* LED output state 0 reg for PCA9634 */ +#define PCA963X_LEDOUT_ADDR(ledout_base, led_num) \ + ((ledout_base) + ((led_num) / 4)) + +#define PCA963X_LEDOUT_LED_OFF 0x0 /* LED off */ +#define PCA963X_LEDOUT_LED_ON 0x1 /* LED on */ +#define PCA963X_LEDOUT_LED_PWM 0x2 /* LED PWM mode */ +#define PCA963X_LEDOUT_LED_GRP_PWM 0x3 /* LED PWM + group PWM mode */ +#define PCA963X_LEDOUT_MASK GENMASK(1, 0) + +#define PCA963X_LEDOUT_LDR(drive, led_num) \ + (((drive) & PCA963X_LEDOUT_MASK) << (((led_num) % 4) << 1)) +#define PCA963X_LEDOUT_LDR_INV(drive, led_num) \ + (((drive) >> (((led_num) % 4) << 1)) & PCA963X_LEDOUT_MASK) + +#define PCA9633_SUBADDR(x) \ + ((((x) - 1) % 0x3) + 0x09) /* I2C subaddr for PCA9633 */ +#define PCA9634_SUBADDR(x) \ + ((((x) - 1) % 0x3) + 0x0e) /* I2C subaddr for PCA9634 */ +#define PCA9635_SUBADDR(x) \ + ((((x) - 1) % 0x3) + 0x18) /* I2C subaddr for PCA9635 */ +#define PCA963X_SUBADDR_SET(x) (((x) << 1) & 0xfe) + +#define PCA9633_ALLCALLADDR 0x0c /* I2C Led all call address for PCA9633 */ +#define PCA9634_ALLCALLADDR 0x11 /* I2C Led all call address for PCA9634 */ +#define PCA9635_ALLCALLADDR 0x1b /* I2C Led all call address for PCA9635 */ +#define PCA963X_ALLCALLADDR_SET(x) (((x) << 1) & 0xfe) + +/* Software reset password */ +#define PCA963X_PASSKEY1 0xa5 +#define PCA963X_PASSKEY2 0x5a enum pca963x_type { pca9633, @@ -65,21 +116,21 @@ struct pca963x_chipdef { static struct pca963x_chipdef pca963x_chipdefs[] = { [pca9633] = { - .grppwm = 0x6, - .grpfreq = 0x7, - .ledout_base = 0x8, + .grppwm = PCA9633_GRPPWM, + .grpfreq = PCA9633_GRPFREQ, + .ledout_base = PCA9633_LEDOUT_BASE, .n_leds = 4, }, [pca9634] = { - .grppwm = 0xa, - .grpfreq = 0xb, - .ledout_base = 0xc, + .grppwm = PCA9634_GRPPWM, + .grpfreq = PCA9634_GRPFREQ, + .ledout_base = PCA9634_LEDOUT_BASE, .n_leds = 8, }, [pca9635] = { - .grppwm = 0x12, - .grpfreq = 0x13, - .ledout_base = 0x14, + .grppwm = PCA9635_GRPPWM, + .grpfreq = PCA9635_GRPFREQ, + .ledout_base = PCA9635_LEDOUT_BASE, .n_leds = 16, }, }; @@ -93,7 +144,7 @@ static const struct i2c_device_id pca963x_id[] = { { "pca9633", pca9633 }, { "pca9634", pca9634 }, { "pca9635", pca9635 }, - { } + { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, pca963x_id); @@ -102,7 +153,7 @@ static const struct acpi_device_id pca963x_acpi_ids[] = { { "PCA9633", pca9633 }, { "PCA9634", pca9634 }, { "PCA9635", pca9635 }, - { } + { /* sentinel */ } }; MODULE_DEVICE_TABLE(acpi, pca963x_acpi_ids); @@ -110,7 +161,7 @@ struct pca963x_led; struct pca963x { struct pca963x_chipdef *chipdef; - struct mutex mutex; + struct mutex mutex; /* lock around parallel i2c access */ struct i2c_client *client; struct pca963x_led *leds; unsigned long leds_on; @@ -120,72 +171,70 @@ struct pca963x_led { struct pca963x *chip; struct led_classdev led_cdev; int led_num; /* 0 .. 15 potentially */ - char name[32]; + char name[PCA963X_MAX_NAME_LEN]; u8 gdc; u8 gfrq; }; static int pca963x_brightness(struct pca963x_led *pca963x, - enum led_brightness brightness) + enum led_brightness brightness) { - u8 ledout_addr = pca963x->chip->chipdef->ledout_base - + (pca963x->led_num / 4); + u8 ledout_addr; u8 ledout; - int shift = 2 * (pca963x->led_num % 4); - u8 mask = 0x3 << shift; int ret; + ledout_addr = PCA963X_LEDOUT_ADDR(pca963x->chip->chipdef->ledout_base, + pca963x->led_num); ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr); + ledout &= ~PCA963X_LEDOUT_LDR(PCA963X_LEDOUT_MASK, pca963x->led_num); switch (brightness) { case LED_FULL: - ret = i2c_smbus_write_byte_data(pca963x->chip->client, - ledout_addr, - (ledout & ~mask) | (PCA963X_LED_ON << shift)); + ledout |= PCA963X_LEDOUT_LDR(PCA963X_LEDOUT_LED_ON, + pca963x->led_num); break; case LED_OFF: - ret = i2c_smbus_write_byte_data(pca963x->chip->client, - ledout_addr, ledout & ~mask); + ledout |= PCA963X_LEDOUT_LDR(PCA963X_LEDOUT_LED_OFF, + pca963x->led_num); break; default: ret = i2c_smbus_write_byte_data(pca963x->chip->client, - PCA963X_PWM_BASE + pca963x->led_num, - brightness); + PCA963X_PWM_ADDR(pca963x->led_num), + brightness); if (ret < 0) return ret; - ret = i2c_smbus_write_byte_data(pca963x->chip->client, - ledout_addr, - (ledout & ~mask) | (PCA963X_LED_PWM << shift)); + ledout |= PCA963X_LEDOUT_LDR(PCA963X_LEDOUT_LED_PWM, + pca963x->led_num); break; } - return ret; + return i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr, + ledout); } static void pca963x_blink(struct pca963x_led *pca963x) { - u8 ledout_addr = pca963x->chip->chipdef->ledout_base + - (pca963x->led_num / 4); + u8 ledout_addr; u8 ledout; - u8 mode2 = i2c_smbus_read_byte_data(pca963x->chip->client, - PCA963X_MODE2); - int shift = 2 * (pca963x->led_num % 4); - u8 mask = 0x3 << shift; i2c_smbus_write_byte_data(pca963x->chip->client, - pca963x->chip->chipdef->grppwm, pca963x->gdc); + pca963x->chip->chipdef->grppwm, + pca963x->gdc); i2c_smbus_write_byte_data(pca963x->chip->client, - pca963x->chip->chipdef->grpfreq, pca963x->gfrq); - - if (!(mode2 & PCA963X_MODE2_DMBLNK)) - i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2, - mode2 | PCA963X_MODE2_DMBLNK); + pca963x->chip->chipdef->grpfreq, + pca963x->gfrq); + ledout_addr = PCA963X_LEDOUT_ADDR(pca963x->chip->chipdef->ledout_base, + pca963x->led_num); mutex_lock(&pca963x->chip->mutex); ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr); - if ((ledout & mask) != (PCA963X_LED_GRP_PWM << shift)) + if (PCA963X_LEDOUT_LDR_INV(ledout, pca963x->led_num) != + PCA963X_LEDOUT_LED_GRP_PWM) { + ledout |= PCA963X_LEDOUT_LDR(PCA963X_LEDOUT_LED_GRP_PWM, + pca963x->led_num); i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr, - (ledout & ~mask) | (PCA963X_LED_GRP_PWM << shift)); + ledout); + } mutex_unlock(&pca963x->chip->mutex); } @@ -199,15 +248,23 @@ static int pca963x_power_state(struct pca963x_led *pca963x) else clear_bit(pca963x->led_num, leds_on); - if (!(*leds_on) != !cached_leds) + if (!(*leds_on) != !cached_leds) { + u8 mode1 = i2c_smbus_read_byte_data(pca963x->chip->client, + PCA963X_MODE1); + + if (*leds_on) + mode1 &= ~PCA963X_MODE1_SLEEP; + else + mode1 |= PCA963X_MODE1_SLEEP; return i2c_smbus_write_byte_data(pca963x->chip->client, - PCA963X_MODE1, *leds_on ? 0 : BIT(4)); + PCA963X_MODE1, mode1); + } return 0; } static int pca963x_led_set(struct led_classdev *led_cdev, - enum led_brightness value) + enum led_brightness value) { struct pca963x_led *pca963x; int ret; @@ -227,7 +284,7 @@ static int pca963x_led_set(struct led_classdev *led_cdev, } static unsigned int pca963x_period_scale(struct pca963x_led *pca963x, - unsigned int val) + unsigned int val) { unsigned int scaling = pca963x->chip->chipdef->scaling; @@ -235,7 +292,8 @@ static unsigned int pca963x_period_scale(struct pca963x_led *pca963x, } static int pca963x_blink_set(struct led_classdev *led_cdev, - unsigned long *delay_on, unsigned long *delay_off) + unsigned long *delay_on, + unsigned long *delay_off) { struct pca963x_led *pca963x; unsigned long time_on, time_off, period; @@ -254,7 +312,7 @@ static int pca963x_blink_set(struct led_classdev *led_cdev, period = pca963x_period_scale(pca963x, time_on + time_off); - /* If period not supported by hardware, default to someting sane. */ + /* If period not supported by hardware, default to something sane. */ if ((period < PCA963X_BLINK_PERIOD_MIN) || (period > PCA963X_BLINK_PERIOD_MAX)) { time_on = 500; @@ -313,10 +371,8 @@ pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip) res = of_property_read_u32(child, "reg", ®); if ((res != 0) || (reg >= chip->n_leds)) continue; - led.name = - of_get_property(child, "label", NULL) ? : child->name; - led.default_trigger = - of_get_property(child, "linux,default-trigger", NULL); + led.name = of_get_property(child, "label", NULL) ? : child->name; + led.default_trigger = of_get_property(child, "linux,default-trigger", NULL); pca963x_leds[reg] = led; } pdata = devm_kzalloc(&client->dev, @@ -368,7 +424,7 @@ pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip) #endif static int pca963x_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct pca963x *pca963x_chip; struct pca963x_led *pca963x; @@ -397,21 +453,25 @@ static int pca963x_probe(struct i2c_client *client, } if (pdata && (pdata->leds.num_leds < 1 || - pdata->leds.num_leds > chip->n_leds)) { - dev_err(&client->dev, "board info must claim 1-%d LEDs", - chip->n_leds); + pdata->leds.num_leds > chip->n_leds)) { + dev_err(&client->dev, "board info must claim 1-%d LEDs", chip->n_leds); return -EINVAL; } - pca963x_chip = devm_kzalloc(&client->dev, sizeof(*pca963x_chip), - GFP_KERNEL); + pca963x_chip = devm_kzalloc(&client->dev, sizeof(*pca963x_chip), GFP_KERNEL); if (!pca963x_chip) return -ENOMEM; - pca963x = devm_kzalloc(&client->dev, chip->n_leds * sizeof(*pca963x), - GFP_KERNEL); + pca963x = devm_kzalloc(&client->dev, chip->n_leds * sizeof(*pca963x), GFP_KERNEL); if (!pca963x) return -ENOMEM; + /* Check if chip actually exists on the bus */ + err = i2c_smbus_read_byte_data(client, PCA963X_MODE1); + if (err < 0) { + dev_err(&client->dev, "controller not found"); + return err; + } + i2c_set_clientdata(client, pca963x_chip); mutex_init(&pca963x_chip->mutex); @@ -421,7 +481,8 @@ static int pca963x_probe(struct i2c_client *client, /* Turn off LEDs by default*/ for (i = 0; i < chip->n_leds / 4; i++) - i2c_smbus_write_byte_data(client, chip->ledout_base + i, 0x00); + i2c_smbus_write_byte_data(client, chip->ledout_base + i, + PCA963X_LEDOUT_LDR(PCA963X_LEDOUT_LED_OFF, i)); for (i = 0; i < chip->n_leds; i++) { pca963x[i].led_num = i; @@ -438,7 +499,7 @@ static int pca963x_probe(struct i2c_client *client, pdata->leds.leds[i].default_trigger; } if (!pdata || i >= pdata->leds.num_leds || - !pdata->leds.leds[i].name) + !pdata->leds.leds[i].name) snprintf(pca963x[i].name, sizeof(pca963x[i].name), "pca963x:%d:%.2x:%d", client->adapter->nr, client->addr, i); @@ -455,19 +516,20 @@ static int pca963x_probe(struct i2c_client *client, } /* Disable LED all-call address, and power down initially */ - i2c_smbus_write_byte_data(client, PCA963X_MODE1, BIT(4)); + i2c_smbus_write_byte_data(client, PCA963X_MODE1, PCA963X_MODE1_SLEEP); if (pdata) { u8 mode2 = i2c_smbus_read_byte_data(pca963x->chip->client, PCA963X_MODE2); + + /* Always enable LED output and group blink mode */ + mode2 |= PCA963X_MODE2_OUTNE_OUTDRV | PCA963X_MODE2_DMBLNK; /* Configure output: open-drain or totem pole (push-pull) */ - if (pdata->outdrv == PCA963X_OPEN_DRAIN) - mode2 |= 0x01; - else - mode2 |= 0x05; + if (pdata->outdrv == PCA963X_TOTEM_POLE) + mode2 |= PCA963X_MODE2_OUTDRV_TOTEM_POLE; /* Configure direction: normal or inverted */ if (pdata->dir == PCA963X_INVERTED) - mode2 |= 0x10; + mode2 |= PCA963X_MODE2_INVRT; i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2, mode2); } diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 336de66ca40807..66be1b6d83eb8e 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -16,20 +16,21 @@ * published by the Free Software Foundation. */ -#include +#include +#include #include +#include #include #include +#include +#include #include +#include #include #include #include -#include -#include -#include -#include -#define AXP20X_OFF 0x80 +#define AXP20X_OFF BIT(7) #define AXP806_REG_ADDR_EXT_ADDR_MASTER_MODE 0 #define AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE BIT(4) diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index 44f70dcea61e3c..56970eb4339ff1 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -10,6 +10,7 @@ * option) any later version. */ +#include #include #include #include diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index d51ebd1da65e77..2fd48ad9fdfcc5 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -28,17 +29,17 @@ #include #include -#define PS_STAT_VBUS_TRIGGER (1 << 0) -#define PS_STAT_BAT_CHRG_DIR (1 << 2) -#define PS_STAT_VBAT_ABOVE_VHOLD (1 << 3) -#define PS_STAT_VBUS_VALID (1 << 4) -#define PS_STAT_VBUS_PRESENT (1 << 5) +#define PS_STAT_VBUS_TRIGGER BIT(0) +#define PS_STAT_BAT_CHRG_DIR BIT(2) +#define PS_STAT_VBAT_ABOVE_VHOLD BIT(3) +#define PS_STAT_VBUS_VALID BIT(4) +#define PS_STAT_VBUS_PRESENT BIT(5) -#define CHRG_STAT_BAT_SAFE_MODE (1 << 3) -#define CHRG_STAT_BAT_VALID (1 << 4) -#define CHRG_STAT_BAT_PRESENT (1 << 5) -#define CHRG_STAT_CHARGING (1 << 6) -#define CHRG_STAT_PMIC_OTP (1 << 7) +#define CHRG_STAT_BAT_SAFE_MODE BIT(3) +#define CHRG_STAT_BAT_VALID BIT(4) +#define CHRG_STAT_BAT_PRESENT BIT(5) +#define CHRG_STAT_CHARGING BIT(6) +#define CHRG_STAT_PMIC_OTP BIT(7) #define VBUS_ISPOUT_CUR_LIM_MASK 0x03 #define VBUS_ISPOUT_CUR_LIM_BIT_POS 0 @@ -51,33 +52,33 @@ #define VBUS_ISPOUT_VHOLD_SET_OFFSET 4000 /* 4000mV */ #define VBUS_ISPOUT_VHOLD_SET_LSB_RES 100 /* 100mV */ #define VBUS_ISPOUT_VHOLD_SET_4300MV 0x3 /* 4300mV */ -#define VBUS_ISPOUT_VBUS_PATH_DIS (1 << 7) +#define VBUS_ISPOUT_VBUS_PATH_DIS BIT(7) #define CHRG_CCCV_CC_MASK 0xf /* 4 bits */ #define CHRG_CCCV_CC_BIT_POS 0 #define CHRG_CCCV_CC_OFFSET 200 /* 200mA */ #define CHRG_CCCV_CC_LSB_RES 200 /* 200mA */ -#define CHRG_CCCV_ITERM_20P (1 << 4) /* 20% of CC */ +#define CHRG_CCCV_ITERM_20P BIT(4) /* 20% of CC */ #define CHRG_CCCV_CV_MASK 0x60 /* 2 bits */ #define CHRG_CCCV_CV_BIT_POS 5 #define CHRG_CCCV_CV_4100MV 0x0 /* 4.10V */ #define CHRG_CCCV_CV_4150MV 0x1 /* 4.15V */ #define CHRG_CCCV_CV_4200MV 0x2 /* 4.20V */ #define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */ -#define CHRG_CCCV_CHG_EN (1 << 7) +#define CHRG_CCCV_CHG_EN BIT(7) #define CNTL2_CC_TIMEOUT_MASK 0x3 /* 2 bits */ #define CNTL2_CC_TIMEOUT_OFFSET 6 /* 6 Hrs */ #define CNTL2_CC_TIMEOUT_LSB_RES 2 /* 2 Hrs */ #define CNTL2_CC_TIMEOUT_12HRS 0x3 /* 12 Hrs */ -#define CNTL2_CHGLED_TYPEB (1 << 4) -#define CNTL2_CHG_OUT_TURNON (1 << 5) +#define CNTL2_CHGLED_TYPEB BIT(4) +#define CNTL2_CHG_OUT_TURNON BIT(5) #define CNTL2_PC_TIMEOUT_MASK 0xC0 #define CNTL2_PC_TIMEOUT_OFFSET 40 /* 40 mins */ #define CNTL2_PC_TIMEOUT_LSB_RES 10 /* 10 mins */ #define CNTL2_PC_TIMEOUT_70MINS 0x3 -#define CHRG_ILIM_TEMP_LOOP_EN (1 << 3) +#define CHRG_ILIM_TEMP_LOOP_EN BIT(3) #define CHRG_VBUS_ILIM_MASK 0xf0 #define CHRG_VBUS_ILIM_BIT_POS 4 #define CHRG_VBUS_ILIM_100MA 0x0 /* 100mA */ @@ -91,7 +92,7 @@ #define CHRG_VLTFC_0C 0xA5 /* 0 DegC */ #define CHRG_VHTFC_45C 0x1F /* 45 DegC */ -#define FG_CNTL_OCV_ADJ_EN (1 << 3) +#define FG_CNTL_OCV_ADJ_EN BIT(3) #define CV_4100MV 4100 /* 4100mV */ #define CV_4150MV 4150 /* 4150mV */ diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 1581f6ab1b1f42..ecca745dce8e24 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -468,13 +468,15 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state) { int err; - if (!pwm || !state || !state->period || - state->duty_cycle > state->period) + if (!pwm || !state || state->duty_cycle > state->period) return -EINVAL; if (!memcmp(state, &pwm->state, sizeof(*state))) return 0; + if (state->period == 0) + state->enabled = false; + if (pwm->chip->ops->apply) { err = pwm->chip->ops->apply(pwm->chip, pwm, state); if (err) @@ -507,8 +509,9 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state) pwm->state.polarity = state->polarity; } - if (state->period != pwm->state.period || - state->duty_cycle != pwm->state.duty_cycle) { + if (state->period && + (state->period != pwm->state.period || + state->duty_cycle != pwm->state.duty_cycle)) { err = pwm->chip->ops->config(pwm->chip, pwm, state->duty_cycle, state->period); diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index 376a99b7cf5da1..992c5ff7155963 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -13,31 +13,258 @@ * GNU General Public License for more details. */ +#include +#include #include #include +#include #include #include #include #include #include -#include #include +#include #include +#define AXP20X_GPIO0_FUNC_MASK GENMASK(3, 0) +#define AXP20X_GPIO1_FUNC_MASK GENMASK(3, 0) + #define AXP20X_IO_ENABLED 0x03 #define AXP20X_IO_DISABLED 0x07 +#define AXP20X_WORKMODE_DCDC2_MASK BIT_MASK(2) +#define AXP20X_WORKMODE_DCDC3_MASK BIT_MASK(1) + +#define AXP20X_FREQ_DCDC_MASK GENMASK(3, 0) + +#define AXP20X_VBUS_IPSOUT_MGMT_MASK BIT_MASK(2) + +#define AXP20X_DCDC2_V_OUT_MASK GENMASK(5, 0) +#define AXP20X_DCDC3_V_OUT_MASK GENMASK(7, 0) +#define AXP20X_LDO24_V_OUT_MASK GENMASK(7, 4) +#define AXP20X_LDO3_V_OUT_MASK GENMASK(6, 0) +#define AXP20X_LDO5_V_OUT_MASK GENMASK(7, 4) + +#define AXP20X_PWR_OUT_EXTEN_MASK BIT_MASK(0) +#define AXP20X_PWR_OUT_DCDC3_MASK BIT_MASK(1) +#define AXP20X_PWR_OUT_LDO2_MASK BIT_MASK(2) +#define AXP20X_PWR_OUT_LDO4_MASK BIT_MASK(3) +#define AXP20X_PWR_OUT_DCDC2_MASK BIT_MASK(4) +#define AXP20X_PWR_OUT_LDO3_MASK BIT_MASK(6) + +#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE_MASK BIT_MASK(0) +#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE(x) \ + ((x) << 0) +#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE_MASK BIT_MASK(1) +#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE(x) \ + ((x) << 1) +#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN_MASK BIT_MASK(2) +#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN BIT(2) +#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN_MASK BIT_MASK(3) +#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN BIT(3) + +#define AXP20X_LDO4_V_OUT_1250mV_START 0x0 +#define AXP20X_LDO4_V_OUT_1250mV_STEPS 0 +#define AXP20X_LDO4_V_OUT_1250mV_END \ + (AXP20X_LDO4_V_OUT_1250mV_START + AXP20X_LDO4_V_OUT_1250mV_STEPS) +#define AXP20X_LDO4_V_OUT_1300mV_START 0x1 +#define AXP20X_LDO4_V_OUT_1300mV_STEPS 7 +#define AXP20X_LDO4_V_OUT_1300mV_END \ + (AXP20X_LDO4_V_OUT_1300mV_START + AXP20X_LDO4_V_OUT_1300mV_STEPS) +#define AXP20X_LDO4_V_OUT_2500mV_START 0x9 +#define AXP20X_LDO4_V_OUT_2500mV_STEPS 0 +#define AXP20X_LDO4_V_OUT_2500mV_END \ + (AXP20X_LDO4_V_OUT_2500mV_START + AXP20X_LDO4_V_OUT_2500mV_STEPS) +#define AXP20X_LDO4_V_OUT_2700mV_START 0xa +#define AXP20X_LDO4_V_OUT_2700mV_STEPS 1 +#define AXP20X_LDO4_V_OUT_2700mV_END \ + (AXP20X_LDO4_V_OUT_2700mV_START + AXP20X_LDO4_V_OUT_2700mV_STEPS) +#define AXP20X_LDO4_V_OUT_3000mV_START 0xc +#define AXP20X_LDO4_V_OUT_3000mV_STEPS 3 +#define AXP20X_LDO4_V_OUT_3000mV_END \ + (AXP20X_LDO4_V_OUT_3000mV_START + AXP20X_LDO4_V_OUT_3000mV_STEPS) +#define AXP20X_LDO4_V_OUT_NUM_VOLTAGES 16 + #define AXP22X_IO_ENABLED 0x03 #define AXP22X_IO_DISABLED 0x04 -#define AXP20X_WORKMODE_DCDC2_MASK BIT(2) -#define AXP20X_WORKMODE_DCDC3_MASK BIT(1) -#define AXP22X_WORKMODE_DCDCX_MASK(x) BIT(x) - -#define AXP20X_FREQ_DCDC_MASK 0x0f +#define AXP22X_WORKMODE_DCDCX_MASK(x) BIT_MASK(x) #define AXP22X_MISC_N_VBUSEN_FUNC BIT(4) +#define AXP22X_DCDC1_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DCDC2_V_OUT_MASK GENMASK(5, 0) +#define AXP22X_DCDC3_V_OUT_MASK GENMASK(5, 0) +#define AXP22X_DCDC4_V_OUT_MASK GENMASK(5, 0) +#define AXP22X_DCDC5_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DC5LDO_V_OUT_MASK GENMASK(2, 0) +#define AXP22X_ALDO1_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ALDO2_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ALDO3_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DLDO1_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DLDO2_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DLDO3_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DLDO4_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ELDO1_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ELDO2_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ELDO3_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_LDO_IO0_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_LDO_IO1_V_OUT_MASK GENMASK(4, 0) + +#define AXP22X_PWR_OUT_DC5LDO_MASK BIT_MASK(0) +#define AXP22X_PWR_OUT_DCDC1_MASK BIT_MASK(1) +#define AXP22X_PWR_OUT_DCDC2_MASK BIT_MASK(2) +#define AXP22X_PWR_OUT_DCDC3_MASK BIT_MASK(3) +#define AXP22X_PWR_OUT_DCDC4_MASK BIT_MASK(4) +#define AXP22X_PWR_OUT_DCDC5_MASK BIT_MASK(5) +#define AXP22X_PWR_OUT_ALDO1_MASK BIT_MASK(6) +#define AXP22X_PWR_OUT_ALDO2_MASK BIT_MASK(7) + +#define AXP22X_PWR_OUT_SW_MASK BIT_MASK(6) +#define AXP22X_PWR_OUT_DC1SW_MASK BIT_MASK(7) + +#define AXP22X_PWR_OUT_ELDO1_MASK BIT_MASK(0) +#define AXP22X_PWR_OUT_ELDO2_MASK BIT_MASK(1) +#define AXP22X_PWR_OUT_ELDO3_MASK BIT_MASK(2) +#define AXP22X_PWR_OUT_DLDO1_MASK BIT_MASK(3) +#define AXP22X_PWR_OUT_DLDO2_MASK BIT_MASK(4) +#define AXP22X_PWR_OUT_DLDO3_MASK BIT_MASK(5) +#define AXP22X_PWR_OUT_DLDO4_MASK BIT_MASK(6) +#define AXP22X_PWR_OUT_ALDO3_MASK BIT_MASK(7) + +#define AXP803_PWR_OUT_DCDC1_MASK BIT_MASK(0) +#define AXP803_PWR_OUT_DCDC2_MASK BIT_MASK(1) +#define AXP803_PWR_OUT_DCDC3_MASK BIT_MASK(2) +#define AXP803_PWR_OUT_DCDC4_MASK BIT_MASK(3) +#define AXP803_PWR_OUT_DCDC5_MASK BIT_MASK(4) +#define AXP803_PWR_OUT_DCDC6_MASK BIT_MASK(5) + +#define AXP803_PWR_OUT_FLDO1_MASK BIT_MASK(2) +#define AXP803_PWR_OUT_FLDO2_MASK BIT_MASK(3) + +#define AXP803_DCDC1_V_OUT_MASK GENMASK(4, 0) +#define AXP803_DCDC2_V_OUT_MASK GENMASK(6, 0) +#define AXP803_DCDC3_V_OUT_MASK GENMASK(6, 0) +#define AXP803_DCDC4_V_OUT_MASK GENMASK(6, 0) +#define AXP803_DCDC5_V_OUT_MASK GENMASK(6, 0) +#define AXP803_DCDC6_V_OUT_MASK GENMASK(6, 0) + +#define AXP803_FLDO1_V_OUT_MASK GENMASK(3, 0) +#define AXP803_FLDO2_V_OUT_MASK GENMASK(3, 0) + +#define AXP803_DCDC23_POLYPHASE_DUAL BIT(6) +#define AXP803_DCDC56_POLYPHASE_DUAL BIT(5) + +#define AXP803_DCDC234_500mV_START 0x00 +#define AXP803_DCDC234_500mV_STEPS 70 +#define AXP803_DCDC234_500mV_END \ + (AXP803_DCDC234_500mV_START + AXP803_DCDC234_500mV_STEPS) +#define AXP803_DCDC234_1220mV_START 0x47 +#define AXP803_DCDC234_1220mV_STEPS 4 +#define AXP803_DCDC234_1220mV_END \ + (AXP803_DCDC234_1220mV_START + AXP803_DCDC234_1220mV_STEPS) +#define AXP803_DCDC234_NUM_VOLTAGES 76 + +#define AXP803_DCDC5_800mV_START 0x00 +#define AXP803_DCDC5_800mV_STEPS 32 +#define AXP803_DCDC5_800mV_END \ + (AXP803_DCDC5_800mV_START + AXP803_DCDC5_800mV_STEPS) +#define AXP803_DCDC5_1140mV_START 0x21 +#define AXP803_DCDC5_1140mV_STEPS 35 +#define AXP803_DCDC5_1140mV_END \ + (AXP803_DCDC5_1140mV_START + AXP803_DCDC5_1140mV_STEPS) +#define AXP803_DCDC5_NUM_VOLTAGES 68 + +#define AXP803_DCDC6_600mV_START 0x00 +#define AXP803_DCDC6_600mV_STEPS 50 +#define AXP803_DCDC6_600mV_END \ + (AXP803_DCDC6_600mV_START + AXP803_DCDC6_600mV_STEPS) +#define AXP803_DCDC6_1120mV_START 0x33 +#define AXP803_DCDC6_1120mV_STEPS 14 +#define AXP803_DCDC6_1120mV_END \ + (AXP803_DCDC6_1120mV_START + AXP803_DCDC6_1120mV_STEPS) +#define AXP803_DCDC6_NUM_VOLTAGES 72 + +#define AXP803_DLDO2_700mV_START 0x00 +#define AXP803_DLDO2_700mV_STEPS 26 +#define AXP803_DLDO2_700mV_END \ + (AXP803_DLDO2_700mV_START + AXP803_DLDO2_700mV_STEPS) +#define AXP803_DLDO2_3400mV_START 0x1b +#define AXP803_DLDO2_3400mV_STEPS 4 +#define AXP803_DLDO2_3400mV_END \ + (AXP803_DLDO2_3400mV_START + AXP803_DLDO2_3400mV_STEPS) +#define AXP803_DLDO2_NUM_VOLTAGES 32 + +#define AXP806_DCDCA_V_CTRL_MASK GENMASK(6, 0) +#define AXP806_DCDCB_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_DCDCC_V_CTRL_MASK GENMASK(6, 0) +#define AXP806_DCDCD_V_CTRL_MASK GENMASK(5, 0) +#define AXP806_DCDCE_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_ALDO1_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_ALDO2_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_ALDO3_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_BLDO1_V_CTRL_MASK GENMASK(3, 0) +#define AXP806_BLDO2_V_CTRL_MASK GENMASK(3, 0) +#define AXP806_BLDO3_V_CTRL_MASK GENMASK(3, 0) +#define AXP806_BLDO4_V_CTRL_MASK GENMASK(3, 0) +#define AXP806_CLDO1_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_CLDO2_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_CLDO3_V_CTRL_MASK GENMASK(4, 0) + +#define AXP806_PWR_OUT_DCDCA_MASK BIT_MASK(0) +#define AXP806_PWR_OUT_DCDCB_MASK BIT_MASK(1) +#define AXP806_PWR_OUT_DCDCC_MASK BIT_MASK(2) +#define AXP806_PWR_OUT_DCDCD_MASK BIT_MASK(3) +#define AXP806_PWR_OUT_DCDCE_MASK BIT_MASK(4) +#define AXP806_PWR_OUT_ALDO1_MASK BIT_MASK(5) +#define AXP806_PWR_OUT_ALDO2_MASK BIT_MASK(6) +#define AXP806_PWR_OUT_ALDO3_MASK BIT_MASK(7) +#define AXP806_PWR_OUT_BLDO1_MASK BIT_MASK(0) +#define AXP806_PWR_OUT_BLDO2_MASK BIT_MASK(1) +#define AXP806_PWR_OUT_BLDO3_MASK BIT_MASK(2) +#define AXP806_PWR_OUT_BLDO4_MASK BIT_MASK(3) +#define AXP806_PWR_OUT_CLDO1_MASK BIT_MASK(4) +#define AXP806_PWR_OUT_CLDO2_MASK BIT_MASK(5) +#define AXP806_PWR_OUT_CLDO3_MASK BIT_MASK(6) +#define AXP806_PWR_OUT_SW_MASK BIT_MASK(7) + +#define AXP806_DCDCAB_POLYPHASE_DUAL 0x40 +#define AXP806_DCDCABC_POLYPHASE_TRI 0x80 +#define AXP806_DCDCABC_POLYPHASE_MASK GENMASK(7, 6) + +#define AXP806_DCDCDE_POLYPHASE_DUAL BIT(5) + +#define AXP806_DCDCA_600mV_START 0x00 +#define AXP806_DCDCA_600mV_STEPS 50 +#define AXP806_DCDCA_600mV_END \ + (AXP806_DCDCA_600mV_START + AXP806_DCDCA_600mV_STEPS) +#define AXP806_DCDCA_1120mV_START 0x33 +#define AXP806_DCDCA_1120mV_STEPS 14 +#define AXP806_DCDCA_1120mV_END \ + (AXP806_DCDCA_1120mV_START + AXP806_DCDCA_1120mV_STEPS) +#define AXP806_DCDCA_NUM_VOLTAGES 72 + +#define AXP806_DCDCD_600mV_START 0x00 +#define AXP806_DCDCD_600mV_STEPS 45 +#define AXP806_DCDCD_600mV_END \ + (AXP806_DCDCD_600mV_START + AXP806_DCDCD_600mV_STEPS) +#define AXP806_DCDCD_1600mV_START 0x2e +#define AXP806_DCDCD_1600mV_STEPS 17 +#define AXP806_DCDCD_1600mV_END \ + (AXP806_DCDCD_1600mV_START + AXP806_DCDCD_1600mV_STEPS) +#define AXP806_DCDCD_NUM_VOLTAGES 64 + +#define AXP809_DCDC4_600mV_START 0x00 +#define AXP809_DCDC4_600mV_STEPS 47 +#define AXP809_DCDC4_600mV_END \ + (AXP809_DCDC4_600mV_START + AXP809_DCDC4_600mV_STEPS) +#define AXP809_DCDC4_1800mV_START 0x30 +#define AXP809_DCDC4_1800mV_STEPS 8 +#define AXP809_DCDC4_1800mV_END \ + (AXP809_DCDC4_1800mV_START + AXP809_DCDC4_1800mV_STEPS) +#define AXP809_DCDC4_NUM_VOLTAGES 57 + #define AXP_DESC_IO(_family, _id, _match, _supply, _min, _max, _step, _vreg, \ _vmask, _ereg, _emask, _enable_val, _disable_val) \ [_family##_##_id] = { \ @@ -128,6 +355,133 @@ .ops = &axp20x_ops_range, \ } +static const int axp209_dcdc2_ldo3_slew_rates[] = { + 1600, + 800, +}; + +static int axp20x_set_ramp_delay(struct regulator_dev *rdev, int ramp) +{ + struct axp20x_dev *axp20x = rdev_get_drvdata(rdev); + const struct regulator_desc *desc = rdev->desc; + u8 reg, mask, enable, cfg = 0xff; + const int *slew_rates; + int slew_rates_cnt = 0; + + if (!rdev) + return -EINVAL; + + switch (axp20x->variant) { + case AXP209_ID: + if (desc->id == AXP20X_DCDC2) { + slew_rates_cnt = ARRAY_SIZE(axp209_dcdc2_ldo3_slew_rates); + reg = AXP20X_DCDC2_LDO3_V_RAMP; + mask = AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE_MASK | + AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN_MASK; + enable = (ramp > 0) ? + AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN : + !AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN; + break; + } + + if (desc->id == AXP20X_LDO3) { + slew_rates = axp209_dcdc2_ldo3_slew_rates; + slew_rates = axp209_dcdc2_ldo3_slew_rates; + slew_rates_cnt = ARRAY_SIZE(axp209_dcdc2_ldo3_slew_rates); + reg = AXP20X_DCDC2_LDO3_V_RAMP; + mask = AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE_MASK | + AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN_MASK; + enable = (ramp > 0) ? + AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN : + !AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN; + break; + } + + if (slew_rates_cnt > 0) + break; + + /* fall through */ + default: + /* Not supported for this regulator */ + return -ENOTSUPP; + } + + if (ramp == 0) { + cfg = enable; + } else { + int i; + + for (i = 0; i < slew_rates_cnt; i++) { + if (ramp <= slew_rates[i]) + cfg = AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE(i); + else + break; + } + + if (cfg == 0xff) { + dev_err(axp20x->dev, "unsupported ramp value %d", ramp); + return -EINVAL; + } + + cfg |= enable; + } + + return regmap_update_bits(axp20x->regmap, reg, mask, cfg); +} + +static int axp20x_regulator_enable_regmap(struct regulator_dev *rdev) +{ + struct axp20x_dev *axp20x = rdev_get_drvdata(rdev); + const struct regulator_desc *desc = rdev->desc; + + if (!rdev) + return -EINVAL; + + switch (axp20x->variant) { + case AXP209_ID: + if ((desc->id == AXP20X_LDO3) && + rdev->constraints && rdev->constraints->soft_start) { + int v_out; + int ret; + + /* + * On some boards, the LDO3 can be overloaded when + * turning on, causing the entire PMIC to shutdown + * without warning. Turning it on at the minimal voltage + * and then setting the voltage to the requested value + * works reliably. + */ + if (regulator_is_enabled_regmap(rdev)) + break; + + v_out = regulator_get_voltage_sel_regmap(rdev); + if (v_out < 0) + return v_out; + + if (v_out == 0) + break; + + ret = regulator_set_voltage_sel_regmap(rdev, 0x00); + /* + * A small pause is needed between + * setting the voltage and enabling the LDO to give the + * internal state machine time to process the request. + */ + usleep_range(1000, 5000); + ret |= regulator_enable_regmap(rdev); + ret |= regulator_set_voltage_sel_regmap(rdev, v_out); + + return ret; + } + break; + default: + /* No quirks */ + break; + } + + return regulator_enable_regmap(rdev); +}; + static const struct regulator_ops axp20x_ops_fixed = { .list_voltage = regulator_list_voltage_linear, }; @@ -145,9 +499,10 @@ static const struct regulator_ops axp20x_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, - .enable = regulator_enable_regmap, + .enable = axp20x_regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, + .set_ramp_delay = axp20x_set_ramp_delay, }; static const struct regulator_ops axp20x_ops_sw = { @@ -157,77 +512,116 @@ static const struct regulator_ops axp20x_ops_sw = { }; static const struct regulator_linear_range axp20x_ldo4_ranges[] = { - REGULATOR_LINEAR_RANGE(1250000, 0x0, 0x0, 0), - REGULATOR_LINEAR_RANGE(1300000, 0x1, 0x8, 100000), - REGULATOR_LINEAR_RANGE(2500000, 0x9, 0x9, 0), - REGULATOR_LINEAR_RANGE(2700000, 0xa, 0xb, 100000), - REGULATOR_LINEAR_RANGE(3000000, 0xc, 0xf, 100000), + REGULATOR_LINEAR_RANGE(1250000, + AXP20X_LDO4_V_OUT_1250mV_START, + AXP20X_LDO4_V_OUT_1250mV_END, + 0), + REGULATOR_LINEAR_RANGE(1300000, + AXP20X_LDO4_V_OUT_1300mV_START, + AXP20X_LDO4_V_OUT_1300mV_END, + 100000), + REGULATOR_LINEAR_RANGE(2500000, + AXP20X_LDO4_V_OUT_2500mV_START, + AXP20X_LDO4_V_OUT_2500mV_END, + 0), + REGULATOR_LINEAR_RANGE(2700000, + AXP20X_LDO4_V_OUT_2700mV_START, + AXP20X_LDO4_V_OUT_2700mV_END, + 100000), + REGULATOR_LINEAR_RANGE(3000000, + AXP20X_LDO4_V_OUT_3000mV_START, + AXP20X_LDO4_V_OUT_3000mV_END, + 100000), }; static const struct regulator_desc axp20x_regulators[] = { AXP_DESC(AXP20X, DCDC2, "dcdc2", "vin2", 700, 2275, 25, - AXP20X_DCDC2_V_OUT, 0x3f, AXP20X_PWR_OUT_CTRL, 0x10), + AXP20X_DCDC2_V_OUT, AXP20X_DCDC2_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_DCDC2_MASK), AXP_DESC(AXP20X, DCDC3, "dcdc3", "vin3", 700, 3500, 25, - AXP20X_DCDC3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x02), + AXP20X_DCDC3_V_OUT, AXP20X_DCDC3_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_DCDC3_MASK), AXP_DESC_FIXED(AXP20X, LDO1, "ldo1", "acin", 1300), AXP_DESC(AXP20X, LDO2, "ldo2", "ldo24in", 1800, 3300, 100, - AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04), + AXP20X_LDO24_V_OUT, AXP20X_LDO24_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_LDO2_MASK), AXP_DESC(AXP20X, LDO3, "ldo3", "ldo3in", 700, 3500, 25, - AXP20X_LDO3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x40), - AXP_DESC_RANGES(AXP20X, LDO4, "ldo4", "ldo24in", axp20x_ldo4_ranges, - 16, AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, - 0x08), + AXP20X_LDO3_V_OUT, AXP20X_LDO3_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_LDO3_MASK), + AXP_DESC_RANGES(AXP20X, LDO4, "ldo4", "ldo24in", + axp20x_ldo4_ranges, AXP20X_LDO4_V_OUT_NUM_VOLTAGES, + AXP20X_LDO24_V_OUT, AXP20X_LDO24_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_LDO4_MASK), AXP_DESC_IO(AXP20X, LDO5, "ldo5", "ldo5in", 1800, 3300, 100, - AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07, + AXP20X_LDO5_V_OUT, AXP20X_LDO5_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP20X_IO_ENABLED, AXP20X_IO_DISABLED), }; static const struct regulator_desc axp22x_regulators[] = { AXP_DESC(AXP22X, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, - AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)), + AXP22X_DCDC1_V_OUT, AXP22X_DCDC1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC1_MASK), AXP_DESC(AXP22X, DCDC2, "dcdc2", "vin2", 600, 1540, 20, - AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)), + AXP22X_DCDC2_V_OUT, AXP22X_DCDC2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC2_MASK), AXP_DESC(AXP22X, DCDC3, "dcdc3", "vin3", 600, 1860, 20, - AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)), + AXP22X_DCDC3_V_OUT, AXP22X_DCDC3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC3_MASK), AXP_DESC(AXP22X, DCDC4, "dcdc4", "vin4", 600, 1540, 20, - AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(4)), + AXP22X_DCDC4_V_OUT, AXP22X_DCDC4_V_OUT, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC4_MASK), AXP_DESC(AXP22X, DCDC5, "dcdc5", "vin5", 1000, 2550, 50, - AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(5)), + AXP22X_DCDC5_V_OUT, AXP22X_DCDC5_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC5_MASK), /* secondary switchable output of DCDC1 */ - AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2, - BIT(7)), + AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", NULL, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK), /* LDO regulator internally chained to DCDC5 */ AXP_DESC(AXP22X, DC5LDO, "dc5ldo", NULL, 700, 1400, 100, - AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)), + AXP22X_DC5LDO_V_OUT, AXP22X_DC5LDO_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DC5LDO_MASK), AXP_DESC(AXP22X, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)), + AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP22X, ALDO2, "aldo2", "aldoin", 700, 3300, 100, - AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)), + AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP22X, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)), + AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP22X_PWR_OUT_ALDO3_MASK), AXP_DESC(AXP22X, DLDO1, "dldo1", "dldoin", 700, 3300, 100, - AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)), + AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), AXP_DESC(AXP22X, DLDO2, "dldo2", "dldoin", 700, 3300, 100, - AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)), + AXP22X_DLDO2_V_OUT, AXP22X_PWR_OUT_DLDO2_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP22X, DLDO3, "dldo3", "dldoin", 700, 3300, 100, - AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), + AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO3_MASK), AXP_DESC(AXP22X, DLDO4, "dldo4", "dldoin", 700, 3300, 100, - AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)), + AXP22X_DLDO4_V_OUT, AXP22X_DLDO4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO4_MASK), AXP_DESC(AXP22X, ELDO1, "eldo1", "eldoin", 700, 3300, 100, - AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)), + AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP22X, ELDO2, "eldo2", "eldoin", 700, 3300, 100, - AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), + AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP22X, ELDO3, "eldo3", "eldoin", 700, 3300, 100, - AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), + AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK), /* Note the datasheet only guarantees reliable operation up to * 3.3V, this needs to be enforced via dts provided constraints */ AXP_DESC_IO(AXP22X, LDO_IO0, "ldo_io0", "ips", 700, 3800, 100, - AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, + AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), /* Note the datasheet only guarantees reliable operation up to * 3.3V, this needs to be enforced via dts provided constraints */ AXP_DESC_IO(AXP22X, LDO_IO1, "ldo_io1", "ips", 700, 3800, 100, - AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, + AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK, + AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_FIXED(AXP22X, RTC_LDO, "rtc_ldo", "ips", 3000), }; @@ -240,190 +634,285 @@ static const struct regulator_desc axp22x_drivevbus_regulator = { .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .enable_reg = AXP20X_VBUS_IPSOUT_MGMT, - .enable_mask = BIT(2), + .enable_mask = AXP20X_VBUS_IPSOUT_MGMT_MASK, .ops = &axp20x_ops_sw, }; static const struct regulator_linear_range axp803_dcdc234_ranges[] = { - REGULATOR_LINEAR_RANGE(500000, 0x0, 0x46, 10000), - REGULATOR_LINEAR_RANGE(1220000, 0x47, 0x4b, 20000), + REGULATOR_LINEAR_RANGE(500000, + AXP803_DCDC234_500mV_START, + AXP803_DCDC234_500mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1220000, + AXP803_DCDC234_1220mV_START, + AXP803_DCDC234_1220mV_END, + 20000), }; static const struct regulator_linear_range axp803_dcdc5_ranges[] = { - REGULATOR_LINEAR_RANGE(800000, 0x0, 0x20, 10000), - REGULATOR_LINEAR_RANGE(1140000, 0x21, 0x44, 20000), + REGULATOR_LINEAR_RANGE(800000, + AXP803_DCDC5_800mV_START, + AXP803_DCDC5_800mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1140000, + AXP803_DCDC5_1140mV_START, + AXP803_DCDC5_1140mV_END, + 20000), }; static const struct regulator_linear_range axp803_dcdc6_ranges[] = { - REGULATOR_LINEAR_RANGE(600000, 0x0, 0x32, 10000), - REGULATOR_LINEAR_RANGE(1120000, 0x33, 0x47, 20000), + REGULATOR_LINEAR_RANGE(600000, + AXP803_DCDC6_600mV_START, + AXP803_DCDC6_600mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1120000, + AXP803_DCDC6_1120mV_START, + AXP803_DCDC6_1120mV_END, + 20000), }; -/* AXP806's CLDO2 and AXP809's DLDO1 shares the same range */ +/* AXP806's CLDO2 and AXP809's DLDO1 share the same range */ static const struct regulator_linear_range axp803_dldo2_ranges[] = { - REGULATOR_LINEAR_RANGE(700000, 0x0, 0x1a, 100000), - REGULATOR_LINEAR_RANGE(3400000, 0x1b, 0x1f, 200000), + REGULATOR_LINEAR_RANGE(700000, + AXP803_DLDO2_700mV_START, + AXP803_DLDO2_700mV_END, + 100000), + REGULATOR_LINEAR_RANGE(3400000, + AXP803_DLDO2_3400mV_START, + AXP803_DLDO2_3400mV_END, + 200000), }; static const struct regulator_desc axp803_regulators[] = { AXP_DESC(AXP803, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, - AXP803_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(0)), - AXP_DESC_RANGES(AXP803, DCDC2, "dcdc2", "vin2", axp803_dcdc234_ranges, - 76, AXP803_DCDC2_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(1)), - AXP_DESC_RANGES(AXP803, DCDC3, "dcdc3", "vin3", axp803_dcdc234_ranges, - 76, AXP803_DCDC3_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(2)), - AXP_DESC_RANGES(AXP803, DCDC4, "dcdc4", "vin4", axp803_dcdc234_ranges, - 76, AXP803_DCDC4_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(3)), - AXP_DESC_RANGES(AXP803, DCDC5, "dcdc5", "vin5", axp803_dcdc5_ranges, - 68, AXP803_DCDC5_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(4)), - AXP_DESC_RANGES(AXP803, DCDC6, "dcdc6", "vin6", axp803_dcdc6_ranges, - 72, AXP803_DCDC6_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(5)), + AXP803_DCDC1_V_OUT, AXP803_DCDC1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC1_MASK), + AXP_DESC_RANGES(AXP803, DCDC2, "dcdc2", "vin2", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC2_V_OUT, AXP803_DCDC2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC2_MASK), + AXP_DESC_RANGES(AXP803, DCDC3, "dcdc3", "vin3", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC3_V_OUT, AXP803_DCDC3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC3_MASK), + AXP_DESC_RANGES(AXP803, DCDC4, "dcdc4", "vin4", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC4_V_OUT, AXP803_DCDC4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC4_MASK), + AXP_DESC_RANGES(AXP803, DCDC5, "dcdc5", "vin5", + axp803_dcdc5_ranges, AXP803_DCDC5_NUM_VOLTAGES, + AXP803_DCDC5_V_OUT, AXP803_DCDC5_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC5_MASK), + AXP_DESC_RANGES(AXP803, DCDC6, "dcdc6", "vin6", + axp803_dcdc6_ranges, AXP803_DCDC6_NUM_VOLTAGES, + AXP803_DCDC6_V_OUT, AXP803_DCDC6_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC6_MASK), /* secondary switchable output of DCDC1 */ - AXP_DESC_SW(AXP803, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2, - BIT(7)), + AXP_DESC_SW(AXP803, DC1SW, "dc1sw", NULL, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK), AXP_DESC(AXP803, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(5)), + AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP803, ALDO2, "aldo2", "aldoin", 700, 3300, 100, - AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(6)), + AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP803, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)), + AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO3_MASK), AXP_DESC(AXP803, DLDO1, "dldo1", "dldoin", 700, 3300, 100, - AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)), - AXP_DESC_RANGES(AXP803, DLDO2, "dldo2", "dldoin", axp803_dldo2_ranges, - 32, AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, - BIT(4)), + AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), + AXP_DESC_RANGES(AXP803, DLDO2, "dldo2", "dldoin", + axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES, + AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP803, DLDO3, "dldo3", "dldoin", 700, 3300, 100, - AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), + AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO3_MASK), AXP_DESC(AXP803, DLDO4, "dldo4", "dldoin", 700, 3300, 100, - AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)), + AXP22X_DLDO4_V_OUT, AXP22X_DLDO4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO4_MASK), AXP_DESC(AXP803, ELDO1, "eldo1", "eldoin", 700, 1900, 50, - AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)), + AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP803, ELDO2, "eldo2", "eldoin", 700, 1900, 50, - AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), + AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO2_MASK), AXP_DESC(AXP803, ELDO3, "eldo3", "eldoin", 700, 1900, 50, - AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), + AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK), AXP_DESC(AXP803, FLDO1, "fldo1", "fldoin", 700, 1450, 50, - AXP803_FLDO1_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(2)), + AXP803_FLDO1_V_OUT, AXP803_FLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO1_MASK), AXP_DESC(AXP803, FLDO2, "fldo2", "fldoin", 700, 1450, 50, - AXP803_FLDO2_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(3)), + AXP803_FLDO2_V_OUT, AXP803_FLDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO2_MASK), AXP_DESC_IO(AXP803, LDO_IO0, "ldo-io0", "ips", 700, 3300, 100, - AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, + AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_IO(AXP803, LDO_IO1, "ldo-io1", "ips", 700, 3300, 100, - AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, + AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK, + AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_FIXED(AXP803, RTC_LDO, "rtc-ldo", "ips", 3000), }; static const struct regulator_linear_range axp806_dcdca_ranges[] = { - REGULATOR_LINEAR_RANGE(600000, 0x0, 0x32, 10000), - REGULATOR_LINEAR_RANGE(1120000, 0x33, 0x47, 20000), + REGULATOR_LINEAR_RANGE(600000, + AXP806_DCDCA_600mV_START, + AXP806_DCDCA_600mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1120000, + AXP806_DCDCA_1120mV_START, + AXP806_DCDCA_1120mV_END, + 20000), }; static const struct regulator_linear_range axp806_dcdcd_ranges[] = { - REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2d, 20000), - REGULATOR_LINEAR_RANGE(1600000, 0x2e, 0x3f, 100000), + REGULATOR_LINEAR_RANGE(600000, + AXP806_DCDCD_600mV_START, + AXP806_DCDCD_600mV_END, + 20000), + REGULATOR_LINEAR_RANGE(1600000, + AXP806_DCDCD_600mV_START, + AXP806_DCDCD_600mV_END, + 100000), }; static const struct regulator_desc axp806_regulators[] = { - AXP_DESC_RANGES(AXP806, DCDCA, "dcdca", "vina", axp806_dcdca_ranges, - 72, AXP806_DCDCA_V_CTRL, 0x7f, AXP806_PWR_OUT_CTRL1, - BIT(0)), + AXP_DESC_RANGES(AXP806, DCDCA, "dcdca", "vina", + axp806_dcdca_ranges, AXP806_DCDCA_NUM_VOLTAGES, + AXP806_DCDCA_V_CTRL, AXP806_DCDCA_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCA_MASK), AXP_DESC(AXP806, DCDCB, "dcdcb", "vinb", 1000, 2550, 50, - AXP806_DCDCB_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(1)), - AXP_DESC_RANGES(AXP806, DCDCC, "dcdcc", "vinc", axp806_dcdca_ranges, - 72, AXP806_DCDCC_V_CTRL, 0x7f, AXP806_PWR_OUT_CTRL1, - BIT(2)), - AXP_DESC_RANGES(AXP806, DCDCD, "dcdcd", "vind", axp806_dcdcd_ranges, - 64, AXP806_DCDCD_V_CTRL, 0x3f, AXP806_PWR_OUT_CTRL1, - BIT(3)), + AXP806_DCDCB_V_CTRL, AXP806_DCDCB_V_CTRL, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCB_MASK), + AXP_DESC_RANGES(AXP806, DCDCC, "dcdcc", "vinc", + axp806_dcdca_ranges, AXP806_DCDCA_NUM_VOLTAGES, + AXP806_DCDCC_V_CTRL, AXP806_DCDCC_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCC_MASK), + AXP_DESC_RANGES(AXP806, DCDCD, "dcdcd", "vind", + axp806_dcdcd_ranges, AXP806_DCDCD_NUM_VOLTAGES, + AXP806_DCDCD_V_CTRL, AXP806_DCDCD_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCD_MASK), AXP_DESC(AXP806, DCDCE, "dcdce", "vine", 1100, 3400, 100, - AXP806_DCDCE_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(4)), + AXP806_DCDCE_V_CTRL, AXP806_DCDCE_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCE_MASK), AXP_DESC(AXP806, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP806_ALDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(5)), + AXP806_ALDO1_V_CTRL, AXP806_ALDO1_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP806, ALDO2, "aldo2", "aldoin", 700, 3400, 100, - AXP806_ALDO2_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(6)), + AXP806_ALDO2_V_CTRL, AXP806_ALDO2_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP806, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP806_ALDO3_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(7)), + AXP806_ALDO3_V_CTRL, AXP806_ALDO3_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_ALDO3_MASK), AXP_DESC(AXP806, BLDO1, "bldo1", "bldoin", 700, 1900, 100, - AXP806_BLDO1_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(0)), + AXP806_BLDO1_V_CTRL, AXP806_BLDO1_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO1_MASK), AXP_DESC(AXP806, BLDO2, "bldo2", "bldoin", 700, 1900, 100, - AXP806_BLDO2_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(1)), + AXP806_BLDO2_V_CTRL, AXP806_BLDO2_V_CTRL, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO2_MASK), AXP_DESC(AXP806, BLDO3, "bldo3", "bldoin", 700, 1900, 100, - AXP806_BLDO3_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(2)), + AXP806_BLDO3_V_CTRL, AXP806_BLDO3_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO3_MASK), AXP_DESC(AXP806, BLDO4, "bldo4", "bldoin", 700, 1900, 100, - AXP806_BLDO4_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(3)), + AXP806_BLDO4_V_CTRL, AXP806_BLDO4_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO4_MASK), AXP_DESC(AXP806, CLDO1, "cldo1", "cldoin", 700, 3300, 100, - AXP806_CLDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, BIT(4)), - AXP_DESC_RANGES(AXP806, CLDO2, "cldo2", "cldoin", axp803_dldo2_ranges, - 32, AXP806_CLDO2_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, - BIT(5)), + AXP806_CLDO1_V_CTRL, AXP806_CLDO1_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_CLDO1_MASK), + AXP_DESC_RANGES(AXP806, CLDO2, "cldo2", "cldoin", + axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES, + AXP806_CLDO2_V_CTRL, AXP806_CLDO2_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_CLDO2_MASK), AXP_DESC(AXP806, CLDO3, "cldo3", "cldoin", 700, 3300, 100, - AXP806_CLDO3_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, BIT(6)), - AXP_DESC_SW(AXP806, SW, "sw", "swin", AXP806_PWR_OUT_CTRL2, BIT(7)), + AXP806_CLDO3_V_CTRL, AXP806_CLDO3_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_CLDO3_MASK), + AXP_DESC_SW(AXP806, SW, "sw", "swin", + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_SW_MASK), }; static const struct regulator_linear_range axp809_dcdc4_ranges[] = { - REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2f, 20000), - REGULATOR_LINEAR_RANGE(1800000, 0x30, 0x38, 100000), + REGULATOR_LINEAR_RANGE(600000, + AXP809_DCDC4_600mV_START, + AXP809_DCDC4_600mV_END, + 20000), + REGULATOR_LINEAR_RANGE(1800000, + AXP809_DCDC4_1800mV_START, + AXP809_DCDC4_1800mV_END, + 100000), }; static const struct regulator_desc axp809_regulators[] = { AXP_DESC(AXP809, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, - AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)), + AXP22X_DCDC1_V_OUT, AXP22X_DCDC1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC1_MASK), AXP_DESC(AXP809, DCDC2, "dcdc2", "vin2", 600, 1540, 20, - AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)), + AXP22X_DCDC2_V_OUT, AXP22X_DCDC2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC2_MASK), AXP_DESC(AXP809, DCDC3, "dcdc3", "vin3", 600, 1860, 20, - AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)), - AXP_DESC_RANGES(AXP809, DCDC4, "dcdc4", "vin4", axp809_dcdc4_ranges, - 57, AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, - BIT(4)), + AXP22X_DCDC3_V_OUT, AXP22X_DCDC3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC3_MASK), + AXP_DESC_RANGES(AXP809, DCDC4, "dcdc4", "vin4", + axp809_dcdc4_ranges, AXP809_DCDC4_NUM_VOLTAGES, + AXP22X_DCDC4_V_OUT, AXP22X_DCDC4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC4_MASK), AXP_DESC(AXP809, DCDC5, "dcdc5", "vin5", 1000, 2550, 50, - AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(5)), + AXP22X_DCDC5_V_OUT, AXP22X_DCDC5_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC5_MASK), /* secondary switchable output of DCDC1 */ - AXP_DESC_SW(AXP809, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2, - BIT(7)), + AXP_DESC_SW(AXP809, DC1SW, "dc1sw", NULL, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK), /* LDO regulator internally chained to DCDC5 */ AXP_DESC(AXP809, DC5LDO, "dc5ldo", NULL, 700, 1400, 100, - AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)), + AXP22X_DC5LDO_V_OUT, AXP22X_DC5LDO_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DC5LDO_MASK), AXP_DESC(AXP809, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)), + AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP809, ALDO2, "aldo2", "aldoin", 700, 3300, 100, - AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)), + AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP809, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), - AXP_DESC_RANGES(AXP809, DLDO1, "dldo1", "dldoin", axp803_dldo2_ranges, - 32, AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, - BIT(3)), + AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ALDO3_MASK), + AXP_DESC_RANGES(AXP809, DLDO1, "dldo1", "dldoin", + axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES, + AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), AXP_DESC(AXP809, DLDO2, "dldo2", "dldoin", 700, 3300, 100, - AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)), + AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP809, ELDO1, "eldo1", "eldoin", 700, 3300, 100, - AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)), + AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP809, ELDO2, "eldo2", "eldoin", 700, 3300, 100, - AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), + AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO2_MASK), AXP_DESC(AXP809, ELDO3, "eldo3", "eldoin", 700, 3300, 100, - AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), + AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK), /* * Note the datasheet only guarantees reliable operation up to * 3.3V, this needs to be enforced via dts provided constraints */ AXP_DESC_IO(AXP809, LDO_IO0, "ldo_io0", "ips", 700, 3800, 100, - AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, + AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), /* * Note the datasheet only guarantees reliable operation up to * 3.3V, this needs to be enforced via dts provided constraints */ AXP_DESC_IO(AXP809, LDO_IO1, "ldo_io1", "ips", 700, 3800, 100, - AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, + AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK, + AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_FIXED(AXP809, RTC_LDO, "rtc_ldo", "ips", 1800), - AXP_DESC_SW(AXP809, SW, "sw", "swin", AXP22X_PWR_OUT_CTRL2, BIT(6)), + AXP_DESC_SW(AXP809, SW, "sw", "swin", + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_SW_MASK), }; static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) @@ -588,9 +1077,9 @@ static bool axp20x_is_polyphase_slave(struct axp20x_dev *axp20x, int id) switch (id) { case AXP803_DCDC3: - return !!(reg & BIT(6)); + return !!(reg & AXP803_DCDC23_POLYPHASE_DUAL); case AXP803_DCDC6: - return !!(reg & BIT(5)); + return !!(reg & AXP803_DCDC56_POLYPHASE_DUAL); } break; @@ -599,12 +1088,15 @@ static bool axp20x_is_polyphase_slave(struct axp20x_dev *axp20x, int id) switch (id) { case AXP806_DCDCB: - return (((reg & GENMASK(7, 6)) == BIT(6)) || - ((reg & GENMASK(7, 6)) == BIT(7))); + return (((reg & AXP806_DCDCABC_POLYPHASE_MASK) == + AXP806_DCDCAB_POLYPHASE_DUAL) || + ((reg & AXP806_DCDCABC_POLYPHASE_MASK) == + AXP806_DCDCABC_POLYPHASE_TRI)); case AXP806_DCDCC: - return ((reg & GENMASK(7, 6)) == BIT(7)); + return ((reg & AXP806_DCDCABC_POLYPHASE_MASK) == + AXP806_DCDCABC_POLYPHASE_TRI); case AXP806_DCDCE: - return !!(reg & BIT(5)); + return !!(reg & AXP806_DCDCDE_POLYPHASE_DUAL); } break; diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index b64b7916507f28..13466d07a7f896 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1088,17 +1088,6 @@ static int set_machine_constraints(struct regulator_dev *rdev, } } - /* If the constraints say the regulator should be on at this point - * and we have control then make sure it is enabled. - */ - if (rdev->constraints->always_on || rdev->constraints->boot_on) { - ret = _regulator_do_enable(rdev); - if (ret < 0 && ret != -EINVAL) { - rdev_err(rdev, "failed to enable\n"); - return ret; - } - } - if ((rdev->constraints->ramp_delay || rdev->constraints->ramp_disable) && ops->set_ramp_delay) { ret = ops->set_ramp_delay(rdev, rdev->constraints->ramp_delay); @@ -1144,6 +1133,17 @@ static int set_machine_constraints(struct regulator_dev *rdev, } } + /* If the constraints say the regulator should be on at this point + * and we have control then make sure it is enabled. + */ + if (rdev->constraints->always_on || rdev->constraints->boot_on) { + ret = _regulator_do_enable(rdev); + if (ret < 0 && ret != -EINVAL) { + rdev_err(rdev, "failed to enable\n"); + return ret; + } + } + print_constraints(rdev); return 0; } diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c index 3d2216ccd860c6..d941c66138df0d 100644 --- a/drivers/rtc/rtc-sun6i.c +++ b/drivers/rtc/rtc-sun6i.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index b2bdc35f74955f..69e5a5307388a0 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -115,6 +115,17 @@ static inline int serial_in(struct uart_8250_port *up, int offset) return up->port.serial_in(&up->port, offset); } +/** + * serial_in_iir - Helper function to obtain the interrupt ID from the + * Interrupt Identification Register. + * @up: UART port to get the interrupt ID from + * @mask: Additional mask to filter against + */ +static inline int serial_in_iir(struct uart_8250_port *up, int mask) +{ + return serial_in(up, UART_IIR) & (UART_IIR_MASK | mask); +} + static inline void serial_out(struct uart_8250_port *up, int offset, int value) { up->port.serial_out(&up->port, offset, value); diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index d29b512a7d9fac..1b18d929e19b25 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -18,30 +18,33 @@ * (at your option) any later version. */ -#include -#include -#include -#include #include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include +#include #include -#include #include #include -#include -#include +#include #include -#include -#include -#include +#include +#include #ifdef CONFIG_SPARC #include #endif - -#include +#include +#include +#include +#include #include "8250.h" @@ -137,7 +140,8 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id) if (l == i->head && pass_counter++ > PASS_LIMIT) { /* If we hit this, we're dead. */ printk_ratelimited(KERN_ERR - "serial8250: too much work for irq%d\n", irq); + "serial8250: too much work for irq%d (iir: 0x%02x)\n", + irq, serial_port_in(port, UART_IIR)); break; } } while (l != end); @@ -287,7 +291,7 @@ static void serial8250_backup_timeout(unsigned long data) serial_out(up, UART_IER, 0); } - iir = serial_in(up, UART_IIR); + iir = serial_in_iir(up, 0); /* * This should be a safe test for anyone who doesn't trust the @@ -297,14 +301,15 @@ static void serial8250_backup_timeout(unsigned long data) */ lsr = serial_in(up, UART_LSR); up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; - if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) && + if ((iir == UART_IIR_NO_INT) && + (up->ier & UART_IER_THRI) && (!uart_circ_empty(&up->port.state->xmit) || up->port.x_char) && (lsr & UART_LSR_THRE)) { - iir &= ~(UART_IIR_ID | UART_IIR_NO_INT); + iir &= ~(UART_IIR_MASK); iir |= UART_IIR_THRI; } - if (!(iir & UART_IIR_NO_INT)) + if (iir != UART_IIR_NO_INT) serial8250_tx_chars(up); if (up->port.irq) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 3015789265dd05..825ff5a8808711 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -13,42 +13,48 @@ * LCR is written whilst busy. If it is, then a busy detect interrupt is * raised, the LCR needs to be rewritten and the uart status register read. */ +#include + +#include +#include +#include +#include #include #include #include -#include -#include #include #include #include #include -#include -#include -#include -#include #include - -#include +#include +#include +#include +#include +#include #include "8250.h" -/* Offsets for the DesignWare specific registers */ +/* Byte aligned offsets for the DesignWare specific registers */ #define DW_UART_USR 0x1f /* UART Status Register */ -#define DW_UART_CPR 0xf4 /* Component Parameter Register */ -#define DW_UART_UCV 0xf8 /* UART Component Version */ +#define DW_UART_CPR 0x3d /* Component Parameter Register */ +#define DW_UART_UCV 0x3e /* UART Component Version */ + +/* Offsets for the Octeon specific registers */ +#define OCTEON_UART_USR 0x27 /* UART Status Register */ /* Component Parameter Register bits */ #define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0) -#define DW_UART_CPR_AFCE_MODE (1 << 4) -#define DW_UART_CPR_THRE_MODE (1 << 5) -#define DW_UART_CPR_SIR_MODE (1 << 6) -#define DW_UART_CPR_SIR_LP_MODE (1 << 7) -#define DW_UART_CPR_ADDITIONAL_FEATURES (1 << 8) -#define DW_UART_CPR_FIFO_ACCESS (1 << 9) -#define DW_UART_CPR_FIFO_STAT (1 << 10) -#define DW_UART_CPR_SHADOW (1 << 11) -#define DW_UART_CPR_ENCODED_PARMS (1 << 12) -#define DW_UART_CPR_DMA_EXTRA (1 << 13) +#define DW_UART_CPR_AFCE_MODE BIT(4) +#define DW_UART_CPR_THRE_MODE BIT(5) +#define DW_UART_CPR_SIR_MODE BIT(6) +#define DW_UART_CPR_SIR_LP_MODE BIT(7) +#define DW_UART_CPR_ADDITIONAL_FEATURES BIT(8) +#define DW_UART_CPR_FIFO_ACCESS BIT(9) +#define DW_UART_CPR_FIFO_STAT BIT(10) +#define DW_UART_CPR_SHADOW BIT(11) +#define DW_UART_CPR_ENCODED_PARMS BIT(12) +#define DW_UART_CPR_DMA_EXTRA BIT(13) #define DW_UART_CPR_FIFO_MODE (0xff << 16) /* Helper for fifo size calculation */ #define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16) @@ -88,7 +94,7 @@ static void dw8250_force_idle(struct uart_port *p) struct uart_8250_port *up = up_to_u8250p(p); serial8250_clear_and_reinit_fifos(up); - (void)p->serial_in(p, UART_RX); + (void)serial_port_in(p, UART_RX); } static void dw8250_check_lcr(struct uart_port *p, int value) @@ -98,7 +104,7 @@ static void dw8250_check_lcr(struct uart_port *p, int value) /* Make sure LCR write wasn't ignored */ while (tries--) { - unsigned int lcr = p->serial_in(p, UART_LCR); + unsigned int lcr = serial_port_in(p, UART_LCR); if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) return; @@ -193,17 +199,16 @@ static void dw8250_serial_out32be(struct uart_port *p, int offset, int value) static unsigned int dw8250_serial_in32be(struct uart_port *p, int offset) { - unsigned int value = ioread32be(p->membase + (offset << p->regshift)); + unsigned int value = ioread32be(p->membase + (offset << p->regshift)); - return dw8250_modify_msr(p, offset, value); + return dw8250_modify_msr(p, offset, value); } - static int dw8250_handle_irq(struct uart_port *p) { struct uart_8250_port *up = up_to_u8250p(p); struct dw8250_data *d = p->private_data; - unsigned int iir = p->serial_in(p, UART_IIR); + unsigned int iir = serial_in_iir(up, 0); unsigned int status; unsigned long flags; @@ -217,12 +222,12 @@ static int dw8250_handle_irq(struct uart_port *p) * This problem has only been observed so far when not in DMA mode * so we limit the workaround only to non-DMA mode. */ - if (!up->dma && ((iir & 0x3f) == UART_IIR_RX_TIMEOUT)) { + if (!up->dma && (iir == UART_IIR_RX_TIMEOUT)) { spin_lock_irqsave(&p->lock, flags); - status = p->serial_in(p, UART_LSR); + status = serial_port_in(p, UART_LSR); if (!(status & (UART_LSR_DR | UART_LSR_BI))) - (void) p->serial_in(p, UART_RX); + (void)serial_port_in(p, UART_RX); spin_unlock_irqrestore(&p->lock, flags); } @@ -230,9 +235,9 @@ static int dw8250_handle_irq(struct uart_port *p) if (serial8250_handle_irq(p, iir)) return 1; - if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { + if (iir == UART_IIR_BUSY) { /* Clear the USR */ - (void)p->serial_in(p, d->usr_reg); + (void)serial_port_in(p, d->usr_reg); return 1; } @@ -334,7 +339,7 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) p->serial_out = dw8250_serial_outq; p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; p->type = PORT_OCTEON; - data->usr_reg = 0x27; + data->usr_reg = OCTEON_UART_USR; data->skip_autocfg = true; } #endif @@ -375,9 +380,9 @@ static void dw8250_setup_port(struct uart_port *p) * ADDITIONAL_FEATURES are not enabled. No need to go any further. */ if (p->iotype == UPIO_MEM32BE) - reg = ioread32be(p->membase + DW_UART_UCV); + reg = ioread32be(p->membase + (DW_UART_UCV << p->regshift)); else - reg = readl(p->membase + DW_UART_UCV); + reg = readl(p->membase + (DW_UART_UCV << p->regshift)); if (!reg) return; @@ -385,9 +390,9 @@ static void dw8250_setup_port(struct uart_port *p) (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); if (p->iotype == UPIO_MEM32BE) - reg = ioread32be(p->membase + DW_UART_CPR); + reg = ioread32be(p->membase + (DW_UART_CPR << p->regshift)); else - reg = readl(p->membase + DW_UART_CPR); + reg = readl(p->membase + (DW_UART_CPR << p->regshift)); if (!reg) return; diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c index 910bfee5a88b7f..29bb55db8b967b 100644 --- a/drivers/tty/serial/8250/8250_fsl.c +++ b/drivers/tty/serial/8250/8250_fsl.c @@ -32,8 +32,8 @@ int fsl8250_handle_irq(struct uart_port *port) spin_lock_irqsave(&up->port.lock, flags); - iir = port->serial_in(port, UART_IIR); - if (iir & UART_IIR_NO_INT) { + iir = serial_in_iir(up, 0); + if (iir == UART_IIR_NO_INT) { spin_unlock_irqrestore(&up->port.lock, flags); return 0; } diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index ec957cce8c9a72..28f81321aa0edf 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -102,7 +102,7 @@ static int tng_handle_irq(struct uart_port *p) ret |= hsu_dma_do_irq(chip, mid->dma_index * 2, status); /* UART */ - ret |= serial8250_handle_irq(p, serial_port_in(p, UART_IIR)); + ret |= serial8250_handle_irq(p, serial_in_iir(up, 0)); return IRQ_RETVAL(ret); } @@ -151,7 +151,7 @@ static int dnv_handle_irq(struct uart_port *p) ret |= hsu_dma_do_irq(&mid->dma_chip, 0, status); } if (fisr & BIT(0)) - ret |= serial8250_handle_irq(p, serial_port_in(p, UART_IIR)); + ret |= serial8250_handle_irq(p, serial_in_iir(up, 0)); return IRQ_RETVAL(ret); } diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 833771bca0a593..d2661c4a2ce21a 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -584,7 +584,7 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id) #endif serial8250_rpm_get(up); - iir = serial_port_in(port, UART_IIR); + iir = serial_in_iir(up, 0); ret = serial8250_handle_irq(port, iir); serial8250_rpm_put(up); @@ -1019,7 +1019,7 @@ static int omap_8250_tx_dma(struct uart_8250_port *p) static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) { - switch (iir & 0x3f) { + switch (iir & (UART_IIR_MASK | UART_IIR_EXT_MASK)) { case UART_IIR_RLSI: case UART_IIR_RX_TIMEOUT: case UART_IIR_RDI: @@ -1043,8 +1043,8 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) serial8250_rpm_get(up); - iir = serial_port_in(port, UART_IIR); - if (iir & UART_IIR_NO_INT) { + iir = serial_in_iir(up, UART_IIR_EXT_MASK); + if (iir == UART_IIR_NO_INT) { serial8250_rpm_put(up); return 0; } diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 8dcfd4978a036c..d0e1c1ea60ba52 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1836,7 +1836,7 @@ EXPORT_SYMBOL_GPL(serial8250_modem_status); static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) { - switch (iir & 0x3f) { + switch (iir & UART_IIR_MASK) { case UART_IIR_RX_TIMEOUT: serial8250_rx_dma_flush(up); /* fall-through */ @@ -1846,8 +1846,22 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) return up->dma->rx_dma(up); } -/* - * This handles the interrupt from one port. +/** + * serial8250_handle_irq - Handles the generic 8250 interrupts + * @port: UART port structure holding the IRQ generating port + * @iir: Interrupt Identification Register containing the masked + * interrupt source, such as supplied via serial_in_IIR() + * + * Handle the following generic 8250 common interrupts + * o Modem Status Interrupt + * o Transmitter holding register empty + * o Receiver Data Interrupt + * o Receiver Line Status Interrupt + * o RX Timeout Interrupt + * + * Other interrupts are expected to be handled by the caller where needed. + * + * Returns 0 when an interrupt was not handled, 1 otherwise. */ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) { @@ -1855,7 +1869,11 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) unsigned long flags; struct uart_8250_port *up = up_to_u8250p(port); - if (iir & UART_IIR_NO_INT) + if ((iir != UART_IIR_MSI) && + (iir != UART_IIR_THRI) && + (iir != UART_IIR_RDI) && + (iir != UART_IIR_RLSI) && + (iir != UART_IIR_RX_TIMEOUT)) return 0; spin_lock_irqsave(&port->lock, flags); @@ -1883,7 +1901,7 @@ static int serial8250_default_handle_irq(struct uart_port *port) serial8250_rpm_get(up); - iir = serial_port_in(port, UART_IIR); + iir = serial_in_iir(up, 0); ret = serial8250_handle_irq(port, iir); serial8250_rpm_put(up); @@ -1919,10 +1937,11 @@ static int exar_handle_irq(struct uart_port *port) static int serial8250_tx_threshold_handle_irq(struct uart_port *port) { unsigned long flags; - unsigned int iir = serial_port_in(port, UART_IIR); + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int iir = serial_in_iir(up, 0); /* TX Threshold IRQ triggered so load up FIFO */ - if ((iir & UART_IIR_ID) == UART_IIR_THRI) { + if (iir == UART_IIR_THRI) { struct uart_8250_port *up = up_to_u8250p(port); spin_lock_irqsave(&port->lock, flags); @@ -1930,7 +1949,7 @@ static int serial8250_tx_threshold_handle_irq(struct uart_port *port) spin_unlock_irqrestore(&port->lock, flags); } - iir = serial_port_in(port, UART_IIR); + iir = serial_in(up, 0); return serial8250_handle_irq(port, iir); } @@ -2274,11 +2293,11 @@ int serial8250_do_startup(struct uart_port *port) wait_for_xmitr(up, UART_LSR_THRE); serial_port_out_sync(port, UART_IER, UART_IER_THRI); udelay(1); /* allow THRE to set */ - iir1 = serial_port_in(port, UART_IIR); + iir1 = serial_in_iir(up, 0); serial_port_out(port, UART_IER, 0); serial_port_out_sync(port, UART_IER, UART_IER_THRI); udelay(1); /* allow a working UART time to re-assert THRE */ - iir = serial_port_in(port, UART_IIR); + iir = serial_in_iir(up, 0); serial_port_out(port, UART_IER, 0); if (port->irqflags & IRQF_SHARED) @@ -2290,7 +2309,7 @@ int serial8250_do_startup(struct uart_port *port) * don't trust the iir, setup a timer to kick the UART * on a regular basis. */ - if ((!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) || + if (((iir1 != UART_IIR_NO_INT) && (iir == UART_IIR_NO_INT)) || up->port.flags & UPF_BUG_THRE) { up->bugs |= UART_BUG_THRE; } @@ -2338,10 +2357,10 @@ int serial8250_do_startup(struct uart_port *port) */ serial_port_out(port, UART_IER, UART_IER_THRI); lsr = serial_port_in(port, UART_LSR); - iir = serial_port_in(port, UART_IIR); + iir = serial_in_iir(up, 0); serial_port_out(port, UART_IER, 0); - if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { + if (lsr & UART_LSR_TEMT && (iir == UART_IIR_NO_INT)) { if (!(up->bugs & UART_BUG_TXEN)) { up->bugs |= UART_BUG_TXEN; pr_debug("ttyS%d - enabling bad tx status workarounds\n", diff --git a/drivers/video/fbdev/core/Makefile b/drivers/video/fbdev/core/Makefile index d34fd182ca6806..7691cd82cbb647 100644 --- a/drivers/video/fbdev/core/Makefile +++ b/drivers/video/fbdev/core/Makefile @@ -1,32 +1,36 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_FB_CMDLINE) += fb_cmdline.o -obj-$(CONFIG_FB_NOTIFY) += fb_notify.o -obj-$(CONFIG_FB) += fb.o -fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \ - modedb.o fbcvt.o -fb-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o +obj-$(CONFIG_FB_CMDLINE) += fb_cmdline.o +obj-$(CONFIG_FB_NOTIFY) += fb_notify.o +obj-$(CONFIG_FB) += fb.o +fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \ + modedb.o fbcvt.o +fb-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o + +ifeq ($(CONFIG_OF),y) +fb-y += fb_of.o +endif ifeq ($(CONFIG_FRAMEBUFFER_CONSOLE),y) -fb-y += fbcon.o bitblit.o softcursor.o +fb-y += fbcon.o bitblit.o softcursor.o ifeq ($(CONFIG_FB_TILEBLITTING),y) -fb-y += tileblit.o +fb-y += tileblit.o endif ifeq ($(CONFIG_FRAMEBUFFER_CONSOLE_ROTATION),y) -fb-y += fbcon_rotate.o fbcon_cw.o fbcon_ud.o \ - fbcon_ccw.o +fb-y += fbcon_rotate.o fbcon_cw.o \ + fbcon_ud.o fbcon_ccw.o endif ifeq ($(CONFIG_DMI),y) fb-y += fbcon_dmi_quirks.o endif endif -fb-objs := $(fb-y) +fb-objs := $(fb-y) -obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o -obj-$(CONFIG_FB_CFB_COPYAREA) += cfbcopyarea.o -obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o -obj-$(CONFIG_FB_SYS_FILLRECT) += sysfillrect.o -obj-$(CONFIG_FB_SYS_COPYAREA) += syscopyarea.o -obj-$(CONFIG_FB_SYS_IMAGEBLIT) += sysimgblt.o -obj-$(CONFIG_FB_SYS_FOPS) += fb_sys_fops.o -obj-$(CONFIG_FB_SVGALIB) += svgalib.o -obj-$(CONFIG_FB_DDC) += fb_ddc.o +obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o +obj-$(CONFIG_FB_CFB_COPYAREA) += cfbcopyarea.o +obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o +obj-$(CONFIG_FB_SYS_FILLRECT) += sysfillrect.o +obj-$(CONFIG_FB_SYS_COPYAREA) += syscopyarea.o +obj-$(CONFIG_FB_SYS_IMAGEBLIT) += sysimgblt.o +obj-$(CONFIG_FB_SYS_FOPS) += fb_sys_fops.o +obj-$(CONFIG_FB_SVGALIB) += svgalib.o +obj-$(CONFIG_FB_DDC) += fb_ddc.o diff --git a/drivers/video/fbdev/core/fb_of.c b/drivers/video/fbdev/core/fb_of.c new file mode 100644 index 00000000000000..cb2de340d614c9 --- /dev/null +++ b/drivers/video/fbdev/core/fb_of.c @@ -0,0 +1,46 @@ +/* + * Generic DT helper functions for fbdev devices + * + * Copyright (C) 2018 Olliver Schinagl + * + * Olliver Schinagl + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#include +#include +#include +#include +#include + +void fb_parse_properties(struct device *dev, struct fb_of_properties *prop) +{ + u16 rotate; + + prop->no_clear_on_probe = device_property_read_bool(dev, "no-clear-on-probe"); + + if (device_property_read_u16(dev, "rotate", &rotate)) + rotate = FB_ROTATE_UR; + + switch (rotate) { + case 270: /* fall through */ + case 3: + prop->rotate = FB_ROTATE_CCW; + break; + case 180: /* fall through */ + case 2: + prop->rotate = FB_ROTATE_UD; + break; + case 90: /* fall through */ + case 1: + prop->rotate = FB_ROTATE_CW; + break; + case 0: /* fall through */ + default: + prop->rotate = FB_ROTATE_UR; + break; + } +} +EXPORT_SYMBOL_GPL(fb_parse_properties); diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c index 15755ce1d26c81..97178b6b9820c4 100644 --- a/drivers/video/fbdev/core/fbsysfs.c +++ b/drivers/video/fbdev/core/fbsysfs.c @@ -15,11 +15,11 @@ * are converted to use it a sysfsification will open OOPSable races. */ -#include -#include -#include #include +#include +#include #include +#include #define FB_SYSFS_FLAG_ATTR 1 @@ -244,11 +244,34 @@ static ssize_t store_rotate(struct device *device, { struct fb_info *fb_info = dev_get_drvdata(device); struct fb_var_screeninfo var; - char **last = NULL; + unsigned long rotate; int err; var = fb_info->var; - var.rotate = simple_strtoul(buf, last, 0); + + err = kstrtoul(buf, 0, &rotate); + if (err) + return err; + + switch (rotate) { + case 270: /* fall through */ + case 3: + var.rotate = FB_ROTATE_CCW; + break; + case 180: /* fall through */ + case 2: + var.rotate = FB_ROTATE_UD; + break; + case 90: /* fall through */ + case 1: + var.rotate = FB_ROTATE_CW; + break; + case 0: + var.rotate = FB_ROTATE_UR; + break; + default: + return -EINVAL; + } if ((err = activate(fb_info, &var))) return err; diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index f599520374ddf5..b599faeb7d1f3a 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -43,7 +44,7 @@ #define MAX_CONTRAST 255 -#define REFRESHRATE 1 +#define REFRESHRATE 15 static u_int refreshrate = REFRESHRATE; module_param(refreshrate, uint, 0); @@ -76,6 +77,7 @@ struct ssd1307fb_par { struct pwm_device *pwm; u32 pwm_period; struct gpio_desc *reset; + bool no_clear_on_probe; struct regulator *vbat_reg; u32 seg_remap; u32 vcomh; @@ -123,7 +125,7 @@ static int ssd1307fb_write_array(struct i2c_client *client, ret = i2c_master_send(client, (u8 *)array, len); if (ret != len) { - dev_err(&client->dev, "Couldn't send I2C command.\n"); + dev_err_ratelimited(&client->dev, "couldn't send I2C command\n"); return ret; } @@ -248,6 +250,65 @@ static int ssd1307fb_blank(int blank_mode, struct fb_info *info) return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON); } +static int ssd1307fb_set_par(struct fb_info *info) +{ + struct ssd1307fb_par *par = info->par; + int ret; + u32 com_invdir; + + /* Set segment re-map */ + if (par->seg_remap) { + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON); + if (ret < 0) + return ret; + }; + + /* Set COM direction */ + com_invdir = 0xc0 | (par->com_invdir & 0x1) << 3; + ret = ssd1307fb_write_cmd(par->client, com_invdir); + if (ret < 0) + return ret; + + /* Switch to horizontal addressing mode */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, + SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL); + if (ret < 0) + return ret; + + /* Set column range */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0x0); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, par->width - 1); + if (ret < 0) + return ret; + + /* Set page range */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0x0); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, + par->page_offset + (par->height / 8) - 1); + if (ret < 0) + return ret; + + return 0; +} + static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { struct ssd1307fb_par *par = info->par; @@ -274,6 +335,7 @@ static struct fb_ops ssd1307fb_ops = { .fb_read = fb_sys_read, .fb_write = ssd1307fb_write, .fb_blank = ssd1307fb_blank, + .fb_set_par = ssd1307fb_set_par, .fb_fillrect = ssd1307fb_fillrect, .fb_copyarea = ssd1307fb_copyarea, .fb_imageblit = ssd1307fb_imageblit, @@ -290,11 +352,13 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) int ret; u32 precharge, dclk, com_invdir, compins; struct pwm_args pargs; + char status; + struct device *dev = &par->client->dev; if (par->device_info->need_pwm) { - par->pwm = pwm_get(&par->client->dev, NULL); + par->pwm = pwm_get(dev, NULL); if (IS_ERR(par->pwm)) { - dev_err(&par->client->dev, "Could not get PWM from device tree!\n"); + dev_err(dev, "could not get PWM from device tree\n"); return PTR_ERR(par->pwm); } @@ -311,10 +375,17 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period); pwm_enable(par->pwm); - dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n", + dev_dbg(dev, "using PWM%d with a %dns period\n", par->pwm->pwm, par->pwm_period); }; + /* Check if we can talk to the display */ + ret = i2c_master_recv(par->client, &status, 1); + if (ret < 0) { + dev_err(dev, "controller not found\n"); + return ret; + } + /* Set initial contrast */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST); if (ret < 0) @@ -405,45 +476,9 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) if (ret < 0) return ret; - /* Switch to horizontal addressing mode */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE); - if (ret < 0) - return ret; - - ret = ssd1307fb_write_cmd(par->client, - SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL); - if (ret < 0) - return ret; - - /* Set column range */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE); - if (ret < 0) - return ret; - - ret = ssd1307fb_write_cmd(par->client, 0x0); - if (ret < 0) - return ret; - - ret = ssd1307fb_write_cmd(par->client, par->width - 1); - if (ret < 0) - return ret; - - /* Set page range */ - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE); - if (ret < 0) - return ret; - - ret = ssd1307fb_write_cmd(par->client, 0x0); - if (ret < 0) - return ret; - - ret = ssd1307fb_write_cmd(par->client, - par->page_offset + (par->height / 8) - 1); - if (ret < 0) - return ret; - /* Clear the screen */ - ssd1307fb_update_display(par); + if (!par->no_clear_on_probe) + ssd1307fb_update_display(par); /* Turn on the display */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON); @@ -547,6 +582,7 @@ static int ssd1307fb_probe(struct i2c_client *client, struct fb_deferred_io *ssd1307fb_defio; u32 vmem_size; struct ssd1307fb_par *par; + struct fb_of_properties prop; u8 *vmem; int ret; @@ -588,7 +624,10 @@ static int ssd1307fb_probe(struct i2c_client *client, } } - if (of_property_read_u32(node, "solomon,width", &par->width)) + fb_parse_properties(&client->dev, &prop); + par->no_clear_on_probe = prop.no_clear_on_probe; + + if (of_property_read_u32(node, "solomon,width", &par->width)) par->width = 96; if (of_property_read_u32(node, "solomon,height", &par->height)) @@ -685,6 +724,12 @@ static int ssd1307fb_probe(struct i2c_client *client, if (ret) goto regulator_enable_error; + ret = ssd1307fb_set_par(info); + if (ret) { + dev_err(&client->dev, "unable to setup parameters\n"); + goto bl_init_error; + } + ret = register_framebuffer(info); if (ret) { dev_err(&client->dev, "Couldn't register the framebuffer\n"); @@ -748,10 +793,10 @@ static int ssd1307fb_remove(struct i2c_client *client) } static const struct i2c_device_id ssd1307fb_i2c_id[] = { - { "ssd1305fb", 0 }, - { "ssd1306fb", 0 }, - { "ssd1307fb", 0 }, - { "ssd1309fb", 0 }, + { "ssd1305fb-i2c", 0 }, + { "ssd1306fb-i2c", 0 }, + { "ssd1307fb-i2c", 0 }, + { "ssd1309fb-i2c", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id); diff --git a/include/linux/fb.h b/include/linux/fb.h index bc24e48e396d0b..b62113926c5703 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -120,6 +120,13 @@ struct fb_cursor_user { struct fb_image_user image; /* Cursor image */ }; +struct fb_of_properties { + bool no_clear_on_probe; + __u8 rotate; +}; + +void fb_parse_properties(struct device *dev, struct fb_of_properties *prop); + /* * Register/unregister for framebuffer events */ diff --git a/include/linux/input/touchscreen.h b/include/linux/input/touchscreen.h index 09d22ccb9e415e..3c94403183b14b 100644 --- a/include/linux/input/touchscreen.h +++ b/include/linux/input/touchscreen.h @@ -13,7 +13,9 @@ struct input_dev; struct input_mt_pos; struct touchscreen_properties { + unsigned int min_x; unsigned int max_x; + unsigned int min_y; unsigned int max_y; bool invert_x; bool invert_y; diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index e9c908c4fba8a2..fa52c2b37dd6fc 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -35,7 +35,7 @@ enum axp20x_variants { #define AXP152_ALDO_OP_MODE 0x13 #define AXP152_LDO0_CTRL 0x15 #define AXP152_DCDC2_V_OUT 0x23 -#define AXP152_DCDC2_V_SCAL 0x25 +#define AXP152_DCDC2_V_RAMP 0x25 #define AXP152_DCDC1_V_OUT 0x26 #define AXP152_DCDC3_V_OUT 0x27 #define AXP152_ALDO12_V_OUT 0x28 @@ -53,7 +53,7 @@ enum axp20x_variants { #define AXP20X_USB_OTG_STATUS 0x02 #define AXP20X_PWR_OUT_CTRL 0x12 #define AXP20X_DCDC2_V_OUT 0x23 -#define AXP20X_DCDC2_LDO3_V_SCAL 0x25 +#define AXP20X_DCDC2_LDO3_V_RAMP 0x25 #define AXP20X_DCDC3_V_OUT 0x27 #define AXP20X_LDO24_V_OUT 0x28 #define AXP20X_LDO3_V_OUT 0x29 diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index 619fe6111dc942..e54800b22c4c52 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -32,18 +32,19 @@ #define UART_IERX_SLEEP 0x10 /* Enable sleep mode */ #define UART_IIR 2 /* In: Interrupt ID Register */ -#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ -#define UART_IIR_ID 0x0e /* Mask for the interrupt ID */ #define UART_IIR_MSI 0x00 /* Modem status interrupt */ +#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ #define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ #define UART_IIR_RDI 0x04 /* Receiver data interrupt */ #define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ - -#define UART_IIR_BUSY 0x07 /* DesignWare APB Busy Detect */ - -#define UART_IIR_RX_TIMEOUT 0x0c /* OMAP RX Timeout interrupt */ -#define UART_IIR_XOFF 0x10 /* OMAP XOFF/Special Character */ -#define UART_IIR_CTS_RTS_DSR 0x20 /* OMAP CTS/RTS/DSR Change */ +#define UART_IIR_BUSY 0x07 /* Busy Detect */ +#define UART_IIR_RX_TIMEOUT 0x0c /* RX Timeout interrupt */ +#define UART_IIR_ID 0x0e /* Legacy mask for the interrupt ID */ +#define UART_IIR_MASK 0x0f /* Generic IIR mask */ + +#define UART_IIR_XOFF 0x10 /* XOFF/Special Character */ +#define UART_IIR_CTS_RTS_DSR 0x20 /* CTS/RTS/DSR Change */ +#define UART_IIR_EXT_MASK 0x30 /* Extended IIR mask */ #define UART_FCR 2 /* Out: FIFO Control Register */ #define UART_FCR_ENABLE_FIFO 0x01 /* Enable the FIFO */ diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c index 41bd496531d45e..96f9b0c6e6288d 100644 --- a/net/rfkill/rfkill-gpio.c +++ b/net/rfkill/rfkill-gpio.c @@ -16,16 +16,19 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include +#include #include +#include #include +#include #include +#include #include -#include +#include #include -#include +#include #include -#include -#include struct rfkill_gpio_data { const char *name; @@ -165,12 +168,21 @@ static const struct acpi_device_id rfkill_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rfkill_acpi_match); #endif +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id rfkill_of_match[] = { + { .compatible = "linux,rfkill-gpio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rfkill_of_match); +#endif + static struct platform_driver rfkill_gpio_driver = { .probe = rfkill_gpio_probe, .remove = rfkill_gpio_remove, .driver = { .name = "rfkill_gpio", .acpi_match_table = ACPI_PTR(rfkill_acpi_match), + .of_match_table = of_match_ptr(rfkill_of_match), }, }; diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index b4af5ce78ecbde..0e4489dde2177d 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -1033,13 +1033,13 @@ static int sun4i_i2s_probe(struct platform_device *pdev) if (i2s->variant->has_reset) { i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(i2s->rst)) { + if (IS_ERR_OR_NULL(i2s->rst)) { dev_err(&pdev->dev, "Failed to get reset control\n"); return PTR_ERR(i2s->rst); } } - if (!IS_ERR(i2s->rst)) { + if (!IS_ERR_OR_NULL(i2s->rst)) { ret = reset_control_deassert(i2s->rst); if (ret) { dev_err(&pdev->dev, @@ -1089,7 +1089,7 @@ static int sun4i_i2s_probe(struct platform_device *pdev) sun4i_i2s_runtime_suspend(&pdev->dev); err_pm_disable: pm_runtime_disable(&pdev->dev); - if (!IS_ERR(i2s->rst)) + if (!IS_ERR_OR_NULL(i2s->rst)) reset_control_assert(i2s->rst); return ret;