diff options
author | Andrey Konovalov <andrey.konovalov@linaro.org> | 2013-09-06 23:28:00 +0400 |
---|---|---|
committer | Andrey Konovalov <andrey.konovalov@linaro.org> | 2013-09-06 23:28:00 +0400 |
commit | dadafb94ba81815cf23ca28bd59614f68b74a24e (patch) | |
tree | 64aac7cabbe7fea9680cedbabd0ff182e7fcf52e | |
parent | 0a7875e02eafe840fb4cdc009c795f2637cc7c72 (diff) | |
parent | 8c03a2759bb93416165fc842221da78494346944 (diff) |
Merge branch 'tracking-samslt-all' into merge-linux-linaroll-20130906.0
62 files changed, 4290 insertions, 692 deletions
diff --git a/Documentation/devicetree/bindings/clock/exynos5250-clock.txt b/Documentation/devicetree/bindings/clock/exynos5250-clock.txt index 781a6276adf7..4810e4f62699 100644 --- a/Documentation/devicetree/bindings/clock/exynos5250-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos5250-clock.txt @@ -24,6 +24,7 @@ clock which they consume. ---------------------------- fin_pll 1 + sclk_vpll 2 [Clock Gate for Special Clocks] @@ -59,6 +60,9 @@ clock which they consume. sclk_spi0 154 sclk_spi1 155 sclk_spi2 156 + div_i2s1 157 + div_i2s2 158 + sclk_hdmiphy 159 [Peripheral Clock Gates] @@ -154,7 +158,16 @@ clock which they consume. dsim0 341 dp 342 mixer 343 - hdmi 345 + hdmi 344 + smmu_mixer 345 + + + [Clock Muxes] + + Clock ID + ---------------------------- + mout_hdmi 1024 + Example 1: An example of a clock controller node is listed below. diff --git a/Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt b/Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt index 296eb4536129..278de8e64bbf 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt @@ -10,6 +10,8 @@ Required properties: inside HDMIPHY block found on several samsung SoCs (d) "samsung, exynos5440-i2c", for s3c2440-like i2c used on EXYNOS5440 which does not need GPIO configuration. + (e) "samsung, exynos5-sata-phy-i2c", for s3c2440-like i2c used as + a host to SATA PHY controller on an internal bus. - reg: physical base address of the controller and length of memory mapped region. - interrupts: interrupt number to the cpu. diff --git a/Documentation/devicetree/bindings/video/exynos/mipi-dsi.txt b/Documentation/devicetree/bindings/video/exynos/mipi-dsi.txt new file mode 100644 index 000000000000..7460b1ff85c8 --- /dev/null +++ b/Documentation/devicetree/bindings/video/exynos/mipi-dsi.txt @@ -0,0 +1,167 @@ +* Samsung Exynos MIPI-DSI bindings + +Properties for MIPI-DSI node :: +=============================== +- compatible: should be "samsung,exynos-mipi" + +- reg: should contain mipi-dsi physical address location and length. + +- interrupts: should contain mipi-dsi interrupt number + +- enabled: Describes whether MIPI DSI got enabled in uboot + +- mipi-lcd: phandle to lcd specific information. It can be anything + specific to lcd driver. + +- mipi-phy: phandle to D-PHY node. + +- mipi-config: subnode for mipi config information + - auto_flush: enable or disable Auto flush of MD FIFO using VSYNC pulse + - eot_disable: enable or disable EoT packet in HS mode + - auto_vertical_cnt: specifies auto vertical count mode. + In Video mode, the vertical line transition uses line counter + configured by VSA, VBP, and Vertical resolution. If this bit is + set to '1', the line counter does not use VSA and VBP registers. + In command mode, this property is ignored. + - hse: set horizontal sync event mode. + In VSYNC pulse and Vporch area, MIPI DSI master transfers only + HSYNC start packet to MIPI DSI slave at MIPI DSI spec1.1r02. + This bit transfers HSYNC end packet in VSYNC pulse and Vporch + area. In command mode, this property is ignored. + - hfp: specifies HFP disable mode. + If this property is set, DSI master ignores HFP area in + VIDEO mode. In command mode, this property is ignored. + - hbp: specifies HBP disable mode. + If this property is set, DSI master ignores HBP area in + VIDEO mode. In command mode, this property is ignored. + - hsa: specifies HSA disable mode. + If this property is set, DSI master ignores HSA area in + VIDEO mode. In command mode, this property is ignored. + - cmd_allow: specifies the number of horizontal lines, where command + packet transmission is allowed after Stable VFP period. + - e_interface: specifies interface to be used.(CPU or RGB interface) + - e_virtual_ch: specifies virtual channel number that main or + sub display uses. + - e_pixel_format: specifies pixel stream format for main or sub display. + - e_burst_mode: selects Burst mode in Video mode. + In Non-burst mode, RGB data area is filled with RGB data + and NULL packets, according to input bandwidth of RGB interface. + In Burst mode, RGB data area is filled with RGB data only. + - e_no_data_lane: specifies data lane count to be used by Master. + - e_byte_clk: select byte clock source. (it must be DSIM_PLL_OUT_DIV8) + DSIM_EXT_CLK_DIV8 and DSIM_EXT_CLK_BYPASSS are not supported. + - pll_stable_time: specifies the PLL Timer for stability of the + generated clock(System clock cycle base). If the timer value + goes to 0x00000000, the clock stable bit of status and interrupt + register is set. + - esc_clk: specifies escape clock frequency for getting the escape clock + prescaler value. + - stop_holding_cnt: specifies the interval value between transmitting + read packet(or write "set_tear_on" command) and BTA request. + after transmitting read packet or write "set_tear_on" command, + BTA requests to D-PHY automatically. this counter value + specifies the interval between them. + - bta_timeout: specifies the timer for BTA. + this register specifies time out from BTA request to change + the direction with respect to Tx escape clock. + - rx_timeout: specifies the timer for LP Rx mode timeout. + this register specifies time out on how long RxValid deasserts, + after RxLpdt asserts with respect to Tx escape clock. + - RxValid specifies Rx data valid indicator. + - RxLpdt specifies an indicator that D-PHY is under RxLpdt mode. + - RxValid and RxLpdt specifies signal from D-PHY. + +- panel-info: timing information from the panel used. + - lcd-htiming : xres, left_margin, right_margin, hsync-len info + - lcd-vtiming : yres, upper_margin, lower_margin, vsync-len info + +Properties for D-PHY node :: +============================= + Instead of passing D-PHY related callbacks as part of platform data, +we can pass the phy nodes to the mipi driver. Depending on the type of PHY +settings, we can implement multiple PHY node types and corresponding +enable/disable/reset callbacks in the driver itself. Currently we support +only one type of PHY node. + +D-PHY node type1: +------------------ +- compatible: "samsung,exynos-mipi-phy-type1" +- reg_enable_dphy: should contain physical address location of + D-PHY enable register +- mask_enable_dphy: should contain the mask for D-PHY enable register +- reg_reset_dsim: should contain physical address location of + D-PHY DSIM reset register +- mask_reset_dsim: should contain the mask for D-PHY DSIM reset register + +MIPI-LCD node :: +================= + Apart from the following three properties, driver specific +properties can be sent through this node. The following example sends +some more properties for driver's use. + +- lcd-name: name of the device to use with this device +- id: id of device to be registered (default -1 in case not specified) +- bus-id: bus id for identifing connected bus and this bus id should be + same as id of mipi_dsim_device (default -1 incase not specified) + +Example: +-------- + mipi_lcd: mipi-lcd@toshiba { + lcd-name = "tc358764"; + id = <0>; + enabled = <1>; + reset-delay = <120>; + power-on-delay = <25>; + power-off-delay = <200>; + gpio-poweron = <&gpx1 5 0 0 0>; + }; + + mipi_dsim_phy: mipi-phy@exynos5250 { + compatible = "samsung-exynos,mipi-phy-type1"; + reg_enable_dphy = <0x10040714>; + mask_enable_dphy = <0x00000001>; + reg_reset_dsim = <0x10040714>; + mask_reset_dsim = <0x00000004>; + }; + + mipi_dsi_tc358764: lcd_panel_tc358764 { + lcd-htiming = <24 24 2 800>; + lcd-vtiming = <1 13 2 1280>; + }; + + mipi { + compatible = "samsung,exynos-mipi"; + reg = <0x14500000 0x10000>; + interrupts = <0 82 0>; + + mipi-lcd = <&mipi_lcd>; + mipi-phy = <&mipi_dsim_phy>; + enabled = <0>; + + panel-info = <&lcd_mipi_dsi_tc358764>; + mipi-config { + e_interface = <1>; + e_pixel_format = <7>; + auto_flush = <0>; + eot_disable = <0>; + auto_vertical_cnt = <0>; + hse = <0>; + hfp = <0>; + hbp = <0>; + hsa = <0>; + e_no_data_lane = <3>; + e_byte_clk = <0>; + e_burst_mode = <3>; + p = <3>; + m = <115>; + s = <1>; + pll_stable_time = <500>; + esc_clk = <400000>; + stop_holding_cnt =<0x0f>; + bta_timeout = <0xff>; + rx_timeout = <0xffff>; + e_virtual_ch = <0>; + cmd_allow = <0xf>; + }; + + }; diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 97247fb528cd..293d40548ca0 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1549,6 +1549,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ltpc= [NET] Format: <io>,<irq>,<dma> + mac= [NET] + Used ASIX drivers. + Example: mac=12:34:56:78:ab:cd + machvec= [IA-64] Force the use of a particular machine-vector (machvec) in a generic kernel. Example: machvec=hpzx1_swiotlb @@ -799,7 +799,6 @@ include/config/kernel.release: include/config/auto.conf FORCE $(Q)rm -f $@ $(Q)echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))" > $@ - # Things we need to do before we recursively start building the kernel # or the modules are listed in "prepare". # A multi level approach is used. prepareN is processed before prepareN-1. @@ -849,7 +848,9 @@ define filechk_utsrelease.h echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2; \ exit 1; \ fi; \ - (echo \#define UTS_RELEASE \"$(KERNELRELEASE)\";) + (echo \#define UTS_RELEASE \"$(KERNELRELEASE)\"; \ + echo \#define KERNEL_GIT_ID \"$(shell \ + git rev-parse --verify --short HEAD 2>/dev/null)\";) endef define filechk_version.h diff --git a/arch/arm/boot/dts/exynos5.dtsi b/arch/arm/boot/dts/exynos5.dtsi index f65e124c04a6..d83573a573ca 100644 --- a/arch/arm/boot/dts/exynos5.dtsi +++ b/arch/arm/boot/dts/exynos5.dtsi @@ -23,6 +23,11 @@ reg = <0x10000000 0x100>; }; + sys_reg: sysreg { + compatible = "samsung,exynos5-sysreg"; + reg = <0x10050000 0x2000>; + }; + combiner:interrupt-controller@10440000 { compatible = "samsung,exynos4210-combiner"; #interrupt-cells = <2>; diff --git a/arch/arm/boot/dts/exynos5250-arndale.dts b/arch/arm/boot/dts/exynos5250-arndale.dts index 3937993b95f3..c3c93037eb52 100644 --- a/arch/arm/boot/dts/exynos5250-arndale.dts +++ b/arch/arm/boot/dts/exynos5250-arndale.dts @@ -29,6 +29,29 @@ samsung,mfc-l = <0x51000000 0x800000>; }; + i2s0: i2s@03830000 { + status = "okay"; + }; + + i2s1: i2s@12D60000 { + status = "disabled"; + }; + + i2s2: i2s@12D70000 { + status = "disabled"; + }; + + i2s_stub: i2s-stub { + compatible = "linux,i2s-stub"; + }; + + sound { + compatible = "samsung,smdk-i2s-stub"; + + samsung,i2s-controller = <&i2s0>; + samsung,audio-codec = <&i2s_stub>; + }; + i2c@12C60000 { samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <20000>; @@ -291,7 +314,13 @@ }; i2c@12C80000 { - status = "disabled"; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <66000>; + + hdmiddc@50 { + compatible = "samsung,exynos5-hdmiddc"; + reg = <0x50>; + }; }; i2c@12C90000 { @@ -314,10 +343,31 @@ status = "disabled"; }; + i2c@12CE0000 { + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <66000>; + + hdmiphy@38 { + compatible = "samsung,exynos5-hdmiphy"; + reg = <0x38>; + }; + }; + i2c@121D0000 { - status = "disabled"; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <40000>; + samsung,i2c-slave-addr = <0x38>; + + sata-phy { + compatible = "samsung,sata-phy"; + reg = <0x38>; + }; }; + sata@122F0000 { + samsung,sata-freq = <66>; + }; + dwmmc_0: dwmmc0@12200000 { num-slots = <1>; supports-highspeed; @@ -448,6 +498,22 @@ hub-connect = <&gpd1 7 1>; }; + vdd11:fixed-regulator@0 { + compatible = "regulator-fixed"; + regulator-name = "vdd11-supply"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + }; + + vdd18:fixed-regulator@1 { + compatible = "regulator-fixed"; + regulator-name = "vdd18-supply"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + fixed-rate-clocks { xxti { compatible = "samsung,clock-xxti"; @@ -462,6 +528,11 @@ samsung,color-depth = <1>; samsung,link-rate = <0x0a>; samsung,lane-count = <4>; + + pinctrl-names = "default"; + pinctrl-0 = <&dp_hpd>; + lcd_bl_gpio = <&gpb2 0 1>; + lcd_en_gpio = <&gpx1 5 1>; }; fimd: fimd@14400000 { @@ -469,7 +540,7 @@ native-mode = <&timing0>; timing0: timing@0 { /* 2560x1600 DP panel */ - clock-frequency = <50000>; + clock-frequency = <266000000>; hactive = <2560>; vactive = <1600>; hfront-porch = <48>; @@ -479,6 +550,80 @@ vfront-porch = <8>; vsync-len = <6>; }; + timing1: timing@1 { + /* 800x1280 */ + clock-frequency = <266000000>; + hactive = <800>; + vactive = <1280>; + hfront-porch = <11>; + hback-porch = <3>; + hsync-len = <2>; + vback-porch = <11>; + vfront-porch = <3>; + vsync-len = <2>; + }; + }; + }; + + lcd_mipi_dsi_s6e8ax0: lcd_panel_s6e8ax0 { + lcd-htiming = <11 3 2 800>; + lcd-vtiming = <11 3 2 1280>; + }; + + mipi_lcd_s6e8ax0: mipi-lcd@s6e8ax0 { + compatible = "s6e8ax0"; + lcd-name = "s6e8ax0"; + lcd_enabled = <1>; + reset-delay = <50>; + power-on-delay = <120>; + power-off-delay = <200>; + gpio-reset = <&gpx1 5 0 0 0>; + gpio-power = <&gpx3 0 0 0 0>; + gpio-bl = <&gpb2 0 0 0 0>; + }; + + mipi_dsim_phy: mipi-phy@exynos5250 { + compatible = "samsung-exynos,mipi-phy-type1"; + reg_enable_dphy = <0x10040714>; + mask_enable_dphy = <0x00000001>; + reg_reset_dsim = <0x10040714>; + mask_reset_dsim = <0x00000004>; + }; + + mipi-dsim { + status = "okay"; + mipi-lcd = <&mipi_lcd_s6e8ax0>; + mipi-phy = <&mipi_dsim_phy>; + enabled = <0>; + panel-info = <&lcd_mipi_dsi_s6e8ax0>; + vdd11-supply = <&vdd11>; + vdd18-supply = <&vdd18>; + vdd3-supply = <&vdd18>; + vci-supply = <&vdd18>; + + mipi-config { + e_interface = <1>; + e_pixel_format = <7>; + auto_flush = <0>; + eot_disable = <0>; + auto_vertical_cnt = <0>; + hse = <0>; + hfp = <0>; + hbp = <0>; + hsa = <0>; + e_no_data_lane = <3>; + e_byte_clk = <0>; + e_burst_mode = <1>; + p = <3>; + m = <115>; + s = <1>; + pll_stable_time = <500>; + esc_clk = <7000000>; + stop_holding_cnt =<0x0fff>; + bta_timeout = <0xff>; + rx_timeout = <0xffff>; + e_virtual_ch = <0>; + cmd_allow = <0x1>; }; }; diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index 49f18c24a576..874c91a092a3 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -61,6 +61,22 @@ regulator-always-on; }; + vdd11:fixed-regulator@3 { + compatible = "regulator-fixed"; + regulator-name = "vdd11-supply"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + }; + + vdd18:fixed-regulator@4 { + compatible = "regulator-fixed"; + regulator-name = "vdd18-supply"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + i2c@12C70000 { samsung,i2c-sda-delay = <100>; samsung,i2c-max-bus-freq = <20000>; @@ -260,21 +276,49 @@ pinctrl-names = "default"; pinctrl-0 = <&dp_hpd>; - }; - - display-timings { - native-mode = <&timing0>; - timing0: timing@0 { - /* 1280x800 */ - clock-frequency = <50000>; - hactive = <1280>; - vactive = <800>; - hfront-porch = <4>; - hback-porch = <4>; - hsync-len = <4>; - vback-porch = <4>; - vfront-porch = <4>; - vsync-len = <4>; + lcd_bl_gpio = <&gpb2 0 1>; + lcd_en_gpio = <&gpx1 5 1>; + }; + + fimd { + display-timings { + native-mode = <&timing1>; + timing0: timing@0 { + /* 1280x800 */ + clock-frequency = <50000>; + hactive = <1280>; + vactive = <800>; + hfront-porch = <4>; + hback-porch = <4>; + hsync-len = <4>; + vback-porch = <4>; + vfront-porch = <4>; + vsync-len = <4>; + }; + timing1: timing@1 { + /* 800x1280 */ + clock-frequency = <266000000>; + hactive = <800>; + vactive = <1280>; + hfront-porch = <11>; + hback-porch = <3>; + hsync-len = <2>; + vback-porch = <11>; + vfront-porch = <3>; + vsync-len = <2>; + }; + timing2: timing@2 { + /* 2560x1600 */ + clock-frequency = <266000000>; + hactive = <2560>; + vactive = <1600>; + hfront-porch = <48>; + hback-porch = <80>; + hsync-len = <32>; + vback-porch = <16>; + vfront-porch = <8>; + vsync-len = <6>; + }; }; }; @@ -284,4 +328,67 @@ clock-frequency = <24000000>; }; }; + + lcd_mipi_dsi_s6e8ax0: lcd_panel_s6e8ax0 { + lcd-htiming = <11 3 2 800>; + lcd-vtiming = <11 3 2 1280>; + }; + + mipi_lcd_s6e8ax0: mipi-lcd@s6e8ax0 { + compatible = "s6e8ax0"; + lcd-name = "s6e8ax0"; + lcd_enabled = <1>; + reset-delay = <50>; + power-on-delay = <120>; + power-off-delay = <200>; + gpio-reset = <&gpx1 5 0 0 0>; + gpio-power = <&gpx3 0 0 0 0>; + gpio-bl = <&gpb2 0 0 0 0>; + }; + + mipi_dsim_phy: mipi-phy@exynos5250 { + compatible = "samsung-exynos,mipi-phy-type1"; + reg_enable_dphy = <0x10040714>; + mask_enable_dphy = <0x00000001>; + reg_reset_dsim = <0x10040714>; + mask_reset_dsim = <0x00000004>; + }; + + mipi-dsim { + status = "okay"; + mipi-lcd = <&mipi_lcd_s6e8ax0>; + mipi-phy = <&mipi_dsim_phy>; + enabled = <0>; + panel-info = <&lcd_mipi_dsi_s6e8ax0>; + + vdd11-supply = <&vdd11>; + vdd18-supply = <&vdd18>; + vdd3-supply = <&vdd18>; + vci-supply = <&vdd18>; + + mipi-config { + e_interface = <1>; + e_pixel_format = <7>; + auto_flush = <0>; + eot_disable = <0>; + auto_vertical_cnt = <0>; + hse = <0>; + hfp = <0>; + hbp = <0>; + hsa = <0>; + e_no_data_lane = <3>; + e_byte_clk = <0>; + e_burst_mode = <1>; + p = <3>; + m = <115>; + s = <1>; + pll_stable_time = <500>; + esc_clk = <7000000>; + stop_holding_cnt =<0x0fff>; + bta_timeout = <0xff>; + rx_timeout = <0xffff>; + e_virtual_ch = <0>; + cmd_allow = <0x1>; + }; + }; }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index ef57277fc38f..6cd0ef7f6bc7 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -205,15 +205,24 @@ sata@122F0000 { compatible = "samsung,exynos5-sata-ahci"; + samsung,exynos-sata-phy = <&phy0>; reg = <0x122F0000 0x1ff>; interrupts = <0 115 0>; clocks = <&clock 277>, <&clock 143>; clock-names = "sata", "sclk_sata"; }; - sata-phy@12170000 { + phy0:sata-phy@12170000 { compatible = "samsung,exynos5-sata-phy"; reg = <0x12170000 0x1ff>; + clocks = <&clock 287>; + clock-names = "sata_phyctrl"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + sataphy-pmu { + reg = <0x10040724 0x4>; + }; }; i2c_0: i2c@12C60000 { @@ -602,16 +611,24 @@ compatible = "samsung,exynos4212-hdmi"; reg = <0x14530000 0x70000>; interrupts = <0 95 0>; - clocks = <&clock 333>, <&clock 136>, <&clock 137>, - <&clock 333>, <&clock 333>; + clocks = <&clock 344>, <&clock 136>, <&clock 137>, + <&clock 159>, <&clock 1024>; clock-names = "hdmi", "sclk_hdmi", "sclk_pixel", - "sclk_hdmiphy", "hdmiphy"; + "sclk_hdmiphy", "mout_hdmi"; + #address-cells = <1>; + #size-cells = <1>; + + phy-power-control { + reg = <0x10040700 0x04>; + }; }; mixer { compatible = "samsung,exynos5250-mixer"; reg = <0x14450000 0x10000>; interrupts = <0 94 0>; + clocks = <&clock 343>, <&clock 136>; + clock-names = "mixer", "sclk_hdmi"; }; dp-controller { @@ -636,7 +653,17 @@ reg = <0x14400000 0x40000>; interrupt-names = "fifo", "vsync", "lcd_sys"; interrupts = <18 4>, <18 5>, <18 6>; - clocks = <&clock 133>, <&clock 339>; - clock-names = "sclk_fimd", "fimd"; + clocks = <&clock 133>, <&clock 339>, <&clock 1025>, <&clock 2>; + clock-names = "sclk_fimd", "fimd", "mout_fimd", "sclk_mout_fimd"; + samsung,sys-reg = <&sys_reg>; + }; + + mipi-dsim { + status = "disabled"; + compatible = "samsung,exynos-mipidsim"; + reg = <0x14500000 0x10000>; + interrupts = <0 82 0>; + clocks = <&clock 341>; + clock-names = "dsim0"; }; }; diff --git a/arch/arm/configs/arndale_android_defconfig b/arch/arm/configs/arndale_android_defconfig new file mode 100644 index 000000000000..60f627e0eca9 --- /dev/null +++ b/arch/arm/configs/arndale_android_defconfig @@ -0,0 +1,139 @@ +CONFIG_EXPERIMENTAL=y +CONFIG_SYSVIPC=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_KALLSYMS_ALL=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_PARTITION_ADVANCED=y +CONFIG_BSD_DISKLABEL=y +CONFIG_SOLARIS_X86_PARTITION=y +# CONFIG_EFI_PARTITION is not set +CONFIG_ARCH_EXYNOS=y +CONFIG_S3C_LOWLEVEL_UART_PORT=2 +# CONFIG_ARCH_EXYNOS4 is not set +CONFIG_ARCH_EXYNOS5=y +CONFIG_NUMA=y +CONFIG_NODES_SHIFT=2 +CONFIG_SMP=y +CONFIG_NR_CPUS=2 +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_ARM_APPENDED_DTB=y +CONFIG_ARM_ATAG_DTB_COMPAT=y +CONFIG_CMDLINE="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init= mem=256M" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IPV6=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=m +CONFIG_INET6_XFRM_MODE_TUNNEL=m +CONFIG_INET6_XFRM_MODE_BEET=m +CONFIG_IPV6_SIT=m +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_ATA=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_SATA_EXYNOS=y +CONFIG_NETDEVICES=y +CONFIG_AX88796=y +CONFIG_AX88796_93CX6=y +CONFIG_SMC91X=y +CONFIG_SMC911X=y +CONFIG_SMSC911X=y +CONFIG_USB_USBNET=y +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_SAMSUNG=y +CONFIG_SERIAL_SAMSUNG_CONSOLE=y +CONFIG_HW_RANDOM=y +CONFIG_I2C_GPIO=y +# CONFIG_HWMON is not set +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y +CONFIG_EXYNOS_THERMAL=y +CONFIG_MFD_SEC_CORE=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_S5M8767=y +CONFIG_HID_LOGITECH_DJ=m +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_DWC3=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_EHCI_S5P=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_EXYNOS=y +CONFIG_USB_STORAGE=y +CONFIG_SAMSUNG_USBPHY=y +CONFIG_USB_GADGET=y +CONFIG_MMC=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_IDMAC=y +CONFIG_MMC_DW_EXYNOS=y +CONFIG_STAGING=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_PERSISTENT_TRACER=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ANDROID_SWITCH=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT2_FS_XIP=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_DEBUG=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y +CONFIG_CRAMFS=y +CONFIG_ROMFS_FS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_KERNEL=y +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEBUG_RT_MUTEXES=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_LL=y +CONFIG_DEBUG_S3C_UART2=y +CONFIG_EARLY_PRINTK=y +CONFIG_CRC_CCITT=y diff --git a/arch/arm/configs/arndale_ubuntu_defconfig b/arch/arm/configs/arndale_ubuntu_defconfig new file mode 100644 index 000000000000..4714cea21083 --- /dev/null +++ b/arch/arm/configs/arndale_ubuntu_defconfig @@ -0,0 +1,228 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=16 +CONFIG_CGROUPS=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_SLAB=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_BSD_DISKLABEL=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_ARCH_EXYNOS=y +CONFIG_S3C_LOWLEVEL_UART_PORT=2 +CONFIG_ARCH_EXYNOS5=y +CONFIG_ARM_LPAE=y +CONFIG_SMP=y +CONFIG_SCHED_MC=y +CONFIG_SCHED_SMT=y +CONFIG_VMSPLIT_2G=y +CONFIG_NR_CPUS=2 +CONFIG_THUMB2_KERNEL=y +CONFIG_HIGHMEM=y +# CONFIG_COMPACTION is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_SECCOMP=y +CONFIG_CC_STACKPROTECTOR=y +CONFIG_ARM_APPENDED_DTB=y +CONFIG_ARM_ATAG_DTB_COMPAT=y +CONFIG_CMDLINE="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init= mem=256M" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_BINFMT_MISC=y +CONFIG_PM_RUNTIME=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_NET_KEY_MIGRATE=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_SYN_COOKIES=y +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=m +CONFIG_NETFILTER_XT_MARK=m +CONFIG_NETFILTER_XT_CONNMARK=m +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m +CONFIG_NF_CONNTRACK_IPV4=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_FILTER=m +CONFIG_NF_NAT_IPV4=m +CONFIG_IP_NF_MANGLE=m +CONFIG_NF_CONNTRACK_IPV6=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_NF_NAT_IPV6=m +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_EXTRA_FIRMWARE="edid-1920x1080.fw" +CONFIG_CMA=y +CONFIG_CMA_SIZE_MBYTES=128 +CONFIG_CONNECTOR=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_OOPS=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_NAND=y +CONFIG_PROC_DEVICETREE=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=65536 +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_ATA=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_SATA_EXYNOS=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_NETDEVICES=y +CONFIG_TUN=y +CONFIG_AX88796=y +CONFIG_AX88796_93CX6=y +CONFIG_USB_USBNET=y +CONFIG_USB_NET_DM9601=y +CONFIG_USB_NET_MCS7830=y +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_GPIO=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_SAMSUNG=y +CONFIG_SERIAL_SAMSUNG_CONSOLE=y +CONFIG_HW_RANDOM=y +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y +CONFIG_EXYNOS_THERMAL=y +CONFIG_MFD_SEC_CORE=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_S5M8767=y +CONFIG_DRM=y +CONFIG_DRM_LOAD_EDID_FIRMWARE=y +CONFIG_DRM_EXYNOS=y +CONFIG_DRM_EXYNOS_DMABUF=y +CONFIG_DRM_EXYNOS_FIMD=y +CONFIG_DRM_EXYNOS_HDMI=y +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_EXYNOS_VIDEO=y +CONFIG_EXYNOS_MIPI_DSI=y +CONFIG_EXYNOS_LCD_S6E8AX0=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_PLATFORM=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_SAMSUNG=y +CONFIG_SND_SOC_SMDK_I2S_STUB=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_S5P=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_EXYNOS=y +CONFIG_USB_STORAGE=y +CONFIG_USB_DWC3=y +CONFIG_USB_DWC3_HOST=y +CONFIG_USB_PHY=y +CONFIG_SAMSUNG_USB2PHY=y +CONFIG_SAMSUNG_USB3PHY=y +CONFIG_USB_GADGET=y +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_IDMAC=y +CONFIG_MMC_DW_EXYNOS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_S3C=y +CONFIG_PWM=y +CONFIG_PWM_SAMSUNG=y +CONFIG_EXT2_FS=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS=y +CONFIG_BTRFS_FS=y +CONFIG_QUOTA=y +CONFIG_QFMT_V2=y +CONFIG_AUTOFS4_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_ECRYPT_FS=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_SUMMARY=y +CONFIG_JFFS2_FS_XATTR=y +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_LZO=y +CONFIG_JFFS2_RUBIN=y +CONFIG_CRAMFS=y +CONFIG_NFS_FS=y +# CONFIG_NFS_V2 is not set +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DETECT_HUNG_TASK=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_DEBUG_RT_MUTEXES=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_FUNCTION_TRACER=y +CONFIG_STRICT_DEVMEM=y +CONFIG_DEBUG_USER=y +CONFIG_SECURITY=y +CONFIG_LSM_MMAP_MIN_ADDR=0 +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SMACK=y +CONFIG_SECURITY_APPARMOR=y +CONFIG_DEFAULT_SECURITY_APPARMOR=y +CONFIG_CRYPTO_MICHAEL_MIC=y +CONFIG_CRC_CCITT=y +CONFIG_CRC_T10DIF=y +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y +CONFIG_VIRTUALIZATION=y +CONFIG_KVM=y diff --git a/arch/arm/mach-exynos/headsmp.S b/arch/arm/mach-exynos/headsmp.S index cdd9d91e9933..348c61509e62 100644 --- a/arch/arm/mach-exynos/headsmp.S +++ b/arch/arm/mach-exynos/headsmp.S @@ -12,12 +12,105 @@ */ #include <linux/linkage.h> #include <linux/init.h> +#include <asm/assembler.h> /* * exynos4 specific entry point for secondary CPUs. This provides * a "holding pen" into which all secondary cores are held until we're * ready for them to initialise. */ + +.arch_extension sec +.arch_extension virt +.text + +.align 5 +/* We use the same vector table for Hyp and Monitor mode, since + * we will only use each once and they don't overlap. + */ +mon_vectors: + W(b) . /* reset */ + W(b) . /* undef */ + W(b) 2f /* smc */ + W(b) . /* pabt */ + W(b) . /* dabt */ + W(b) 1f /* hyp */ + W(b) . /* irq */ + W(b) . /* fiq */ + +/* Return directly back to the caller without leaving Hyp mode: */ +1: mrs lr, elr_hyp + mov pc, lr + +/* In monitor mode, set up HVBAR and SCR then return to caller in NS-SVC. */ +2: + mrc p15, 0, r1, c1, c1, 0 @ SCR + /* + * Set SCR.NS=1(needed for setting HVBAR and also returning to NS state) + * .IRQ,FIQ,EA=0 (don't take aborts/exceptions to Monitor mode) + * .FW,AW=1 (CPSR.A,F modifiable in NS state) + * .nET=0 (early termination OK) + * .SCD=0 (SMC in NS mode OK, so we can call secure firmware) + * .HCE=1 (HVC does Hyp call) + */ + bic r1, r1, #0x07f + ldr r2, =0x131 + orr r1, r1, r2 + mcr p15, 0, r2, c1, c1, 0 @ SCR + isb + ldr r2, =mon_vectors + + + adr r4, 1f + ldmia r4, {r5} + sub r4, r4, r5 + add r2, r2, r4 + + mcr p15, 4, r2, c12, c0, 0 @ set HVBAR + + THUMB( mrc p15, 4, r2, c1, c0, 0 ) @ ctrl register + THUMB( orr r2, r2, #1 << 30 ) @ HSCTLR.TE (Thumb exceptions) + THUMB( mcr p15, 4, r2, c1, c0, 0 ) + THUMB( isb) + + /* ...and return to calling code in NS state */ + movs pc, lr + + + .globl monitor_init +monitor_init: + ldr ip, =mon_vectors + + adr r4, 1f + ldmia r4, {r5} + sub r4, r4, r5 + add ip, ip, r4 + mcr p15, 0, ip, c12, c0, 1 + + THUMB( mrc p15, 0, r1, c1, c0, 0 ) @ ctrl register + THUMB( orr r1, r1, #1 << 30 ) @ SCTLR.TE (Thumb exceptions) + THUMB( mcr p15, 0, r1, c1, c0, 0 ) + THUMB( isb ) + + mov pc, lr + + /* Try to go into NS-SVC: void enter_ns(void); */ + .globl enter_ns +enter_ns: + smc #0 + mov pc, lr + + /* void enter_hyp(void); */ + .globl enter_hyp +enter_hyp: + /* Now we're in NS-SVC, make a Hyp call to get into Hyp mode */ + mov r0, lr + mov r1, sp + hvc #0 + /* We will end up here in NS-Hyp. */ + mov sp, r1 + mov pc, r0 + ENTRY(exynos4_secondary_startup) mrc p15, 0, r0, c0, c0, 5 and r0, r0, #15 @@ -29,6 +122,27 @@ pen: ldr r7, [r6] cmp r7, r0 bne pen + ldr r1, =__boot_cpu_mode + add r1, r1, r4 + ldr r2, [r1] + mrs r0, cpsr + ands r0, r0, #MODE_MASK + subs r1, r0, r2 + beq 3f + subs r2, r2, #HYP_MODE + bne 3f + + /* Setting NSACR to allow coprocessor access from non-secure mode */ + mrc p15, 0, r0, c1, c1, 2 + movw r1, #0x3fff + orr r0, r0, r1 + mcr p15, 0, r0, c1, c1, 2 +5: + bl monitor_init + bl enter_ns + bl enter_hyp + +3: /* * we've been released from the holding pen: secondary_stack * should now contain the SVC stack for this core diff --git a/arch/arm/mach-exynos/hotplug.c b/arch/arm/mach-exynos/hotplug.c index af90cfa2f826..cce2b0d960e7 100644 --- a/arch/arm/mach-exynos/hotplug.c +++ b/arch/arm/mach-exynos/hotplug.c @@ -98,13 +98,7 @@ static inline void platform_do_lowpower(unsigned int cpu, int *spurious) if (cpu == 1) __raw_writel(0, S5P_ARM_CORE1_CONFIGURATION); - /* - * here's the WFI - */ - asm(".word 0xe320f003\n" - : - : - : "memory", "cc"); + wfi(); if (pen_release == cpu_logical_map(cpu)) { /* diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h index 7b046b59d9ec..a5df67b15718 100644 --- a/arch/arm/mach-exynos/include/mach/map.h +++ b/arch/arm/mach-exynos/include/mach/map.h @@ -60,6 +60,10 @@ #define EXYNOS4_PA_COREPERI 0x10500000 #define EXYNOS4_PA_L2CC 0x10502000 +#define EXYNOS5_PA_SATA_PHY_CTRL 0x12170000 +#define EXYNOS5_PA_SATA_PHY_I2C 0x121D0000 +#define EXYNOS5_PA_SATA_BASE 0x122F0000 + #define EXYNOS4_PA_SROMC 0x12570000 #define EXYNOS5_PA_SROMC 0x12250000 diff --git a/arch/arm/mach-exynos/include/mach/regs-pmu.h b/arch/arm/mach-exynos/include/mach/regs-pmu.h index 57344b7e98ce..66ffff8069f1 100644 --- a/arch/arm/mach-exynos/include/mach/regs-pmu.h +++ b/arch/arm/mach-exynos/include/mach/regs-pmu.h @@ -370,4 +370,7 @@ #define EXYNOS5_OPTION_USE_RETENTION (1 << 4) +/* Only for EXYNOS5250 */ +#define EXYNOS5_SATA_PHY_CONTROL S5P_PMUREG(0x0724) + #endif /* __ASM_ARCH_REGS_PMU_H */ diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 4e737728aee2..c81fe31b3da6 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -97,6 +97,28 @@ config SATA_AHCI_PLATFORM If unsure, say N. +config SATA_PHY + bool "SATA PHY Framework" + default n + help + This option enables the SATA PHY utility framework APIs. + The framework acts as an interface between the SATA device + and the PHY device. The SATA PHY device registers itself + with the framework through the APIs provided and the SATA + device finds and requests for an appropriate PHY device. + +config SATA_EXYNOS + bool "Exynos SATA AHCI support" + select I2C + select HAVE_S3C2410_I2C + select I2C_S3C2410 + select SATA_PHY + help + This option enables support for Exynos AHCI Serial ATA + controllers. + + If unsure, say N. + config AHCI_IMX tristate "Freescale i.MX AHCI SATA support" depends on SATA_AHCI_PLATFORM && MFD_SYSCON diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 46518c622460..a4e5cf56d475 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -10,6 +10,8 @@ obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o obj-$(CONFIG_SATA_SIL24) += sata_sil24.o obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o +obj-$(CONFIG_SATA_PHY) += sata_phy.o +obj-$(CONFIG_SATA_EXYNOS) += sata_exynos.o sata_exynos_phy.o libahci.o obj-$(CONFIG_AHCI_IMX) += ahci_imx.o # SFF w/ custom DMA diff --git a/drivers/ata/sata_exynos.c b/drivers/ata/sata_exynos.c new file mode 100644 index 000000000000..1dcfb2c8d0de --- /dev/null +++ b/drivers/ata/sata_exynos.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS - SATA controller platform driver wrapper + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/kernel.h> +#include <linux/gfp.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/libata.h> +#include <linux/ahci_platform.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> + +#include "ahci.h" +#include "sata_phy.h" + +#define MHZ (1000 * 1000) +#define DEFERED 1 +#define NO_PORT 0 + +static const struct ata_port_info ahci_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_ops, +}; + +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT("ahci_platform"), +}; + +struct exynos_sata { + struct clk *sclk; + struct clk *clk; + int irq; + unsigned int freq; + struct sata_phy *phy[]; +}; + +static int exynos_sata_parse_dt(struct device_node *np, + struct exynos_sata *sata) +{ + if (!np) + return -EINVAL; + + return of_property_read_u32(np, "samsung,sata-freq", + &sata->freq); +} + +static int exynos_sata_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ata_port_info pi = ahci_port_info; + const struct ata_port_info *ppi[] = { &pi, NULL }; + struct ahci_host_priv *hpriv; + struct exynos_sata *sata; + struct ata_host *host; + struct device_node *of_node_phy = NULL; + static int flag = 0, port_init = NO_PORT; + int n_ports, i, ret; + + sata = devm_kzalloc(dev, sizeof(*sata), GFP_KERNEL); + if (!sata) { + dev_err(dev, "can't alloc sata\n"); + return -ENOMEM; + } + + hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); + if (!hpriv) { + dev_err(dev, "can't alloc ahci_host_priv\n"); + return -ENOMEM; + } + + hpriv->flags |= (unsigned long)pi.private_data; + + sata->irq = irq_of_parse_and_map(dev->of_node, 0); + if (sata->irq <= 0) { + dev_err(dev, "irq not specified\n"); + return -EINVAL; + } + + hpriv->mmio = of_iomap(dev->of_node, 0); + if (!hpriv->mmio) { + dev_err(dev, "failed to map IO\n"); + return -ENOMEM; + } + + ret = exynos_sata_parse_dt(dev->of_node, sata); + if (ret < 0) { + dev_err(dev, "failed to get frequency for sata ctrl\n"); + goto err_iomap; + } + + sata->sclk = devm_clk_get(dev, "sclk_sata"); + if (IS_ERR(sata->sclk)) { + dev_err(dev, "failed to get sclk_sata\n"); + ret = PTR_ERR(sata->sclk); + goto err_iomap; + } + + ret = clk_prepare_enable(sata->sclk); + if (ret < 0) { + dev_err(dev, "failed to enable source clk\n"); + goto err_iomap; + } + + ret = clk_set_rate(sata->sclk, sata->freq * MHZ); + if (ret < 0) { + dev_err(dev, "failed to set clk frequency\n"); + goto err_clkstrt; + } + + sata->clk = devm_clk_get(dev, "sata"); + if (IS_ERR(sata->clk)) { + dev_err(dev, "failed to get sata clock\n"); + ret = PTR_ERR(sata->clk); + goto err_clkstrt; + } + + ret = clk_prepare_enable(sata->clk); + if (ret < 0) { + dev_err(dev, "failed to enable source clk\n"); + goto err_clkstrt; + } + + ahci_save_initial_config(dev, hpriv, 0, 0); + + /* prepare host */ + if (hpriv->cap & HOST_CAP_NCQ) + pi.flags |= ATA_FLAG_NCQ; + + if (hpriv->cap & HOST_CAP_PMP) + pi.flags |= ATA_FLAG_PMP; + + ahci_set_em_messages(hpriv, &pi); + + /* CAP.NP sometimes indicate the index of the last enabled + * port, at other times, that of the last possible port, so + * determining the maximum port number requires looking at + * both CAP.NP and port_map. + */ + n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + + host = ata_host_alloc_pinfo(dev, ppi, n_ports); + if (!host) { + ret = -ENOMEM; + goto err_clken; + } + + host->private_data = hpriv; + + if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) + host->flags |= ATA_HOST_PARALLEL_SCAN; + else + pr_info(KERN_INFO + "ahci: SSS flag set, parallel bus scan disabled\n"); + + if (pi.flags & ATA_FLAG_EM) + ahci_reset_em(host); + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + of_node_phy = of_parse_phandle(dev->of_node, + "samsung,exynos-sata-phy", i); + if (!of_node_phy) { + dev_err(dev, + "phandle of phy not found for port %d\n", i); + break; + } + + sata->phy[i] = sata_get_phy(of_node_phy); + if (IS_ERR(sata->phy[i])) { + if (PTR_ERR(sata->phy[i]) == -EBUSY) + continue; + dev_err(dev, + "failed to get sata phy for port %d\n", i); + if (flag != DEFERED) { + flag = DEFERED ; + return -EPROBE_DEFER; + } else + continue; + + } + /* Initialize the PHY */ + ret = sata_init_phy(sata->phy[i]); + if (ret < 0) { + if (ret == -EPROBE_DEFER) { + if (flag != DEFERED) { + flag = DEFERED ; + sata_put_phy(sata->phy[i]); + return -EPROBE_DEFER; + } else { + continue; + } + } else { + dev_err(dev, + "failed to initialize sata phy for port %d\n", + i); + sata_put_phy(sata->phy[i]); + } + + } + + /* set enclosure management message type */ + if (ap->flags & ATA_FLAG_EM) + ap->em_message_type = hpriv->em_msg_type; + + /* disabled/not-implemented port */ + if (!(hpriv->port_map & (1 << i))) + ap->ops = &ata_dummy_port_ops; + + port_init++; + } + + if (port_init == NO_PORT) + goto err_initphy; + + ret = ahci_reset_controller(host); + if (ret) + goto err_rst; + + ahci_init_controller(host); + ahci_print_info(host, "platform"); + + ret = ata_host_activate(host, sata->irq, ahci_interrupt, IRQF_SHARED, + &ahci_platform_sht); + if (ret) + goto err_rst; + + platform_set_drvdata(pdev, sata); + + return 0; + + err_rst: + for (i = 0; i < host->n_ports; i++) + sata_shutdown_phy(sata->phy[i]); + + err_initphy: + for (i = 0; i < host->n_ports; i++) + sata_put_phy(sata->phy[i]); + + err_clken: + clk_disable_unprepare(sata->clk); + + err_clkstrt: + clk_disable_unprepare(sata->sclk); + + err_iomap: + iounmap(hpriv->mmio); + + return ret; +} + +static int exynos_sata_remove(struct platform_device *pdev) +{ + unsigned int i; + struct device *dev = &pdev->dev; + struct ata_host *host = dev_get_drvdata(dev); + struct exynos_sata *sata = platform_get_drvdata(pdev); + struct ahci_host_priv *hpriv = + (struct ahci_host_priv *)host->private_data; + + ata_host_detach(host); + + for (i = 0; i < host->n_ports; i++) { + sata_shutdown_phy(sata->phy[i]); + sata_put_phy(sata->phy[i]); + } + iounmap(hpriv->mmio); + + return 0; +} + +static const struct of_device_id ahci_of_match[] = { + { .compatible = "samsung,exynos5-sata-ahci", }, +}; + +MODULE_DEVICE_TABLE(of, ahci_of_match); + +static struct platform_driver exynos_sata_driver = { + .probe = exynos_sata_probe, + .remove = exynos_sata_remove, + .driver = { + .name = "exynos-sata", + .owner = THIS_MODULE, + .of_match_table = ahci_of_match, + }, +}; + +module_platform_driver(exynos_sata_driver); + +MODULE_DESCRIPTION("EXYNOS SATA DRIVER"); +MODULE_AUTHOR("Vasanth Ananthan, <vasanth.a@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ata/sata_exynos_phy.c b/drivers/ata/sata_exynos_phy.c new file mode 100644 index 000000000000..e9417097ffb2 --- /dev/null +++ b/drivers/ata/sata_exynos_phy.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS - SATA PHY controller driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/ahci_platform.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/io.h> +#include <linux/of_address.h> + +#include <plat/cpu.h> +#include <plat/irqs.h> + +#include <mach/map.h> +#include <mach/regs-pmu.h> + +#include "sata_phy.h" +#include "sata_exynos_phy.h" + +#define SATA_TIME_LIMIT 1000 + +static struct i2c_client *i2c_client; + +static struct i2c_driver sataphy_i2c_driver; + +struct exynos_sata_phy { + void __iomem *mmio; + void __iomem *pmureg; + struct clk *clk; + struct device *dev; + struct sata_phy phy; +}; + +static bool sata_is_reg(void __iomem *base, u32 reg, u32 checkbit, u32 status) +{ + if ((readl(base + reg) & checkbit) == status) + return true; + else + return false; +} + +static bool wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit, + u32 status) +{ + unsigned long timeout = jiffies + usecs_to_jiffies(1000); + + while (time_before(jiffies, timeout)) { + if (sata_is_reg(base, reg, checkbit, status)) + return true; + } + + return false; +} + +static int exynos_sataphy_parse_dt(struct device *dev, + struct exynos_sata_phy *phy) +{ + struct device_node *sataphy_pmu; + + sataphy_pmu = of_get_child_by_name(dev->of_node, "sataphy-pmu"); + if (!sataphy_pmu) { + dev_err(dev, + "PMU control register for sata-phy not specified\n"); + return -ENODEV; + } + + phy->pmureg = of_iomap(sataphy_pmu, 0); + + of_node_put(sataphy_pmu); + + if (!phy->pmureg) { + dev_err(dev, "Can't get sata-phy pmu control register\n"); + return -EADDRNOTAVAIL; + } + + return 0; +} + +static int exynos_sataphy_init(struct sata_phy *phy) +{ + int ret; + u32 val; + + /* Values to be written to enable 40 bits interface */ + u8 buf[] = { 0x3A, 0x0B }; + + struct exynos_sata_phy *sata_phy; + + if (!i2c_client) + return -EPROBE_DEFER; + + sata_phy = container_of(phy, struct exynos_sata_phy, phy); + + ret = clk_prepare_enable(sata_phy->clk); + if (ret < 0) { + dev_err(phy->dev, "failed to enable source clk\n"); + return ret; + } + + if (sata_is_reg(sata_phy->mmio , EXYNOS5_SATA_CTRL0, + CTRL0_P0_PHY_CALIBRATED, CTRL0_P0_PHY_CALIBRATED)) + return 0; + + writel(SATA_PHY_PMU_EN, sata_phy->pmureg); + + val = 0; + writel(val, sata_phy->mmio + EXYNOS5_SATA_RESET); + + val = readl(sata_phy->mmio + EXYNOS5_SATA_RESET); + val |= 0xFF; + writel(val, sata_phy->mmio + EXYNOS5_SATA_RESET); + + val = readl(sata_phy->mmio + EXYNOS5_SATA_RESET); + val |= LINK_RESET; + writel(val, sata_phy->mmio + EXYNOS5_SATA_RESET); + + val = readl(sata_phy->mmio + EXYNOS5_SATA_RESET); + val |= RESET_CMN_RST_N; + writel(val, sata_phy->mmio + EXYNOS5_SATA_RESET); + + val = readl(sata_phy->mmio + EXYNOS5_SATA_PHSATA_CTRLM); + val &= ~PHCTRLM_REF_RATE; + writel(val, sata_phy->mmio + EXYNOS5_SATA_PHSATA_CTRLM); + + /* High speed enable for Gen3 */ + val = readl(sata_phy->mmio + EXYNOS5_SATA_PHSATA_CTRLM); + val |= PHCTRLM_HIGH_SPEED; + writel(val, sata_phy->mmio + EXYNOS5_SATA_PHSATA_CTRLM); + + val = readl(sata_phy->mmio + EXYNOS5_SATA_CTRL0); + val |= CTRL0_P0_PHY_CALIBRATED_SEL | CTRL0_P0_PHY_CALIBRATED; + writel(val, sata_phy->mmio + EXYNOS5_SATA_CTRL0); + + writel(0x2, sata_phy->mmio + EXYNOS5_SATA_MODE0); + + ret = i2c_master_send(i2c_client, buf, sizeof(buf)); + if (ret < 0) + return -ENXIO; + + /* release cmu reset */ + val = readl(sata_phy->mmio + EXYNOS5_SATA_RESET); + val &= ~RESET_CMN_RST_N; + writel(val, sata_phy->mmio + EXYNOS5_SATA_RESET); + + val = readl(sata_phy->mmio + EXYNOS5_SATA_RESET); + val |= RESET_CMN_RST_N; + writel(val, sata_phy->mmio + EXYNOS5_SATA_RESET); + + if (wait_for_reg_status(sata_phy->mmio, EXYNOS5_SATA_PHSATA_STATM, + PHSTATM_PLL_LOCKED, 1)) { + return 0; + } + return -EINVAL; +} + +static int exynos_sataphy_shutdown(struct sata_phy *phy) +{ + + struct exynos_sata_phy *sata_phy; + + sata_phy = container_of(phy, struct exynos_sata_phy, phy); + clk_disable_unprepare(sata_phy->clk); + + return 0; +} + +static int exynos_sata_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + i2c_client = client; + return 0; +} + +static int exynos_sata_phy_probe(struct platform_device *pdev) +{ + struct exynos_sata_phy *sataphy; + struct device *dev = &pdev->dev; + int ret = 0; + sataphy = devm_kzalloc(dev, sizeof(struct exynos_sata_phy), GFP_KERNEL); + if (!sataphy) { + dev_err(dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + sataphy->mmio = of_iomap(dev->of_node, 0); + if (!sataphy->mmio) { + dev_err(dev, "failed to remap IO\n"); + return -EADDRNOTAVAIL; + } + + ret = exynos_sataphy_parse_dt(dev, sataphy); + if (ret != 0) + goto err_iomap; + + sataphy->clk = devm_clk_get(dev, "sata_phyctrl"); + if (IS_ERR(sataphy->clk)) { + dev_err(dev, "failed to get clk for PHY\n"); + ret = PTR_ERR(sataphy->clk); + goto err_pmu; + } + + sataphy->phy.init = exynos_sataphy_init; + sataphy->phy.shutdown = exynos_sataphy_shutdown; + sataphy->phy.dev = dev; + + ret = sata_add_phy(&sataphy->phy); + if (ret < 0) { + dev_err(dev, "PHY not registered with framework\n"); + goto err_iomap; + } + + ret = i2c_add_driver(&sataphy_i2c_driver); + if (ret < 0) + goto err_phy; + + platform_set_drvdata(pdev, sataphy); + + return ret; + + err_phy: + sata_remove_phy(&sataphy->phy); + + err_pmu: + iounmap(sataphy->pmureg); + + err_iomap: + iounmap(sataphy->mmio); + + return ret; +} + +static int exynos_sata_phy_remove(struct platform_device *pdev) +{ + struct exynos_sata_phy *sataphy; + + sataphy = platform_get_drvdata(pdev); + iounmap(sataphy->mmio); + i2c_del_driver(&sataphy_i2c_driver); + sata_remove_phy(&sataphy->phy); + + return 0; +} + +static const struct of_device_id sata_phy_of_match[] = { + { .compatible = "samsung,exynos5-sata-phy", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, sata_phy_of_match); + +static const struct i2c_device_id phy_i2c_device_match[] = { + { "sata-phy", 0 }, +}; + +MODULE_DEVICE_TABLE(of, phy_i2c_device_match); + +static struct platform_driver sata_phy_driver = { + .probe = exynos_sata_phy_probe, + .remove = exynos_sata_phy_remove, + .driver = { + .name = "sata-phy", + .owner = THIS_MODULE, + .of_match_table = sata_phy_of_match, + }, +}; + +static struct i2c_driver sataphy_i2c_driver = { + .driver = { + .name = "sata-phy-i2c", + .owner = THIS_MODULE, + .of_match_table = (void *)phy_i2c_device_match, + }, + .probe = exynos_sata_i2c_probe, + .id_table = phy_i2c_device_match, +}; + +module_platform_driver(sata_phy_driver); + +MODULE_DESCRIPTION("EXYNOS SATA PHY DRIVER"); +MODULE_AUTHOR("Vasanth Ananthan, <vasanth.a@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ata/sata_exynos_phy.h b/drivers/ata/sata_exynos_phy.h new file mode 100644 index 000000000000..f9889ccd9abc --- /dev/null +++ b/drivers/ata/sata_exynos_phy.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS - SATA PHY controller definition + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#define EXYNOS5_SATA_RESET 0x4 +#define RESET_CMN_RST_N (1 << 1) +#define LINK_RESET 0xF0000 + +#define EXYNOS5_SATA_MODE0 0x10 + +#define EXYNOS5_SATA_CTRL0 0x14 +#define CTRL0_P0_PHY_CALIBRATED_SEL (1 << 9) +#define CTRL0_P0_PHY_CALIBRATED (1 << 8) + +#define EXYNOS5_SATA_PHSATA_CTRLM 0xE0 +#define PHCTRLM_REF_RATE (1 << 1) +#define PHCTRLM_HIGH_SPEED (1 << 0) + +#define EXYNOS5_SATA_PHSATA_STATM 0xF0 +#define PHSTATM_PLL_LOCKED (1 << 0) + +#define SATA_PHY_CON_RESET 0xF003F + +#define SATA_PHY_PMU_EN 0x1 + diff --git a/drivers/ata/sata_phy.c b/drivers/ata/sata_phy.c new file mode 100644 index 000000000000..53d441775d74 --- /dev/null +++ b/drivers/ata/sata_phy.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * SATA PHY framework. + * + * This file provides a set of functions/interfaces for establishing + * communication between SATA controller and the PHY controller. A + * PHY controller driver registers call backs for its initialization and + * shutdown. The SATA controller driver finds the appropriate PHYs for + * its implemented ports and initialize/shutdown PHYs through the + * call backs provided. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/list.h> +#include "sata_phy.h" + +static LIST_HEAD(phy_list); +static DEFINE_SPINLOCK(phy_lock); + +struct sata_phy *sata_get_phy(struct device_node *phy_np) +{ + struct sata_phy *phy; + unsigned long flag; + + spin_lock_irqsave(&phy_lock, flag); + + if (list_empty(&phy_list)) { + spin_unlock_irqrestore(&phy_lock, flag); + return ERR_PTR(-ENODEV); + } + + list_for_each_entry(phy, &phy_list, head) { + if (phy->dev->of_node == phy_np) { + if (phy->status == IN_USE) { + pr_info(KERN_INFO + "PHY already in use\n"); + spin_unlock_irqrestore(&phy_lock, flag); + return ERR_PTR(-EBUSY); + } + + get_device(phy->dev); + phy->status = IN_USE; + spin_unlock_irqrestore(&phy_lock, flag); + return phy; + } + } + + spin_unlock_irqrestore(&phy_lock, flag); + return ERR_PTR(-ENODEV); +} +EXPORT_SYMBOL(sata_get_phy); + +int sata_add_phy(struct sata_phy *sataphy) +{ + unsigned long flag; + unsigned int ret = -EINVAL; + struct sata_phy *phy; + + if (!sataphy) + return ret; + + spin_lock_irqsave(&phy_lock, flag); + + list_for_each_entry(phy, &phy_list, head) { + if (phy->dev->of_node == sataphy->dev->of_node) { + dev_err(sataphy->dev, "PHY already exists in the list\n"); + goto out; + } + } + + sataphy->status = NOT_IN_USE; + list_add_tail(&sataphy->head, &phy_list); + ret = 0; + + out: + spin_unlock_irqrestore(&phy_lock, flag); + return ret; +} +EXPORT_SYMBOL(sata_add_phy); + +void sata_remove_phy(struct sata_phy *sataphy) +{ + unsigned long flag; + struct sata_phy *phy; + + if (!sataphy) + return; + + if (sataphy->status == IN_USE) { + pr_info(KERN_INFO + "PHY in use, cannot be removed\n"); + return; + } + + spin_lock_irqsave(&phy_lock, flag); + + list_for_each_entry(phy, &phy_list, head) { + if (phy->dev->of_node == sataphy->dev->of_node) + list_del(&phy->head); + } + + spin_unlock_irqrestore(&phy_lock, flag); +} +EXPORT_SYMBOL(sata_remove_phy); + +void sata_put_phy(struct sata_phy *sataphy) +{ + unsigned long flag; + + if (!sataphy) + return; + + spin_lock_irqsave(&phy_lock, flag); + + put_device(sataphy->dev); + sataphy->status = NOT_IN_USE; + + spin_unlock_irqrestore(&phy_lock, flag); +} +EXPORT_SYMBOL(sata_put_phy); + +int sata_init_phy(struct sata_phy *sataphy) +{ + if (sataphy && sataphy->init) + return sataphy->init(sataphy); + + return -EINVAL; +} +EXPORT_SYMBOL(sata_init_phy); + +void sata_shutdown_phy(struct sata_phy *sataphy) +{ + if (sataphy && sataphy->shutdown) + sataphy->shutdown(sataphy); +} +EXPORT_SYMBOL(sata_shutdown_phy); + diff --git a/drivers/ata/sata_phy.h b/drivers/ata/sata_phy.h new file mode 100644 index 000000000000..9ed1dbed5a11 --- /dev/null +++ b/drivers/ata/sata_phy.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * SATA utility framework definitions. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#define IN_USE 1 +#define NOT_IN_USE 0 + +struct sata_phy { + int (*init) (struct sata_phy *); + int (*shutdown) (struct sata_phy *); + struct device *dev; + void *priv_data; + struct list_head head; + unsigned char status; +}; + +struct sata_phy *sata_get_phy(struct device_node *); +int sata_add_phy(struct sata_phy *); +void sata_remove_phy(struct sata_phy *); +void sata_put_phy(struct sata_phy *); +int sata_init_phy(struct sata_phy *); +void sata_shutdown_phy(struct sata_phy *); + diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 4e5739773c33..f1fd7144cf3c 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -17,7 +17,6 @@ #include <linux/of_address.h> #include "clk.h" -#include "clk-pll.h" /* Exynos4 clock controller register offsets */ #define SRC_LEFTBUS 0x4200 @@ -97,12 +96,14 @@ #define GATE_IP_PERIL 0xc950 #define E4210_GATE_IP_PERIR 0xc960 #define GATE_BLOCK 0xc970 +#define E4X12_MPLL_LOCK 0x10008 #define E4X12_MPLL_CON0 0x10108 #define SRC_DMC 0x10200 #define SRC_MASK_DMC 0x10300 #define DIV_DMC0 0x10500 #define DIV_DMC1 0x10504 #define GATE_IP_DMC 0x10900 +#define APLL_LOCK 0x14000 #define APLL_CON0 0x14100 #define E4210_MPLL_CON0 0x14108 #define SRC_CPU 0x14200 @@ -121,6 +122,12 @@ enum exynos4_soc { EXYNOS4X12, }; +/* list of PLLs to be registered */ +enum exynos4_plls { + apll, mpll, epll, vpll, + nr_plls /* number of PLLs */ +}; + /* * Let each supported clock get a unique id. This id is used to lookup the clock * for device tree based platforms. The clocks are categorized into three @@ -993,6 +1000,17 @@ static __initdata struct of_device_id ext_clk_match[] = { {}, }; +struct __initdata samsung_pll_clock exynos4_plls[nr_plls] = { + [apll] = PLL_A(pll_35xx, fout_apll, "fout_apll", "fin_pll", APLL_LOCK, + APLL_CON0, "fout_apll", NULL), + [mpll] = PLL_A(pll_35xx, fout_mpll, "fout_mpll", "fin_pll", + E4X12_MPLL_LOCK, E4X12_MPLL_CON0, "fout_mpll", NULL), + [epll] = PLL_A(pll_36xx, fout_epll, "fout_epll", "fin_pll", EPLL_LOCK, + EPLL_CON0, "fout_epll", NULL), + [vpll] = PLL_A(pll_36xx, fout_vpll, "fout_vpll", "fin_pll", VPLL_LOCK, + VPLL_CON0, "fout_vpll", NULL), +}; + /* register exynos4 clocks */ void __init exynos4_clk_init(struct device_node *np, enum exynos4_soc exynos4_soc, void __iomem *reg_base, unsigned long xom) { @@ -1029,22 +1047,16 @@ void __init exynos4_clk_init(struct device_node *np, enum exynos4_soc exynos4_so reg_base + EPLL_CON0, pll_4600); vpll = samsung_clk_register_pll46xx("fout_vpll", "mout_vpllsrc", reg_base + VPLL_CON0, pll_4650c); + + samsung_clk_add_lookup(apll, fout_apll); + samsung_clk_add_lookup(mpll, fout_mpll); + samsung_clk_add_lookup(epll, fout_epll); + samsung_clk_add_lookup(vpll, fout_vpll); } else { - apll = samsung_clk_register_pll35xx("fout_apll", "fin_pll", - reg_base + APLL_CON0); - mpll = samsung_clk_register_pll35xx("fout_mpll", "fin_pll", - reg_base + E4X12_MPLL_CON0); - epll = samsung_clk_register_pll36xx("fout_epll", "fin_pll", - reg_base + EPLL_CON0); - vpll = samsung_clk_register_pll36xx("fout_vpll", "fin_pll", - reg_base + VPLL_CON0); + samsung_clk_register_pll(exynos4_plls, + ARRAY_SIZE(exynos4_plls), reg_base); } - samsung_clk_add_lookup(apll, fout_apll); - samsung_clk_add_lookup(mpll, fout_mpll); - samsung_clk_add_lookup(epll, fout_epll); - samsung_clk_add_lookup(vpll, fout_vpll); - samsung_clk_register_fixed_rate(exynos4_fixed_rate_clks, ARRAY_SIZE(exynos4_fixed_rate_clks)); samsung_clk_register_mux(exynos4_mux_clks, diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index 6f767c515ec7..0f9a957f6054 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -17,13 +17,25 @@ #include <linux/of_address.h> #include "clk.h" -#include "clk-pll.h" +#define APLL_LOCK 0x0 +#define APLL_CON0 0x100 #define SRC_CPU 0x200 #define DIV_CPU0 0x500 +#define MPLL_LOCK 0x4000 +#define MPLL_CON0 0x4100 #define SRC_CORE1 0x4204 +#define CPLL_LOCK 0x10020 +#define EPLL_LOCK 0x10030 +#define VPLL_LOCK 0x10040 +#define GPLL_LOCK 0x10050 +#define CPLL_CON0 0x10120 +#define EPLL_CON0 0x10130 +#define VPLL_CON0 0x10140 +#define GPLL_CON0 0x10150 #define SRC_TOP0 0x10210 #define SRC_TOP2 0x10218 +#define SRC_TOP3 0x1021C #define SRC_GSCL 0x10220 #define SRC_DISP1_0 0x1022c #define SRC_MAU 0x10240 @@ -58,6 +70,8 @@ #define GATE_IP_GEN 0x10934 #define GATE_IP_FSYS 0x10944 #define GATE_IP_PERIC 0x10950 +#define BPLL_LOCK 0x20010 +#define BPLL_CON0 0x20110 #define GATE_IP_PERIS 0x10960 #define SRC_CDREX 0x20200 #define PLL_DIV2_SEL 0x20a24 @@ -75,11 +89,18 @@ * device tree files. This limitation would go away when pre-processor support * for dtc would be available. */ + +/* list of PLLs to be registered */ +enum exynos5250_plls { + apll, mpll, cpll, epll, vpll, gpll, bpll, + nr_plls /* number of PLLs */ +}; + enum exynos5250_clks { none, /* core clocks */ - fin_pll, + fin_pll, sclk_vpll, fout_apll, fout_mpll, fout_bpll, fout_gpll, fout_cpll, fout_epll, fout_vpll, /* gate for special clocks (sclk) */ sclk_cam_bayer = 128, sclk_cam0, sclk_cam1, sclk_gscl_wa, sclk_gscl_wb, @@ -87,7 +108,7 @@ enum exynos5250_clks { sclk_mmc0, sclk_mmc1, sclk_mmc2, sclk_mmc3, sclk_sata, sclk_usb3, sclk_jpeg, sclk_uart0, sclk_uart1, sclk_uart2, sclk_uart3, sclk_pwm, sclk_audio1, sclk_audio2, sclk_spdif, sclk_spi0, sclk_spi1, sclk_spi2, - div_i2s1, div_i2s2, + div_i2s1, div_i2s2, sclk_hdmiphy, /* gate clocks */ gscl0 = 256, gscl1, gscl2, gscl3, gscl_wa, gscl_wb, smmu_gscl0, @@ -99,7 +120,13 @@ enum exynos5250_clks { spi2, i2s1, i2s2, pcm1, pcm2, pwm, spdif, ac97, hsi2c0, hsi2c1, hsi2c2, hsi2c3, chipid, sysreg, pmu, cmu_top, cmu_core, cmu_mem, tzpc0, tzpc1, tzpc2, tzpc3, tzpc4, tzpc5, tzpc6, tzpc7, tzpc8, tzpc9, hdmi_cec, mct, - wdt, rtc, tmu, fimd1, mie1, dsim0, dp, mixer, hdmi, + wdt, rtc, tmu, fimd1, mie1, dsim0, dp, mixer, hdmi, smmu_mixer, + + /* mux clocks */ + mout_hdmi = 1024, + + /* mux clocks */ + mout_fimd1, nr_clks, }; @@ -169,6 +196,7 @@ PNAME(mout_mpll_user_p) = { "fin_pll", "sclk_mpll" }; PNAME(mout_bpll_user_p) = { "fin_pll", "sclk_bpll" }; PNAME(mout_aclk166_p) = { "sclk_cpll", "sclk_mpll_user" }; PNAME(mout_aclk200_p) = { "sclk_mpll_user", "sclk_bpll_user" }; +PNAME(mout_aclk200_disp1_sub_p) = { "fin_pll", "aclk200" }; PNAME(mout_hdmi_p) = { "div_hdmi_pixel", "sclk_hdmiphy" }; PNAME(mout_usb3_p) = { "sclk_mpll_user", "sclk_cpll" }; PNAME(mout_group1_p) = { "fin_pll", "fin_pll", "sclk_hdmi27m", @@ -197,7 +225,7 @@ struct samsung_fixed_rate_clock exynos5250_fixed_rate_ext_clks[] __initdata = { /* fixed rate clocks generated inside the soc */ struct samsung_fixed_rate_clock exynos5250_fixed_rate_clks[] __initdata = { - FRATE(none, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 24000000), + FRATE(sclk_hdmiphy, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 24000000), FRATE(none, "sclk_hdmi27m", NULL, CLK_IS_ROOT, 27000000), FRATE(none, "sclk_dptxphy", NULL, CLK_IS_ROOT, 24000000), FRATE(none, "sclk_uhostphy", NULL, CLK_IS_ROOT, 48000000), @@ -208,6 +236,10 @@ struct samsung_fixed_factor_clock exynos5250_fixed_factor_clks[] __initdata = { FFACTOR(none, "fout_bplldiv2", "fout_bpll", 1, 2, 0), }; +static struct samsung_mux_clock exynos5250_pll_pmux_clks[] __initdata = { + MUX(none, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP2, 0, 1), +}; + struct samsung_mux_clock exynos5250_mux_clks[] __initdata = { MUX_A(none, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, "mout_apll"), MUX_A(none, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1, "mout_cpu"), @@ -215,8 +247,8 @@ struct samsung_mux_clock exynos5250_mux_clks[] __initdata = { MUX_A(none, "sclk_mpll", mout_mpll_p, SRC_CORE1, 8, 1, "mout_mpll"), MUX(none, "mout_bpll_fout", mout_bpll_fout_p, PLL_DIV2_SEL, 0, 1), MUX(none, "sclk_bpll", mout_bpll_p, SRC_CDREX, 0, 1), - MUX(none, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP2, 0, 1), - MUX(none, "sclk_vpll", mout_vpll_p, SRC_TOP2, 16, 1), + MUX_F(sclk_vpll, "sclk_vpll", mout_vpll_p, SRC_TOP2, 16, 1, + CLK_SET_RATE_PARENT, 0), MUX(none, "sclk_epll", mout_epll_p, SRC_TOP2, 12, 1), MUX(none, "sclk_cpll", mout_cpll_p, SRC_TOP2, 8, 1), MUX(none, "sclk_mpll_user", mout_mpll_user_p, SRC_TOP2, 20, 1), @@ -224,15 +256,18 @@ struct samsung_mux_clock exynos5250_mux_clks[] __initdata = { MUX(none, "mout_aclk166", mout_aclk166_p, SRC_TOP0, 8, 1), MUX(none, "mout_aclk333", mout_aclk166_p, SRC_TOP0, 16, 1), MUX(none, "mout_aclk200", mout_aclk200_p, SRC_TOP0, 12, 1), + MUX(none, "mout_aclk200_disp1", mout_aclk200_disp1_sub_p, + SRC_TOP3, 4, 1), MUX(none, "mout_cam_bayer", mout_group1_p, SRC_GSCL, 12, 4), MUX(none, "mout_cam0", mout_group1_p, SRC_GSCL, 16, 4), MUX(none, "mout_cam1", mout_group1_p, SRC_GSCL, 20, 4), MUX(none, "mout_gscl_wa", mout_group1_p, SRC_GSCL, 24, 4), MUX(none, "mout_gscl_wb", mout_group1_p, SRC_GSCL, 28, 4), - MUX(none, "mout_fimd1", mout_group1_p, SRC_DISP1_0, 0, 4), + MUX_F(mout_fimd1, "mout_fimd1", mout_group1_p, SRC_DISP1_0, 0, 4, + CLK_SET_RATE_PARENT, 0), MUX(none, "mout_mipi1", mout_group1_p, SRC_DISP1_0, 12, 4), MUX(none, "mout_dp", mout_group1_p, SRC_DISP1_0, 16, 4), - MUX(none, "mout_hdmi", mout_hdmi_p, SRC_DISP1_0, 20, 1), + MUX(mout_hdmi, "mout_hdmi", mout_hdmi_p, SRC_DISP1_0, 20, 1), MUX(none, "mout_audio0", mout_audio0_p, SRC_MAU, 0, 4), MUX(none, "mout_mmc0", mout_group1_p, SRC_FSYS, 0, 4), MUX(none, "mout_mmc1", mout_group1_p, SRC_FSYS, 4, 4), @@ -268,7 +303,8 @@ struct samsung_div_clock exynos5250_div_clks[] __initdata = { DIV(none, "div_cam1", "mout_cam1", DIV_GSCL, 20, 4), DIV(none, "div_gscl_wa", "mout_gscl_wa", DIV_GSCL, 24, 4), DIV(none, "div_gscl_wb", "mout_gscl_wb", DIV_GSCL, 28, 4), - DIV(none, "div_fimd1", "mout_fimd1", DIV_DISP1_0, 0, 4), + DIV_F(none, "div_fimd1", "mout_fimd1", DIV_DISP1_0, 0, 4, + CLK_SET_RATE_PARENT, 0), DIV(none, "div_mipi1", "mout_mipi1", DIV_DISP1_0, 16, 4), DIV(none, "div_dp", "mout_dp", DIV_DISP1_0, 24, 4), DIV(none, "div_jpeg", "mout_jpeg", DIV_GEN, 4, 4), @@ -325,6 +361,7 @@ struct samsung_gate_clock exynos5250_gate_clks[] __initdata = { GATE(smmu_gscl1, "smmu_gscl1", "aclk266", GATE_IP_GSCL, 8, 0, 0), GATE(smmu_gscl2, "smmu_gscl2", "aclk266", GATE_IP_GSCL, 9, 0, 0), GATE(smmu_gscl3, "smmu_gscl3", "aclk266", GATE_IP_GSCL, 10, 0, 0), + GATE(smmu_mixer, "smmu_mixer", "mout_aclk200_disp1", GATE_IP_DISP1, 9, 0, 0), GATE(mfc, "mfc", "aclk333", GATE_IP_MFC, 0, 0, 0), GATE(smmu_mfcl, "smmu_mfcl", "aclk333", GATE_IP_MFC, 1, 0, 0), GATE(smmu_mfcr, "smmu_mfcr", "aclk333", GATE_IP_MFC, 2, 0, 0), @@ -461,8 +498,48 @@ struct samsung_gate_clock exynos5250_gate_clks[] __initdata = { GATE(mie1, "mie1", "aclk200", GATE_IP_DISP1, 1, 0, 0), GATE(dsim0, "dsim0", "aclk200", GATE_IP_DISP1, 3, 0, 0), GATE(dp, "dp", "aclk200", GATE_IP_DISP1, 4, 0, 0), - GATE(mixer, "mixer", "aclk200", GATE_IP_DISP1, 5, 0, 0), - GATE(hdmi, "hdmi", "aclk200", GATE_IP_DISP1, 6, 0, 0), + GATE(mixer, "mixer", "mout_aclk200_disp1", GATE_IP_DISP1, 5, 0, 0), + GATE(hdmi, "hdmi", "mout_aclk200_disp1", GATE_IP_DISP1, 6, 0, 0), +}; + +static __initdata struct samsung_pll_rate_table vpll_24mhz_tbl[] = { + /* sorted in descending order */ + /* PLL_36XX_RATE(rate, m, p, s, k) */ + PLL_36XX_RATE(266000000, 266, 3, 3, 0), + /* Not in UM, but need for eDP on snow */ + PLL_36XX_RATE(70500000, 94, 2, 4, 0), + { }, +}; + +static __initdata struct samsung_pll_rate_table epll_24mhz_tbl[] = { + /* sorted in descending order */ + /* PLL_36XX_RATE(rate, m, p, s, k) */ + PLL_36XX_RATE(192000000, 64, 2, 2, 0), + PLL_36XX_RATE(180633600, 90, 3, 2, 20762), + PLL_36XX_RATE(180000000, 90, 3, 2, 0), + PLL_36XX_RATE(73728000, 98, 2, 4, 19923), + PLL_36XX_RATE(67737600, 90, 2, 4, 20762), + PLL_36XX_RATE(49152000, 98, 3, 4, 19923), + PLL_36XX_RATE(45158400, 90, 3, 4, 20762), + PLL_36XX_RATE(32768000, 131, 3, 5, 4719), + { }, +}; + +struct __initdata samsung_pll_clock exynos5250_plls[nr_plls] = { + [apll] = PLL_A(pll_35xx, fout_apll, "fout_apll", "fin_pll", APLL_LOCK, + APLL_CON0, "fout_apll", NULL), + [mpll] = PLL_A(pll_35xx, fout_mpll, "fout_mpll", "fin_pll", MPLL_LOCK, + MPLL_CON0, "fout_mpll", NULL), + [bpll] = PLL(pll_35xx, fout_bpll, "fout_bpll", "fin_pll", BPLL_LOCK, + BPLL_CON0, NULL), + [gpll] = PLL(pll_35xx, fout_gpll, "fout_gpll", "fin_pll", GPLL_LOCK, + GPLL_CON0, NULL), + [cpll] = PLL(pll_35xx, fout_cpll, "fout_cpll", "fin_pll", CPLL_LOCK, + CPLL_CON0, NULL), + [epll] = PLL(pll_36xx, fout_epll, "fout_epll", "fin_pll", EPLL_LOCK, + EPLL_CON0, NULL), + [vpll] = PLL(pll_36xx, fout_vpll, "fout_vpll", "mout_vpllsrc", + VPLL_LOCK, VPLL_CON0, NULL), }; static __initdata struct of_device_id ext_clk_match[] = { @@ -474,7 +551,8 @@ static __initdata struct of_device_id ext_clk_match[] = { void __init exynos5250_clk_init(struct device_node *np) { void __iomem *reg_base; - struct clk *apll, *mpll, *epll, *vpll, *bpll, *gpll, *cpll; + struct clk *vpllsrc; + unsigned long fin_pll_rate, mout_vpllsrc_rate = 0; if (np) { reg_base = of_iomap(np, 0); @@ -490,22 +568,23 @@ void __init exynos5250_clk_init(struct device_node *np) samsung_clk_of_register_fixed_ext(exynos5250_fixed_rate_ext_clks, ARRAY_SIZE(exynos5250_fixed_rate_ext_clks), ext_clk_match); + samsung_clk_register_mux(exynos5250_pll_pmux_clks, + ARRAY_SIZE(exynos5250_pll_pmux_clks)); + + fin_pll_rate = _get_rate("fin_pll"); + + if (fin_pll_rate == 24 * MHZ) + exynos5250_plls[epll].rate_table = epll_24mhz_tbl; + + vpllsrc = __clk_lookup("mout_vpllsrc"); + if (vpllsrc) + mout_vpllsrc_rate = clk_get_rate(vpllsrc); - apll = samsung_clk_register_pll35xx("fout_apll", "fin_pll", - reg_base + 0x100); - mpll = samsung_clk_register_pll35xx("fout_mpll", "fin_pll", - reg_base + 0x4100); - bpll = samsung_clk_register_pll35xx("fout_bpll", "fin_pll", - reg_base + 0x20110); - gpll = samsung_clk_register_pll35xx("fout_gpll", "fin_pll", - reg_base + 0x10150); - cpll = samsung_clk_register_pll35xx("fout_cpll", "fin_pll", - reg_base + 0x10120); - epll = samsung_clk_register_pll36xx("fout_epll", "fin_pll", - reg_base + 0x10130); - vpll = samsung_clk_register_pll36xx("fout_vpll", "mout_vpllsrc", - reg_base + 0x10140); + if (mout_vpllsrc_rate == 24 * MHZ) + exynos5250_plls[vpll].rate_table = vpll_24mhz_tbl; + samsung_clk_register_pll(exynos5250_plls, ARRAY_SIZE(exynos5250_plls), + reg_base); samsung_clk_register_fixed_rate(exynos5250_fixed_rate_clks, ARRAY_SIZE(exynos5250_fixed_rate_clks)); samsung_clk_register_fixed_factor(exynos5250_fixed_factor_clks, diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 68a96cbd4936..86dfc6440438 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -17,13 +17,30 @@ #include <linux/of_address.h> #include "clk.h" -#include "clk-pll.h" +#define APLL_LOCK 0x0 +#define APLL_CON0 0x100 #define SRC_CPU 0x200 #define DIV_CPU0 0x500 #define DIV_CPU1 0x504 #define GATE_BUS_CPU 0x700 #define GATE_SCLK_CPU 0x800 +#define CPLL_LOCK 0x10020 +#define DPLL_LOCK 0x10030 +#define EPLL_LOCK 0x10040 +#define RPLL_LOCK 0x10050 +#define IPLL_LOCK 0x10060 +#define SPLL_LOCK 0x10070 +#define VPLL_LOCK 0x10070 +#define MPLL_LOCK 0x10090 +#define CPLL_CON0 0x10120 +#define DPLL_CON0 0x10128 +#define EPLL_CON0 0x10130 +#define RPLL_CON0 0x10140 +#define IPLL_CON0 0x10150 +#define SPLL_CON0 0x10160 +#define VPLL_CON0 0x10170 +#define MPLL_CON0 0x10180 #define SRC_TOP0 0x10200 #define SRC_TOP1 0x10204 #define SRC_TOP2 0x10208 @@ -75,15 +92,27 @@ #define GATE_TOP_SCLK_MAU 0x1083c #define GATE_TOP_SCLK_FSYS 0x10840 #define GATE_TOP_SCLK_PERIC 0x10850 +#define BPLL_LOCK 0x20010 +#define BPLL_CON0 0x20110 #define SRC_CDREX 0x20200 +#define KPLL_LOCK 0x28000 +#define KPLL_CON0 0x28100 #define SRC_KFC 0x28200 #define DIV_KFC0 0x28500 +/* list of PLLs */ +enum exynos5420_plls { + apll, cpll, dpll, epll, rpll, ipll, spll, vpll, mpll, + bpll, kpll, + nr_plls /* number of PLLs */ +}; + enum exynos5420_clks { none, /* core clocks */ - fin_pll, + fin_pll, fout_apll, fout_cpll, fout_dpll, fout_epll, fout_rpll, + fout_ipll, fout_spll, fout_vpll, fout_mpll, fout_bpll, fout_kpll, /* gate for special clocks (sclk) */ sclk_uart0 = 128, sclk_uart1, sclk_uart2, sclk_uart3, sclk_mmc0, @@ -698,6 +727,31 @@ struct samsung_gate_clock exynos5420_gate_clks[] __initdata = { GATE(smmu_mscl2, "smmu_mscl2", "aclk400_mscl", GATE_IP_MSCL, 10, 0, 0), }; +struct __initdata samsung_pll_clock exynos5420_plls[nr_plls] = { + [apll] = PLL(pll_2550, fout_apll, "fout_apll", "fin_pll", APLL_LOCK, + APLL_CON0, NULL), + [cpll] = PLL(pll_2550, fout_mpll, "fout_mpll", "fin_pll", MPLL_LOCK, + MPLL_CON0, NULL), + [dpll] = PLL(pll_2550, fout_dpll, "fout_dpll", "fin_pll", DPLL_LOCK, + DPLL_CON0, NULL), + [epll] = PLL(pll_2650, fout_epll, "fout_epll", "fin_pll", EPLL_LOCK, + EPLL_CON0, NULL), + [rpll] = PLL(pll_2650, fout_rpll, "fout_rpll", "fin_pll", RPLL_LOCK, + RPLL_CON0, NULL), + [ipll] = PLL(pll_2550, fout_ipll, "fout_ipll", "fin_pll", IPLL_LOCK, + IPLL_CON0, NULL), + [spll] = PLL(pll_2550, fout_spll, "fout_spll", "fin_pll", SPLL_LOCK, + SPLL_CON0, NULL), + [vpll] = PLL(pll_2550, fout_vpll, "fout_vpll", "fin_pll", VPLL_LOCK, + VPLL_CON0, NULL), + [mpll] = PLL(pll_2550, fout_mpll, "fout_mpll", "fin_pll", MPLL_LOCK, + MPLL_CON0, NULL), + [bpll] = PLL(pll_2550, fout_bpll, "fout_bpll", "fin_pll", BPLL_LOCK, + BPLL_CON0, NULL), + [kpll] = PLL(pll_2550, fout_kpll, "fout_kpll", "fin_pll", KPLL_LOCK, + KPLL_CON0, NULL), +}; + static __initdata struct of_device_id ext_clk_match[] = { { .compatible = "samsung,exynos5420-oscclk", .data = (void *)0, }, { }, @@ -707,8 +761,6 @@ static __initdata struct of_device_id ext_clk_match[] = { void __init exynos5420_clk_init(struct device_node *np) { void __iomem *reg_base; - struct clk *apll, *bpll, *cpll, *dpll, *epll, *ipll, *kpll, *mpll; - struct clk *rpll, *spll, *vpll; if (np) { reg_base = of_iomap(np, 0); @@ -724,30 +776,8 @@ void __init exynos5420_clk_init(struct device_node *np) samsung_clk_of_register_fixed_ext(exynos5420_fixed_rate_ext_clks, ARRAY_SIZE(exynos5420_fixed_rate_ext_clks), ext_clk_match); - - apll = samsung_clk_register_pll35xx("fout_apll", "fin_pll", - reg_base + 0x100); - bpll = samsung_clk_register_pll35xx("fout_bpll", "fin_pll", - reg_base + 0x20110); - cpll = samsung_clk_register_pll35xx("fout_cpll", "fin_pll", - reg_base + 0x10120); - dpll = samsung_clk_register_pll35xx("fout_dpll", "fin_pll", - reg_base + 0x10128); - epll = samsung_clk_register_pll36xx("fout_epll", "fin_pll", - reg_base + 0x10130); - ipll = samsung_clk_register_pll35xx("fout_ipll", "fin_pll", - reg_base + 0x10150); - kpll = samsung_clk_register_pll35xx("fout_kpll", "fin_pll", - reg_base + 0x28100); - mpll = samsung_clk_register_pll35xx("fout_mpll", "fin_pll", - reg_base + 0x10180); - rpll = samsung_clk_register_pll36xx("fout_rpll", "fin_pll", - reg_base + 0x10140); - spll = samsung_clk_register_pll35xx("fout_spll", "fin_pll", - reg_base + 0x10160); - vpll = samsung_clk_register_pll35xx("fout_vpll", "fin_pll", - reg_base + 0x10170); - + samsung_clk_register_pll(exynos5420_plls, ARRAY_SIZE(exynos5420_plls), + reg_base); samsung_clk_register_fixed_rate(exynos5420_fixed_rate_clks, ARRAY_SIZE(exynos5420_fixed_rate_clks)); samsung_clk_register_fixed_factor(exynos5420_fixed_factor_clks, diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index 362f12dcd944..f80efb6bc0b3 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -13,28 +13,67 @@ #include "clk.h" #include "clk-pll.h" +struct samsung_clk_pll { + struct clk_hw hw; + void __iomem *lock_reg; + void __iomem *con_reg; + enum samsung_pll_type type; + unsigned int rate_count; + const struct samsung_pll_rate_table *rate_table; +}; + +#define to_clk_pll(_hw) container_of(_hw, struct samsung_clk_pll, hw) + +static const struct samsung_pll_rate_table *samsung_get_pll_settings( + struct samsung_clk_pll *pll, unsigned long rate) +{ + const struct samsung_pll_rate_table *rate_table = pll->rate_table; + int i; + + for (i = 0; i < pll->rate_count; i++) { + if (rate == rate_table[i].rate) + return &rate_table[i]; + } + + return NULL; +} + +static long samsung_pll_round_rate(struct clk_hw *hw, + unsigned long drate, unsigned long *prate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + const struct samsung_pll_rate_table *rate_table = pll->rate_table; + int i; + + /* Assumming rate_table is in descending order */ + for (i = 0; i < pll->rate_count; i++) { + if (drate >= rate_table[i].rate) + return rate_table[i].rate; + } + + /* return minimum supported value */ + return rate_table[i - 1].rate; +} + /* * PLL35xx Clock Type */ +/* Maximum lock time can be 270 * PDIV cycles */ +#define PLL35XX_LOCK_FACTOR (270) #define PLL35XX_MDIV_MASK (0x3FF) #define PLL35XX_PDIV_MASK (0x3F) #define PLL35XX_SDIV_MASK (0x7) +#define PLL35XX_LOCK_STAT_MASK (0x1) #define PLL35XX_MDIV_SHIFT (16) #define PLL35XX_PDIV_SHIFT (8) #define PLL35XX_SDIV_SHIFT (0) - -struct samsung_clk_pll35xx { - struct clk_hw hw; - const void __iomem *con_reg; -}; - -#define to_clk_pll35xx(_hw) container_of(_hw, struct samsung_clk_pll35xx, hw) +#define PLL35XX_LOCK_STAT_SHIFT (29) static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct samsung_clk_pll35xx *pll = to_clk_pll35xx(hw); + struct samsung_clk_pll *pll = to_clk_pll(hw); u32 mdiv, pdiv, sdiv, pll_con; u64 fvco = parent_rate; @@ -49,48 +88,80 @@ static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw, return (unsigned long)fvco; } -static const struct clk_ops samsung_pll35xx_clk_ops = { - .recalc_rate = samsung_pll35xx_recalc_rate, -}; - -struct clk * __init samsung_clk_register_pll35xx(const char *name, - const char *pname, const void __iomem *con_reg) +static inline bool samsung_pll35xx_mp_change( + const struct samsung_pll_rate_table *rate, u32 pll_con) { - struct samsung_clk_pll35xx *pll; - struct clk *clk; - struct clk_init_data init; + u32 old_mdiv, old_pdiv; - pll = kzalloc(sizeof(*pll), GFP_KERNEL); - if (!pll) { - pr_err("%s: could not allocate pll clk %s\n", __func__, name); - return NULL; + old_mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK; + old_pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK; + + return (rate->mdiv != old_mdiv || rate->pdiv != old_pdiv); +} + +static int samsung_pll35xx_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + const struct samsung_pll_rate_table *rate; + u32 tmp; + + /* Get required rate settings from table */ + rate = samsung_get_pll_settings(pll, drate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, __clk_get_name(hw->clk)); + return -EINVAL; } - init.name = name; - init.ops = &samsung_pll35xx_clk_ops; - init.flags = CLK_GET_RATE_NOCACHE; - init.parent_names = &pname; - init.num_parents = 1; + tmp = __raw_readl(pll->con_reg); - pll->hw.init = &init; - pll->con_reg = con_reg; + if (!(samsung_pll35xx_mp_change(rate, tmp))) { + /* If only s change, change just s value only*/ + tmp &= ~(PLL35XX_SDIV_MASK << PLL35XX_SDIV_SHIFT); + tmp |= rate->sdiv << PLL35XX_SDIV_SHIFT; + __raw_writel(tmp, pll->con_reg); - clk = clk_register(NULL, &pll->hw); - if (IS_ERR(clk)) { - pr_err("%s: failed to register pll clock %s\n", __func__, - name); - kfree(pll); + return 0; } - if (clk_register_clkdev(clk, name, NULL)) - pr_err("%s: failed to register lookup for %s", __func__, name); - - return clk; + /* Set PLL lock time. */ + __raw_writel(rate->pdiv * PLL35XX_LOCK_FACTOR, + pll->lock_reg); + + /* Change PLL PMS values */ + tmp &= ~((PLL35XX_MDIV_MASK << PLL35XX_MDIV_SHIFT) | + (PLL35XX_PDIV_MASK << PLL35XX_PDIV_SHIFT) | + (PLL35XX_SDIV_MASK << PLL35XX_SDIV_SHIFT)); + tmp |= (rate->mdiv << PLL35XX_MDIV_SHIFT) | + (rate->pdiv << PLL35XX_PDIV_SHIFT) | + (rate->sdiv << PLL35XX_SDIV_SHIFT); + __raw_writel(tmp, pll->con_reg); + + /* wait_lock_time */ + do { + cpu_relax(); + tmp = __raw_readl(pll->con_reg); + } while (!(tmp & (PLL35XX_LOCK_STAT_MASK + << PLL35XX_LOCK_STAT_SHIFT))); + return 0; } +static const struct clk_ops samsung_pll35xx_clk_ops = { + .recalc_rate = samsung_pll35xx_recalc_rate, + .round_rate = samsung_pll_round_rate, + .set_rate = samsung_pll35xx_set_rate, +}; + +static const struct clk_ops samsung_pll35xx_clk_min_ops = { + .recalc_rate = samsung_pll35xx_recalc_rate, +}; + /* * PLL36xx Clock Type */ +/* Maximum lock time can be 3000 * PDIV cycles */ +#define PLL36XX_LOCK_FACTOR (3000) #define PLL36XX_KDIV_MASK (0xFFFF) #define PLL36XX_MDIV_MASK (0x1FF) @@ -99,18 +170,13 @@ struct clk * __init samsung_clk_register_pll35xx(const char *name, #define PLL36XX_MDIV_SHIFT (16) #define PLL36XX_PDIV_SHIFT (8) #define PLL36XX_SDIV_SHIFT (0) - -struct samsung_clk_pll36xx { - struct clk_hw hw; - const void __iomem *con_reg; -}; - -#define to_clk_pll36xx(_hw) container_of(_hw, struct samsung_clk_pll36xx, hw) +#define PLL36XX_KDIV_SHIFT (0) +#define PLL36XX_LOCK_STAT_SHIFT (29) static unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct samsung_clk_pll36xx *pll = to_clk_pll36xx(hw); + struct samsung_clk_pll *pll = to_clk_pll(hw); u32 mdiv, pdiv, sdiv, pll_con0, pll_con1; s16 kdiv; u64 fvco = parent_rate; @@ -129,45 +195,80 @@ static unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw, return (unsigned long)fvco; } -static const struct clk_ops samsung_pll36xx_clk_ops = { - .recalc_rate = samsung_pll36xx_recalc_rate, -}; - -struct clk * __init samsung_clk_register_pll36xx(const char *name, - const char *pname, const void __iomem *con_reg) +static inline bool samsung_pll36xx_mpk_change( + const struct samsung_pll_rate_table *rate, u32 pll_con0, u32 pll_con1) { - struct samsung_clk_pll36xx *pll; - struct clk *clk; - struct clk_init_data init; + u32 old_mdiv, old_pdiv, old_kdiv; - pll = kzalloc(sizeof(*pll), GFP_KERNEL); - if (!pll) { - pr_err("%s: could not allocate pll clk %s\n", __func__, name); - return NULL; + old_mdiv = (pll_con0 >> PLL36XX_MDIV_SHIFT) & PLL36XX_MDIV_MASK; + old_pdiv = (pll_con0 >> PLL36XX_PDIV_SHIFT) & PLL36XX_PDIV_MASK; + old_kdiv = (pll_con1 >> PLL36XX_KDIV_SHIFT) & PLL36XX_KDIV_MASK; + + return (rate->mdiv != old_mdiv || rate->pdiv != old_pdiv || + rate->kdiv != old_kdiv); +} + +static int samsung_pll36xx_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long parent_rate) +{ + struct samsung_clk_pll *pll = to_clk_pll(hw); + u32 tmp, pll_con0, pll_con1; + const struct samsung_pll_rate_table *rate; + + rate = samsung_get_pll_settings(pll, drate); + if (!rate) { + pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, + drate, __clk_get_name(hw->clk)); + return -EINVAL; } - init.name = name; - init.ops = &samsung_pll36xx_clk_ops; - init.flags = CLK_GET_RATE_NOCACHE; - init.parent_names = &pname; - init.num_parents = 1; + pll_con0 = __raw_readl(pll->con_reg); + pll_con1 = __raw_readl(pll->con_reg + 4); - pll->hw.init = &init; - pll->con_reg = con_reg; + if (!(samsung_pll36xx_mpk_change(rate, pll_con0, pll_con1))) { + /* If only s change, change just s value only*/ + pll_con0 &= ~(PLL36XX_SDIV_MASK << PLL36XX_SDIV_SHIFT); + pll_con0 |= (rate->sdiv << PLL36XX_SDIV_SHIFT); + __raw_writel(pll_con0, pll->con_reg); - clk = clk_register(NULL, &pll->hw); - if (IS_ERR(clk)) { - pr_err("%s: failed to register pll clock %s\n", __func__, - name); - kfree(pll); + return 0; } - if (clk_register_clkdev(clk, name, NULL)) - pr_err("%s: failed to register lookup for %s", __func__, name); - - return clk; + /* Set PLL lock time. */ + __raw_writel(rate->pdiv * PLL36XX_LOCK_FACTOR, pll->lock_reg); + + /* Change PLL PMS values */ + pll_con0 &= ~((PLL36XX_MDIV_MASK << PLL36XX_MDIV_SHIFT) | + (PLL36XX_PDIV_MASK << PLL36XX_PDIV_SHIFT) | + (PLL36XX_SDIV_MASK << PLL36XX_SDIV_SHIFT)); + pll_con0 |= (rate->mdiv << PLL36XX_MDIV_SHIFT) | + (rate->pdiv << PLL36XX_PDIV_SHIFT) | + (rate->sdiv << PLL36XX_SDIV_SHIFT); + __raw_writel(pll_con0, pll->con_reg); + + pll_con1 &= ~(PLL36XX_KDIV_MASK << PLL36XX_KDIV_SHIFT); + pll_con1 |= rate->kdiv << PLL36XX_KDIV_SHIFT; + __raw_writel(pll_con1, pll->con_reg + 4); + + /* wait_lock_time */ + do { + cpu_relax(); + tmp = __raw_readl(pll->con_reg); + } while (!(tmp & (1 << PLL36XX_LOCK_STAT_SHIFT))); + + return 0; } +static const struct clk_ops samsung_pll36xx_clk_ops = { + .recalc_rate = samsung_pll36xx_recalc_rate, + .set_rate = samsung_pll36xx_set_rate, + .round_rate = samsung_pll_round_rate, +}; + +static const struct clk_ops samsung_pll36xx_clk_min_ops = { + .recalc_rate = samsung_pll36xx_recalc_rate, +}; + /* * PLL45xx Clock Type */ @@ -418,3 +519,93 @@ struct clk * __init samsung_clk_register_pll2550x(const char *name, return clk; } + +static void __init _samsung_clk_register_pll(struct samsung_pll_clock *pll_clk, + void __iomem *base) +{ + struct samsung_clk_pll *pll; + struct clk *clk; + struct clk_init_data init; + int ret, len; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) { + pr_err("%s: could not allocate pll clk %s\n", + __func__, pll_clk->name); + return; + } + + init.name = pll_clk->name; + init.flags = pll_clk->flags; + init.parent_names = &pll_clk->parent_name; + init.num_parents = 1; + + if (pll_clk->rate_table) { + /* find count of rates in rate_table */ + for (len = 0; pll_clk->rate_table[len].rate != 0; ) + len++; + + pll->rate_count = len; + pll->rate_table = kmemdup(pll_clk->rate_table, + pll->rate_count * + sizeof(struct samsung_pll_rate_table), + GFP_KERNEL); + WARN(!pll->rate_table, + "%s: could not allocate rate table for %s\n", + __func__, pll_clk->name); + } + + switch (pll_clk->type) { + /* clk_ops for 35xx and 2550 are similar */ + case pll_35xx: + case pll_2550: + if (!pll->rate_table) + init.ops = &samsung_pll35xx_clk_min_ops; + else + init.ops = &samsung_pll35xx_clk_ops; + break; + /* clk_ops for 36xx and 2650 are similar */ + case pll_36xx: + case pll_2650: + if (!pll->rate_table) + init.ops = &samsung_pll36xx_clk_min_ops; + else + init.ops = &samsung_pll36xx_clk_ops; + break; + default: + pr_warn("%s: Unknown pll type for pll clk %s\n", + __func__, pll_clk->name); + } + + pll->hw.init = &init; + pll->type = pll_clk->type; + pll->lock_reg = base + pll_clk->lock_offset; + pll->con_reg = base + pll_clk->con_offset; + + clk = clk_register(NULL, &pll->hw); + if (IS_ERR(clk)) { + pr_err("%s: failed to register pll clock %s : %ld\n", + __func__, pll_clk->name, PTR_ERR(clk)); + kfree(pll); + return; + } + + samsung_clk_add_lookup(clk, pll_clk->id); + + if (!pll_clk->alias) + return; + + ret = clk_register_clkdev(clk, pll_clk->alias, pll_clk->dev_name); + if (ret) + pr_err("%s: failed to register lookup for %s : %d", + __func__, pll_clk->name, ret); +} + +void __init samsung_clk_register_pll(struct samsung_pll_clock *pll_list, + unsigned int nr_pll, void __iomem *base) +{ + int cnt; + + for (cnt = 0; cnt < nr_pll; cnt++) + _samsung_clk_register_pll(&pll_list[cnt], base); +} diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h index f33786e9a78b..95ae23d75b3c 100644 --- a/drivers/clk/samsung/clk-pll.h +++ b/drivers/clk/samsung/clk-pll.h @@ -12,6 +12,40 @@ #ifndef __SAMSUNG_CLK_PLL_H #define __SAMSUNG_CLK_PLL_H +enum samsung_pll_type { + pll_35xx, + pll_36xx, + pll_2550, + pll_2650, +}; + +#define PLL_35XX_RATE(_rate, _m, _p, _s) \ + { \ + .rate = (_rate), \ + .mdiv = (_m), \ + .pdiv = (_p), \ + .sdiv = (_s), \ + } + +#define PLL_36XX_RATE(_rate, _m, _p, _s, _k) \ + { \ + .rate = (_rate), \ + .mdiv = (_m), \ + .pdiv = (_p), \ + .sdiv = (_s), \ + .kdiv = (_k), \ + } + +/* NOTE: Rate table should be kept sorted in descending order. */ + +struct samsung_pll_rate_table { + unsigned int rate; + unsigned int pdiv; + unsigned int mdiv; + unsigned int sdiv; + unsigned int kdiv; +}; + enum pll45xx_type { pll_4500, pll_4502, @@ -24,10 +58,6 @@ enum pll46xx_type { pll_4650c, }; -extern struct clk * __init samsung_clk_register_pll35xx(const char *name, - const char *pname, const void __iomem *con_reg); -extern struct clk * __init samsung_clk_register_pll36xx(const char *name, - const char *pname, const void __iomem *con_reg); extern struct clk * __init samsung_clk_register_pll45xx(const char *name, const char *pname, const void __iomem *con_reg, enum pll45xx_type type); diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h index 2f7dba20ced8..e7dfccb5d981 100644 --- a/drivers/clk/samsung/clk.h +++ b/drivers/clk/samsung/clk.h @@ -19,6 +19,7 @@ #include <linux/clk-provider.h> #include <linux/of.h> #include <linux/of_address.h> +#include "clk-pll.h" /** * struct samsung_clock_alias: information about mux clock @@ -39,6 +40,8 @@ struct samsung_clock_alias { .alias = a, \ } +#define MHZ (1000 * 1000) + /** * struct samsung_fixed_rate_clock: information about fixed-rate clock * @id: platform specific id of the clock. @@ -261,6 +264,54 @@ struct samsung_clk_reg_dump { u32 value; }; +/** + * struct samsung_pll_clock: information about pll clock + * @id: platform specific id of the clock. + * @dev_name: name of the device to which this clock belongs. + * @name: name of this pll clock. + * @parent_name: name of the parent clock. + * @flags: optional flags for basic clock. + * @con_offset: offset of the register for configuring the PLL. + * @lock_offset: offset of the register for locking the PLL. + * @type: Type of PLL to be registered. + * @alias: optional clock alias name to be assigned to this clock. + */ +struct samsung_pll_clock { + unsigned int id; + const char *dev_name; + const char *name; + const char *parent_name; + unsigned long flags; + int con_offset; + int lock_offset; + enum samsung_pll_type type; + const struct samsung_pll_rate_table *rate_table; + const char *alias; +}; + +#define __PLL(_typ, _id, _dname, _name, _pname, _flags, _lock, _con, \ + _rtable, _alias) \ + { \ + .id = _id, \ + .type = _typ, \ + .dev_name = _dname, \ + .name = _name, \ + .parent_name = _pname, \ + .flags = CLK_GET_RATE_NOCACHE, \ + .con_offset = _con, \ + .lock_offset = _lock, \ + .rate_table = _rtable, \ + .alias = _alias, \ + } + +#define PLL(_typ, _id, _name, _pname, _lock, _con, _rtable) \ + __PLL(_typ, _id, NULL, _name, _pname, CLK_GET_RATE_NOCACHE, \ + _lock, _con, _rtable, _name) + +#define PLL_A(_typ, _id, _name, _pname, _lock, _con, _alias, _rtable) \ + __PLL(_typ, _id, NULL, _name, _pname, CLK_GET_RATE_NOCACHE, \ + _lock, _con, _rtable, _alias) + extern void __init samsung_clk_init(struct device_node *np, void __iomem *base, unsigned long nr_clks, unsigned long *rdump, unsigned long nr_rdump, unsigned long *soc_rdump, @@ -284,6 +335,8 @@ extern void __init samsung_clk_register_div(struct samsung_div_clock *clk_list, unsigned int nr_clk); extern void __init samsung_clk_register_gate( struct samsung_gate_clock *clk_list, unsigned int nr_clk); +extern void __init samsung_clk_register_pll(struct samsung_pll_clock *pll_list, + unsigned int nr_clk, void __iomem *base); extern unsigned long _get_rate(const char *clk_name); diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index b2bbc415f120..23d318267da8 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -27,6 +27,8 @@ #include <asm/localtimer.h> #include <asm/mach/time.h> +#include <plat/cpu.h> + #define EXYNOS4_MCTREG(x) (x) #define EXYNOS4_MCT_G_CNT_L EXYNOS4_MCTREG(0x100) #define EXYNOS4_MCT_G_CNT_U EXYNOS4_MCTREG(0x104) @@ -194,6 +196,10 @@ static void __init exynos4_clocksource_init(void) { exynos4_mct_frc_start(0, 0); + if (soc_is_exynos5250()) { + mct_frc.rating = 399; + } + if (clocksource_register_hz(&mct_frc, clk_rate)) panic("%s: can't register clocksource\n", mct_frc.name); } diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index 0d32f02ef4d6..630b503736e0 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -17,6 +17,8 @@ #include <linux/regulator/consumer.h> #include <linux/cpufreq.h> #include <linux/suspend.h> +#include <linux/notifier.h> +#include <linux/reboot.h> #include <plat/cpu.h> @@ -245,8 +247,35 @@ static struct notifier_block exynos_cpufreq_nb = { .notifier_call = exynos_cpufreq_pm_notifier, }; +static int exynos_cpufreq_reboot_notifier(struct notifier_block *this, + unsigned long code, void *_cmd) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */ + mutex_lock(&cpufreq_lock); + + if (frequency_locked) + goto out; + frequency_locked = true; + + if (locking_frequency) { + mutex_unlock(&cpufreq_lock); + exynos_target(policy, locking_frequency, CPUFREQ_RELATION_H); + mutex_lock(&cpufreq_lock); + } + +out: + mutex_unlock(&cpufreq_lock); + return NOTIFY_DONE; +} + +static struct notifier_block exynos_cpufreq_reboot_nb = { + .notifier_call = exynos_cpufreq_reboot_notifier, +}; + static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) { + int ret; + policy->cur = policy->min = policy->max = exynos_getspeed(policy->cpu); cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu); @@ -256,7 +285,13 @@ static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) cpumask_setall(policy->cpus); - return cpufreq_frequency_table_cpuinfo(policy, exynos_info->freq_table); + ret = cpufreq_frequency_table_cpuinfo(policy, exynos_info->freq_table); + if (ret) + return ret; + + cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu); + return 0; + } static int exynos_cpufreq_cpu_exit(struct cpufreq_policy *policy) @@ -319,6 +354,7 @@ static int __init exynos_cpufreq_init(void) locking_frequency = exynos_getspeed(0); register_pm_notifier(&exynos_cpufreq_nb); + register_reboot_notifier(&exynos_cpufreq_reboot_nb); if (cpufreq_register_driver(&exynos_driver)) { pr_err("%s: failed to register cpufreq driver\n", __func__); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index ca2729a85129..812178df0ee2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -287,7 +287,8 @@ static struct drm_driver exynos_drm_driver = { static int exynos_drm_platform_probe(struct platform_device *pdev) { - pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + if (!pdev->dev.coherent_dma_mask) + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); exynos_drm_driver.num_ioctls = DRM_ARRAY_SIZE(exynos_ioctls); return drm_platform_init(&exynos_drm_driver, pdev); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 1c263dac3c1c..a0346ae3b59c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -17,6 +17,7 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/of_device.h> +#include <linux/of_address.h> #include <linux/pm_runtime.h> #include <video/of_display_timing.h> @@ -101,9 +102,12 @@ struct fimd_context { struct exynos_drm_subdrv subdrv; int irq; struct drm_crtc *crtc; + struct clk *sclk_mout_fimd; + struct clk *fimd_mux_clk; struct clk *bus_clk; struct clk *lcd_clk; void __iomem *regs; + void __iomem *sys_reg; struct fimd_win_data win_data[WINDOWS_NR]; unsigned int clkdiv; unsigned int default_win; @@ -784,6 +788,7 @@ static int fimd_calc_clkdiv(struct fimd_context *ctx, } } + clkdiv = 5; return clkdiv; } @@ -859,6 +864,7 @@ static int fimd_activate(struct fimd_context *ctx, bool enable) if (ret < 0) return ret; + writel((3 << 0), ctx->regs + 0x27c); ctx->suspended = false; /* if vblank was enabled status, enable it again. */ @@ -884,6 +890,7 @@ static int fimd_probe(struct platform_device *pdev) struct exynos_drm_fimd_pdata *pdata; struct exynos_drm_panel_info *panel; struct resource *res; + struct device_node *sys_reg_node; int win; int ret = -EINVAL; @@ -930,6 +937,24 @@ static int fimd_probe(struct platform_device *pdev) return PTR_ERR(ctx->lcd_clk); } + /* Set the parent for FIMD pixel clock */ + ctx->fimd_mux_clk = devm_clk_get(dev, "mout_fimd"); + if (IS_ERR(ctx->fimd_mux_clk)) { + dev_err(dev, "failed to get fimd mux clk\n"); + return PTR_ERR(ctx->fimd_mux_clk); + } + + ctx->sclk_mout_fimd = devm_clk_get(dev, "sclk_mout_fimd"); + if (IS_ERR(ctx->sclk_mout_fimd)) { + dev_err(dev, "failed to get mout_fimd parent\n"); + return PTR_ERR(ctx->sclk_mout_fimd); + } + + clk_set_parent(ctx->fimd_mux_clk, ctx->sclk_mout_fimd); + + /* Set the FIMD pixel clock to desired value */ + clk_set_rate(ctx->lcd_clk, (1000 * PICOS2KHZ(panel->timing.pixclock))); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ctx->regs = devm_ioremap_resource(dev, res); @@ -984,6 +1009,19 @@ static int fimd_probe(struct platform_device *pdev) exynos_drm_subdrv_register(subdrv); + sys_reg_node = of_parse_phandle(dev->of_node, "samsung,sys-reg", 0); + + if(!sys_reg_node) + return -EINVAL; + + ctx->sys_reg = of_iomap(sys_reg_node, 0); + if (!ctx->sys_reg) + return -EINVAL; + + writel(((1 << 15) | readl(ctx->sys_reg + 0x214)), ctx->sys_reg + 0x214); + + writel((3 << 0), ctx->regs + 0x27c); + return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 2f5c6942c968..44361abe2320 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -33,6 +33,7 @@ #include <linux/regulator/consumer.h> #include <linux/io.h> #include <linux/of_gpio.h> +#include <linux/of_address.h> #include <drm/exynos_drm.h> @@ -81,7 +82,6 @@ struct hdmi_resources { struct clk *sclk_hdmi; struct clk *sclk_pixel; struct clk *sclk_hdmiphy; - struct clk *hdmiphy; struct clk *mout_hdmi; struct regulator_bulk_data *regul_bulk; int regul_count; @@ -188,6 +188,7 @@ struct hdmi_context { struct mutex hdmi_mutex; void __iomem *regs; + void __iomem *phy_pow_ctrl_reg; void *parent_ctx; int irq; @@ -403,6 +404,14 @@ static inline void hdmi_reg_writemask(struct hdmi_context *hdata, writel(value, hdata->regs + reg_id); } +static inline void hdmi_phy_pow_ctrl_reg_writemask(struct hdmi_context *hdata, + u32 value, u32 mask) +{ + u32 old = readl(hdata->phy_pow_ctrl_reg); + value = (value & mask) | (old & ~mask); + writel(value, hdata->phy_pow_ctrl_reg); +} + static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix) { #define DUMPREG(reg_id) \ @@ -1685,7 +1694,8 @@ static void hdmi_poweron(struct hdmi_context *hdata) if (regulator_bulk_enable(res->regul_count, res->regul_bulk)) DRM_DEBUG_KMS("failed to enable regulator bulk\n"); - clk_prepare_enable(res->hdmiphy); + hdmi_phy_pow_ctrl_reg_writemask(hdata, PMU_HDMI_PHY_ENABLE, + PMU_HDMI_PHY_CONTROL_MASK); clk_prepare_enable(res->hdmi); clk_prepare_enable(res->sclk_hdmi); @@ -1710,7 +1720,8 @@ static void hdmi_poweroff(struct hdmi_context *hdata) clk_disable_unprepare(res->sclk_hdmi); clk_disable_unprepare(res->hdmi); - clk_disable_unprepare(res->hdmiphy); + hdmi_phy_pow_ctrl_reg_writemask(hdata, PMU_HDMI_PHY_DISABLE, + PMU_HDMI_PHY_CONTROL_MASK); regulator_bulk_disable(res->regul_count, res->regul_bulk); mutex_lock(&hdata->hdmi_mutex); @@ -1763,7 +1774,7 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg) struct hdmi_context *hdata = ctx->ctx; mutex_lock(&hdata->hdmi_mutex); - hdata->hpd = gpio_get_value(hdata->hpd_gpio); + hdata->hpd = true; /* gpio_get_value(hdata->hpd_gpio); */ mutex_unlock(&hdata->hdmi_mutex); if (ctx->drm_dev) @@ -1809,11 +1820,6 @@ static int hdmi_resources_init(struct hdmi_context *hdata) DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n"); goto fail; } - res->hdmiphy = devm_clk_get(dev, "hdmiphy"); - if (IS_ERR(res->hdmiphy)) { - DRM_ERROR("failed to get clock 'hdmiphy'\n"); - goto fail; - } res->mout_hdmi = devm_clk_get(dev, "mout_hdmi"); if (IS_ERR(res->mout_hdmi)) { DRM_ERROR("failed to get clock 'mout_hdmi'\n"); @@ -1839,6 +1845,13 @@ static int hdmi_resources_init(struct hdmi_context *hdata) } res->regul_count = ARRAY_SIZE(supply); + clk_prepare(res->hdmi); + clk_prepare(res->sclk_hdmi); + clk_prepare(res->sclk_pixel); + clk_prepare(res->sclk_hdmiphy); + + clk_set_parent(res->sclk_hdmi, res->sclk_pixel); + return 0; fail: DRM_ERROR("HDMI resource init - failed\n"); @@ -1885,12 +1898,52 @@ static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata err_data: return NULL; } + +static int drm_hdmi_dt_parse_phy_pow_control(struct hdmi_context *hdata) +{ + struct device_node *phy_pow_ctrl_node; + u32 buf[2]; + int ret = 0; + + phy_pow_ctrl_node = of_find_node_by_name(NULL, "phy-power-control"); + if (!phy_pow_ctrl_node) { + DRM_ERROR("Failed to find phy power control node\n"); + ret = -ENODEV; + goto fail; + } + + /* reg property holds two informations: addr of pmu register, size */ + if (of_property_read_u32_array(phy_pow_ctrl_node, "reg", + (u32 *)&buf, 2)) { + DRM_ERROR("faild to get phy power control reg\n"); + ret = -EINVAL; + goto fail; + } + + hdata->phy_pow_ctrl_reg = devm_ioremap(hdata->dev, buf[0], buf[1]); + if (!hdata->phy_pow_ctrl_reg) { + DRM_ERROR("failed to ioremap phy pmu reg\n"); + ret = -ENOMEM; + goto fail; + } + +fail: + of_node_put(phy_pow_ctrl_node); + return ret; +} + #else static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata (struct device *dev) { return NULL; } + +static int drm_hdmi_dt_parse_phy_pow_control(struct hdmi_context *hdata) +{ + return 0; +} + #endif static struct platform_device_id hdmi_driver_types[] = { @@ -1993,20 +2046,30 @@ static int hdmi_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hdata->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(hdata->regs)) - return PTR_ERR(hdata->regs); + hdata->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hdata->regs)) { + ret = PTR_ERR(hdata->regs); + goto err_clk_res; + } ret = devm_gpio_request(dev, hdata->hpd_gpio, "HPD"); if (ret) { DRM_ERROR("failed to request HPD gpio\n"); + goto err_clk_res; + } + + /* map hdmiphy power control reg */ + ret = drm_hdmi_dt_parse_phy_pow_control(hdata); + if (ret) { + DRM_ERROR("failed to map phy power control registers\n"); return ret; } /* DDC i2c driver */ if (i2c_add_driver(&ddc_driver)) { DRM_ERROR("failed to register ddc i2c driver\n"); - return -ENOENT; + ret = -ENOENT; + goto err_clk_res; } hdata->ddc_port = hdmi_ddc; @@ -2027,7 +2090,7 @@ static int hdmi_probe(struct platform_device *pdev) goto err_hdmiphy; } - hdata->hpd = gpio_get_value(hdata->hpd_gpio); + hdata->hpd = true; /* gpio_get_value(hdata->hpd_gpio); */ ret = devm_request_threaded_irq(dev, hdata->irq, NULL, hdmi_irq_thread, IRQF_TRIGGER_RISING | @@ -2052,12 +2115,19 @@ err_hdmiphy: i2c_del_driver(&hdmiphy_driver); err_ddc: i2c_del_driver(&ddc_driver); +err_clk_res: + clk_unprepare(hdata->res.hdmi); + clk_unprepare(hdata->res.sclk_hdmi); + clk_unprepare(hdata->res.sclk_pixel); + clk_unprepare(hdata->res.sclk_hdmiphy); return ret; } static int hdmi_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); + struct hdmi_context *hdata = ctx->ctx; pm_runtime_disable(dev); @@ -2066,9 +2136,22 @@ static int hdmi_remove(struct platform_device *pdev) /* DDC i2c driver */ i2c_del_driver(&ddc_driver); + clk_unprepare(hdata->res.hdmi); + clk_unprepare(hdata->res.sclk_hdmi); + clk_unprepare(hdata->res.sclk_pixel); + clk_unprepare(hdata->res.sclk_hdmiphy); return 0; } +static void hdmi_shutdown(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); + struct hdmi_context *hdata = ctx->ctx; + + hdmi_poweroff(hdata); +} + #ifdef CONFIG_PM_SLEEP static int hdmi_suspend(struct device *dev) { @@ -2096,7 +2179,7 @@ static int hdmi_resume(struct device *dev) struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); struct hdmi_context *hdata = ctx->ctx; - hdata->hpd = gpio_get_value(hdata->hpd_gpio); + hdata->hpd = true; /* gpio_get_value(hdata->hpd_gpio); */ enable_irq(hdata->irq); @@ -2141,6 +2224,7 @@ static const struct dev_pm_ops hdmi_pm_ops = { struct platform_driver hdmi_driver = { .probe = hdmi_probe, .remove = hdmi_remove, + .shutdown = hdmi_shutdown, .id_table = hdmi_driver_types, .driver = { .name = "exynos-hdmi", diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index c9a137caea41..b6d7bc54f1fa 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -1085,6 +1085,9 @@ static int mixer_resources_init(struct exynos_drm_hdmi_context *ctx, } mixer_res->irq = res->start; + clk_prepare(mixer_res->mixer); + clk_prepare(mixer_res->sclk_hdmi); + return 0; } @@ -1112,9 +1115,6 @@ static int vp_resources_init(struct exynos_drm_hdmi_context *ctx, return -ENODEV; } - if (mixer_res->sclk_hdmi) - clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (res == NULL) { dev_err(dev, "get memory resource failed.\n"); @@ -1128,6 +1128,13 @@ static int vp_resources_init(struct exynos_drm_hdmi_context *ctx, return -ENXIO; } + clk_prepare(mixer_res->vp); + clk_prepare(mixer_res->sclk_mixer); + clk_prepare(mixer_res->sclk_dac); + + if (mixer_res->sclk_hdmi) + clk_set_parent(mixer_res->sclk_mixer, mixer_res->sclk_hdmi); + return 0; } @@ -1229,7 +1236,7 @@ static int mixer_probe(struct platform_device *pdev) ret = vp_resources_init(drm_hdmi_ctx, pdev); if (ret) { DRM_ERROR("vp_resources_init failed\n"); - goto fail; + goto out_vp; } } @@ -1243,7 +1250,9 @@ static int mixer_probe(struct platform_device *pdev) return 0; - +out_vp: + clk_unprepare(ctx->mixer_res.sclk_hdmi); + clk_unprepare(ctx->mixer_res.mixer); fail: dev_info(dev, "probe failed\n"); return ret; @@ -1251,10 +1260,23 @@ fail: static int mixer_remove(struct platform_device *pdev) { + struct exynos_drm_hdmi_context *drm_hdmi_ctx = + get_mixer_context(&pdev->dev); + struct mixer_context *ctx = drm_hdmi_ctx->ctx; + dev_info(&pdev->dev, "remove successful\n"); pm_runtime_disable(&pdev->dev); + clk_unprepare(ctx->mixer_res.mixer); + clk_unprepare(ctx->mixer_res.sclk_hdmi); + + if (ctx->vp_enabled) { + clk_unprepare(ctx->mixer_res.vp); + clk_unprepare(ctx->mixer_res.sclk_mixer); + clk_unprepare(ctx->mixer_res.sclk_dac); + } + return 0; } diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h index ef1b3eb3ba6e..8d9ca2543ed8 100644 --- a/drivers/gpu/drm/exynos/regs-hdmi.h +++ b/drivers/gpu/drm/exynos/regs-hdmi.h @@ -578,4 +578,8 @@ #define HDMI_TG_VACT_ST4_H HDMI_TG_BASE(0x0074) #define HDMI_TG_3D HDMI_TG_BASE(0x00F0) +#define PMU_HDMI_PHY_CONTROL_MASK (1 << 0) +#define PMU_HDMI_PHY_ENABLE (1) +#define PMU_HDMI_PHY_DISABLE (0) + #endif /* SAMSUNG_REGS_HDMI_H */ diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index cab1c91b75a3..4b166bce60c2 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -86,6 +86,7 @@ #define QUIRK_S3C2440 (1 << 0) #define QUIRK_HDMIPHY (1 << 1) #define QUIRK_NO_GPIO (1 << 2) +#define QUIRK_POLL (1 << 3) /* Max time to wait for bus to become idle after a xfer (in us) */ #define S3C2410_IDLE_TIMEOUT 5000 @@ -142,6 +143,8 @@ static struct platform_device_id s3c24xx_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); +static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat); + #ifdef CONFIG_OF static const struct of_device_id s3c24xx_i2c_match[] = { { .compatible = "samsung,s3c2410-i2c", .data = (void *)0 }, @@ -150,6 +153,8 @@ static const struct of_device_id s3c24xx_i2c_match[] = { .data = (void *)(QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO) }, { .compatible = "samsung,exynos5440-i2c", .data = (void *)(QUIRK_S3C2440 | QUIRK_NO_GPIO) }, + { .compatible = "samsung,exynos5-sata-phy-i2c", + .data = (void *)(QUIRK_S3C2440 | QUIRK_POLL | QUIRK_NO_GPIO) }, {}, }; MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match); @@ -188,7 +193,8 @@ static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret) if (ret) i2c->msg_idx = ret; - wake_up(&i2c->wait); + if (!(i2c->quirks & QUIRK_POLL)) + wake_up(&i2c->wait); } static inline void s3c24xx_i2c_disable_ack(struct s3c24xx_i2c *i2c) @@ -225,6 +231,22 @@ static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c) writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON); } +static bool is_ack(struct s3c24xx_i2c *i2c) +{ + u32 time_out = i2c->tx_setup; + + while (--time_out) { + if (readl(i2c->regs + S3C2410_IICCON) + & S3C2410_IICCON_IRQPEND) { + if (!(readl(i2c->regs + S3C2410_IICSTAT) + & S3C2410_IICSTAT_LASTBIT)) + return true; + } + udelay(time_out); + } + + return false; +} /* s3c24xx_i2c_message_start * @@ -269,6 +291,16 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, stat |= S3C2410_IICSTAT_START; writel(stat, i2c->regs + S3C2410_IICSTAT); + + if (i2c->quirks & QUIRK_POLL) { + while ((i2c->msg_num != 0) && is_ack(i2c)) { + i2c_s3c_irq_nextbyte(i2c, stat); + stat = readl(i2c->regs + S3C2410_IICSTAT); + + if (stat & S3C2410_IICSTAT_ARBITR) + dev_err(i2c->dev, "deal with arbitration loss\n"); + } + } } static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret) @@ -676,6 +708,15 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, s3c24xx_i2c_enable_irq(i2c); s3c24xx_i2c_message_start(i2c, msgs); + if (i2c->quirks & QUIRK_POLL) { + ret = i2c->msg_idx; + + if (ret != num) + dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); + + goto out; + } + timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); ret = i2c->msg_idx; @@ -821,6 +862,9 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got) if (div1 == 512) iiccon |= S3C2410_IICCON_TXDIV_512; + if (i2c->quirks & QUIRK_POLL) + iiccon |= S3C2410_IICCON_SCALE(2); + writel(iiccon, i2c->regs + S3C2410_IICCON); if (i2c->quirks & QUIRK_S3C2440) { @@ -1118,18 +1162,20 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) * ensure no current IRQs pending */ - i2c->irq = ret = platform_get_irq(pdev, 0); - if (ret <= 0) { - dev_err(&pdev->dev, "cannot find IRQ\n"); - return ret; - } + if (!(i2c->quirks & QUIRK_POLL)) { + i2c->irq = ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + dev_err(&pdev->dev, "cannot find IRQ\n"); + return ret; + } - ret = devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq, 0, - dev_name(&pdev->dev), i2c); + ret = devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq, 0, + dev_name(&pdev->dev), i2c); - if (ret != 0) { - dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); - return ret; + if (ret != 0) { + dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); + return ret; + } } ret = s3c24xx_i2c_register_cpufreq(i2c); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 4f6dd42c9adb..da08a1701b66 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -197,6 +197,16 @@ static struct mfc_control controls[] = { .default_value = 1, .is_volatile = 1, }, + { + .id = V4L2_CID_CODEC_FRAME_TAG, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Frame Tag", + .minimum = 0, + .maximum = INT_MAX, + .step = 1, + .default_value = 0, + .is_volatile = 1, + }, }; #define NUM_CTRLS ARRAY_SIZE(controls) @@ -718,6 +728,9 @@ static int s5p_mfc_dec_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE: ctx->slice_interface = ctrl->val; break; + case V4L2_CID_CODEC_FRAME_TAG: + ctx->frame_tag = ctrl->val; + break; default: mfc_err("Invalid control 0x%08x\n", ctrl->id); return -EINVAL; @@ -752,6 +765,9 @@ static int s5p_mfc_dec_g_v_ctrl(struct v4l2_ctrl *ctrl) return -EINVAL; } break; + case V4L2_CID_CODEC_FRAME_TAG: + ctrl->val = s5p_mfc_hw_call(dev->mfc_ops, get_frame_tag, ctx); + break; } return 0; } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h index 754c540e7a7e..498ff09a8158 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h @@ -77,6 +77,7 @@ struct s5p_mfc_hw_ops { unsigned int (*get_pic_type_bot)(struct s5p_mfc_ctx *ctx); unsigned int (*get_crop_info_h)(struct s5p_mfc_ctx *ctx); unsigned int (*get_crop_info_v)(struct s5p_mfc_ctx *ctx); + unsigned int (*get_frame_tag)(struct s5p_mfc_ctx *ctx); }; void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index 368582b091bf..c7408bcffa6f 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -1075,6 +1075,9 @@ static void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush) dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) & ~(S5P_FIMV_DPB_FLUSH_MASK << S5P_FIMV_DPB_FLUSH_SHIFT); mfc_write(dev, dpb, S5P_FIMV_SI_CH0_DPB_CONF_CTRL); + + s5p_mfc_write_info_v5(ctx, ctx->frame_tag, + S5P_FIMV_SHARED_SET_FRAME_TAG); } /* Decode a single frame */ @@ -1682,6 +1685,11 @@ static unsigned int s5p_mfc_get_crop_info_v_v5(struct s5p_mfc_ctx *ctx) return s5p_mfc_read_info_v5(ctx, CROP_INFO_V); } +unsigned int s5p_mfc_get_frame_tag(struct s5p_mfc_ctx *ctx) +{ + return s5p_mfc_read_info_v5(ctx, GET_FRAME_TAG_TOP); +} + /* Initialize opr function pointers for MFC v5 */ static struct s5p_mfc_hw_ops s5p_mfc_ops_v5 = { .alloc_dec_temp_buffers = s5p_mfc_alloc_dec_temp_buffers_v5, @@ -1735,6 +1743,7 @@ static struct s5p_mfc_hw_ops s5p_mfc_ops_v5 = { .get_pic_type_bot = s5p_mfc_get_pic_type_bot_v5, .get_crop_info_h = s5p_mfc_get_crop_info_h_v5, .get_crop_info_v = s5p_mfc_get_crop_info_v_v5, + .get_frame_tag = s5p_mfc_get_frame_tag, }; struct s5p_mfc_hw_ops *s5p_mfc_init_hw_ops_v5(void) diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index fccd08b66d1a..1294b215c42c 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -699,6 +699,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_DEC_FRAME: return "Video Decoder Frame Count"; case V4L2_CID_MPEG_VIDEO_VBV_DELAY: return "Initial Delay for VBV Control"; case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER: return "Repeat Sequence Header"; + case V4L2_CID_CODEC_FRAME_TAG: return "Video Decoder Frame Tag"; /* CAMERA controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -973,6 +974,12 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *flags |= V4L2_CTRL_FLAG_READ_ONLY; *min = *max = *step = *def = 0; break; + case V4L2_CID_CODEC_FRAME_TAG: + *type = V4L2_CTRL_TYPE_INTEGER; + *step = 1; + *min = 0; + *max = INT_MAX; + break; default: *type = V4L2_CTRL_TYPE_INTEGER; break; diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index ad5d1e4384db..defda0e64bde 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -44,6 +44,35 @@ struct ax88172_int_data { __le16 res3; } __packed; +static char asix_mac_addr[6]; +static int __init asix_setup_mac(char *macstr) +{ + int i, h, l; + + if (!macstr) + return 0; + + for (i = 0; i < 6; i++) { + if (i != 5 && *(macstr + 2) != ':') + return 0; + + h = hex_to_bin(*macstr++); + if (h == -1) + return 0; + + l = hex_to_bin(*macstr++); + if (l == -1) + return 0; + + macstr++; + asix_mac_addr[i] = (h << 4) + l; + } + + return 0; +} + +__setup("mac=", asix_setup_mac); + static void asix_status(struct usbnet *dev, struct urb *urb) { struct ax88172_int_data *event; @@ -62,6 +91,9 @@ static void asix_status(struct usbnet *dev, struct urb *urb) static void asix_set_netdev_dev_addr(struct usbnet *dev, u8 *addr) { + if (!is_valid_ether_addr(addr)) + memcpy(addr, asix_mac_addr, ETH_ALEN); + if (is_valid_ether_addr(addr)) { memcpy(dev->net->dev_addr, addr, ETH_ALEN); } else { diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 376079b9bd75..85d7d8c94905 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -528,6 +528,28 @@ static int s3c64xx_serial_startup(struct uart_port *port) return ret; } +static void s3c64xx_serial_shutdown(struct uart_port *port) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + + if (ourport->tx_claimed) { + free_irq(port->irq, ourport); + tx_enabled(port) = 0; + ourport->tx_claimed = 0; + } + + if (ourport->rx_claimed) { + ourport->rx_claimed = 0; + rx_enabled(port) = 0; + } + + /* Clear pending interrupts and mask all interrupts */ + if (s3c24xx_serial_has_interrupt_mask(port)) { + wr_regl(port, S3C64XX_UINTP, 0xf); + wr_regl(port, S3C64XX_UINTM, 0xf); + } +} + /* power power management control */ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, @@ -1013,6 +1035,9 @@ static void s3c24xx_serial_resetport(struct uart_port *port, wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); wr_regl(port, S3C2410_UFCON, cfg->ufcon); + wr_regl(port, S3C64XX_UINTM, 0xf); + wr_regl(port, S3C64XX_UINTP, 0xf); + /* some delay is required after fifo reset */ udelay(1); } @@ -1125,8 +1150,10 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, port->dev = &platdev->dev; /* Startup sequence is different for s3c64xx and higher SoC's */ - if (s3c24xx_serial_has_interrupt_mask(port)) + if (s3c24xx_serial_has_interrupt_mask(port)) { s3c24xx_serial_ops.startup = s3c64xx_serial_startup; + s3c24xx_serial_ops.shutdown = s3c64xx_serial_shutdown; + } port->uartclk = 1; diff --git a/drivers/video/exynos/exynos_dp_core.c b/drivers/video/exynos/exynos_dp_core.c index 12bbede3b091..27985b0852b9 100644 --- a/drivers/video/exynos/exynos_dp_core.c +++ b/drivers/video/exynos/exynos_dp_core.c @@ -19,6 +19,7 @@ #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/of.h> +#include <linux/of_gpio.h> #include <video/exynos_dp.h> @@ -865,13 +866,13 @@ static void exynos_dp_hotplug(struct work_struct *work) ret = exynos_dp_detect_hpd(dp); if (ret) { /* Cable has been disconnected, we're done */ - return; + //return; } ret = exynos_dp_handle_edid(dp); if (ret) { dev_err(dp->dev, "unable to handle edid\n"); - return; + //return; } ret = exynos_dp_set_link_train(dp, dp->video_info->lane_count, @@ -895,11 +896,50 @@ static void exynos_dp_hotplug(struct work_struct *work) } #ifdef CONFIG_OF +static int exynos_parse_gpio(struct device *dev) +{ + int gpio = -1; + struct device_node *np = dev->of_node; + enum of_gpio_flags flags; + u32 value; + int ret = -1; + + if (!of_find_property(np, "lcd_bl_gpio", &value)) { + dev_err(dev, "no bl gpio property found\n"); + return -1; + } + + gpio = of_get_named_gpio_flags(np, "lcd_bl_gpio", 0, &flags); + if (gpio_is_valid(gpio)) { + ret = gpio_request_one(gpio, GPIOF_OUT_INIT_HIGH, "exynos4-fb"); + if (ret < 0) { + return ret; + } + } + gpio_set_value(gpio, 1); + + if (!of_find_property(np, "lcd_en_gpio", &value)) { + dev_err(dev, "no bl gpio property found\n"); + return -1; + } + gpio = of_get_named_gpio_flags(np, "lcd_en_gpio", 0, &flags); + if (gpio_is_valid(gpio)) { + ret = gpio_request_one(gpio, GPIOF_OUT_INIT_HIGH, "exynos4-fb"); + if (ret < 0) { + return ret; + } + } + gpio_set_value(gpio, 1); + + return 0; +} + static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev) { struct device_node *dp_node = dev->of_node; struct exynos_dp_platdata *pd; struct video_info *dp_video_config; + int ret = -1; pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); if (!pd) { @@ -960,6 +1000,10 @@ static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev) return ERR_PTR(-EINVAL); } + ret = exynos_parse_gpio(dev); + if (ret != 0) + return NULL; + return pd; } @@ -1116,6 +1160,8 @@ static int exynos_dp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dp); + schedule_work(&dp->hotplug_work); + return 0; } @@ -1207,7 +1253,21 @@ static struct platform_driver exynos_dp_driver = { }, }; -module_platform_driver(exynos_dp_driver); +//module_platform_driver(exynos_dp_driver); +static int __init exynos_dp_init(void) +{ + return platform_driver_probe(&exynos_dp_driver, exynos_dp_probe); +} + +static void __exit exynos_dp_exit(void) +{ + platform_driver_unregister(&exynos_dp_driver); +} +/* TODO: Register as module_platform_driver */ +/* Currently, we make it late_initcall to make */ +/* sure that s3c-fb is probed before DP driver */ +late_initcall(exynos_dp_init); +module_exit(exynos_dp_exit); MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); MODULE_DESCRIPTION("Samsung SoC DP Driver"); diff --git a/drivers/video/exynos/exynos_mipi_dsi.c b/drivers/video/exynos/exynos_mipi_dsi.c index 32e540600f99..f804d9717510 100644..100755 --- a/drivers/video/exynos/exynos_mipi_dsi.c +++ b/drivers/video/exynos/exynos_mipi_dsi.c @@ -33,6 +33,7 @@ #include <linux/regulator/consumer.h> #include <linux/pm_runtime.h> #include <linux/err.h> +#include <linux/lcd.h> #include <video/exynos_mipi_dsim.h> @@ -42,6 +43,8 @@ struct mipi_dsim_ddi { int bus_id; struct list_head list; + struct device_node *ofnode_dsim_lcd_dev; + struct device_node *ofnode_dsim_dphy; struct mipi_dsim_lcd_device *dsim_lcd_dev; struct mipi_dsim_lcd_driver *dsim_lcd_drv; }; @@ -180,6 +183,37 @@ static int exynos_mipi_dsi_blank_mode(struct mipi_dsim_device *dsim, int power) return 0; } +struct mipi_dsim_ddi *exynos_mipi_dsi_find_lcd_driver( + struct mipi_dsim_lcd_device *lcd_dev) +{ + struct mipi_dsim_ddi *dsim_ddi, *next; + struct mipi_dsim_lcd_driver *lcd_drv; + + mutex_lock(&mipi_dsim_lock); + + list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) { + if (!dsim_ddi) + goto out; + + lcd_drv = dsim_ddi->dsim_lcd_drv; + if (!lcd_drv) + continue; + + if ((strcmp(lcd_dev->name, lcd_drv->name)) == 0) { + + mutex_unlock(&mipi_dsim_lock); + return dsim_ddi; + } + + list_del(&dsim_ddi->list); + kfree(dsim_ddi); + } + +out: + mutex_unlock(&mipi_dsim_lock); + return NULL; +} + int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device *lcd_dev) { struct mipi_dsim_ddi *dsim_ddi; @@ -189,17 +223,20 @@ int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device *lcd_dev) return -EFAULT; } - dsim_ddi = kzalloc(sizeof(struct mipi_dsim_ddi), GFP_KERNEL); + dsim_ddi = exynos_mipi_dsi_find_lcd_driver(lcd_dev); if (!dsim_ddi) { - pr_err("failed to allocate dsim_ddi object.\n"); - return -ENOMEM; + dsim_ddi = kzalloc(sizeof(struct mipi_dsim_ddi), GFP_KERNEL); + if (!dsim_ddi) { + pr_err("failed to allocate dsim_ddi object.\n"); + return -ENOMEM; + } + mutex_lock(&mipi_dsim_lock); + list_add_tail(&dsim_ddi->list, &dsim_ddi_list); + mutex_unlock(&mipi_dsim_lock); } dsim_ddi->dsim_lcd_dev = lcd_dev; - - mutex_lock(&mipi_dsim_lock); - list_add_tail(&dsim_ddi->list, &dsim_ddi_list); - mutex_unlock(&mipi_dsim_lock); + dsim_ddi->bus_id = lcd_dev->bus_id; return 0; } @@ -252,11 +289,26 @@ int exynos_mipi_dsi_register_lcd_driver(struct mipi_dsim_lcd_driver *lcd_drv) dsim_ddi = exynos_mipi_dsi_find_lcd_device(lcd_drv); if (!dsim_ddi) { - pr_err("mipi_dsim_ddi object not found.\n"); - return -EFAULT; - } + /* + * If driver specific device is not registered then create a + * dsim_ddi object, fill the driver information and add to the + * end of the dsim_ddi_list list + */ + dsim_ddi = kzalloc(sizeof(struct mipi_dsim_ddi), GFP_KERNEL); + if (!dsim_ddi) { + pr_err("failed to allocate dsim_ddi object.\n"); + return -ENOMEM; + } - dsim_ddi->dsim_lcd_drv = lcd_drv; + dsim_ddi->dsim_lcd_drv = lcd_drv; + + mutex_lock(&mipi_dsim_lock); + list_add_tail(&dsim_ddi->list, &dsim_ddi_list); + mutex_unlock(&mipi_dsim_lock); + + } else { + dsim_ddi->dsim_lcd_drv = lcd_drv; + } pr_info("registered panel driver(%s) to mipi-dsi driver.\n", lcd_drv->name); @@ -328,6 +380,414 @@ static struct mipi_dsim_master_ops master_ops = { .set_blank_mode = exynos_mipi_dsi_blank_mode, }; +struct device_node *exynos_mipi_find_ofnode_dsim_phy( + struct platform_device *pdev) +{ + struct device_node *dn, *dn_dphy; + const __be32 *prop; + + dn = pdev->dev.of_node; + prop = of_get_property(dn, "mipi-phy", NULL); + if (NULL == prop) { + dev_err(&pdev->dev, "Could not find property mipi-phy\n"); + return NULL; + } + + dn_dphy = of_find_node_by_phandle(be32_to_cpup(prop)); + if (NULL == dn_dphy) { + dev_err(&pdev->dev, "Could not find node\n"); + return NULL; + } + + return dn_dphy; +} + +struct device_node *exynos_mipi_find_ofnode_lcd_device( + struct platform_device *pdev) +{ + struct device_node *dn, *dn_lcd_panel; + const __be32 *prop; + + dn = pdev->dev.of_node; + prop = of_get_property(dn, "mipi-lcd", NULL); + if (NULL == prop) { + dev_err(&pdev->dev, "could not find property mipi-lcd\n"); + return NULL; + } + + dn_lcd_panel = of_find_node_by_phandle(be32_to_cpup(prop)); + if (NULL == dn_lcd_panel) { + dev_err(&pdev->dev, "could not find node\n"); + return NULL; + } + + return dn_lcd_panel; +} + +static void exynos_mipi_dsim_enable_d_phy_type1( + struct platform_device *pdev, + bool enable) +{ + struct mipi_dsim_device *dsim = (struct mipi_dsim_device *) + platform_get_drvdata(pdev); + struct mipi_dsim_phy_config_type1 *dphy_cfg_type1 = + &dsim->dsim_phy_config->phy_cfg_type1; + u32 reg_enable; + + reg_enable = __raw_readl(dphy_cfg_type1->reg_enable_dphy); + reg_enable &= ~(dphy_cfg_type1->ctrlbit_enable_dphy); + + if (enable) + reg_enable |= dphy_cfg_type1->ctrlbit_enable_dphy; + + __raw_writel(reg_enable, dphy_cfg_type1->reg_enable_dphy); +} + +static void exynos_mipi_dsim_reset_type1( + struct platform_device *pdev, + bool enable) +{ + struct mipi_dsim_device *dsim = (struct mipi_dsim_device *) + platform_get_drvdata(pdev); + struct mipi_dsim_phy_config_type1 *dphy_cfg_type1 = + &dsim->dsim_phy_config->phy_cfg_type1; + u32 reg_reset; + + reg_reset = __raw_readl(dphy_cfg_type1->reg_reset_dsim); + reg_reset &= ~(dphy_cfg_type1->ctrlbit_reset_dsim); + + if (enable) + reg_reset |= dphy_cfg_type1->ctrlbit_reset_dsim; + + __raw_writel(reg_reset, dphy_cfg_type1->reg_reset_dsim); +} + +static int exynos_mipi_dsim_phy_init_type1( + struct platform_device *pdev, + bool on_off) +{ + exynos_mipi_dsim_enable_d_phy_type1(pdev, on_off); + exynos_mipi_dsim_reset_type1(pdev, on_off); + return 0; +} + +static int exynos_mipi_parse_ofnode_dsim_phy_type1( + struct platform_device *pdev, + struct mipi_dsim_phy_config_type1 *dphy_cfg_type1, + struct device_node *np) +{ + struct mipi_dsim_device *dsim = (struct mipi_dsim_device *) + platform_get_drvdata(pdev); + u32 paddr_phy_enable, paddr_dsim_reset; + + if (of_property_read_u32(np, "reg_enable_dphy", &paddr_phy_enable)) + return -EINVAL; + + dphy_cfg_type1->reg_enable_dphy = ioremap(paddr_phy_enable, SZ_4); + if (!dphy_cfg_type1->reg_enable_dphy) + return -EINVAL; + + if (of_property_read_u32(np, "reg_reset_dsim", &paddr_dsim_reset)) + return -EINVAL; + + dphy_cfg_type1->reg_reset_dsim = ioremap(paddr_dsim_reset, SZ_4); + if (!dphy_cfg_type1->reg_reset_dsim) + goto err_ioremap_01; + + if (of_property_read_u32(np, "mask_enable_dphy", + &dphy_cfg_type1->ctrlbit_enable_dphy)) + goto err_ioremap_02; + + if (of_property_read_u32(np, "mask_reset_dsim", + &dphy_cfg_type1->ctrlbit_reset_dsim)) + goto err_ioremap_02; + + dsim->pd->phy_enable = exynos_mipi_dsim_phy_init_type1; + + return 0; + +err_ioremap_02: + iounmap(dphy_cfg_type1->reg_reset_dsim); + +err_ioremap_01: + iounmap(dphy_cfg_type1->reg_enable_dphy); + return -EINVAL; +} + +static struct mipi_dsim_phy_config *exynos_mipi_parse_ofnode_dsim_phy( + struct platform_device *pdev, + struct device_node *np) +{ + struct mipi_dsim_phy_config *mipi_dphy_config; + const char *compatible; + + mipi_dphy_config = devm_kzalloc(&pdev->dev, + sizeof(struct mipi_dsim_phy_config), GFP_KERNEL); + if (!mipi_dphy_config) { + dev_err(&pdev->dev, + "failed to allocate mipi_dsim_phy_config object.\n"); + return NULL; + } + + if (of_property_read_string(np, "compatible", &compatible)) { + dev_err(&pdev->dev, "compatible property not found"); + return NULL; + } + + if (!strcmp(compatible, "samsung-exynos,mipi-phy-type1")) + mipi_dphy_config->type = MIPI_DSIM_PHY_CONFIG_TYPE1; + else + mipi_dphy_config->type = -1; + + switch (mipi_dphy_config->type) { + case MIPI_DSIM_PHY_CONFIG_TYPE1: + if (exynos_mipi_parse_ofnode_dsim_phy_type1( + pdev, &mipi_dphy_config->phy_cfg_type1, np)) + return NULL; + break; + default: + dev_err(&pdev->dev, "mipi phy - unknown type"); + return NULL; + } + + return mipi_dphy_config; +} + +static struct mipi_dsim_lcd_device *exynos_mipi_parse_ofnode_lcd( + struct platform_device *pdev, struct device_node *np) +{ + struct mipi_dsim_lcd_device *active_mipi_dsim_lcd_device; + struct lcd_platform_data *active_lcd_platform_data; + const char *lcd_name; + + active_mipi_dsim_lcd_device = devm_kzalloc(&pdev->dev, + sizeof(struct mipi_dsim_lcd_device), GFP_KERNEL); + if (!active_mipi_dsim_lcd_device) { + dev_err(&pdev->dev, + "failed to allocate active_mipi_dsim_lcd_device object.\n"); + return NULL; + } + + if (of_property_read_string(np, "lcd-name", &lcd_name)) { + dev_err(&pdev->dev, "lcd name property not found"); + return NULL; + } + + active_mipi_dsim_lcd_device->name = (char *)lcd_name; + + if (of_property_read_u32(np, "id", &active_mipi_dsim_lcd_device->id)) + active_mipi_dsim_lcd_device->id = -1; + + if (of_property_read_u32(np, "bus-id", + &active_mipi_dsim_lcd_device->bus_id)) + active_mipi_dsim_lcd_device->bus_id = -1; + + active_lcd_platform_data = devm_kzalloc(&pdev->dev, + sizeof(struct lcd_platform_data), GFP_KERNEL); + if (!active_lcd_platform_data) { + dev_err(&pdev->dev, + "failed to allocate active_lcd_platform_data object.\n"); + return NULL; + } + + /* store the lcd node pointer for futher use in lcd driver */ + active_lcd_platform_data->pdata = (void *) np; + active_mipi_dsim_lcd_device->platform_data = + (void *)active_lcd_platform_data; + + return active_mipi_dsim_lcd_device; +} + +static int exynos_mipi_parse_ofnode_config(struct platform_device *pdev, + struct device_node *np, struct mipi_dsim_config *dsim_config) +{ + unsigned int u32Val; + + if (of_property_read_u32(np, "e_interface", &u32Val)) { + dev_err(&pdev->dev, "e_interface property not found\n"); + return -EINVAL; + } + dsim_config->e_interface = (enum mipi_dsim_interface_type)u32Val; + + if (of_property_read_u32(np, "e_pixel_format", &u32Val)) { + dev_err(&pdev->dev, "e_pixel_format property not found\n"); + return -EINVAL; + } + dsim_config->e_pixel_format = (enum mipi_dsim_pixel_format)u32Val; + + if (of_property_read_u32(np, "auto_flush", &u32Val)) { + dev_err(&pdev->dev, "auto_flush property not found\n"); + return -EINVAL; + } + dsim_config->auto_flush = (unsigned char)u32Val; + + if (of_property_read_u32(np, "eot_disable", &u32Val)) { + dev_err(&pdev->dev, "eot_disable property not found\n"); + return -EINVAL; + } + dsim_config->eot_disable = (unsigned char)u32Val; + + if (of_property_read_u32(np, "auto_vertical_cnt", &u32Val)) { + dev_err(&pdev->dev, "auto_vertical_cnt property not found\n"); + return -EINVAL; + } + dsim_config->auto_vertical_cnt = (unsigned char)u32Val; + + if (of_property_read_u32(np, "hse", &u32Val)) { + dev_err(&pdev->dev, "hse property not found\n"); + return -EINVAL; + } + dsim_config->hse = (unsigned char)u32Val; + + if (of_property_read_u32(np, "hfp", &u32Val)) { + dev_err(&pdev->dev, "hfp property not found\n"); + return -EINVAL; + } + dsim_config->hfp = (unsigned char)u32Val; + + if (of_property_read_u32(np, "hbp", &u32Val)) { + dev_err(&pdev->dev, "hbp property not found\n"); + return -EINVAL; + } + dsim_config->hbp = (unsigned char)u32Val; + + if (of_property_read_u32(np, "hsa", &u32Val)) { + dev_err(&pdev->dev, "hsa property not found\n"); + return -EINVAL; + } + dsim_config->hsa = (unsigned char)u32Val; + + if (of_property_read_u32(np, "e_no_data_lane", &u32Val)) { + dev_err(&pdev->dev, "e_no_data_lane property not found\n"); + return -EINVAL; + } + dsim_config->e_no_data_lane = (enum mipi_dsim_no_of_data_lane)u32Val; + + if (of_property_read_u32(np, "e_byte_clk", &u32Val)) { + dev_err(&pdev->dev, "e_byte_clk property not found\n"); + return -EINVAL; + } + dsim_config->e_byte_clk = (enum mipi_dsim_byte_clk_src)u32Val; + + if (of_property_read_u32(np, "e_burst_mode", &u32Val)) { + dev_err(&pdev->dev, "e_burst_mode property not found\n"); + return -EINVAL; + } + dsim_config->e_burst_mode = (enum mipi_dsim_burst_mode_type)u32Val; + + if (of_property_read_u32(np, "p", &u32Val)) { + dev_err(&pdev->dev, "p property not found\n"); + return -EINVAL; + } + dsim_config->p = (unsigned char)u32Val; + + if (of_property_read_u32(np, "m", &u32Val)) { + dev_err(&pdev->dev, "m property not found\n"); + return -EINVAL; + } + dsim_config->m = (unsigned short)u32Val; + + if (of_property_read_u32(np, "s", &u32Val)) { + dev_err(&pdev->dev, "s property not found\n"); + return -EINVAL; + } + dsim_config->s = (unsigned char)u32Val; + + if (of_property_read_u32(np, "pll_stable_time", &u32Val)) { + dev_err(&pdev->dev, "pll_stable_time property not found\n"); + return -EINVAL; + } + dsim_config->pll_stable_time = (unsigned int)u32Val; + + if (of_property_read_u32(np, "esc_clk", &u32Val)) { + dev_err(&pdev->dev, "esc_clk property not found\n"); + return -EINVAL; + } + dsim_config->esc_clk = (unsigned long)u32Val; + + if (of_property_read_u32(np, "stop_holding_cnt", &u32Val)) { + dev_err(&pdev->dev, "stop_holding_cnt property not found\n"); + return -EINVAL; + } + dsim_config->stop_holding_cnt = (unsigned short)u32Val; + + if (of_property_read_u32(np, "bta_timeout", &u32Val)) { + dev_err(&pdev->dev, "bta_timeout property not found\n"); + return -EINVAL; + } + dsim_config->bta_timeout = (unsigned char)u32Val; + + if (of_property_read_u32(np, "rx_timeout", &u32Val)) { + dev_err(&pdev->dev, "rx_timeout property not found\n"); + return -EINVAL; + } + dsim_config->rx_timeout = (unsigned short)u32Val; + + if (of_property_read_u32(np, "e_virtual_ch", &u32Val)) { + dev_err(&pdev->dev, "e_virtual_ch property not found\n"); + return -EINVAL; + } + dsim_config->e_virtual_ch = (enum mipi_dsim_virtual_ch_no)u32Val; + + if (of_property_read_u32(np, "cmd_allow", &u32Val)) { + dev_err(&pdev->dev, "cmd_allow property not found\n"); + return -EINVAL; + } + dsim_config->cmd_allow = (unsigned char)u32Val; + + return 0; +} + +static int exynos_mipi_parse_ofnode_panel_info(struct platform_device *pdev, + struct device_node *np, struct fb_videomode *panel_info) +{ + unsigned int data[4]; + + if (of_property_read_u32_array(np, "lcd-htiming", data, 4)) { + dev_err(&pdev->dev, "invalid horizontal timing\n"); + return -EINVAL; + } + panel_info->left_margin = data[0]; + panel_info->right_margin = data[1]; + panel_info->hsync_len = data[2]; + panel_info->xres = data[3]; + + if (of_property_read_u32_array(np, "lcd-vtiming", data, 4)) { + dev_err(&pdev->dev, "invalid vertical timing\n"); + return -EINVAL; + } + panel_info->upper_margin = data[0]; + panel_info->lower_margin = data[1]; + panel_info->vsync_len = data[2]; + panel_info->yres = data[3]; + + return 0; +} + +static int exynos_mipi_parse_ofnode(struct platform_device *pdev, + struct mipi_dsim_config *dsim_config, struct fb_videomode *panel_info) +{ + struct device_node *np_dsim_config, *np_panel_info; + struct device_node *np = pdev->dev.of_node; + + np_dsim_config = of_find_node_by_name(np, "mipi-config"); + if (!np_dsim_config) + return -EINVAL; + + if (exynos_mipi_parse_ofnode_config(pdev, np_dsim_config, dsim_config)) + return -EINVAL; + + np_panel_info = of_parse_phandle(np, "panel-info", 0); + if (!np_panel_info) + return -EINVAL; + + if (exynos_mipi_parse_ofnode_panel_info(pdev, + np_panel_info, panel_info)) + return -EINVAL; + + return 0; +} + static int exynos_mipi_dsi_probe(struct platform_device *pdev) { struct resource *res; @@ -335,6 +795,12 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) struct mipi_dsim_config *dsim_config; struct mipi_dsim_platform_data *dsim_pd; struct mipi_dsim_ddi *dsim_ddi; + struct device_node *ofnode_lcd = NULL; + struct device_node *ofnode_dphy = NULL; + struct mipi_dsim_lcd_device *active_mipi_dsim_lcd_device = NULL; + struct mipi_dsim_phy_config *mipi_dphy_config; + struct fb_videomode *panel_info; + unsigned int u32Val; int ret = -EINVAL; dsim = devm_kzalloc(&pdev->dev, sizeof(struct mipi_dsim_device), @@ -344,21 +810,86 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) return -ENOMEM; } + if (pdev->dev.of_node) { + ofnode_lcd = exynos_mipi_find_ofnode_lcd_device(pdev); + if (!ofnode_lcd) + return -EINVAL; + + active_mipi_dsim_lcd_device = + exynos_mipi_parse_ofnode_lcd(pdev, ofnode_lcd); + + if (NULL == active_mipi_dsim_lcd_device) + return -EINVAL; + + if (NULL == exynos_mipi_dsi_find_lcd_driver( + active_mipi_dsim_lcd_device)) + return -EPROBE_DEFER; + + exynos_mipi_dsi_register_lcd_device( + active_mipi_dsim_lcd_device); + } + dsim->pd = to_dsim_plat(pdev); dsim->dev = &pdev->dev; dsim->id = pdev->id; - /* get mipi_dsim_platform_data. */ - dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd; - if (dsim_pd == NULL) { - dev_err(&pdev->dev, "failed to get platform data for dsim.\n"); - return -EINVAL; - } - /* get mipi_dsim_config. */ - dsim_config = dsim_pd->dsim_config; - if (dsim_config == NULL) { - dev_err(&pdev->dev, "failed to get dsim config data.\n"); - return -EINVAL; + if (pdev->dev.of_node) { + dsim_config = devm_kzalloc(&pdev->dev, + sizeof(struct mipi_dsim_config), GFP_KERNEL); + if (!dsim_config) { + dev_err(&pdev->dev, + "failed to allocate dsim_config object.\n"); + return -ENOMEM; + } + + panel_info = devm_kzalloc(&pdev->dev, + sizeof(struct fb_videomode), GFP_KERNEL); + if (!panel_info) { + dev_err(&pdev->dev, + "failed to allocate fb_videomode object.\n"); + return -ENOMEM; + } + + /* parse the mipi of_node for dism_config and panel info. */ + if (exynos_mipi_parse_ofnode(pdev, dsim_config, panel_info)) { + dev_err(&pdev->dev, + "failed to read mipi-config, panel-info\n"); + return -EINVAL; + } + + dsim_pd = devm_kzalloc(&pdev->dev, + sizeof(struct mipi_dsim_platform_data), GFP_KERNEL); + if (!dsim_pd) { + dev_err(&pdev->dev, + "failed to allocate mipi_dsim_platform_data\n"); + return -ENOMEM; + } + + if (of_property_read_u32(pdev->dev.of_node, "enabled", &u32Val)) + dev_err(&pdev->dev, "enabled property not found\n"); + else + dsim_pd->enabled = !(!u32Val); + + dsim_pd->lcd_panel_info = (void *)panel_info; + dsim_pd->dsim_config = dsim_config; + dsim->pd = dsim_pd; + + } else { + /* get mipi_dsim_platform_data. */ + dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd; + if (dsim_pd == NULL) { + dev_err(&pdev->dev, + "failed to get platform data for dsim.\n"); + return -EFAULT; + } + + /* get mipi_dsim_config. */ + dsim_config = dsim_pd->dsim_config; + if (dsim_config == NULL) { + dev_err(&pdev->dev, + "failed to get dsim config data.\n"); + return -EFAULT; + } } dsim->dsim_config = dsim_config; @@ -379,7 +910,7 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) return -ENODEV; } - clk_enable(dsim->clock); + clk_prepare_enable(dsim->clock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -392,12 +923,22 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) mutex_init(&dsim->lock); /* bind lcd ddi matched with panel name. */ - dsim_ddi = exynos_mipi_dsi_bind_lcd_ddi(dsim, dsim_pd->lcd_panel_name); + if (pdev->dev.of_node) { + dsim_ddi = exynos_mipi_dsi_bind_lcd_ddi(dsim, + active_mipi_dsim_lcd_device->name); + } else { + dsim_ddi = exynos_mipi_dsi_bind_lcd_ddi(dsim, + dsim_pd->lcd_panel_name); + } + if (!dsim_ddi) { dev_err(&pdev->dev, "mipi_dsim_ddi object not found.\n"); - ret = -EINVAL; + ret = -ENXIO; goto error; - } + } else if (pdev->dev.of_node) { + dsim_ddi->ofnode_dsim_lcd_dev = ofnode_lcd; + dsim_ddi->ofnode_dsim_dphy = ofnode_dphy; + } dsim->irq = platform_get_irq(pdev, 0); if (IS_ERR_VALUE(dsim->irq)) { @@ -410,6 +951,20 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) init_completion(&dsim_rd_comp); platform_set_drvdata(pdev, dsim); + /* update dsim phy config node */ + if (pdev->dev.of_node) { + ofnode_dphy = exynos_mipi_find_ofnode_dsim_phy(pdev); + if (!ofnode_dphy) + return -EINVAL; + + mipi_dphy_config = exynos_mipi_parse_ofnode_dsim_phy(pdev, + ofnode_dphy); + if (NULL == mipi_dphy_config) + return -EINVAL; + + dsim->dsim_phy_config = mipi_dphy_config; + } + ret = devm_request_irq(&pdev->dev, dsim->irq, exynos_mipi_dsi_interrupt_handler, IRQF_SHARED, dev_name(&pdev->dev), dsim); @@ -444,10 +999,22 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) exynos_mipi_update_cfg(dsim); + /* enable the LPDT mode */ + exynos_mipi_dsi_stand_by(dsim, 0); + exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 1); + exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 1); + exynos_mipi_dsi_enable_hs_clock(dsim, 0); + /* set lcd panel sequence commands. */ if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->set_sequence) dsim_ddi->dsim_lcd_drv->set_sequence(dsim_ddi->dsim_lcd_dev); + /* enable the HS mode */ + exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0); + exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0); + exynos_mipi_dsi_enable_hs_clock(dsim, 1); + exynos_mipi_dsi_stand_by(dsim, 1); + dsim->suspended = false; done: @@ -557,13 +1124,33 @@ static const struct dev_pm_ops exynos_mipi_dsi_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(exynos_mipi_dsi_suspend, exynos_mipi_dsi_resume) }; +static struct platform_device_id exynos_mipi_driver_ids[] = { + { + .name = "exynos-mipidsim", + .driver_data = (unsigned long)0, + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, exynos_mipi_driver_ids); + +static const struct of_device_id exynos_mipi_match[] = { + { + .compatible = "samsung,exynos-mipidsim", + .data = NULL, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_mipi_match); + static struct platform_driver exynos_mipi_dsi_driver = { .probe = exynos_mipi_dsi_probe, .remove = exynos_mipi_dsi_remove, + .id_table = exynos_mipi_driver_ids, .driver = { .name = "exynos-mipi-dsim", .owner = THIS_MODULE, .pm = &exynos_mipi_dsi_pm_ops, + .of_match_table = exynos_mipi_match, }, }; diff --git a/drivers/video/exynos/exynos_mipi_dsi_common.c b/drivers/video/exynos/exynos_mipi_dsi_common.c index 520fc9bd887b..c54ebb6407e8 100644 --- a/drivers/video/exynos/exynos_mipi_dsi_common.c +++ b/drivers/video/exynos/exynos_mipi_dsi_common.c @@ -718,7 +718,7 @@ int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, /* in case of VIDEO MODE (RGB INTERFACE), it sets polarities. */ if (dsim_config->e_interface == (u32) DSIM_VIDEO) { - if (dsim_config->auto_vertical_cnt == 0) { + exynos_mipi_dsi_set_main_disp_vporch(dsim, dsim_config->cmd_allow, timing->lower_margin, @@ -729,7 +729,6 @@ int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, exynos_mipi_dsi_set_main_disp_sync_area(dsim, timing->vsync_len, timing->hsync_len); - } } exynos_mipi_dsi_set_main_disp_resol(dsim, timing->xres, diff --git a/drivers/video/exynos/s6e8ax0.c b/drivers/video/exynos/s6e8ax0.c index ca2602413aa4..2e3f3442abd5 100644..100755 --- a/drivers/video/exynos/s6e8ax0.c +++ b/drivers/video/exynos/s6e8ax0.c @@ -1,9 +1,9 @@ -/* linux/drivers/video/exynos/s6e8ax0.c +/* linux/drivers/video/exynos/s6e8aa0.c * - * MIPI-DSI based s6e8ax0 AMOLED lcd 4.65 inch panel driver. + * MIPI-DSI based s6e8aa0 AMOLED lcd 4.65 inch panel driver. + * This driver is implemented according to the s6e8ax0 panel driver. * - * Inki Dae, <inki.dae@samsung.com> - * Donghwa Lee, <dh09.lee@samsung.com> + * Shaik Ameer Basha <shaik.ameer@samsung.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -24,6 +24,7 @@ #include <linux/fb.h> #include <linux/backlight.h> #include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> #include <video/mipi_display.h> #include <video/exynos_mipi_dsim.h> @@ -31,7 +32,7 @@ #define LDI_MTP_LENGTH 24 #define DSIM_PM_STABLE_TIME 10 #define MIN_BRIGHTNESS 0 -#define MAX_BRIGHTNESS 24 +#define MAX_BRIGHTNESS 26 #define GAMMA_TABLE_COUNT 26 #define POWER_IS_ON(pwr) ((pwr) == FB_BLANK_UNBLANK) @@ -40,6 +41,7 @@ #define lcd_to_master(a) (a->dsim_dev->master) #define lcd_to_master_ops(a) ((lcd_to_master(a))->master_ops) +#define lcd_to_master_ver(a) ((lcd_to_master(a))->version) enum { DSIM_NONE_STATE = 0, @@ -47,13 +49,14 @@ enum { DSIM_FRAME_DONE = 2, }; -struct s6e8ax0 { +struct s6e8aa0 { struct device *dev; + unsigned int gpio_reset; + unsigned int gpio_power; + unsigned int gpio_bl; unsigned int power; unsigned int id; unsigned int gamma; - unsigned int acl_enable; - unsigned int cur_acl; struct lcd_device *ld; struct backlight_device *bd; @@ -64,13 +67,15 @@ struct s6e8ax0 { bool enabled; }; +static struct s6e8aa0 *lcd_global; + static struct regulator_bulk_data supplies[] = { { .supply = "vdd3", }, { .supply = "vci", }, }; -static void s6e8ax0_regulator_enable(struct s6e8ax0 *lcd) +static void s6e8aa0_regulator_enable(struct s6e8aa0 *lcd) { int ret = 0; struct lcd_platform_data *pd = NULL; @@ -89,7 +94,7 @@ out: mutex_unlock(&lcd->lock); } -static void s6e8ax0_regulator_disable(struct s6e8ax0 *lcd) +static void s6e8aa0_regulator_disable(struct s6e8aa0 *lcd) { int ret = 0; @@ -105,194 +110,275 @@ out: mutex_unlock(&lcd->lock); } -static const unsigned char s6e8ax0_22_gamma_30[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xf5, 0x00, 0xff, 0xad, 0xaf, - 0xbA, 0xc3, 0xd8, 0xc5, 0x9f, 0xc6, 0x9e, 0xc1, 0xdc, 0xc0, - 0x00, 0x61, 0x00, 0x5a, 0x00, 0x74, -}; - -static const unsigned char s6e8ax0_22_gamma_50[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xe8, 0x1f, 0xf7, 0xad, 0xc0, - 0xb5, 0xc4, 0xdc, 0xc4, 0x9e, 0xc6, 0x9c, 0xbb, 0xd8, 0xbb, - 0x00, 0x70, 0x00, 0x68, 0x00, 0x86, -}; - -static const unsigned char s6e8ax0_22_gamma_60[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xde, 0x1f, 0xef, 0xad, 0xc4, - 0xb3, 0xc3, 0xdd, 0xc4, 0x9e, 0xc6, 0x9c, 0xbc, 0xd6, 0xba, - 0x00, 0x75, 0x00, 0x6e, 0x00, 0x8d, -}; -static const unsigned char s6e8ax0_22_gamma_70[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xd8, 0x1f, 0xe7, 0xaf, 0xc8, - 0xb4, 0xc4, 0xdd, 0xc3, 0x9d, 0xc6, 0x9c, 0xbb, 0xd6, 0xb9, - 0x00, 0x7a, 0x00, 0x72, 0x00, 0x93, +static const unsigned char gamma22_30[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xDF, 0x86, 0xF5, + 0xD5, 0xC7, 0xCF, 0xDF, 0xE0, 0xE0, + 0xC9, 0xC9, 0xCC, 0xD7, 0xD6, 0xD5, + 0x00, 0x68, 0x00, 0x68, 0x00, 0x75, }; -static const unsigned char s6e8ax0_22_gamma_80[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xc9, 0x1f, 0xde, 0xae, 0xc9, - 0xb1, 0xc3, 0xdd, 0xc2, 0x9d, 0xc5, 0x9b, 0xbc, 0xd6, 0xbb, - 0x00, 0x7f, 0x00, 0x77, 0x00, 0x99, +static const unsigned char gamma22_40[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xE5, 0xAA, 0xF2, + 0xD6, 0xCC, 0xCF, 0xE0, 0xE2, 0xE2, + 0xC8, 0xC9, 0xCA, 0xD2, 0xD2, 0xCF, + 0x00, 0x71, 0x00, 0x70, 0x00, 0x80, }; -static const unsigned char s6e8ax0_22_gamma_90[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xc7, 0x1f, 0xd9, 0xb0, 0xcc, - 0xb2, 0xc3, 0xdc, 0xc1, 0x9c, 0xc6, 0x9c, 0xbc, 0xd4, 0xb9, - 0x00, 0x83, 0x00, 0x7b, 0x00, 0x9e, +static const unsigned char gamma22_50[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xE7, 0xBB, 0xEE, + 0xD6, 0xCE, 0xD0, 0xE0, 0xE3, 0xE4, + 0xC5, 0xC4, 0xC5, 0xD2, 0xD2, 0xCF, + 0x00, 0x78, 0x00, 0x78, 0x00, 0x88, }; -static const unsigned char s6e8ax0_22_gamma_100[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xbd, 0x80, 0xcd, 0xba, 0xce, - 0xb3, 0xc4, 0xde, 0xc3, 0x9c, 0xc4, 0x9, 0xb8, 0xd3, 0xb6, - 0x00, 0x88, 0x00, 0x80, 0x00, 0xa5, +static const unsigned char gamma22_60[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xE9, 0xC4, 0xEB, + 0xD6, 0xD0, 0xD1, 0xE0, 0xE3, 0xE4, + 0xC3, 0xC2, 0xC2, 0xD2, 0xD1, 0xCF, + 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x8F, }; -static const unsigned char s6e8ax0_22_gamma_120[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb9, 0x95, 0xc8, 0xb1, 0xcf, - 0xb2, 0xc6, 0xdf, 0xc5, 0x9b, 0xc3, 0x99, 0xb6, 0xd2, 0xb6, - 0x00, 0x8f, 0x00, 0x86, 0x00, 0xac, +static const unsigned char gamma22_70[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xEA, 0xC9, 0xEA, + 0xD6, 0xD2, 0xD2, 0xDF, 0xE1, 0xE3, + 0xC2, 0xC1, 0xC0, 0xD1, 0xD0, 0xCE, + 0x00, 0x84, 0x00, 0x84, 0x00, 0x96, }; -static const unsigned char s6e8ax0_22_gamma_130[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc7, 0xb1, 0xd0, - 0xb2, 0xc4, 0xdd, 0xc3, 0x9a, 0xc3, 0x98, 0xb6, 0xd0, 0xb4, - 0x00, 0x92, 0x00, 0x8a, 0x00, 0xb1, +static const unsigned char gamma22_80[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xEB, 0xCC, 0xE9, + 0xD5, 0xD4, 0xD3, 0xDE, 0xE1, 0xE2, + 0xC2, 0xBF, 0xBF, 0xCF, 0xCF, 0xCC, + 0x00, 0x89, 0x00, 0x89, 0x00, 0x9C, }; -static const unsigned char s6e8ax0_22_gamma_140[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc5, 0xb2, 0xd0, - 0xb3, 0xc3, 0xde, 0xc3, 0x9b, 0xc2, 0x98, 0xb6, 0xd0, 0xb4, - 0x00, 0x95, 0x00, 0x8d, 0x00, 0xb5, +static const unsigned char gamma22_90[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xEB, 0xD0, 0xE9, + 0xD4, 0xD5, 0xD4, 0xDF, 0xE0, 0xE1, + 0xC1, 0xBE, 0xBD, 0xCD, 0xCD, 0xCA, + 0x00, 0x8E, 0x00, 0x8F, 0x00, 0xA2, }; -static const unsigned char s6e8ax0_22_gamma_150[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xa0, 0xc2, 0xb2, 0xd0, - 0xb2, 0xc1, 0xdd, 0xc2, 0x9b, 0xc2, 0x98, 0xb4, 0xcf, 0xb1, - 0x00, 0x99, 0x00, 0x90, 0x00, 0xba, +static const unsigned char gamma22_100[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xEA, 0xD2, 0xE7, + 0xD7, 0xD6, 0xD6, 0xDF, 0xDF, 0xE2, + 0xBF, 0xBD, 0xBC, 0xCD, 0xCD, 0xC9, + 0x00, 0x92, 0x00, 0x93, 0x00, 0xA7, }; -static const unsigned char s6e8ax0_22_gamma_160[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xa5, 0xbf, 0xb0, 0xd0, - 0xb1, 0xc3, 0xde, 0xc2, 0x99, 0xc1, 0x97, 0xb4, 0xce, 0xb1, - 0x00, 0x9c, 0x00, 0x93, 0x00, 0xbe, +static const unsigned char gamma22_110[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xEB, 0xD4, 0xE5, + 0xD6, 0xD6, 0xD7, 0xDE, 0xDF, 0xE0, + 0xBE, 0xBC, 0xBB, 0xCE, 0xCC, 0xC9, + 0x00, 0x96, 0x00, 0x97, 0x00, 0xAC, }; -static const unsigned char s6e8ax0_22_gamma_170[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb5, 0xbf, 0xb1, 0xd1, - 0xb1, 0xc3, 0xde, 0xc3, 0x99, 0xc0, 0x96, 0xb4, 0xce, 0xb1, - 0x00, 0x9f, 0x00, 0x96, 0x00, 0xc2, +static const unsigned char gamma22_120[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xED, 0xD6, 0xE6, + 0xD6, 0xD7, 0xD8, 0xDE, 0xDE, 0xE0, + 0xBC, 0xBC, 0xB9, 0xCD, 0xCA, 0xC8, + 0x00, 0x9A, 0x00, 0x9C, 0x00, 0xB1, }; -static const unsigned char s6e8ax0_22_gamma_180[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb7, 0xbe, 0xb3, 0xd2, - 0xb3, 0xc3, 0xde, 0xc2, 0x97, 0xbf, 0x95, 0xb4, 0xcd, 0xb1, - 0x00, 0xa2, 0x00, 0x99, 0x00, 0xc5, +static const unsigned char gamma22_130[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xEC, 0xD7, 0xE6, + 0xD3, 0xD8, 0xD7, 0xDE, 0xDD, 0xDF, + 0xBD, 0xBB, 0xB8, 0xCA, 0xC9, 0xC6, + 0x00, 0x9F, 0x00, 0xA0, 0x00, 0xB7, }; -static const unsigned char s6e8ax0_22_gamma_190[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbe, 0xb2, 0xd2, - 0xb2, 0xc3, 0xdd, 0xc3, 0x98, 0xbf, 0x95, 0xb2, 0xcc, 0xaf, - 0x00, 0xa5, 0x00, 0x9c, 0x00, 0xc9, +static const unsigned char gamma22_140[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xEC, 0xD9, 0xE5, + 0xD4, 0xD8, 0xD9, 0xDE, 0xDD, 0xDF, + 0xBB, 0xB9, 0xB7, 0xCA, 0xC9, 0xC5, + 0x00, 0xA3, 0x00, 0xA4, 0x00, 0xBB, }; -static const unsigned char s6e8ax0_22_gamma_200[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbc, 0xb2, 0xd2, - 0xb1, 0xc4, 0xdd, 0xc3, 0x97, 0xbe, 0x95, 0xb1, 0xcb, 0xae, - 0x00, 0xa8, 0x00, 0x9f, 0x00, 0xcd, +static const unsigned char gamma22_150[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xEC, 0xDA, 0xE5, + 0xD4, 0xD8, 0xD9, 0xDD, 0xDD, 0xDD, + 0xBB, 0xB9, 0xB6, 0xC9, 0xC7, 0xC5, + 0x00, 0xA6, 0x00, 0xA8, 0x00, 0xBF, }; -static const unsigned char s6e8ax0_22_gamma_210[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc1, 0xbd, 0xb1, 0xd1, - 0xb1, 0xc2, 0xde, 0xc2, 0x97, 0xbe, 0x94, 0xB0, 0xc9, 0xad, - 0x00, 0xae, 0x00, 0xa4, 0x00, 0xd4, +static const unsigned char gamma22_160[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xED, 0xDB, 0xE6, + 0xD4, 0xD7, 0xD9, 0xDC, 0xDD, 0xDD, + 0xB9, 0xB8, 0xB4, 0xC9, 0xC6, 0xC4, + 0x00, 0xAA, 0x00, 0xAC, 0x00, 0xC4, }; -static const unsigned char s6e8ax0_22_gamma_220[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc7, 0xbd, 0xb1, 0xd1, - 0xb1, 0xc2, 0xdd, 0xc2, 0x97, 0xbd, 0x94, 0xb0, 0xc9, 0xad, - 0x00, 0xad, 0x00, 0xa2, 0x00, 0xd3, +static const unsigned char gamma22_170[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xEC, 0xDC, 0xE5, + 0xD5, 0xD8, 0xD9, 0xDD, 0xDC, 0xDD, + 0xBA, 0xB7, 0xB5, 0xC7, 0xC6, 0xC3, + 0x00, 0xAD, 0x00, 0xAF, 0x00, 0xC7, }; -static const unsigned char s6e8ax0_22_gamma_230[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc3, 0xbd, 0xb2, 0xd1, - 0xb1, 0xc3, 0xdd, 0xc1, 0x96, 0xbd, 0x94, 0xb0, 0xc9, 0xad, - 0x00, 0xb0, 0x00, 0xa7, 0x00, 0xd7, +static const unsigned char gamma22_180[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xEE, 0xDD, 0xE6, + 0xD4, 0xD7, 0xD9, 0xDB, 0xDC, 0xDB, + 0xB9, 0xB7, 0xB4, 0xC6, 0xC4, 0xC2, + 0x00, 0xB1, 0x00, 0xB3, 0x00, 0xCC, }; -static const unsigned char s6e8ax0_22_gamma_240[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xcb, 0xbd, 0xb1, 0xd2, - 0xb1, 0xc3, 0xdD, 0xc2, 0x95, 0xbd, 0x93, 0xaf, 0xc8, 0xab, - 0x00, 0xb3, 0x00, 0xa9, 0x00, 0xdb, -}; - -static const unsigned char s6e8ax0_22_gamma_250[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xcc, 0xbe, 0xb0, 0xd2, - 0xb0, 0xc3, 0xdD, 0xc2, 0x94, 0xbc, 0x92, 0xae, 0xc8, 0xab, - 0x00, 0xb6, 0x00, 0xab, 0x00, 0xde, -}; - -static const unsigned char s6e8ax0_22_gamma_260[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xd0, 0xbe, 0xaf, 0xd1, - 0xaf, 0xc2, 0xdd, 0xc1, 0x96, 0xbc, 0x93, 0xaf, 0xc8, 0xac, - 0x00, 0xb7, 0x00, 0xad, 0x00, 0xe0, -}; - -static const unsigned char s6e8ax0_22_gamma_270[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xcF, 0xbd, 0xb0, 0xd2, - 0xaf, 0xc2, 0xdc, 0xc1, 0x95, 0xbd, 0x93, 0xae, 0xc6, 0xaa, - 0x00, 0xba, 0x00, 0xb0, 0x00, 0xe4, -}; - -static const unsigned char s6e8ax0_22_gamma_280[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xd0, 0xbd, 0xaf, 0xd0, - 0xad, 0xc4, 0xdd, 0xc3, 0x95, 0xbd, 0x93, 0xac, 0xc5, 0xa9, - 0x00, 0xbd, 0x00, 0xb2, 0x00, 0xe7, -}; - -static const unsigned char s6e8ax0_22_gamma_300[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb5, 0xd3, 0xbd, 0xb1, 0xd2, - 0xb0, 0xc0, 0xdc, 0xc0, 0x94, 0xba, 0x91, 0xac, 0xc5, 0xa9, - 0x00, 0xc2, 0x00, 0xb7, 0x00, 0xed, -}; - -static const unsigned char *s6e8ax0_22_gamma_table[] = { - s6e8ax0_22_gamma_30, - s6e8ax0_22_gamma_50, - s6e8ax0_22_gamma_60, - s6e8ax0_22_gamma_70, - s6e8ax0_22_gamma_80, - s6e8ax0_22_gamma_90, - s6e8ax0_22_gamma_100, - s6e8ax0_22_gamma_120, - s6e8ax0_22_gamma_130, - s6e8ax0_22_gamma_140, - s6e8ax0_22_gamma_150, - s6e8ax0_22_gamma_160, - s6e8ax0_22_gamma_170, - s6e8ax0_22_gamma_180, - s6e8ax0_22_gamma_190, - s6e8ax0_22_gamma_200, - s6e8ax0_22_gamma_210, - s6e8ax0_22_gamma_220, - s6e8ax0_22_gamma_230, - s6e8ax0_22_gamma_240, - s6e8ax0_22_gamma_250, - s6e8ax0_22_gamma_260, - s6e8ax0_22_gamma_270, - s6e8ax0_22_gamma_280, - s6e8ax0_22_gamma_300, -}; - -static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd) +static const unsigned char gamma22_190[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xED, 0xDE, 0xE6, + 0xD3, 0xD8, 0xD8, 0xDD, 0xDB, 0xDC, + 0xB9, 0xB6, 0xB4, 0xC5, 0xC4, 0xC0, + 0x00, 0xB4, 0x00, 0xB6, 0x00, 0xD0, +}; + +static const unsigned char gamma22_200[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xED, 0xDF, 0xE6, + 0xD3, 0xD7, 0xD8, 0xDB, 0xDB, 0xDA, + 0xB8, 0xB6, 0xB3, 0xC4, 0xC3, 0xC0, + 0x00, 0xB8, 0x00, 0xB9, 0x00, 0xD4, +}; + +static const unsigned char gamma22_210[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xEC, 0xE0, 0xE5, + 0xD5, 0xD7, 0xD9, 0xDB, 0xDA, 0xDA, + 0xB7, 0xB5, 0xB1, 0xC4, 0xC2, 0xC0, + 0x00, 0xBA, 0x00, 0xBD, 0x00, 0xD7, +}; + +static const unsigned char gamma22_220[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xED, 0xE0, 0xE6, + 0xD4, 0xD7, 0xD9, 0xDA, 0xDA, 0xD9, + 0xB7, 0xB4, 0xB1, 0xC2, 0xC2, 0xBE, + 0x00, 0xBE, 0x00, 0xC0, 0x00, 0xDC, +}; + +static const unsigned char gamma22_230[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xEC, 0xE2, 0xE6, + 0xD3, 0xD6, 0xD8, 0xDC, 0xD9, 0xD9, + 0xB6, 0xB4, 0xB1, 0xC1, 0xC1, 0xBD, + 0x00, 0xC1, 0x00, 0xC3, 0x00, 0xDF, +}; + +static const unsigned char gamma22_240[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xED, 0xE2, 0xE6, + 0xD4, 0xD6, 0xD8, 0xDA, 0xDA, 0xDA, + 0xB6, 0xB3, 0xB0, 0xC1, 0xBF, 0xBC, + 0x00, 0xC4, 0x00, 0xC7, 0x00, 0xE3, +}; + +static const unsigned char gamma22_250[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xED, 0xE3, 0xE7, + 0xD4, 0xD6, 0xD8, 0xDB, 0xD9, 0xD9, + 0xB3, 0xB2, 0xAE, 0xC1, 0xC0, 0xBC, + 0x00, 0xC7, 0x00, 0xC9, 0x00, 0xE7, +}; + +static const unsigned char gamma22_260[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xED, 0xE4, 0xE7, + 0xD4, 0xD5, 0xD7, 0xDA, 0xD9, 0xD9, + 0xB3, 0xB2, 0xAD, 0xC1, 0xBE, 0xBC, + 0x00, 0xC9, 0x00, 0xCD, 0x00, 0xEA, +}; + +static const unsigned char gamma22_270[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xED, 0xE5, 0xE8, + 0xD3, 0xD5, 0xD5, 0xDB, 0xD9, 0xD9, + 0xB3, 0xB1, 0xAE, 0xBF, 0xBE, 0xBA, + 0x00, 0xCC, 0x00, 0xD0, 0x00, 0xEE, +}; + +static const unsigned char gamma22_280[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xEC, 0xE5, 0xE6, + 0xD2, 0xD4, 0xD6, 0xDA, 0xD9, 0xD8, + 0xB3, 0xB1, 0xAD, 0xBF, 0xBD, 0xBA, + 0x00, 0xCF, 0x00, 0xD3, 0x00, 0xF1, +}; + +static const unsigned char gamma22_290[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xEC, 0xE6, 0xE7, + 0xD2, 0xD4, 0xD5, 0xDB, 0xD8, 0xD8, + 0xB1, 0xB0, 0xAC, 0xBE, 0xBD, 0xB9, + 0x00, 0xD3, 0x00, 0xD6, 0x00, 0xF5, +}; + +static const unsigned char gamma22_300[] = { + 0xFA, 0x01, + 0x1F, 0x1F, 0x1F, 0xED, 0xE6, 0xE7, + 0xD1, 0xD3, 0xD4, 0xDA, 0xD8, 0xD7, + 0xB1, 0xAF, 0xAB, 0xBD, 0xBB, 0xB8, + 0x00, 0xD6, 0x00, 0xDA, 0x00, 0xFA, +}; + + +static const unsigned char *s6e8aa0_22_gamma_table[] = { + gamma22_30, + gamma22_40, + gamma22_50, + gamma22_60, + gamma22_70, + gamma22_80, + gamma22_90, + gamma22_100, + gamma22_110, + gamma22_120, + gamma22_130, + gamma22_140, + gamma22_150, + gamma22_160, + gamma22_170, + gamma22_180, + gamma22_190, + gamma22_200, + gamma22_210, + gamma22_220, + gamma22_230, + gamma22_240, + gamma22_250, + gamma22_260, + gamma22_270, + gamma22_280, + gamma22_290, +}; + +static void s6e8aa0_panel_cond(struct s6e8aa0 *lcd) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); static const unsigned char data_to_send[] = { - 0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d, - 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08, - 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0, - 0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8 + 0xF8, + 0x25, 0x34, 0x00, 0x00, 0x00, 0x95, 0x00, 0x3c, 0x7d, 0x08, + 0x27, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x63, 0xc0, 0xc1, + 0x01, 0x81, 0xc1, 0x00, 0xc8, 0xc1, 0xd3, 0x01 + }; + static const unsigned char data_to_send_panel_reverse[] = { 0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d, 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08, @@ -300,42 +386,49 @@ static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd) 0xc1, 0x01, 0x41, 0xc1, 0x00, 0xc1, 0xf6, 0xf6, 0xc1 }; - if (lcd->dsim_dev->panel_reverse) + if (lcd->dsim_dev->panel_reverse) { + pr_err("Panel Reverse Called\n"); ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, data_to_send_panel_reverse, ARRAY_SIZE(data_to_send_panel_reverse)); - else - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); + } else { + ops->cmd_write(lcd_to_master(lcd), + MIPI_DSI_DCS_LONG_WRITE, data_to_send, + ARRAY_SIZE(data_to_send)); + } } -static void s6e8ax0_display_cond(struct s6e8ax0 *lcd) +static void s6e8aa0_display_cond(struct s6e8aa0 *lcd) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); static const unsigned char data_to_send[] = { - 0xf2, 0x80, 0x03, 0x0d + 0xF2, + 0x80, 0x03, 0x0D }; + pr_err("func: %s, line: %d\n", __func__, __LINE__); ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, data_to_send, ARRAY_SIZE(data_to_send)); } /* Gamma 2.2 Setting (200cd, 7500K, 10MPCD) */ -static void s6e8ax0_gamma_cond(struct s6e8ax0 *lcd) +static void s6e8aa0_gamma_cond(struct s6e8aa0 *lcd) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); unsigned int gamma = lcd->bd->props.brightness; + gamma = 15; ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - s6e8ax0_22_gamma_table[gamma], + s6e8aa0_22_gamma_table[gamma], GAMMA_TABLE_COUNT); } -static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd) +static void s6e8aa0_gamma_update(struct s6e8aa0 *lcd) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); static const unsigned char data_to_send[] = { - 0xf7, 0x03 + 0xF7, + 0x03, 0x00, 0x00 }; ops->cmd_write(lcd_to_master(lcd), @@ -343,110 +436,67 @@ static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd) ARRAY_SIZE(data_to_send)); } -static void s6e8ax0_etc_cond1(struct s6e8ax0 *lcd) +static void s6e8aa0_etc_cond1(struct s6e8aa0 *lcd) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); static const unsigned char data_to_send[] = { - 0xd1, 0xfe, 0x80, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x40, - 0x0d, 0x00, 0x00 + 0xF6, + 0x00, 0x02, 0x00 }; ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, data_to_send, ARRAY_SIZE(data_to_send)); } -static void s6e8ax0_etc_cond2(struct s6e8ax0 *lcd) +static void s6e8aa0_etc_cond2(struct s6e8aa0 *lcd) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); static const unsigned char data_to_send[] = { - 0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0, - 0x00 + 0xB6, + 0x0C, 0x02, 0x03, 0x32, 0xC0, 0x44, 0x44, 0xC0, 0x00 }; ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, data_to_send, ARRAY_SIZE(data_to_send)); } -static void s6e8ax0_etc_cond3(struct s6e8ax0 *lcd) +static void s6e8aa0_etc_cond3(struct s6e8aa0 *lcd) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); static const unsigned char data_to_send[] = { - 0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d + 0xF4, + 0xCF, 0x0A, 0x15, 0x10, 0x19, 0x33, 0x02 }; ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, data_to_send, ARRAY_SIZE(data_to_send)); } -static void s6e8ax0_etc_cond4(struct s6e8ax0 *lcd) +static void s6e8aa0_elvss_set(struct s6e8aa0 *lcd) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); static const unsigned char data_to_send[] = { - 0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03 + 0xB1, 0x04, 0x00 }; ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, data_to_send, ARRAY_SIZE(data_to_send)); } -static void s6e8ax0_etc_cond5(struct s6e8ax0 *lcd) +static void s6e8aa0_elvss_nvm_set(struct s6e8aa0 *lcd) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); static const unsigned char data_to_send[] = { - 0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02 + 0xD9, + 0x14, 0x40, 0x0C, 0xCB, 0xCE, 0x6E, 0xC4, 0x07, 0x40, 0x41, + 0xC1, 0x00, 0x60, 0x19 }; ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, data_to_send, ARRAY_SIZE(data_to_send)); } -static void s6e8ax0_etc_cond6(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xe3, 0x40 - }; - - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_SHORT_WRITE_PARAM, - data_to_send, ARRAY_SIZE(data_to_send)); -} -static void s6e8ax0_etc_cond7(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00 - }; - - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_elvss_set(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xb1, 0x04, 0x00 - }; - - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_elvss_nvm_set(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xd9, 0x5c, 0x20, 0x0c, 0x0f, 0x41, 0x00, 0x10, 0x11, - 0x12, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcb, 0xed, - 0x64, 0xaf - }; - - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd) +static void s6e8aa0_sleep_in(struct s6e8aa0 *lcd) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); static const unsigned char data_to_send[] = { @@ -458,7 +508,7 @@ static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd) data_to_send, ARRAY_SIZE(data_to_send)); } -static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd) +static void s6e8aa0_sleep_out(struct s6e8aa0 *lcd) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); static const unsigned char data_to_send[] = { @@ -470,7 +520,7 @@ static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd) data_to_send, ARRAY_SIZE(data_to_send)); } -static void s6e8ax0_display_on(struct s6e8ax0 *lcd) +static void s6e8aa0_display_on(struct s6e8aa0 *lcd) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); static const unsigned char data_to_send[] = { @@ -482,7 +532,7 @@ static void s6e8ax0_display_on(struct s6e8ax0 *lcd) data_to_send, ARRAY_SIZE(data_to_send)); } -static void s6e8ax0_display_off(struct s6e8ax0 *lcd) +static void s6e8aa0_display_off(struct s6e8aa0 *lcd) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); static const unsigned char data_to_send[] = { @@ -494,116 +544,21 @@ static void s6e8ax0_display_off(struct s6e8ax0 *lcd) data_to_send, ARRAY_SIZE(data_to_send)); } -static void s6e8ax0_apply_level2_key(struct s6e8ax0 *lcd) +static void s6e8aa0_apply_level2_key(struct s6e8aa0 *lcd) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); static const unsigned char data_to_send[] = { - 0xf0, 0x5a, 0x5a + 0xfc, 0x5a, 0x5a }; ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, data_to_send, ARRAY_SIZE(data_to_send)); } -static void s6e8ax0_acl_on(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xc0, 0x01 - }; - - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_SHORT_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_acl_off(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xc0, 0x00 - }; - - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_SHORT_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -/* Full white 50% reducing setting */ -static void s6e8ax0_acl_ctrl_set(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - /* Full white 50% reducing setting */ - static const unsigned char cutoff_50[] = { - 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, - 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38, - 0x3f, 0x46 - }; - /* Full white 45% reducing setting */ - static const unsigned char cutoff_45[] = { - 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, - 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31, - 0x37, 0x3d - }; - /* Full white 40% reducing setting */ - static const unsigned char cutoff_40[] = { - 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, - 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b, - 0x31, 0x36 - }; - - if (lcd->acl_enable) { - if (lcd->cur_acl == 0) { - if (lcd->gamma == 0 || lcd->gamma == 1) { - s6e8ax0_acl_off(lcd); - dev_dbg(&lcd->ld->dev, - "cur_acl=%d\n", lcd->cur_acl); - } else - s6e8ax0_acl_on(lcd); - } - switch (lcd->gamma) { - case 0: /* 30cd */ - s6e8ax0_acl_off(lcd); - lcd->cur_acl = 0; - break; - case 1 ... 3: /* 50cd ~ 90cd */ - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_LONG_WRITE, - cutoff_40, - ARRAY_SIZE(cutoff_40)); - lcd->cur_acl = 40; - break; - case 4 ... 7: /* 120cd ~ 210cd */ - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_LONG_WRITE, - cutoff_45, - ARRAY_SIZE(cutoff_45)); - lcd->cur_acl = 45; - break; - case 8 ... 10: /* 220cd ~ 300cd */ - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_LONG_WRITE, - cutoff_50, - ARRAY_SIZE(cutoff_50)); - lcd->cur_acl = 50; - break; - default: - break; - } - } else { - s6e8ax0_acl_off(lcd); - lcd->cur_acl = 0; - dev_dbg(&lcd->ld->dev, "cur_acl = %d\n", lcd->cur_acl); - } -} - -static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id) +static void s6e8aa0_read_id(struct s6e8aa0 *lcd, u8 *mtp_id) { unsigned int ret; - unsigned int addr = 0xd1; /* MTP ID */ + unsigned int addr = 0xD1; /* MTP ID */ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); ret = ops->cmd_read(lcd_to_master(lcd), @@ -611,61 +566,52 @@ static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id) addr, 3, mtp_id); } -static int s6e8ax0_panel_init(struct s6e8ax0 *lcd) +static int s6e8aa0_panel_init(struct s6e8aa0 *lcd) { - s6e8ax0_apply_level2_key(lcd); - s6e8ax0_sleep_out(lcd); - msleep(1); - s6e8ax0_panel_cond(lcd); - s6e8ax0_display_cond(lcd); - s6e8ax0_gamma_cond(lcd); - s6e8ax0_gamma_update(lcd); - - s6e8ax0_etc_cond1(lcd); - s6e8ax0_etc_cond2(lcd); - s6e8ax0_etc_cond3(lcd); - s6e8ax0_etc_cond4(lcd); - s6e8ax0_etc_cond5(lcd); - s6e8ax0_etc_cond6(lcd); - s6e8ax0_etc_cond7(lcd); + s6e8aa0_apply_level2_key(lcd); + mdelay(16); + s6e8aa0_sleep_out(lcd); + mdelay(5); + s6e8aa0_panel_cond(lcd); + s6e8aa0_display_cond(lcd); + s6e8aa0_gamma_cond(lcd); + s6e8aa0_gamma_update(lcd); - s6e8ax0_elvss_nvm_set(lcd); - s6e8ax0_elvss_set(lcd); + s6e8aa0_etc_cond1(lcd); + s6e8aa0_etc_cond2(lcd); + s6e8aa0_etc_cond3(lcd); - s6e8ax0_acl_ctrl_set(lcd); - s6e8ax0_acl_on(lcd); - - /* if ID3 value is not 33h, branch private elvss mode */ - msleep(lcd->ddi_pd->power_on_delay); + s6e8aa0_elvss_nvm_set(lcd); + s6e8aa0_elvss_set(lcd); + mdelay(120); return 0; } -static int s6e8ax0_update_gamma_ctrl(struct s6e8ax0 *lcd, int brightness) +static int s6e8aa0_update_gamma_ctrl(struct s6e8aa0 *lcd, int brightness) { struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - s6e8ax0_22_gamma_table[brightness], - ARRAY_SIZE(s6e8ax0_22_gamma_table)); + s6e8aa0_22_gamma_table[brightness], + ARRAY_SIZE(s6e8aa0_22_gamma_table)); /* update gamma table. */ - s6e8ax0_gamma_update(lcd); + s6e8aa0_gamma_update(lcd); lcd->gamma = brightness; return 0; } -static int s6e8ax0_gamma_ctrl(struct s6e8ax0 *lcd, int gamma) +static int s6e8aa0_gamma_ctrl(struct s6e8aa0 *lcd, int gamma) { - s6e8ax0_update_gamma_ctrl(lcd, gamma); - + s6e8aa0_update_gamma_ctrl(lcd, gamma); return 0; } -static int s6e8ax0_set_power(struct lcd_device *ld, int power) +static int s6e8aa0_set_power(struct lcd_device *ld, int power) { - struct s6e8ax0 *lcd = lcd_get_data(ld); + struct s6e8aa0 *lcd = lcd_get_data(ld); struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); int ret = 0; @@ -697,22 +643,22 @@ static int s6e8ax0_set_power(struct lcd_device *ld, int power) return ret; } -static int s6e8ax0_get_power(struct lcd_device *ld) +static int s6e8aa0_get_power(struct lcd_device *ld) { - struct s6e8ax0 *lcd = lcd_get_data(ld); + struct s6e8aa0 *lcd = lcd_get_data(ld); return lcd->power; } -static int s6e8ax0_get_brightness(struct backlight_device *bd) +static int s6e8aa0_get_brightness(struct backlight_device *bd) { return bd->props.brightness; } -static int s6e8ax0_set_brightness(struct backlight_device *bd) +static int s6e8aa0_set_brightness(struct backlight_device *bd) { int ret = 0, brightness = bd->props.brightness; - struct s6e8ax0 *lcd = bl_get_data(bd); + struct s6e8aa0 *lcd = bl_get_data(bd); if (brightness < MIN_BRIGHTNESS || brightness > bd->props.max_brightness) { @@ -721,7 +667,7 @@ static int s6e8ax0_set_brightness(struct backlight_device *bd) return -EINVAL; } - ret = s6e8ax0_gamma_ctrl(lcd, brightness); + ret = s6e8aa0_gamma_ctrl(lcd, brightness); if (ret) { dev_err(&bd->dev, "lcd brightness setting failed.\n"); return -EIO; @@ -730,174 +676,267 @@ static int s6e8ax0_set_brightness(struct backlight_device *bd) return ret; } -static struct lcd_ops s6e8ax0_lcd_ops = { - .set_power = s6e8ax0_set_power, - .get_power = s6e8ax0_get_power, +static struct lcd_ops s6e8aa0_lcd_ops = { + .set_power = s6e8aa0_set_power, + .get_power = s6e8aa0_get_power, }; -static const struct backlight_ops s6e8ax0_backlight_ops = { - .get_brightness = s6e8ax0_get_brightness, - .update_status = s6e8ax0_set_brightness, +static const struct backlight_ops s6e8aa0_backlight_ops = { + .get_brightness = s6e8aa0_get_brightness, + .update_status = s6e8aa0_set_brightness, }; -static void s6e8ax0_power_on(struct mipi_dsim_lcd_device *dsim_dev, int power) +static void s6e8aa0_power_on(struct mipi_dsim_lcd_device *dsim_dev, int power) { - struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); + struct s6e8aa0 *lcd = dev_get_drvdata(&dsim_dev->dev); msleep(lcd->ddi_pd->power_on_delay); + if (power) { + gpio_request_one(lcd->gpio_reset, GPIOF_OUT_INIT_HIGH, 0); + mdelay(500); + gpio_set_value(lcd->gpio_reset, 0); + mdelay(500); + gpio_set_value(lcd->gpio_reset, 1); + gpio_free(lcd->gpio_reset); + mdelay(5); + + gpio_request_one(lcd->gpio_power, GPIOF_OUT_INIT_HIGH, 0); + gpio_set_value(lcd->gpio_power, 1); + gpio_free(lcd->gpio_power); + + gpio_request_one(lcd->gpio_bl, GPIOF_OUT_INIT_HIGH, 0); + gpio_set_value(lcd->gpio_bl, 1); + gpio_free(lcd->gpio_bl); + + } else { + gpio_request_one(lcd->gpio_reset, GPIOF_OUT_INIT_LOW, 0); + mdelay(500); + gpio_free(lcd->gpio_reset); + + gpio_request_one(lcd->gpio_power, GPIOF_OUT_INIT_LOW, 0); + mdelay(500); + gpio_free(lcd->gpio_power); + } + + mdelay(500); + /* lcd power on */ if (power) - s6e8ax0_regulator_enable(lcd); + s6e8aa0_regulator_enable(lcd); else - s6e8ax0_regulator_disable(lcd); + s6e8aa0_regulator_disable(lcd); msleep(lcd->ddi_pd->reset_delay); /* lcd reset */ if (lcd->ddi_pd->reset) lcd->ddi_pd->reset(lcd->ld); - msleep(5); } -static void s6e8ax0_set_sequence(struct mipi_dsim_lcd_device *dsim_dev) +static void s6e8aa0_set_sequence(struct mipi_dsim_lcd_device *dsim_dev) { - struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); + struct s6e8aa0 *lcd = dev_get_drvdata(&dsim_dev->dev); + u8 mtp_id[3] = {0, }; - s6e8ax0_panel_init(lcd); - s6e8ax0_display_on(lcd); + s6e8aa0_read_id(lcd, mtp_id); + if (mtp_id[0] == 0x00) + dev_err(lcd->dev, "read id failed\n"); + + dev_info(lcd->dev, "Read ID : %x, %x, %x\n", + mtp_id[0], mtp_id[1], mtp_id[2]); + + if (mtp_id[2] == 0x33) + dev_info(lcd->dev, + "ID-3 is 0xff does not support dynamic elvss\n"); + else + dev_info(lcd->dev, + "ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]); + + s6e8aa0_panel_init(lcd); + s6e8aa0_display_on(lcd); lcd->power = FB_BLANK_UNBLANK; } -static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev) +static int s6e8aa0_update_platform_lcd_data( + struct s6e8aa0 *lcd_s6e8aa0) { - struct s6e8ax0 *lcd; + struct lcd_platform_data *ddi_pd = lcd_s6e8aa0->ddi_pd; + struct device_node *np = (struct device_node *) + lcd_s6e8aa0->ddi_pd->pdata; + + lcd_s6e8aa0->gpio_reset = of_get_named_gpio(np, "gpio-reset", 0); + if (!gpio_is_valid(lcd_s6e8aa0->gpio_reset)) { + dev_err(lcd_s6e8aa0->dev, + "failed to get poweron gpio-reset information.\n"); + return -EINVAL; + } + + lcd_s6e8aa0->gpio_power = of_get_named_gpio(np, "gpio-power", 0); + if (!gpio_is_valid(lcd_s6e8aa0->gpio_power)) { + dev_err(lcd_s6e8aa0->dev, + "failed to get poweron gpio-power information.\n"); + return -EINVAL; + } + + lcd_s6e8aa0->gpio_bl = of_get_named_gpio(np, "gpio-bl", 0); + if (!gpio_is_valid(lcd_s6e8aa0->gpio_bl)) { + dev_err(lcd_s6e8aa0->dev, + "failed to get pwm-bl information.\n"); + return -EINVAL; + } + + if (of_property_read_u32(np, "enabled", + (unsigned int *)&ddi_pd->lcd_enabled)) + ddi_pd->lcd_enabled = 0; + + if (of_property_read_u32(np, "reset-delay", + &ddi_pd->reset_delay)) { + dev_err(lcd_s6e8aa0->dev, "reset-delay property not found"); + return -EINVAL; + } + + if (of_property_read_u32(np, "power-on-delay", + &ddi_pd->power_on_delay)) { + dev_err(lcd_s6e8aa0->dev, "power-on-delay property not found"); + return -EINVAL; + } + + if (of_property_read_u32(np, "power-off-delay", + &ddi_pd->power_off_delay)) { + dev_err(lcd_s6e8aa0->dev, + "power-off-delay property not found"); + return -EINVAL; + } + + return 0; +} + + + +static int s6e8aa0_probe(struct mipi_dsim_lcd_device *dsim_dev) +{ + struct s6e8aa0 *lcd; int ret; - u8 mtp_id[3] = {0, }; - lcd = devm_kzalloc(&dsim_dev->dev, sizeof(struct s6e8ax0), GFP_KERNEL); + lcd = kzalloc(sizeof(struct s6e8aa0), GFP_KERNEL); if (!lcd) { - dev_err(&dsim_dev->dev, "failed to allocate s6e8ax0 structure.\n"); + dev_err(&dsim_dev->dev, "failed to allocate s6e8aa0 structure.\n"); return -ENOMEM; } + lcd_global = lcd; + lcd->dsim_dev = dsim_dev; lcd->ddi_pd = (struct lcd_platform_data *)dsim_dev->platform_data; lcd->dev = &dsim_dev->dev; + /* get platform data information, if lcd device node is present */ + if (lcd->ddi_pd->pdata) + if (s6e8aa0_update_platform_lcd_data(lcd)) + return -EINVAL; + mutex_init(&lcd->lock); - ret = devm_regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies); + ret = regulator_bulk_get(dsim_dev->master->dev, + ARRAY_SIZE(supplies), supplies); if (ret) { dev_err(lcd->dev, "Failed to get regulators: %d\n", ret); - return ret; + goto err_lcd_register; } - lcd->ld = lcd_device_register("s6e8ax0", lcd->dev, lcd, - &s6e8ax0_lcd_ops); + lcd->ld = lcd_device_register("s6e8aa0", lcd->dev, lcd, + &s6e8aa0_lcd_ops); if (IS_ERR(lcd->ld)) { dev_err(lcd->dev, "failed to register lcd ops.\n"); - return PTR_ERR(lcd->ld); + ret = PTR_ERR(lcd->ld); + goto err_lcd_register; } - lcd->bd = backlight_device_register("s6e8ax0-bl", lcd->dev, lcd, - &s6e8ax0_backlight_ops, NULL); + lcd->bd = backlight_device_register("s6e8aa0-bl", lcd->dev, lcd, + &s6e8aa0_backlight_ops, NULL); if (IS_ERR(lcd->bd)) { dev_err(lcd->dev, "failed to register backlight ops.\n"); ret = PTR_ERR(lcd->bd); goto err_backlight_register; } - lcd->bd->props.max_brightness = MAX_BRIGHTNESS; lcd->bd->props.brightness = MAX_BRIGHTNESS; - s6e8ax0_read_id(lcd, mtp_id); - if (mtp_id[0] == 0x00) - dev_err(lcd->dev, "read id failed\n"); - - dev_info(lcd->dev, "Read ID : %x, %x, %x\n", - mtp_id[0], mtp_id[1], mtp_id[2]); - - if (mtp_id[2] == 0x33) - dev_info(lcd->dev, - "ID-3 is 0xff does not support dynamic elvss\n"); - else - dev_info(lcd->dev, - "ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]); - - lcd->acl_enable = 1; - lcd->cur_acl = 0; - dev_set_drvdata(&dsim_dev->dev, lcd); - dev_dbg(lcd->dev, "probed s6e8ax0 panel driver.\n"); + dev_dbg(lcd->dev, "probed s6e8aa0 panel driver.\n"); return 0; err_backlight_register: lcd_device_unregister(lcd->ld); + +err_lcd_register: + regulator_bulk_free(ARRAY_SIZE(supplies), supplies); + kfree(lcd); + return ret; } #ifdef CONFIG_PM -static int s6e8ax0_suspend(struct mipi_dsim_lcd_device *dsim_dev) +static int s6e8aa0_suspend(struct mipi_dsim_lcd_device *dsim_dev) { - struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); + struct s6e8aa0 *lcd = dev_get_drvdata(&dsim_dev->dev); - s6e8ax0_sleep_in(lcd); + s6e8aa0_sleep_in(lcd); msleep(lcd->ddi_pd->power_off_delay); - s6e8ax0_display_off(lcd); + s6e8aa0_display_off(lcd); - s6e8ax0_regulator_disable(lcd); + s6e8aa0_regulator_disable(lcd); return 0; } -static int s6e8ax0_resume(struct mipi_dsim_lcd_device *dsim_dev) +static int s6e8aa0_resume(struct mipi_dsim_lcd_device *dsim_dev) { - struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); + struct s6e8aa0 *lcd = dev_get_drvdata(&dsim_dev->dev); - s6e8ax0_sleep_out(lcd); + s6e8aa0_sleep_out(lcd); msleep(lcd->ddi_pd->power_on_delay); - s6e8ax0_regulator_enable(lcd); - s6e8ax0_set_sequence(dsim_dev); + s6e8aa0_regulator_enable(lcd); + s6e8aa0_set_sequence(dsim_dev); return 0; } #else -#define s6e8ax0_suspend NULL -#define s6e8ax0_resume NULL +#define s6e8aa0_suspend NULL +#define s6e8aa0_resume NULL #endif -static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = { +static struct mipi_dsim_lcd_driver s6e8aa0_dsim_ddi_driver = { .name = "s6e8ax0", .id = -1, - .power_on = s6e8ax0_power_on, - .set_sequence = s6e8ax0_set_sequence, - .probe = s6e8ax0_probe, - .suspend = s6e8ax0_suspend, - .resume = s6e8ax0_resume, + .power_on = s6e8aa0_power_on, + .set_sequence = s6e8aa0_set_sequence, + .probe = s6e8aa0_probe, + .suspend = s6e8aa0_suspend, + .resume = s6e8aa0_resume, }; -static int s6e8ax0_init(void) +static int s6e8aa0_init(void) { - exynos_mipi_dsi_register_lcd_driver(&s6e8ax0_dsim_ddi_driver); + exynos_mipi_dsi_register_lcd_driver(&s6e8aa0_dsim_ddi_driver); return 0; } -static void s6e8ax0_exit(void) +static void s6e8aa0_exit(void) { return; } -module_init(s6e8ax0_init); -module_exit(s6e8ax0_exit); +module_init(s6e8aa0_init); +module_exit(s6e8aa0_exit); -MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); -MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); -MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver"); +MODULE_AUTHOR("Shaik Ameer Basha <shaik.ameer@samsung.com>"); +MODULE_DESCRIPTION("MIPI-DSI based s6e8aa0 AMOLED LCD Panel Driver"); MODULE_LICENSE("GPL"); diff --git a/firmware/edid-1920x1080.fw b/firmware/edid-1920x1080.fw Binary files differnew file mode 100644 index 000000000000..e90256c4fd2c --- /dev/null +++ b/firmware/edid-1920x1080.fw diff --git a/include/linux/printk.h b/include/linux/printk.h index 22c7052e9372..b62c76703532 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -8,6 +8,7 @@ extern const char linux_banner[]; extern const char linux_proc_banner[]; +extern const char linux_scm_version_banner[]; static inline int printk_get_level(const char *buffer) { diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index adae88f5b0ab..ebf28027b53a 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h @@ -253,7 +253,7 @@ size_t sg_pcopy_to_buffer(struct scatterlist *sgl, unsigned int nents, * Maximum number of entries that will be allocated in one piece, if * a list larger than this is required then chaining will be utilized. */ -#define SG_MAX_SINGLE_ALLOC (PAGE_SIZE / sizeof(struct scatterlist)) +#define SG_MAX_SINGLE_ALLOC ((PAGE_SIZE<<4) / sizeof(struct scatterlist)) /* * sg page iterator diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index e90a88a8708f..f81a7e9000e4 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -138,8 +138,10 @@ enum v4l2_colorfx { #define V4L2_CID_ALPHA_COMPONENT (V4L2_CID_BASE+41) #define V4L2_CID_COLORFX_CBCR (V4L2_CID_BASE+42) +#define V4L2_CID_CODEC_FRAME_TAG (V4L2_CID_BASE+43) + /* last CID + 1 */ -#define V4L2_CID_LASTP1 (V4L2_CID_BASE+43) +#define V4L2_CID_LASTP1 (V4L2_CID_BASE+44) /* USER-class private control IDs */ diff --git a/include/video/exynos_mipi_dsim.h b/include/video/exynos_mipi_dsim.h index 89dc88a171af..96b31a4f691f 100644 --- a/include/video/exynos_mipi_dsim.h +++ b/include/video/exynos_mipi_dsim.h @@ -15,7 +15,7 @@ #ifndef _EXYNOS_MIPI_DSIM_H #define _EXYNOS_MIPI_DSIM_H -#include <linux/device.h> +#include <linux/platform_device.h> #include <linux/fb.h> #define PANEL_NAME_SIZE (32) @@ -229,6 +229,7 @@ struct mipi_dsim_device { struct mipi_dsim_master_ops *master_ops; struct mipi_dsim_lcd_device *dsim_lcd_dev; struct mipi_dsim_lcd_driver *dsim_lcd_drv; + struct mipi_dsim_phy_config *dsim_phy_config; unsigned int state; unsigned int data_lane; @@ -294,6 +295,32 @@ struct mipi_dsim_master_ops { }; /* + * phy node structure for mipi-dsim. + * + * @reg_enable_dphy : base address to memory mapped D-PHY enable register + * @ctrl_bit_enable : control bit for enabling D-PHY + * @reg_reset_dphy : base address to memory mapped D-PHY reset register + * @ctrl_bit_reset : control bit for resetting D-PHY + */ +struct mipi_dsim_phy_config_type1 { + void __iomem *reg_enable_dphy; + int ctrlbit_enable_dphy; + void __iomem *reg_reset_dsim; + int ctrlbit_reset_dsim; +}; + +enum mipi_dsim_phy_config_type { + MIPI_DSIM_PHY_CONFIG_TYPE1, +}; + +struct mipi_dsim_phy_config { + enum mipi_dsim_phy_config_type type; + union { + struct mipi_dsim_phy_config_type1 phy_cfg_type1; + }; +}; + +/* * device structure for mipi-dsi based lcd panel. * * @name: name of the device to use with this device, or an diff --git a/init/main.c b/init/main.c index d03d2ec2eacf..aa84693fa67b 100644 --- a/init/main.c +++ b/init/main.c @@ -499,6 +499,7 @@ asmlinkage void __init start_kernel(void) boot_cpu_init(); page_address_init(); pr_notice("%s", linux_banner); + pr_notice("%s", linux_scm_version_banner); setup_arch(&command_line); mm_init_owner(&init_mm, &init_task); mm_init_cpumask(&init_mm); diff --git a/init/version.c b/init/version.c index 1a4718e500fe..3978f0298c98 100644 --- a/init/version.c +++ b/init/version.c @@ -48,3 +48,6 @@ const char linux_proc_banner[] = "%s version %s" " (" LINUX_COMPILE_BY "@" LINUX_COMPILE_HOST ")" " (" LINUX_COMPILER ") %s\n"; + +const char linux_scm_version_banner [] = + "Kernel was built at commit id '" KERNEL_GIT_ID "'\n"; diff --git a/linaro/configs/arndale.conf b/linaro/configs/arndale.conf index f4b022498b15..279f3c71469a 100644 --- a/linaro/configs/arndale.conf +++ b/linaro/configs/arndale.conf @@ -18,11 +18,15 @@ CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_VFP=y CONFIG_NEON=y CONFIG_PM_RUNTIME=y +CONFIG_EXTRA_FIRMWARE="edid-1920x1080.fw" +CONFIG_CMA=y +CONFIG_CMA_SIZE_MBYTES=128 CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_SG=y CONFIG_ATA=y CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_SATA_EXYNOS=y CONFIG_AX88796=y CONFIG_AX88796_93CX6=y CONFIG_USB_USBNET=y @@ -44,9 +48,32 @@ CONFIG_MFD_SEC_CORE=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_S5M8767=y +CONFIG_DRM=y +CONFIG_DRM_LOAD_EDID_FIRMWARE=y +CONFIG_DRM_EXYNOS=y +CONFIG_DRM_EXYNOS_DMABUF=y +CONFIG_DRM_EXYNOS_HDMI=y +CONFIG_DRM_EXYNOS_FIMD=y +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_EXYNOS_VIDEO=y +CONFIG_EXYNOS_MIPI_DSI=y +CONFIG_EXYNOS_LCD_S6E8AX0=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_PLATFORM=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_SAMSUNG=y +CONFIG_SND_SOC_SMDK_I2S_STUB=y CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_DWC3=y +CONFIG_USB_DWC3_HOST=y CONFIG_USB_PHY=y CONFIG_SAMSUNG_USB2PHY=y CONFIG_SAMSUNG_USB3PHY=y @@ -65,6 +92,10 @@ CONFIG_MMC_UNSAFE_RESUME=y CONFIG_MMC_DW=y CONFIG_MMC_DW_IDMAC=y CONFIG_MMC_DW_EXYNOS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_S3C=y +CONFIG_PWM=y +CONFIG_PWM_SAMSUNG=y CONFIG_DEBUG_KERNEL=y CONFIG_DETECT_HUNG_TASK=y CONFIG_DEBUG_RT_MUTEXES=y diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index badb6fbacaa6..58784ca32d90 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -246,6 +246,9 @@ config SND_SOC_CX20442 tristate depends on TTY +config SND_SOC_I2S_STUB + tristate + config SND_SOC_JZ4740_CODEC select REGMAP_MMIO tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 70fd8066f546..7b7d3d032583 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -29,6 +29,7 @@ snd-soc-da732x-objs := da732x.o snd-soc-da9055-objs := da9055.o snd-soc-bt-sco-objs := bt-sco.o snd-soc-dmic-objs := dmic.o +snd-soc-i2s-stub-objs := i2s_stub.o snd-soc-isabelle-objs := isabelle.o snd-soc-jz4740-codec-objs := jz4740.o snd-soc-l3-objs := l3.o @@ -158,6 +159,7 @@ obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o +obj-$(CONFIG_SND_SOC_I2S_STUB) += snd-soc-i2s-stub.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o diff --git a/sound/soc/codecs/i2s_stub.c b/sound/soc/codecs/i2s_stub.c new file mode 100644 index 000000000000..df6b08944ed3 --- /dev/null +++ b/sound/soc/codecs/i2s_stub.c @@ -0,0 +1,74 @@ +/* + * ALSA SoC I2S Stub Codec driver + * + * This driver is used by controllers which can operate in I2S mode with + * Stub codec driver for I2S mode. + * + * The code is based on sound/soc/codec/spdif_transceiver.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <sound/soc.h> +#include <sound/pcm.h> +#include <sound/initval.h> + + +#define I2S_STUB_RATES SNDRV_PCM_RATE_8000_192000 +#define I2S_STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE + +static struct snd_soc_codec_driver soc_codec_i2s_stub; + +static struct snd_soc_dai_driver i2s_stub_dai = { + .name = "i2s-stub-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = I2S_STUB_RATES, + .formats = I2S_STUB_FORMATS, + }, +}; + +static int i2s_stub_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, &soc_codec_i2s_stub, + &i2s_stub_dai, 1); +} + +static int i2s_stub_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id i2s_stub_dt_ids[] = { + { .compatible = "linux,i2s-stub", }, + { } +}; +MODULE_DEVICE_TABLE(of, i2s_stub_dt_ids); +#endif + +static struct platform_driver i2s_stub_driver = { + .probe = i2s_stub_probe, + .remove = i2s_stub_remove, + .driver = { + .name = "i2s-stub", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(i2s_stub_dt_ids), + }, +}; + +module_platform_driver(i2s_stub_driver); + +MODULE_AUTHOR("Tushar Behera"); +MODULE_DESCRIPTION("I2S stub codec driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform: i2s-stub"); diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 9855dfc3e3ec..a3b992dd88d2 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -222,3 +222,12 @@ config SND_SOC_LITTLEMILL select SND_SAMSUNG_I2S select MFD_WM8994 select SND_SOC_WM8994 + +config SND_SOC_SMDK_I2S_STUB + tristate "SoC I2S Audio support for boards without external codec chip" + depends on SND_SOC_SAMSUNG + select SND_SAMSUNG_I2S + select SND_SOC_I2S_STUB + help + Say Y if you want to add support for SoC audio on boards without + exteral codec chip. diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 709f6059ad67..d2253219b4db 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -43,6 +43,7 @@ snd-soc-tobermory-objs := tobermory.o snd-soc-lowland-objs := lowland.o snd-soc-littlemill-objs := littlemill.o snd-soc-bells-objs := bells.o +snd-soc-smdk-i2s-stub-objs := smdk_i2s_stub.o obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o @@ -67,3 +68,4 @@ obj-$(CONFIG_SND_SOC_TOBERMORY) += snd-soc-tobermory.o obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o +obj-$(CONFIG_SND_SOC_SMDK_I2S_STUB) += snd-soc-smdk-i2s-stub.o diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index 21b79262010e..ddea134aff25 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c @@ -76,7 +76,8 @@ static void dma_enqueue(struct snd_pcm_substream *substream) pr_debug("Entered %s\n", __func__); - limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period; + limit = (unsigned int) (prtd->dma_end - prtd->dma_start) + / prtd->dma_period; pr_debug("%s: loaded %d, limit %d\n", __func__, prtd->dma_loaded, limit); diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c index ce1e1e16f250..6bc658a80b3b 100644 --- a/sound/soc/samsung/idma.c +++ b/sound/soc/samsung/idma.c @@ -282,7 +282,7 @@ static irqreturn_t iis_irq(int irqno, void *dev_id) addr = readl(idma.regs + I2SLVL0ADDR) - idma.lp_tx_addr; addr += prtd->periodsz; - addr %= (prtd->end - prtd->start); + addr %= (unsigned int) (prtd->end - prtd->start); addr += idma.lp_tx_addr; writel(addr, idma.regs + I2SLVL0ADDR); diff --git a/sound/soc/samsung/smdk_i2s_stub.c b/sound/soc/samsung/smdk_i2s_stub.c new file mode 100644 index 000000000000..b20c8da308a8 --- /dev/null +++ b/sound/soc/samsung/smdk_i2s_stub.c @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2011 Insignal Co., Ltd. + * + * 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 <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of.h> + +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> + +#include "i2s.h" + +static int set_epll_rate(unsigned long rate) +{ + struct clk *fout_epll; + + fout_epll = clk_get(NULL, "fout_epll"); + if (IS_ERR(fout_epll)) { + printk(KERN_ERR "%s: failed to get fout_epll\n", __func__); + return -ENOENT; + } + + if (rate == clk_get_rate(fout_epll)) + goto out; + + clk_set_rate(fout_epll, rate); +out: + clk_put(fout_epll); + + return 0; +} + +static int smdk_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int bfs, psr, rfs, ret; + unsigned long rclk; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_U24: + case SNDRV_PCM_FORMAT_S24: + bfs = 48; + break; + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_S16_LE: + bfs = 32; + break; + default: + return -EINVAL; + } + + switch (params_rate(params)) { + case 16000: + case 22050: + case 24000: + case 32000: + case 44100: + case 48000: + case 88200: + case 96000: + rfs = (bfs == 48) ? 384 : 256; + break; + case 64000: + rfs = 384; + break; + case 8000: + case 11025: + case 12000: + rfs = (bfs == 48) ? 768 : 512; + break; + default: + return -EINVAL; + } + + rclk = params_rate(params) * rfs; + + switch (rclk) { + case 4096000: + case 5644800: + case 6144000: + case 8467200: + case 9216000: + psr = 8; + break; + case 8192000: + case 11289600: + case 12288000: + case 16934400: + case 18432000: + psr = 4; + break; + case 22579200: + case 24576000: + case 33868800: + case 36864000: + psr = 2; + break; + case 67737600: + case 73728000: + psr = 1; + break; + default: + printk(KERN_ERR "Not yet supported!\n"); + return -EINVAL; + } + + set_epll_rate(rclk * psr); + + /* Set the AP DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK, rfs, + SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(cpu_dai, SAMSUNG_I2S_DIV_BCLK, bfs); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops smdk_ops = { + .hw_params = smdk_hw_params, +}; + +static int smdk_init_paiftx(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_dapm_sync(dapm); + + return 0; +} + +static struct snd_soc_dai_link smdk_dai[] = { + { /* Primary DAI i/f */ + .name = "I2S", + .stream_name = "Pri_Dai", + .cpu_dai_name = "samsung-i2s.0", + .codec_dai_name = "i2s-stub-hifi", + .platform_name = "samsung-i2s.0", + .codec_name = "i2s-stub", + .init = smdk_init_paiftx, + .ops = &smdk_ops, + }, +}; + +static struct snd_soc_card smdk = { + .name = "I2S-STUB", + .owner = THIS_MODULE, + .dai_link = smdk_dai, + .num_links = ARRAY_SIZE(smdk_dai), +}; + +static int smdk_audio_probe(struct platform_device *pdev) +{ + int ret; + struct device_node *np = pdev->dev.of_node; + struct snd_soc_card *card = &smdk; + struct snd_soc_dai_link *dai_link = &smdk_dai[0]; + + card->dev = &pdev->dev; + + if (np) { + dai_link->cpu_of_node = of_parse_phandle(np, + "samsung,i2s-controller", 0); + if (!dai_link->cpu_of_node) { + dev_err(&pdev->dev, + "Property 'samsung,i2s-controller' missing\n"); + return -EINVAL; + } + + dai_link->cpu_dai_name = NULL; + dai_link->platform_name = NULL; + dai_link->platform_of_node = dai_link->cpu_of_node; + + dai_link->codec_of_node = of_parse_phandle(np, + "samsung,audio-codec", 0); + if (!dai_link->codec_of_node) { + dev_err(&pdev->dev, + "Property 'samsung,audio-codec' missing\n"); + return -EINVAL; + } + dai_link->codec_name = NULL; + } + + ret = snd_soc_register_card(card); + + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret); + + return ret; +} + +static int smdk_audio_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id samsung_i2s_stub_of_match[] = { + { .compatible = "samsung,smdk-i2s-stub", }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match); +#endif /* CONFIG_OF */ + +static struct platform_driver smdk_audio_driver = { + .driver = { + .name = "smdk-i2s-audio", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(samsung_i2s_stub_of_match), + }, + .probe = smdk_audio_probe, + .remove = smdk_audio_remove, +}; + +module_platform_driver(smdk_audio_driver); + +MODULE_AUTHOR("Tushar Behera"); +MODULE_DESCRIPTION("ALSA SoC I2S STUB"); +MODULE_LICENSE("GPL"); |