diff options
237 files changed, 9357 insertions, 4684 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-mtd b/Documentation/ABI/testing/sysfs-class-mtd index 4d55a1888981..db1ad7e34fc3 100644 --- a/Documentation/ABI/testing/sysfs-class-mtd +++ b/Documentation/ABI/testing/sysfs-class-mtd @@ -123,3 +123,54 @@ Description: half page, or a quarter page). In the case of ECC NOR, it is the ECC block size. + +What: /sys/class/mtd/mtdX/ecc_strength +Date: April 2012 +KernelVersion: 3.4 +Contact: linux-mtd@lists.infradead.org +Description: + Maximum number of bit errors that the device is capable of + correcting within each region covering an ecc step. This will + always be a non-negative integer. Note that some devices will + have multiple ecc steps within each writesize region. + + In the case of devices lacking any ECC capability, it is 0. + +What: /sys/class/mtd/mtdX/bitflip_threshold +Date: April 2012 +KernelVersion: 3.4 +Contact: linux-mtd@lists.infradead.org +Description: + This allows the user to examine and adjust the criteria by which + mtd returns -EUCLEAN from mtd_read(). If the maximum number of + bit errors that were corrected on any single region comprising + an ecc step (as reported by the driver) equals or exceeds this + value, -EUCLEAN is returned. Otherwise, absent an error, 0 is + returned. Higher layers (e.g., UBI) use this return code as an + indication that an erase block may be degrading and should be + scrutinized as a candidate for being marked as bad. + + The initial value may be specified by the flash device driver. + If not, then the default value is ecc_strength. + + The introduction of this feature brings a subtle change to the + meaning of the -EUCLEAN return code. Previously, it was + interpreted to mean simply "one or more bit errors were + corrected". Its new interpretation can be phrased as "a + dangerously high number of bit errors were corrected on one or + more regions comprising an ecc step". The precise definition of + "dangerously high" can be adjusted by the user with + bitflip_threshold. Users are discouraged from doing this, + however, unless they know what they are doing and have intimate + knowledge of the properties of their device. Broadly speaking, + bitflip_threshold should be low enough to detect genuine erase + block degradation, but high enough to avoid the consequences of + a persistent return value of -EUCLEAN on devices where sticky + bitflips occur. Note that if bitflip_threshold exceeds + ecc_strength, -EUCLEAN is never returned by mtd_read(). + Conversely, if bitflip_threshold is zero, -EUCLEAN is always + returned, absent a hard error. + + This is generally applicable only to NAND flash devices with ECC + capability. It is ignored on devices lacking ECC capability; + i.e., devices for which ecc_strength is zero. diff --git a/Documentation/DocBook/mtdnand.tmpl b/Documentation/DocBook/mtdnand.tmpl index 0c674be0d3c6..e0aedb7a7827 100644 --- a/Documentation/DocBook/mtdnand.tmpl +++ b/Documentation/DocBook/mtdnand.tmpl @@ -1119,8 +1119,6 @@ in this page</entry> These constants are defined in nand.h. They are ored together to describe the chip functionality. <programlisting> -/* Chip can not auto increment pages */ -#define NAND_NO_AUTOINCR 0x00000001 /* Buswitdh is 16 bit */ #define NAND_BUSWIDTH_16 0x00000002 /* Device supports partial programming without padding */ diff --git a/Documentation/arm/OMAP/DSS b/Documentation/arm/OMAP/DSS index 888ae7b83ae4..a564ceea9e98 100644 --- a/Documentation/arm/OMAP/DSS +++ b/Documentation/arm/OMAP/DSS @@ -47,6 +47,51 @@ flexible way to enable non-common multi-display configuration. In addition to modelling the hardware overlays, omapdss supports virtual overlays and overlay managers. These can be used when updating a display with CPU or system DMA. +omapdss driver support for audio +-------------------------------- +There exist several display technologies and standards that support audio as +well. Hence, it is relevant to update the DSS device driver to provide an audio +interface that may be used by an audio driver or any other driver interested in +the functionality. + +The audio_enable function is intended to prepare the relevant +IP for playback (e.g., enabling an audio FIFO, taking in/out of reset +some IP, enabling companion chips, etc). It is intended to be called before +audio_start. The audio_disable function performs the reverse operation and is +intended to be called after audio_stop. + +While a given DSS device driver may support audio, it is possible that for +certain configurations audio is not supported (e.g., an HDMI display using a +VESA video timing). The audio_supported function is intended to query whether +the current configuration of the display supports audio. + +The audio_config function is intended to configure all the relevant audio +parameters of the display. In order to make the function independent of any +specific DSS device driver, a struct omap_dss_audio is defined. Its purpose +is to contain all the required parameters for audio configuration. At the +moment, such structure contains pointers to IEC-60958 channel status word +and CEA-861 audio infoframe structures. This should be enough to support +HDMI and DisplayPort, as both are based on CEA-861 and IEC-60958. + +The audio_enable/disable, audio_config and audio_supported functions could be +implemented as functions that may sleep. Hence, they should not be called +while holding a spinlock or a readlock. + +The audio_start/audio_stop function is intended to effectively start/stop audio +playback after the configuration has taken place. These functions are designed +to be used in an atomic context. Hence, audio_start should return quickly and be +called only after all the needed resources for audio playback (audio FIFOs, +DMA channels, companion chips, etc) have been enabled to begin data transfers. +audio_stop is designed to only stop the audio transfers. The resources used +for playback are released using audio_disable. + +The enum omap_dss_audio_state may be used to help the implementations of +the interface to keep track of the audio state. The initial state is _DISABLED; +then, the state transitions to _CONFIGURED, and then, when it is ready to +play audio, to _ENABLED. The state _PLAYING is used when the audio is being +rendered. + + Panel and controller drivers ---------------------------- @@ -156,6 +201,7 @@ timings Display timings (pixclock,xres/hfp/hbp/hsw,yres/vfp/vbp/vsw) "pal" and "ntsc" panel_name tear_elim Tearing elimination 0=off, 1=on +output_type Output type (video encoder only): "composite" or "svideo" There are also some debugfs files at <debugfs>/omapdss/ which show information about clocks and registers. diff --git a/Documentation/devicetree/bindings/mtd/gpmi-nand.txt b/Documentation/devicetree/bindings/mtd/gpmi-nand.txt new file mode 100644 index 000000000000..1a5bbd346d22 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/gpmi-nand.txt @@ -0,0 +1,33 @@ +* Freescale General-Purpose Media Interface (GPMI) + +The GPMI nand controller provides an interface to control the +NAND flash chips. We support only one NAND chip now. + +Required properties: + - compatible : should be "fsl,<chip>-gpmi-nand" + - reg : should contain registers location and length for gpmi and bch. + - reg-names: Should contain the reg names "gpmi-nand" and "bch" + - interrupts : The first is the DMA interrupt number for GPMI. + The second is the BCH interrupt number. + - interrupt-names : The interrupt names "gpmi-dma", "bch"; + - fsl,gpmi-dma-channel : Should contain the dma channel it uses. + +The device tree may optionally contain sub-nodes describing partitions of the +address space. See partition.txt for more detail. + +Examples: + +gpmi-nand@8000c000 { + compatible = "fsl,imx28-gpmi-nand"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x8000c000 2000>, <0x8000a000 2000>; + reg-names = "gpmi-nand", "bch"; + interrupts = <88>, <41>; + interrupt-names = "gpmi-dma", "bch"; + fsl,gpmi-dma-channel = <4>; + + partition@0 { + ... + }; +}; diff --git a/Documentation/devicetree/bindings/mtd/mxc-nand.txt b/Documentation/devicetree/bindings/mtd/mxc-nand.txt new file mode 100644 index 000000000000..b5833d11c7be --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/mxc-nand.txt @@ -0,0 +1,19 @@ +* Freescale's mxc_nand + +Required properties: +- compatible: "fsl,imxXX-nand" +- reg: address range of the nfc block +- interrupts: irq to be used +- nand-bus-width: see nand.txt +- nand-ecc-mode: see nand.txt +- nand-on-flash-bbt: see nand.txt + +Example: + + nand@d8000000 { + compatible = "fsl,imx27-nand"; + reg = <0xd8000000 0x1000>; + interrupts = <29>; + nand-bus-width = <8>; + nand-ecc-mode = "hw"; + }; diff --git a/Documentation/vm/slub.txt b/Documentation/vm/slub.txt index 6752870c4970..b0c6d1bbb434 100644 --- a/Documentation/vm/slub.txt +++ b/Documentation/vm/slub.txt @@ -17,7 +17,7 @@ data and perform operation on the slabs. By default slabinfo only lists slabs that have data in them. See "slabinfo -h" for more options when running the command. slabinfo can be compiled with -gcc -o slabinfo tools/slub/slabinfo.c +gcc -o slabinfo tools/vm/slabinfo.c Some of the modes of operation of slabinfo require that slub debugging be enabled on the command line. F.e. no tracking information will be diff --git a/arch/arm/boot/dts/db8500.dtsi b/arch/arm/boot/dts/db8500.dtsi index 881bc3987844..4ad5160018cb 100644 --- a/arch/arm/boot/dts/db8500.dtsi +++ b/arch/arm/boot/dts/db8500.dtsi @@ -58,6 +58,8 @@ "st,nomadik-gpio"; reg = <0x8012e000 0x80>; interrupts = <0 119 0x4>; + interrupt-controller; + #interrupt-cells = <2>; supports-sleepmode; gpio-controller; #gpio-cells = <2>; @@ -69,6 +71,8 @@ "st,nomadik-gpio"; reg = <0x8012e080 0x80>; interrupts = <0 120 0x4>; + interrupt-controller; + #interrupt-cells = <2>; supports-sleepmode; gpio-controller; #gpio-cells = <2>; @@ -80,6 +84,8 @@ "st,nomadik-gpio"; reg = <0x8000e000 0x80>; interrupts = <0 121 0x4>; + interrupt-controller; + #interrupt-cells = <2>; supports-sleepmode; gpio-controller; #gpio-cells = <2>; @@ -91,6 +97,8 @@ "st,nomadik-gpio"; reg = <0x8000e080 0x80>; interrupts = <0 122 0x4>; + interrupt-controller; + #interrupt-cells = <2>; supports-sleepmode; gpio-controller; #gpio-cells = <2>; @@ -102,6 +110,8 @@ "st,nomadik-gpio"; reg = <0x8000e100 0x80>; interrupts = <0 123 0x4>; + interrupt-controller; + #interrupt-cells = <2>; supports-sleepmode; gpio-controller; #gpio-cells = <2>; @@ -113,6 +123,8 @@ "st,nomadik-gpio"; reg = <0x8000e180 0x80>; interrupts = <0 124 0x4>; + interrupt-controller; + #interrupt-cells = <2>; supports-sleepmode; gpio-controller; #gpio-cells = <2>; @@ -124,6 +136,8 @@ "st,nomadik-gpio"; reg = <0x8011e000 0x80>; interrupts = <0 125 0x4>; + interrupt-controller; + #interrupt-cells = <2>; supports-sleepmode; gpio-controller; #gpio-cells = <2>; @@ -135,6 +149,8 @@ "st,nomadik-gpio"; reg = <0x8011e080 0x80>; interrupts = <0 126 0x4>; + interrupt-controller; + #interrupt-cells = <2>; supports-sleepmode; gpio-controller; #gpio-cells = <2>; @@ -146,12 +162,18 @@ "st,nomadik-gpio"; reg = <0xa03fe000 0x80>; interrupts = <0 127 0x4>; + interrupt-controller; + #interrupt-cells = <2>; supports-sleepmode; gpio-controller; #gpio-cells = <2>; gpio-bank = <8>; }; + pinctrl { + compatible = "stericsson,nmk_pinctrl"; + }; + usb@a03e0000 { compatible = "stericsson,db8500-musb", "mentor,musb"; @@ -169,20 +191,195 @@ prcmu@80157000 { compatible = "stericsson,db8500-prcmu"; reg = <0x80157000 0x1000>; - interrupts = <46 47>; + interrupts = <0 47 0x4>; #address-cells = <1>; #size-cells = <1>; ranges; - prcmu-timer-4@80157450 { + prcmu-timer-4@80157450 { compatible = "stericsson,db8500-prcmu-timer-4"; reg = <0x80157450 0xC>; }; + db8500-prcmu-regulators { + compatible = "stericsson,db8500-prcmu-regulator"; + + // DB8500_REGULATOR_VAPE + db8500_vape_reg: db8500_vape { + regulator-name = "db8500-vape"; + regulator-always-on; + }; + + // DB8500_REGULATOR_VARM + db8500_varm_reg: db8500_varm { + regulator-name = "db8500-varm"; + }; + + // DB8500_REGULATOR_VMODEM + db8500_vmodem_reg: db8500_vmodem { + regulator-name = "db8500-vmodem"; + }; + + // DB8500_REGULATOR_VPLL + db8500_vpll_reg: db8500_vpll { + regulator-name = "db8500-vpll"; + }; + + // DB8500_REGULATOR_VSMPS1 + db8500_vsmps1_reg: db8500_vsmps1 { + regulator-name = "db8500-vsmps1"; + }; + + // DB8500_REGULATOR_VSMPS2 + db8500_vsmps2_reg: db8500_vsmps2 { + regulator-name = "db8500-vsmps2"; + }; + + // DB8500_REGULATOR_VSMPS3 + db8500_vsmps3_reg: db8500_vsmps3 { + regulator-name = "db8500-vsmps3"; + }; + + // DB8500_REGULATOR_VRF1 + db8500_vrf1_reg: db8500_vrf1 { + regulator-name = "db8500-vrf1"; + }; + + // DB8500_REGULATOR_SWITCH_SVAMMDSP + db8500_sva_mmdsp_reg: db8500_sva_mmdsp { + regulator-name = "db8500-sva-mmdsp"; + }; + + // DB8500_REGULATOR_SWITCH_SVAMMDSPRET + db8500_sva_mmdsp_ret_reg: db8500_sva_mmdsp_ret { + regulator-name = "db8500-sva-mmdsp-ret"; + }; + + // DB8500_REGULATOR_SWITCH_SVAPIPE + db8500_sva_pipe_reg: db8500_sva_pipe { + regulator-name = "db8500_sva_pipe"; + }; + + // DB8500_REGULATOR_SWITCH_SIAMMDSP + db8500_sia_mmdsp_reg: db8500_sia_mmdsp { + regulator-name = "db8500_sia_mmdsp"; + }; + + // DB8500_REGULATOR_SWITCH_SIAMMDSPRET + db8500_sia_mmdsp_ret_reg: db8500_sia_mmdsp_ret { + regulator-name = "db8500-sia-mmdsp-ret"; + }; + + // DB8500_REGULATOR_SWITCH_SIAPIPE + db8500_sia_pipe_reg: db8500_sia_pipe { + regulator-name = "db8500-sia-pipe"; + }; + + // DB8500_REGULATOR_SWITCH_SGA + db8500_sga_reg: db8500_sga { + regulator-name = "db8500-sga"; + vin-supply = <&db8500_vape_reg>; + }; + + // DB8500_REGULATOR_SWITCH_B2R2_MCDE + db8500_b2r2_mcde_reg: db8500_b2r2_mcde { + regulator-name = "db8500-b2r2-mcde"; + vin-supply = <&db8500_vape_reg>; + }; + + // DB8500_REGULATOR_SWITCH_ESRAM12 + db8500_esram12_reg: db8500_esram12 { + regulator-name = "db8500-esram12"; + }; + + // DB8500_REGULATOR_SWITCH_ESRAM12RET + db8500_esram12_ret_reg: db8500_esram12_ret { + regulator-name = "db8500-esram12-ret"; + }; + + // DB8500_REGULATOR_SWITCH_ESRAM34 + db8500_esram34_reg: db8500_esram34 { + regulator-name = "db8500-esram34"; + }; + + // DB8500_REGULATOR_SWITCH_ESRAM34RET + db8500_esram34_ret_reg: db8500_esram34_ret { + regulator-name = "db8500-esram34-ret"; + }; + }; + ab8500@5 { compatible = "stericsson,ab8500"; reg = <5>; /* mailbox 5 is i2c */ interrupts = <0 40 0x4>; + + ab8500-regulators { + compatible = "stericsson,ab8500-regulator"; + + // supplies to the display/camera + ab8500_ldo_aux1_reg: ab8500_ldo_aux1 { + regulator-name = "V-DISPLAY"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2900000>; + regulator-boot-on; + /* BUG: If turned off MMC will be affected. */ + regulator-always-on; + }; + + // supplies to the on-board eMMC + ab8500_ldo_aux2_reg: ab8500_ldo_aux2 { + regulator-name = "V-eMMC1"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <3300000>; + }; + + // supply for VAUX3; SDcard slots + ab8500_ldo_aux3_reg: ab8500_ldo_aux3 { + regulator-name = "V-MMC-SD"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <3300000>; + }; + + // supply for v-intcore12; VINTCORE12 LDO + ab8500_ldo_initcore_reg: ab8500_ldo_initcore { + regulator-name = "V-INTCORE"; + }; + + // supply for tvout; gpadc; TVOUT LDO + ab8500_ldo_tvout_reg: ab8500_ldo_tvout { + regulator-name = "V-TVOUT"; + }; + + // supply for ab8500-usb; USB LDO + ab8500_ldo_usb_reg: ab8500_ldo_usb { + regulator-name = "dummy"; + }; + + // supply for ab8500-vaudio; VAUDIO LDO + ab8500_ldo_audio_reg: ab8500_ldo_audio { + regulator-name = "V-AUD"; + }; + + // supply for v-anamic1 VAMic1-LDO + ab8500_ldo_anamic1_reg: ab8500_ldo_anamic1 { + regulator-name = "V-AMIC1"; + }; + + // supply for v-amic2; VAMIC2 LDO; reuse constants for AMIC1 + ab8500_ldo_amamic2_reg: ab8500_ldo_amamic2 { + regulator-name = "V-AMIC2"; + }; + + // supply for v-dmic; VDMIC LDO + ab8500_ldo_dmic_reg: ab8500_ldo_dmic { + regulator-name = "V-DMIC"; + }; + + // supply for U8500 CSI/DSI; VANA LDO + ab8500_ldo_ana_reg: ab8500_ldo_ana { + regulator-name = "V-CSI/DSI"; + }; + }; }; }; @@ -235,7 +432,8 @@ status = "disabled"; // Add one of these for each child device - cs-gpios = <&gpio0 31 &gpio4 14 &gpio4 16 &gpio6 22 &gpio7 0>; + cs-gpios = <&gpio0 31 0x4 &gpio4 14 0x4 &gpio4 16 0x4 + &gpio6 22 0x4 &gpio7 0 0x4>; }; diff --git a/arch/arm/boot/dts/imx27.dtsi b/arch/arm/boot/dts/imx27.dtsi index 2b1a166d41f9..386c769c38d1 100644 --- a/arch/arm/boot/dts/imx27.dtsi +++ b/arch/arm/boot/dts/imx27.dtsi @@ -213,5 +213,14 @@ status = "disabled"; }; }; + nand@d8000000 { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "fsl,imx27-nand"; + reg = <0xd8000000 0x1000>; + interrupts = <29>; + status = "disabled"; + }; }; }; diff --git a/arch/arm/boot/dts/snowball.dts b/arch/arm/boot/dts/snowball.dts index d99dc04f0d91..ec3c33975110 100644 --- a/arch/arm/boot/dts/snowball.dts +++ b/arch/arm/boot/dts/snowball.dts @@ -20,6 +20,16 @@ reg = <0x00000000 0x20000000>; }; + en_3v3_reg: en_3v3 { + compatible = "regulator-fixed"; + regulator-name = "en-3v3-fixed-supply"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpios = <&gpio0 26 0x4>; // 26 + startup-delay-us = <5000>; + enable-active-high; + }; + gpio_keys { compatible = "gpio-keys"; #address-cells = <1>; @@ -30,35 +40,35 @@ wakeup = <1>; linux,code = <2>; label = "userpb"; - gpios = <&gpio1 0 0>; + gpios = <&gpio1 0 0x4>; }; button@2 { debounce_interval = <50>; wakeup = <1>; linux,code = <3>; label = "extkb1"; - gpios = <&gpio4 23 0>; + gpios = <&gpio4 23 0x4>; }; button@3 { debounce_interval = <50>; wakeup = <1>; linux,code = <4>; label = "extkb2"; - gpios = <&gpio4 24 0>; + gpios = <&gpio4 24 0x4>; }; button@4 { debounce_interval = <50>; wakeup = <1>; linux,code = <5>; label = "extkb3"; - gpios = <&gpio5 1 0>; + gpios = <&gpio5 1 0x4>; }; button@5 { debounce_interval = <50>; wakeup = <1>; linux,code = <6>; label = "extkb4"; - gpios = <&gpio5 2 0>; + gpios = <&gpio5 2 0x4>; }; }; @@ -66,12 +76,11 @@ compatible = "gpio-leds"; used-led { label = "user_led"; - gpios = <&gpio4 14>; + gpios = <&gpio4 14 0x4>; }; }; soc-u9500 { - external-bus@50000000 { status = "okay"; @@ -80,6 +89,9 @@ reg = <0 0x10000>; interrupts = <12 0x1>; interrupt-parent = <&gpio4>; + vdd33a-supply = <&en_3v3_reg>; + vddvario-supply = <&db8500_vape_reg>; + reg-shift = <1>; reg-io-width = <2>; @@ -91,11 +103,13 @@ sdi@80126000 { status = "enabled"; - cd-gpios = <&gpio6 26>; + vmmc-supply = <&ab8500_ldo_aux3_reg>; + cd-gpios = <&gpio6 26 0x4>; // 218 }; sdi@80114000 { status = "enabled"; + vmmc-supply = <&ab8500_ldo_aux2_reg>; }; uart@80120000 { @@ -114,7 +128,7 @@ tc3589x@42 { //compatible = "tc3589x"; reg = <0x42>; - interrupts = <25>; + gpios = <&gpio6 25 0x4>; interrupt-parent = <&gpio6>; }; tps61052@33 { diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig index 7e84f453e8a6..2d4f661d1cf6 100644 --- a/arch/arm/configs/u8500_defconfig +++ b/arch/arm/configs/u8500_defconfig @@ -75,6 +75,7 @@ CONFIG_AB5500_CORE=y CONFIG_AB8500_CORE=y CONFIG_REGULATOR=y CONFIG_REGULATOR_AB8500=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y # CONFIG_HID_SUPPORT is not set CONFIG_USB_GADGET=y CONFIG_AB8500_USB=y diff --git a/arch/arm/mach-ep93xx/snappercl15.c b/arch/arm/mach-ep93xx/snappercl15.c index eb282378fa78..01abd3516a77 100644 --- a/arch/arm/mach-ep93xx/snappercl15.c +++ b/arch/arm/mach-ep93xx/snappercl15.c @@ -82,8 +82,6 @@ static int snappercl15_nand_dev_ready(struct mtd_info *mtd) return !!(__raw_readw(NAND_CTRL_ADDR(chip)) & SNAPPERCL15_NAND_RDY); } -static const char *snappercl15_nand_part_probes[] = {"cmdlinepart", NULL}; - static struct mtd_partition snappercl15_nand_parts[] = { { .name = "Kernel", @@ -100,10 +98,8 @@ static struct mtd_partition snappercl15_nand_parts[] = { static struct platform_nand_data snappercl15_nand_data = { .chip = { .nr_chips = 1, - .part_probe_types = snappercl15_nand_part_probes, .partitions = snappercl15_nand_parts, .nr_partitions = ARRAY_SIZE(snappercl15_nand_parts), - .options = NAND_NO_AUTOINCR, .chip_delay = 25, }, .ctrl = { diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c index d4ef339d961e..75cab2d7ec73 100644 --- a/arch/arm/mach-ep93xx/ts72xx.c +++ b/arch/arm/mach-ep93xx/ts72xx.c @@ -105,8 +105,6 @@ static int ts72xx_nand_device_ready(struct mtd_info *mtd) return !!(__raw_readb(addr) & 0x20); } -static const char *ts72xx_nand_part_probes[] = { "cmdlinepart", NULL }; - #define TS72XX_BOOTROM_PART_SIZE (SZ_16K) #define TS72XX_REDBOOT_PART_SIZE (SZ_2M + SZ_1M) @@ -134,7 +132,6 @@ static struct platform_nand_data ts72xx_nand_data = { .nr_chips = 1, .chip_offset = 0, .chip_delay = 15, - .part_probe_types = ts72xx_nand_part_probes, .partitions = ts72xx_nand_parts, .nr_partitions = ARRAY_SIZE(ts72xx_nand_parts), }, diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c index 972983e392bc..656f8fc9addd 100644 --- a/arch/arm/mach-exynos/mach-nuri.c +++ b/arch/arm/mach-exynos/mach-nuri.c @@ -237,25 +237,29 @@ static struct exynos_drm_fimd_pdata drm_fimd_pdata = { #else /* Frame Buffer */ static struct s3c_fb_pd_win nuri_fb_win0 = { - .win_mode = { - .left_margin = 64, - .right_margin = 16, - .upper_margin = 64, - .lower_margin = 1, - .hsync_len = 48, - .vsync_len = 3, - .xres = 1024, - .yres = 600, - .refresh = 60, - }, .max_bpp = 24, .default_bpp = 16, + .xres = 1024, + .yres = 600, .virtual_x = 1024, .virtual_y = 2 * 600, }; +static struct fb_videomode nuri_lcd_timing = { + .left_margin = 64, + .right_margin = 16, + .upper_margin = 64, + .lower_margin = 1, + .hsync_len = 48, + .vsync_len = 3, + .xres = 1024, + .yres = 600, + .refresh = 60, +}; + static struct s3c_fb_platdata nuri_fb_pdata __initdata = { .win[0] = &nuri_fb_win0, + .vtiming = &nuri_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB | VIDCON0_CLKSEL_LCD, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c index a7f7fd567dde..f5572be9d7bf 100644 --- a/arch/arm/mach-exynos/mach-origen.c +++ b/arch/arm/mach-exynos/mach-origen.c @@ -604,24 +604,28 @@ static struct exynos_drm_fimd_pdata drm_fimd_pdata = { }; #else static struct s3c_fb_pd_win origen_fb_win0 = { - .win_mode = { - .left_margin = 64, - .right_margin = 16, - .upper_margin = 64, - .lower_margin = 16, - .hsync_len = 48, - .vsync_len = 3, - .xres = 1024, - .yres = 600, - }, + .xres = 1024, + .yres = 600, .max_bpp = 32, .default_bpp = 24, .virtual_x = 1024, .virtual_y = 2 * 600, }; +static struct fb_videomode origen_lcd_timing = { + .left_margin = 64, + .right_margin = 16, + .upper_margin = 64, + .lower_margin = 16, + .hsync_len = 48, + .vsync_len = 3, + .xres = 1024, + .yres = 600, +}; + static struct s3c_fb_platdata origen_lcd_pdata __initdata = { .win[0] = &origen_fb_win0, + .vtiming = &origen_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC | VIDCON1_INV_VCLK, diff --git a/arch/arm/mach-exynos/mach-smdkv310.c b/arch/arm/mach-exynos/mach-smdkv310.c index 70df1a0c2118..262e9e446a96 100644 --- a/arch/arm/mach-exynos/mach-smdkv310.c +++ b/arch/arm/mach-exynos/mach-smdkv310.c @@ -178,22 +178,26 @@ static struct exynos_drm_fimd_pdata drm_fimd_pdata = { }; #else static struct s3c_fb_pd_win smdkv310_fb_win0 = { - .win_mode = { - .left_margin = 13, - .right_margin = 8, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, - .max_bpp = 32, - .default_bpp = 24, + .max_bpp = 32, + .default_bpp = 24, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode smdkv310_lcd_timing = { + .left_margin = 13, + .right_margin = 8, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, }; static struct s3c_fb_platdata smdkv310_lcd0_pdata __initdata = { .win[0] = &smdkv310_fb_win0, + .vtiming = &smdkv310_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, .setup_gpio = exynos4_fimd0_gpio_setup_24bpp, diff --git a/arch/arm/mach-exynos/mach-universal_c210.c b/arch/arm/mach-exynos/mach-universal_c210.c index 083b44de9c10..cd92fa86ba41 100644 --- a/arch/arm/mach-exynos/mach-universal_c210.c +++ b/arch/arm/mach-exynos/mach-universal_c210.c @@ -843,25 +843,29 @@ static struct exynos_drm_fimd_pdata drm_fimd_pdata = { #else /* Frame Buffer */ static struct s3c_fb_pd_win universal_fb_win0 = { - .win_mode = { - .left_margin = 16, - .right_margin = 16, - .upper_margin = 2, - .lower_margin = 28, - .hsync_len = 2, - .vsync_len = 1, - .xres = 480, - .yres = 800, - .refresh = 55, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 480, + .yres = 800, .virtual_x = 480, .virtual_y = 2 * 800, }; +static struct fb_videomode universal_lcd_timing = { + .left_margin = 16, + .right_margin = 16, + .upper_margin = 2, + .lower_margin = 28, + .hsync_len = 2, + .vsync_len = 1, + .xres = 480, + .yres = 800, + .refresh = 55, +}; + static struct s3c_fb_platdata universal_lcd_pdata __initdata = { .win[0] = &universal_fb_win0, + .vtiming = &universal_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB | VIDCON0_CLKSEL_LCD, .vidcon1 = VIDCON1_INV_VCLK | VIDCON1_INV_VDEN diff --git a/arch/arm/mach-imx/imx27-dt.c b/arch/arm/mach-imx/imx27-dt.c index ed38d03c61f2..eee0cc8d92a4 100644 --- a/arch/arm/mach-imx/imx27-dt.c +++ b/arch/arm/mach-imx/imx27-dt.c @@ -29,6 +29,7 @@ static const struct of_dev_auxdata imx27_auxdata_lookup[] __initconst = { OF_DEV_AUXDATA("fsl,imx27-cspi", MX27_CSPI2_BASE_ADDR, "imx27-cspi.1", NULL), OF_DEV_AUXDATA("fsl,imx27-cspi", MX27_CSPI3_BASE_ADDR, "imx27-cspi.2", NULL), OF_DEV_AUXDATA("fsl,imx27-wdt", MX27_WDOG_BASE_ADDR, "imx2-wdt.0", NULL), + OF_DEV_AUXDATA("fsl,imx27-nand", MX27_NFC_BASE_ADDR, "mxc_nand.0", NULL), { /* sentinel */ } }; diff --git a/arch/arm/mach-ixp4xx/ixdp425-setup.c b/arch/arm/mach-ixp4xx/ixdp425-setup.c index 3d742aee1773..108a9d3f382d 100644 --- a/arch/arm/mach-ixp4xx/ixdp425-setup.c +++ b/arch/arm/mach-ixp4xx/ixdp425-setup.c @@ -60,8 +60,6 @@ static struct platform_device ixdp425_flash = { #if defined(CONFIG_MTD_NAND_PLATFORM) || \ defined(CONFIG_MTD_NAND_PLATFORM_MODULE) -const char *part_probes[] = { "cmdlinepart", NULL }; - static struct mtd_partition ixdp425_partitions[] = { { .name = "ixp400 NAND FS 0", @@ -100,8 +98,6 @@ static struct platform_nand_data ixdp425_flash_nand_data = { .chip = { .nr_chips = 1, .chip_delay = 30, - .options = NAND_NO_AUTOINCR, - .part_probe_types = part_probes, .partitions = ixdp425_partitions, .nr_partitions = ARRAY_SIZE(ixdp425_partitions), }, diff --git a/arch/arm/mach-nomadik/board-nhk8815.c b/arch/arm/mach-nomadik/board-nhk8815.c index 58cacafcf662..2e8d3e176bc7 100644 --- a/arch/arm/mach-nomadik/board-nhk8815.c +++ b/arch/arm/mach-nomadik/board-nhk8815.c @@ -111,7 +111,7 @@ static struct nomadik_nand_platform_data nhk8815_nand_data = { .parts = nhk8815_partitions, .nparts = ARRAY_SIZE(nhk8815_partitions), .options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING \ - | NAND_NO_READRDY | NAND_NO_AUTOINCR, + | NAND_NO_READRDY, .init = nhk8815_nand_init, }; diff --git a/arch/arm/mach-omap1/board-fsample.c b/arch/arm/mach-omap1/board-fsample.c index c7364fdbda05..6872f3fd400f 100644 --- a/arch/arm/mach-omap1/board-fsample.c +++ b/arch/arm/mach-omap1/board-fsample.c @@ -192,14 +192,11 @@ static int nand_dev_ready(struct mtd_info *mtd) return gpio_get_value(FSAMPLE_NAND_RB_GPIO_PIN); } -static const char *part_probes[] = { "cmdlinepart", NULL }; - static struct platform_nand_data nand_data = { .chip = { .nr_chips = 1, .chip_offset = 0, .options = NAND_SAMSUNG_LP_OPTIONS, - .part_probe_types = part_probes, }, .ctrl = { .cmd_ctrl = omap1_nand_cmd_ctl, diff --git a/arch/arm/mach-omap1/board-h2.c b/arch/arm/mach-omap1/board-h2.c index 7e503686f7af..a28e989a63f4 100644 --- a/arch/arm/mach-omap1/board-h2.c +++ b/arch/arm/mach-omap1/board-h2.c @@ -186,8 +186,6 @@ static int h2_nand_dev_ready(struct mtd_info *mtd) return gpio_get_value(H2_NAND_RB_GPIO_PIN); } -static const char *h2_part_probes[] = { "cmdlinepart", NULL }; - static struct platform_nand_data h2_nand_platdata = { .chip = { .nr_chips = 1, @@ -195,7 +193,6 @@ static struct platform_nand_data h2_nand_platdata = { .nr_partitions = ARRAY_SIZE(h2_nand_partitions), .partitions = h2_nand_partitions, .options = NAND_SAMSUNG_LP_OPTIONS, - .part_probe_types = h2_part_probes, }, .ctrl = { .cmd_ctrl = omap1_nand_cmd_ctl, diff --git a/arch/arm/mach-omap1/board-h3.c b/arch/arm/mach-omap1/board-h3.c index 9fb03f189d93..108a8640fc6f 100644 --- a/arch/arm/mach-omap1/board-h3.c +++ b/arch/arm/mach-omap1/board-h3.c @@ -188,8 +188,6 @@ static int nand_dev_ready(struct mtd_info *mtd) return gpio_get_value(H3_NAND_RB_GPIO_PIN); } -static const char *part_probes[] = { "cmdlinepart", NULL }; - static struct platform_nand_data nand_platdata = { .chip = { .nr_chips = 1, @@ -197,7 +195,6 @@ static struct platform_nand_data nand_platdata = { .nr_partitions = ARRAY_SIZE(nand_partitions), .partitions = nand_partitions, .options = NAND_SAMSUNG_LP_OPTIONS, - .part_probe_types = part_probes, }, .ctrl = { .cmd_ctrl = omap1_nand_cmd_ctl, diff --git a/arch/arm/mach-omap1/board-perseus2.c b/arch/arm/mach-omap1/board-perseus2.c index f2cb24387c22..703d55ecffe2 100644 --- a/arch/arm/mach-omap1/board-perseus2.c +++ b/arch/arm/mach-omap1/board-perseus2.c @@ -150,14 +150,11 @@ static int nand_dev_ready(struct mtd_info *mtd) return gpio_get_value(P2_NAND_RB_GPIO_PIN); } -static const char *part_probes[] = { "cmdlinepart", NULL }; - static struct platform_nand_data nand_data = { .chip = { .nr_chips = 1, .chip_offset = 0, .options = NAND_SAMSUNG_LP_OPTIONS, - .part_probe_types = part_probes, }, .ctrl = { .cmd_ctrl = omap1_nand_cmd_ctl, diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c index db5a88a36c63..54d49ddb9b81 100644 --- a/arch/arm/mach-omap2/display.c +++ b/arch/arm/mach-omap2/display.c @@ -180,16 +180,133 @@ static void omap_dsi_disable_pads(int dsi_id, unsigned lane_mask) omap4_dsi_mux_pads(dsi_id, 0); } +static int omap_dss_set_min_bus_tput(struct device *dev, unsigned long tput) +{ + return omap_pm_set_min_bus_tput(dev, OCP_INITIATOR_AGENT, tput); +} + +static struct platform_device *create_dss_pdev(const char *pdev_name, + int pdev_id, const char *oh_name, void *pdata, int pdata_len, + struct platform_device *parent) +{ + struct platform_device *pdev; + struct omap_device *od; + struct omap_hwmod *ohs[1]; + struct omap_hwmod *oh; + int r; + + oh = omap_hwmod_lookup(oh_name); + if (!oh) { + pr_err("Could not look up %s\n", oh_name); + r = -ENODEV; + goto err; + } + + pdev = platform_device_alloc(pdev_name, pdev_id); + if (!pdev) { + pr_err("Could not create pdev for %s\n", pdev_name); + r = -ENOMEM; + goto err; + } + + if (parent != NULL) + pdev->dev.parent = &parent->dev; + + if (pdev->id != -1) + dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); + else + dev_set_name(&pdev->dev, "%s", pdev->name); + + ohs[0] = oh; + od = omap_device_alloc(pdev, ohs, 1, NULL, 0); + if (!od) { + pr_err("Could not alloc omap_device for %s\n", pdev_name); + r = -ENOMEM; + goto err; + } + + r = platform_device_add_data(pdev, pdata, pdata_len); + if (r) { + pr_err("Could not set pdata for %s\n", pdev_name); + goto err; + } + + r = omap_device_register(pdev); + if (r) { + pr_err("Could not register omap_device for %s\n", pdev_name); + goto err; + } + + return pdev; + +err: + return ERR_PTR(r); +} + +static struct platform_device *create_simple_dss_pdev(const char *pdev_name, + int pdev_id, void *pdata, int pdata_len, + struct platform_device *parent) +{ + struct platform_device *pdev; + int r; + + pdev = platform_device_alloc(pdev_name, pdev_id); + if (!pdev) { + pr_err("Could not create pdev for %s\n", pdev_name); + r = -ENOMEM; + goto err; + } + + if (parent != NULL) + pdev->dev.parent = &parent->dev; + + if (pdev->id != -1) + dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); + else + dev_set_name(&pdev->dev, "%s", pdev->name); + + r = platform_device_add_data(pdev, pdata, pdata_len); + if (r) { + pr_err("Could not set pdata for %s\n", pdev_name); + goto err; + } + + r = omap_device_register(pdev); + if (r) { + pr_err("Could not register omap_device for %s\n", pdev_name); + goto err; + } + + return pdev; + +err: + return ERR_PTR(r); +} + int __init omap_display_init(struct omap_dss_board_info *board_data) { int r = 0; - struct omap_hwmod *oh; struct platform_device *pdev; int i, oh_count; - struct omap_display_platform_data pdata; const struct omap_dss_hwmod_data *curr_dss_hwmod; + struct platform_device *dss_pdev; + + /* create omapdss device */ + + board_data->dsi_enable_pads = omap_dsi_enable_pads; + board_data->dsi_disable_pads = omap_dsi_disable_pads; + board_data->get_context_loss_count = omap_pm_get_dev_context_loss_count; + board_data->set_min_bus_tput = omap_dss_set_min_bus_tput; + + omap_display_device.dev.platform_data = board_data; + + r = platform_device_register(&omap_display_device); + if (r < 0) { + pr_err("Unable to register omapdss device\n"); + return r; + } - memset(&pdata, 0, sizeof(pdata)); + /* create devices for dss hwmods */ if (cpu_is_omap24xx()) { curr_dss_hwmod = omap2_dss_hwmod_data; @@ -202,39 +319,58 @@ int __init omap_display_init(struct omap_dss_board_info *board_data) oh_count = ARRAY_SIZE(omap4_dss_hwmod_data); } - if (board_data->dsi_enable_pads == NULL) - board_data->dsi_enable_pads = omap_dsi_enable_pads; - if (board_data->dsi_disable_pads == NULL) - board_data->dsi_disable_pads = omap_dsi_disable_pads; - - pdata.board_data = board_data; - pdata.board_data->get_context_loss_count = - omap_pm_get_dev_context_loss_count; - - for (i = 0; i < oh_count; i++) { - oh = omap_hwmod_lookup(curr_dss_hwmod[i].oh_name); - if (!oh) { - pr_err("Could not look up %s\n", - curr_dss_hwmod[i].oh_name); - return -ENODEV; + /* + * First create the pdev for dss_core, which is used as a parent device + * by the other dss pdevs. Note: dss_core has to be the first item in + * the hwmod list. + */ + dss_pdev = create_dss_pdev(curr_dss_hwmod[0].dev_name, + curr_dss_hwmod[0].id, + curr_dss_hwmod[0].oh_name, + board_data, sizeof(*board_data), + NULL); + + if (IS_ERR(dss_pdev)) { + pr_err("Could not build omap_device for %s\n", + curr_dss_hwmod[0].oh_name); + + return PTR_ERR(dss_pdev); + } + + for (i = 1; i < oh_count; i++) { + pdev = create_dss_pdev(curr_dss_hwmod[i].dev_name, + curr_dss_hwmod[i].id, + curr_dss_hwmod[i].oh_name, + board_data, sizeof(*board_data), + dss_pdev); + + if (IS_ERR(pdev)) { + pr_err("Could not build omap_device for %s\n", + curr_dss_hwmod[i].oh_name); + + return PTR_ERR(pdev); } + } - pdev = omap_device_build(curr_dss_hwmod[i].dev_name, - curr_dss_hwmod[i].id, oh, &pdata, - sizeof(struct omap_display_platform_data), - NULL, 0, 0); + /* Create devices for DPI and SDI */ - if (WARN((IS_ERR(pdev)), "Could not build omap_device for %s\n", - curr_dss_hwmod[i].oh_name)) - return -ENODEV; + pdev = create_simple_dss_pdev("omapdss_dpi", -1, + board_data, sizeof(*board_data), dss_pdev); + if (IS_ERR(pdev)) { + pr_err("Could not build platform_device for omapdss_dpi\n"); + return PTR_ERR(pdev); } - omap_display_device.dev.platform_data = board_data; - r = platform_device_register(&omap_display_device); - if (r < 0) - printk(KERN_ERR "Unable to register OMAP-Display device\n"); + if (cpu_is_omap34xx()) { + pdev = create_simple_dss_pdev("omapdss_sdi", -1, + board_data, sizeof(*board_data), dss_pdev); + if (IS_ERR(pdev)) { + pr_err("Could not build platform_device for omapdss_sdi\n"); + return PTR_ERR(pdev); + } + } - return r; + return 0; } static void dispc_disable_outputs(void) diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 46b09dae770e..2286410671e7 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -49,6 +49,7 @@ #define GPMC_ECC_CONTROL 0x1f8 #define GPMC_ECC_SIZE_CONFIG 0x1fc #define GPMC_ECC1_RESULT 0x200 +#define GPMC_ECC_BCH_RESULT_0 0x240 /* not available on OMAP2 */ /* GPMC ECC control settings */ #define GPMC_ECC_CTRL_ECCCLEAR 0x100 @@ -935,3 +936,186 @@ int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code) return 0; } EXPORT_SYMBOL_GPL(gpmc_calculate_ecc); + +#ifdef CONFIG_ARCH_OMAP3 + +/** + * gpmc_init_hwecc_bch - initialize hardware BCH ecc functionality + * @cs: chip select number + * @nsectors: how many 512-byte sectors to process + * @nerrors: how many errors to correct per sector (4 or 8) + * + * This function must be executed before any call to gpmc_enable_hwecc_bch. + */ +int gpmc_init_hwecc_bch(int cs, int nsectors, int nerrors) +{ + /* check if ecc module is in use */ + if (gpmc_ecc_used != -EINVAL) + return -EINVAL; + + /* support only OMAP3 class */ + if (!cpu_is_omap34xx()) { + printk(KERN_ERR "BCH ecc is not supported on this CPU\n"); + return -EINVAL; + } + + /* + * For now, assume 4-bit mode is only supported on OMAP3630 ES1.x, x>=1. + * Other chips may be added if confirmed to work. + */ + if ((nerrors == 4) && + (!cpu_is_omap3630() || (GET_OMAP_REVISION() == 0))) { + printk(KERN_ERR "BCH 4-bit mode is not supported on this CPU\n"); + return -EINVAL; + } + + /* sanity check */ + if (nsectors > 8) { + printk(KERN_ERR "BCH cannot process %d sectors (max is 8)\n", + nsectors); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(gpmc_init_hwecc_bch); + +/** + * gpmc_enable_hwecc_bch - enable hardware BCH ecc functionality + * @cs: chip select number + * @mode: read/write mode + * @dev_width: device bus width(1 for x16, 0 for x8) + * @nsectors: how many 512-byte sectors to process + * @nerrors: how many errors to correct per sector (4 or 8) + */ +int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors, + int nerrors) +{ + unsigned int val; + + /* check if ecc module is in use */ + if (gpmc_ecc_used != -EINVAL) + return -EINVAL; + + gpmc_ecc_used = cs; + + /* clear ecc and enable bits */ + gpmc_write_reg(GPMC_ECC_CONTROL, 0x1); + + /* + * When using BCH, sector size is hardcoded to 512 bytes. + * Here we are using wrapping mode 6 both for reading and writing, with: + * size0 = 0 (no additional protected byte in spare area) + * size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) + */ + gpmc_write_reg(GPMC_ECC_SIZE_CONFIG, (32 << 22) | (0 << 12)); + + /* BCH configuration */ + val = ((1 << 16) | /* enable BCH */ + (((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */ + (0x06 << 8) | /* wrap mode = 6 */ + (dev_width << 7) | /* bus width */ + (((nsectors-1) & 0x7) << 4) | /* number of sectors */ + (cs << 1) | /* ECC CS */ + (0x1)); /* enable ECC */ + + gpmc_write_reg(GPMC_ECC_CONFIG, val); + gpmc_write_reg(GPMC_ECC_CONTROL, 0x101); + return 0; +} +EXPORT_SYMBOL_GPL(gpmc_enable_hwecc_bch); + +/** + * gpmc_calculate_ecc_bch4 - Generate 7 ecc bytes per sector of 512 data bytes + * @cs: chip select number + * @dat: The pointer to data on which ecc is computed + * @ecc: The ecc output buffer + */ +int gpmc_calculate_ecc_bch4(int cs, const u_char *dat, u_char *ecc) +{ + int i; + unsigned long nsectors, reg, val1, val2; + + if (gpmc_ecc_used != cs) + return -EINVAL; + + nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1; + + for (i = 0; i < nsectors; i++) { + + reg = GPMC_ECC_BCH_RESULT_0 + 16*i; + + /* Read hw-computed remainder */ + val1 = gpmc_read_reg(reg + 0); + val2 = gpmc_read_reg(reg + 4); + + /* + * Add constant polynomial to remainder, in order to get an ecc + * sequence of 0xFFs for a buffer filled with 0xFFs; and + * left-justify the resulting polynomial. + */ + *ecc++ = 0x28 ^ ((val2 >> 12) & 0xFF); + *ecc++ = 0x13 ^ ((val2 >> 4) & 0xFF); + *ecc++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF)); + *ecc++ = 0x39 ^ ((val1 >> 20) & 0xFF); + *ecc++ = 0x96 ^ ((val1 >> 12) & 0xFF); + *ecc++ = 0xac ^ ((val1 >> 4) & 0xFF); + *ecc++ = 0x7f ^ ((val1 & 0xF) << 4); + } + + gpmc_ecc_used = -EINVAL; + return 0; +} +EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch4); + +/** + * gpmc_calculate_ecc_bch8 - Generate 13 ecc bytes per block of 512 data bytes + * @cs: chip select number + * @dat: The pointer to data on which ecc is computed + * @ecc: The ecc output buffer + */ +int gpmc_calculate_ecc_bch8(int cs, const u_char *dat, u_char *ecc) +{ + int i; + unsigned long nsectors, reg, val1, val2, val3, val4; + + if (gpmc_ecc_used != cs) + return -EINVAL; + + nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1; + + for (i = 0; i < nsectors; i++) { + + reg = GPMC_ECC_BCH_RESULT_0 + 16*i; + + /* Read hw-computed remainder */ + val1 = gpmc_read_reg(reg + 0); + val2 = gpmc_read_reg(reg + 4); + val3 = gpmc_read_reg(reg + 8); + val4 = gpmc_read_reg(reg + 12); + + /* + * Add constant polynomial to remainder, in order to get an ecc + * sequence of 0xFFs for a buffer filled with 0xFFs. + */ + *ecc++ = 0xef ^ (val4 & 0xFF); + *ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF); + *ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF); + *ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF); + *ecc++ = 0xed ^ (val3 & 0xFF); + *ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF); + *ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF); + *ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF); + *ecc++ = 0x97 ^ (val2 & 0xFF); + *ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF); + *ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF); + *ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF); + *ecc++ = 0xb5 ^ (val1 & 0xFF); + } + + gpmc_ecc_used = -EINVAL; + return 0; +} +EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch8); + +#endif /* CONFIG_ARCH_OMAP3 */ diff --git a/arch/arm/mach-orion5x/ts78xx-setup.c b/arch/arm/mach-orion5x/ts78xx-setup.c index a74f3cf54cc5..b4203277f3cd 100644 --- a/arch/arm/mach-orion5x/ts78xx-setup.c +++ b/arch/arm/mach-orion5x/ts78xx-setup.c @@ -251,8 +251,6 @@ static void ts78xx_ts_nand_read_buf(struct mtd_info *mtd, readsb(io_base, buf, len); } -const char *ts_nand_part_probes[] = { "cmdlinepart", NULL }; - static struct mtd_partition ts78xx_ts_nand_parts[] = { { .name = "mbr", @@ -277,7 +275,6 @@ static struct mtd_partition ts78xx_ts_nand_parts[] = { static struct platform_nand_data ts78xx_ts_nand_data = { .chip = { .nr_chips = 1, - .part_probe_types = ts_nand_part_probes, .partitions = ts78xx_ts_nand_parts, .nr_partitions = ARRAY_SIZE(ts78xx_ts_nand_parts), .chip_delay = 15, diff --git a/arch/arm/mach-pxa/balloon3.c b/arch/arm/mach-pxa/balloon3.c index 56e8cebeb7d5..9244493dbcb7 100644 --- a/arch/arm/mach-pxa/balloon3.c +++ b/arch/arm/mach-pxa/balloon3.c @@ -679,8 +679,6 @@ static struct mtd_partition balloon3_partition_info[] = { }, }; -static const char *balloon3_part_probes[] = { "cmdlinepart", NULL }; - struct platform_nand_data balloon3_nand_pdata = { .chip = { .nr_chips = 4, @@ -688,7 +686,6 @@ struct platform_nand_data balloon3_nand_pdata = { .nr_partitions = ARRAY_SIZE(balloon3_partition_info), .partitions = balloon3_partition_info, .chip_delay = 50, - .part_probe_types = balloon3_part_probes, }, .ctrl = { .hwcontrol = 0, diff --git a/arch/arm/mach-pxa/em-x270.c b/arch/arm/mach-pxa/em-x270.c index a3a4a38d4972..97f82ad341bf 100644 --- a/arch/arm/mach-pxa/em-x270.c +++ b/arch/arm/mach-pxa/em-x270.c @@ -338,8 +338,6 @@ static struct mtd_partition em_x270_partition_info[] = { }, }; -static const char *em_x270_part_probes[] = { "cmdlinepart", NULL }; - struct platform_nand_data em_x270_nand_platdata = { .chip = { .nr_chips = 1, @@ -347,7 +345,6 @@ struct platform_nand_data em_x270_nand_platdata = { .nr_partitions = ARRAY_SIZE(em_x270_partition_info), .partitions = em_x270_partition_info, .chip_delay = 20, - .part_probe_types = em_x270_part_probes, }, .ctrl = { .hwcontrol = 0, diff --git a/arch/arm/mach-pxa/palmtx.c b/arch/arm/mach-pxa/palmtx.c index 9507605ed547..0da35dccfd89 100644 --- a/arch/arm/mach-pxa/palmtx.c +++ b/arch/arm/mach-pxa/palmtx.c @@ -268,8 +268,6 @@ static struct mtd_partition palmtx_partition_info[] = { }, }; -static const char *palmtx_part_probes[] = { "cmdlinepart", NULL }; - struct platform_nand_data palmtx_nand_platdata = { .chip = { .nr_chips = 1, @@ -277,7 +275,6 @@ struct platform_nand_data palmtx_nand_platdata = { .nr_partitions = ARRAY_SIZE(palmtx_partition_info), .partitions = palmtx_partition_info, .chip_delay = 20, - .part_probe_types = palmtx_part_probes, }, .ctrl = { .cmd_ctrl = palmtx_nand_cmd_ctl, diff --git a/arch/arm/mach-s3c24xx/mach-smdk2416.c b/arch/arm/mach-s3c24xx/mach-smdk2416.c index 30a44f806e01..c3100a044fbe 100644 --- a/arch/arm/mach-s3c24xx/mach-smdk2416.c +++ b/arch/arm/mach-s3c24xx/mach-smdk2416.c @@ -148,23 +148,25 @@ static struct s3c24xx_hsudc_platdata smdk2416_hsudc_platdata = { static struct s3c_fb_pd_win smdk2416_fb_win[] = { [0] = { - /* think this is the same as the smdk6410 */ - .win_mode = { - .pixclock = 41094, - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, .default_bpp = 16, .max_bpp = 32, + .xres = 800, + .yres = 480, }, }; +static struct fb_videomode smdk2416_lcd_timing = { + .pixclock = 41094, + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, +}; + static void s3c2416_fb_gpio_setup_24bpp(void) { unsigned int gpio; @@ -187,6 +189,7 @@ static void s3c2416_fb_gpio_setup_24bpp(void) static struct s3c_fb_platdata smdk2416_fb_platdata = { .win[0] = &smdk2416_fb_win[0], + .vtiming = &smdk2416_lcd_timing, .setup_gpio = s3c2416_fb_gpio_setup_24bpp, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, diff --git a/arch/arm/mach-s3c64xx/mach-anw6410.c b/arch/arm/mach-s3c64xx/mach-anw6410.c index 314df0518afd..ffa29ddfdfce 100644 --- a/arch/arm/mach-s3c64xx/mach-anw6410.c +++ b/arch/arm/mach-s3c64xx/mach-anw6410.c @@ -134,24 +134,27 @@ static struct platform_device anw6410_lcd_powerdev = { }; static struct s3c_fb_pd_win anw6410_fb_win0 = { - /* this is to ensure we use win0 */ - .win_mode = { - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode anw6410_lcd_timing = { + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, }; /* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */ static struct s3c_fb_platdata anw6410_lcd_pdata __initdata = { .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &anw6410_lcd_timing, .win[0] = &anw6410_fb_win0, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c index 6b20a71d7dbf..d0c352d861f8 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410.c @@ -151,26 +151,29 @@ static struct platform_device crag6410_lcd_powerdev = { /* 640x480 URT */ static struct s3c_fb_pd_win crag6410_fb_win0 = { - /* this is to ensure we use win0 */ - .win_mode = { - .left_margin = 150, - .right_margin = 80, - .upper_margin = 40, - .lower_margin = 5, - .hsync_len = 40, - .vsync_len = 5, - .xres = 640, - .yres = 480, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 640, + .yres = 480, .virtual_y = 480 * 2, .virtual_x = 640, }; +static struct fb_videomode crag6410_lcd_timing = { + .left_margin = 150, + .right_margin = 80, + .upper_margin = 40, + .lower_margin = 5, + .hsync_len = 40, + .vsync_len = 5, + .xres = 640, + .yres = 480, +}; + /* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */ static struct s3c_fb_platdata crag6410_lcd_pdata __initdata = { .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &crag6410_lcd_timing, .win[0] = &crag6410_fb_win0, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, diff --git a/arch/arm/mach-s3c64xx/mach-hmt.c b/arch/arm/mach-s3c64xx/mach-hmt.c index 1bf6b9da20fc..689088162f77 100644 --- a/arch/arm/mach-s3c64xx/mach-hmt.c +++ b/arch/arm/mach-s3c64xx/mach-hmt.c @@ -129,23 +129,27 @@ static struct platform_device hmt_backlight_device = { }; static struct s3c_fb_pd_win hmt_fb_win0 = { - .win_mode = { - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode hmt_lcd_timing = { + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, }; /* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */ static struct s3c_fb_platdata hmt_lcd_pdata __initdata = { .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &hmt_lcd_timing, .win[0] = &hmt_fb_win0, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, diff --git a/arch/arm/mach-s3c64xx/mach-mini6410.c b/arch/arm/mach-s3c64xx/mach-mini6410.c index f8ea61ea3b33..5539a255a704 100644 --- a/arch/arm/mach-s3c64xx/mach-mini6410.c +++ b/arch/arm/mach-s3c64xx/mach-mini6410.c @@ -140,41 +140,59 @@ static struct s3c2410_platform_nand mini6410_nand_info = { .sets = mini6410_nand_sets, }; -static struct s3c_fb_pd_win mini6410_fb_win[] = { +static struct s3c_fb_pd_win mini6410_lcd_type0_fb_win = { + .max_bpp = 32, + .default_bpp = 16, + .xres = 480, + .yres = 272, +}; + +static struct fb_videomode mini6410_lcd_type0_timing = { + /* 4.3" 480x272 */ + .left_margin = 3, + .right_margin = 2, + .upper_margin = 1, + .lower_margin = 1, + .hsync_len = 40, + .vsync_len = 1, + .xres = 480, + .yres = 272, +}; + +static struct s3c_fb_pd_win mini6410_lcd_type1_fb_win = { + .max_bpp = 32, + .default_bpp = 16, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode mini6410_lcd_type1_timing = { + /* 7.0" 800x480 */ + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, +}; + +static struct s3c_fb_platdata mini6410_lcd_pdata[] __initdata = { { - .win_mode = { /* 4.3" 480x272 */ - .left_margin = 3, - .right_margin = 2, - .upper_margin = 1, - .lower_margin = 1, - .hsync_len = 40, - .vsync_len = 1, - .xres = 480, - .yres = 272, - }, - .max_bpp = 32, - .default_bpp = 16, + .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &mini6410_lcd_type0_timing, + .win[0] = &mini6410_lcd_type0_fb_win, + .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, + .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, }, { - .win_mode = { /* 7.0" 800x480 */ - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, - .max_bpp = 32, - .default_bpp = 16, + .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &mini6410_lcd_type1_timing, + .win[0] = &mini6410_lcd_type1_fb_win, + .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, + .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, }, -}; - -static struct s3c_fb_platdata mini6410_lcd_pdata __initdata = { - .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, - .win[0] = &mini6410_fb_win[0], - .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, - .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, + { }, }; static void mini6410_lcd_power_set(struct plat_lcd_data *pd, @@ -272,7 +290,7 @@ static void mini6410_parse_features( "screen type already set\n", f); } else { int li = f - '0'; - if (li >= ARRAY_SIZE(mini6410_fb_win)) + if (li >= ARRAY_SIZE(mini6410_lcd_pdata)) printk(KERN_INFO "MINI6410: '%c' out " "of range LCD mode\n", f); else { @@ -296,14 +314,12 @@ static void __init mini6410_machine_init(void) /* Parse the feature string */ mini6410_parse_features(&features, mini6410_features_str); - mini6410_lcd_pdata.win[0] = &mini6410_fb_win[features.lcd_index]; - printk(KERN_INFO "MINI6410: selected LCD display is %dx%d\n", - mini6410_lcd_pdata.win[0]->win_mode.xres, - mini6410_lcd_pdata.win[0]->win_mode.yres); + mini6410_lcd_pdata[features.lcd_index].win[0]->xres, + mini6410_lcd_pdata[features.lcd_index].win[0]->yres); s3c_nand_set_platdata(&mini6410_nand_info); - s3c_fb_set_platdata(&mini6410_lcd_pdata); + s3c_fb_set_platdata(&mini6410_lcd_pdata[features.lcd_index]); s3c24xx_ts_set_platdata(NULL); /* configure nCS1 width to 16 bits */ diff --git a/arch/arm/mach-s3c64xx/mach-real6410.c b/arch/arm/mach-s3c64xx/mach-real6410.c index b92d8e17d502..326b21604bc3 100644 --- a/arch/arm/mach-s3c64xx/mach-real6410.c +++ b/arch/arm/mach-s3c64xx/mach-real6410.c @@ -106,41 +106,57 @@ static struct platform_device real6410_device_eth = { }, }; -static struct s3c_fb_pd_win real6410_fb_win[] = { +static struct s3c_fb_pd_win real6410_lcd_type0_fb_win = { + .max_bpp = 32, + .default_bpp = 16, + .xres = 480, + .yres = 272, +}; + +static struct fb_videomode real6410_lcd_type0_timing = { + /* 4.3" 480x272 */ + .left_margin = 3, + .right_margin = 2, + .upper_margin = 1, + .lower_margin = 1, + .hsync_len = 40, + .vsync_len = 1, +}; + +static struct s3c_fb_pd_win real6410_lcd_type1_fb_win = { + .max_bpp = 32, + .default_bpp = 16, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode real6410_lcd_type1_timing = { + /* 7.0" 800x480 */ + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, +}; + +static struct s3c_fb_platdata real6410_lcd_pdata[] __initdata = { { - .win_mode = { /* 4.3" 480x272 */ - .left_margin = 3, - .right_margin = 2, - .upper_margin = 1, - .lower_margin = 1, - .hsync_len = 40, - .vsync_len = 1, - .xres = 480, - .yres = 272, - }, - .max_bpp = 32, - .default_bpp = 16, + .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &real6410_lcd_type0_timing, + .win[0] = &real6410_lcd_type0_fb_win, + .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, + .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, }, { - .win_mode = { /* 7.0" 800x480 */ - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, - .max_bpp = 32, - .default_bpp = 16, + .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &real6410_lcd_type1_timing, + .win[0] = &real6410_lcd_type1_fb_win, + .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, + .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, }, -}; - -static struct s3c_fb_platdata real6410_lcd_pdata __initdata = { - .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, - .win[0] = &real6410_fb_win[0], - .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, - .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, + { }, }; static struct mtd_partition real6410_nand_part[] = { @@ -253,7 +269,7 @@ static void real6410_parse_features( "screen type already set\n", f); } else { int li = f - '0'; - if (li >= ARRAY_SIZE(real6410_fb_win)) + if (li >= ARRAY_SIZE(real6410_lcd_pdata)) printk(KERN_INFO "REAL6410: '%c' out " "of range LCD mode\n", f); else { @@ -277,13 +293,11 @@ static void __init real6410_machine_init(void) /* Parse the feature string */ real6410_parse_features(&features, real6410_features_str); - real6410_lcd_pdata.win[0] = &real6410_fb_win[features.lcd_index]; - printk(KERN_INFO "REAL6410: selected LCD display is %dx%d\n", - real6410_lcd_pdata.win[0]->win_mode.xres, - real6410_lcd_pdata.win[0]->win_mode.yres); + real6410_lcd_pdata[features.lcd_index].win[0]->xres, + real6410_lcd_pdata[features.lcd_index].win[0]->yres); - s3c_fb_set_platdata(&real6410_lcd_pdata); + s3c_fb_set_platdata(&real6410_lcd_pdata[features.lcd_index]); s3c_nand_set_platdata(&real6410_nand_info); s3c24xx_ts_set_platdata(NULL); diff --git a/arch/arm/mach-s3c64xx/mach-smartq5.c b/arch/arm/mach-s3c64xx/mach-smartq5.c index c5021d0335c6..d6266d8b43c9 100644 --- a/arch/arm/mach-s3c64xx/mach-smartq5.c +++ b/arch/arm/mach-s3c64xx/mach-smartq5.c @@ -108,23 +108,27 @@ static struct platform_device smartq5_buttons_device = { }; static struct s3c_fb_pd_win smartq5_fb_win0 = { - .win_mode = { - .left_margin = 216, - .right_margin = 40, - .upper_margin = 35, - .lower_margin = 10, - .hsync_len = 1, - .vsync_len = 1, - .xres = 800, - .yres = 480, - .refresh = 80, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode smartq5_lcd_timing = { + .left_margin = 216, + .right_margin = 40, + .upper_margin = 35, + .lower_margin = 10, + .hsync_len = 1, + .vsync_len = 1, + .xres = 800, + .yres = 480, + .refresh = 80, }; static struct s3c_fb_platdata smartq5_lcd_pdata __initdata = { .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &smartq5_lcd_timing, .win[0] = &smartq5_fb_win0, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC | diff --git a/arch/arm/mach-s3c64xx/mach-smartq7.c b/arch/arm/mach-s3c64xx/mach-smartq7.c index aa9072a4cbef..0957d2a980e1 100644 --- a/arch/arm/mach-s3c64xx/mach-smartq7.c +++ b/arch/arm/mach-s3c64xx/mach-smartq7.c @@ -124,23 +124,27 @@ static struct platform_device smartq7_buttons_device = { }; static struct s3c_fb_pd_win smartq7_fb_win0 = { - .win_mode = { - .left_margin = 3, - .right_margin = 5, - .upper_margin = 1, - .lower_margin = 20, - .hsync_len = 10, - .vsync_len = 3, - .xres = 800, - .yres = 480, - .refresh = 80, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode smartq7_lcd_timing = { + .left_margin = 3, + .right_margin = 5, + .upper_margin = 1, + .lower_margin = 20, + .hsync_len = 10, + .vsync_len = 3, + .xres = 800, + .yres = 480, + .refresh = 80, }; static struct s3c_fb_platdata smartq7_lcd_pdata __initdata = { .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &smartq7_lcd_timing, .win[0] = &smartq7_fb_win0, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC | diff --git a/arch/arm/mach-s3c64xx/mach-smdk6410.c b/arch/arm/mach-s3c64xx/mach-smdk6410.c index d44319b09412..df3103d450e2 100644 --- a/arch/arm/mach-s3c64xx/mach-smdk6410.c +++ b/arch/arm/mach-s3c64xx/mach-smdk6410.c @@ -146,26 +146,29 @@ static struct platform_device smdk6410_lcd_powerdev = { }; static struct s3c_fb_pd_win smdk6410_fb_win0 = { - /* this is to ensure we use win0 */ - .win_mode = { - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 800, + .yres = 480, .virtual_y = 480 * 2, .virtual_x = 800, }; +static struct fb_videomode smdk6410_lcd_timing = { + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, +}; + /* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */ static struct s3c_fb_platdata smdk6410_lcd_pdata __initdata = { .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &smdk6410_lcd_timing, .win[0] = &smdk6410_fb_win0, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, diff --git a/arch/arm/mach-s5p64x0/mach-smdk6440.c b/arch/arm/mach-s5p64x0/mach-smdk6440.c index a40e325d62c8..92fefad505cc 100644 --- a/arch/arm/mach-s5p64x0/mach-smdk6440.c +++ b/arch/arm/mach-s5p64x0/mach-smdk6440.c @@ -103,22 +103,26 @@ static struct s3c2410_uartcfg smdk6440_uartcfgs[] __initdata = { /* Frame Buffer */ static struct s3c_fb_pd_win smdk6440_fb_win0 = { - .win_mode = { - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, .max_bpp = 32, .default_bpp = 24, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode smdk6440_lcd_timing = { + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, }; static struct s3c_fb_platdata smdk6440_lcd_pdata __initdata = { .win[0] = &smdk6440_fb_win0, + .vtiming = &smdk6440_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, .setup_gpio = s5p64x0_fb_gpio_setup_24bpp, diff --git a/arch/arm/mach-s5p64x0/mach-smdk6450.c b/arch/arm/mach-s5p64x0/mach-smdk6450.c index efb69e2f2afe..e2335ecf6eae 100644 --- a/arch/arm/mach-s5p64x0/mach-smdk6450.c +++ b/arch/arm/mach-s5p64x0/mach-smdk6450.c @@ -121,22 +121,26 @@ static struct s3c2410_uartcfg smdk6450_uartcfgs[] __initdata = { /* Frame Buffer */ static struct s3c_fb_pd_win smdk6450_fb_win0 = { - .win_mode = { - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, .max_bpp = 32, .default_bpp = 24, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode smdk6450_lcd_timing = { + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, }; static struct s3c_fb_platdata smdk6450_lcd_pdata __initdata = { .win[0] = &smdk6450_fb_win0, + .vtiming = &smdk6450_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, .setup_gpio = s5p64x0_fb_gpio_setup_24bpp, diff --git a/arch/arm/mach-s5pc100/mach-smdkc100.c b/arch/arm/mach-s5pc100/mach-smdkc100.c index 674d22992f3c..0c3ae38d27ca 100644 --- a/arch/arm/mach-s5pc100/mach-smdkc100.c +++ b/arch/arm/mach-s5pc100/mach-smdkc100.c @@ -136,24 +136,27 @@ static struct platform_device smdkc100_lcd_powerdev = { /* Frame Buffer */ static struct s3c_fb_pd_win smdkc100_fb_win0 = { - /* this is to ensure we use win0 */ - .win_mode = { - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - .refresh = 80, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode smdkc100_lcd_timing = { + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, + .refresh = 80, }; static struct s3c_fb_platdata smdkc100_lcd_pdata __initdata = { .win[0] = &smdkc100_fb_win0, + .vtiming = &smdkc100_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, .setup_gpio = s5pc100_fb_gpio_setup_24bpp, diff --git a/arch/arm/mach-s5pv210/mach-aquila.c b/arch/arm/mach-s5pv210/mach-aquila.c index 48d018f2332b..af528f9e97f9 100644 --- a/arch/arm/mach-s5pv210/mach-aquila.c +++ b/arch/arm/mach-s5pv210/mach-aquila.c @@ -96,38 +96,34 @@ static struct s3c2410_uartcfg aquila_uartcfgs[] __initdata = { /* Frame Buffer */ static struct s3c_fb_pd_win aquila_fb_win0 = { - .win_mode = { - .left_margin = 16, - .right_margin = 16, - .upper_margin = 3, - .lower_margin = 28, - .hsync_len = 2, - .vsync_len = 2, - .xres = 480, - .yres = 800, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 480, + .yres = 800, }; static struct s3c_fb_pd_win aquila_fb_win1 = { - .win_mode = { - .left_margin = 16, - .right_margin = 16, - .upper_margin = 3, - .lower_margin = 28, - .hsync_len = 2, - .vsync_len = 2, - .xres = 480, - .yres = 800, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 480, + .yres = 800, +}; + +static struct fb_videomode aquila_lcd_timing = { + .left_margin = 16, + .right_margin = 16, + .upper_margin = 3, + .lower_margin = 28, + .hsync_len = 2, + .vsync_len = 2, + .xres = 480, + .yres = 800, }; static struct s3c_fb_platdata aquila_lcd_pdata __initdata = { .win[0] = &aquila_fb_win0, .win[1] = &aquila_fb_win1, + .vtiming = &aquila_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC | VIDCON1_INV_VCLK | VIDCON1_INV_VDEN, diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index f20a97c8e411..bf5087c2b7fe 100644 --- a/arch/arm/mach-s5pv210/mach-goni.c +++ b/arch/arm/mach-s5pv210/mach-goni.c @@ -107,25 +107,29 @@ static struct s3c2410_uartcfg goni_uartcfgs[] __initdata = { /* Frame Buffer */ static struct s3c_fb_pd_win goni_fb_win0 = { - .win_mode = { - .left_margin = 16, - .right_margin = 16, - .upper_margin = 2, - .lower_margin = 28, - .hsync_len = 2, - .vsync_len = 1, - .xres = 480, - .yres = 800, - .refresh = 55, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 480, + .yres = 800, .virtual_x = 480, .virtual_y = 2 * 800, }; +static struct fb_videomode goni_lcd_timing = { + .left_margin = 16, + .right_margin = 16, + .upper_margin = 2, + .lower_margin = 28, + .hsync_len = 2, + .vsync_len = 1, + .xres = 480, + .yres = 800, + .refresh = 55, +}; + static struct s3c_fb_platdata goni_lcd_pdata __initdata = { .win[0] = &goni_fb_win0, + .vtiming = &goni_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB | VIDCON0_CLKSEL_LCD, .vidcon1 = VIDCON1_INV_VCLK | VIDCON1_INV_VDEN diff --git a/arch/arm/mach-s5pv210/mach-smdkv210.c b/arch/arm/mach-s5pv210/mach-smdkv210.c index fa1b61209fd9..0d7ddec88eb7 100644 --- a/arch/arm/mach-s5pv210/mach-smdkv210.c +++ b/arch/arm/mach-s5pv210/mach-smdkv210.c @@ -178,22 +178,26 @@ static struct platform_device smdkv210_lcd_lte480wv = { }; static struct s3c_fb_pd_win smdkv210_fb_win0 = { - .win_mode = { - .left_margin = 13, - .right_margin = 8, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, .max_bpp = 32, .default_bpp = 24, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode smdkv210_lcd_timing = { + .left_margin = 13, + .right_margin = 8, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, }; static struct s3c_fb_platdata smdkv210_lcd0_pdata __initdata = { .win[0] = &smdkv210_fb_win0, + .vtiming = &smdkv210_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, .setup_gpio = s5pv210_fb_gpio_setup_24bpp, diff --git a/arch/arm/mach-ux500/board-mop500-uib.c b/arch/arm/mach-ux500/board-mop500-uib.c index b29a788f498c..1f47d962e3a1 100644 --- a/arch/arm/mach-ux500/board-mop500-uib.c +++ b/arch/arm/mach-ux500/board-mop500-uib.c @@ -96,7 +96,7 @@ static void __init __mop500_uib_init(struct uib *uib, const char *why) /* * Detect the UIB attached based on the presence or absence of i2c devices. */ -static int __init mop500_uib_init(void) +int __init mop500_uib_init(void) { struct uib *uib = mop500_uib; struct i2c_adapter *i2c0; @@ -131,5 +131,3 @@ static int __init mop500_uib_init(void) return 0; } - -module_init(mop500_uib_init); diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index fba8adea421e..9c74ac545849 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -673,9 +673,15 @@ static void __init u8500_cryp1_hash1_init(struct device *parent) static struct platform_device *snowball_platform_devs[] __initdata = { &snowball_led_dev, &snowball_key_dev, + &snowball_sbnet_dev, &ab8500_device, }; +static struct platform_device *snowball_of_platform_devs[] __initdata = { + &snowball_led_dev, + &snowball_key_dev, +}; + static void __init mop500_init_machine(void) { struct device *parent = NULL; @@ -710,6 +716,8 @@ static void __init mop500_init_machine(void) /* This board has full regulator constraints */ regulator_has_full_constraints(); + + mop500_uib_init(); } static void __init snowball_init_machine(void) @@ -774,6 +782,8 @@ static void __init hrefv60_init_machine(void) /* This board has full regulator constraints */ regulator_has_full_constraints(); + + mop500_uib_init(); } MACHINE_START(U8500, "ST-Ericsson MOP500 platform") @@ -834,6 +844,10 @@ struct of_dev_auxdata u8500_auxdata_lookup[] __initdata = { static const struct of_device_id u8500_local_bus_nodes[] = { /* only create devices below soc node */ { .compatible = "stericsson,db8500", }, + { .compatible = "stericsson,db8500-prcmu", }, + { .compatible = "stericsson,db8500-prcmu-regulator", }, + { .compatible = "stericsson,ab8500", }, + { .compatible = "stericsson,ab8500-regulator", }, { .compatible = "simple-bus"}, { }, }; @@ -852,7 +866,7 @@ static void __init u8500_init_machine(void) else if (of_machine_is_compatible("st-ericsson,hrefv60+")) hrefv60_pinmaps_init(); - parent = u8500_init_devices(); + parent = u8500_of_init_devices(); for (i = 0; i < ARRAY_SIZE(mop500_platform_devs); i++) mop500_platform_devs[i]->dev.parent = parent; @@ -869,15 +883,23 @@ static void __init u8500_init_machine(void) ARRAY_SIZE(mop500_platform_devs)); mop500_sdi_init(parent); - i2c0_devs = ARRAY_SIZE(mop500_i2c0_devices); i2c_register_board_info(0, mop500_i2c0_devices, i2c0_devs); i2c_register_board_info(2, mop500_i2c2_devices, ARRAY_SIZE(mop500_i2c2_devices)); + mop500_uib_init(); + } else if (of_machine_is_compatible("calaosystems,snowball-a9500")) { - platform_add_devices(snowball_platform_devs, - ARRAY_SIZE(snowball_platform_devs)); + /* + * Devices to be DT:ed: + * snowball_led_dev = todo + * snowball_key_dev = todo + * snowball_sbnet_dev = done + * ab8500_device = done + */ + platform_add_devices(snowball_of_platform_devs, + ARRAY_SIZE(snowball_of_platform_devs)); snowball_sdi_init(parent); } else if (of_machine_is_compatible("st-ericsson,hrefv60+")) { @@ -898,6 +920,8 @@ static void __init u8500_init_machine(void) i2c_register_board_info(0, mop500_i2c0_devices, i2c0_devs); i2c_register_board_info(2, mop500_i2c2_devices, ARRAY_SIZE(mop500_i2c2_devices)); + + mop500_uib_init(); } mop500_i2c_init(parent); diff --git a/arch/arm/mach-ux500/board-mop500.h b/arch/arm/mach-ux500/board-mop500.h index bc44c07c71a9..2f87b25a908a 100644 --- a/arch/arm/mach-ux500/board-mop500.h +++ b/arch/arm/mach-ux500/board-mop500.h @@ -89,7 +89,11 @@ void __init mop500_pinmaps_init(void); void __init snowball_pinmaps_init(void); void __init hrefv60_pinmaps_init(void); +int __init mop500_uib_init(void); void mop500_uib_i2c_add(int busnum, struct i2c_board_info *info, unsigned n); +/* TODO: Once all pieces are DT:ed, remove completely. */ +struct device * __init u8500_of_init_devices(void); + #endif diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c index 16169c4bf6ca..33275eb4c689 100644 --- a/arch/arm/mach-ux500/cpu-db8500.c +++ b/arch/arm/mach-ux500/cpu-db8500.c @@ -140,7 +140,6 @@ static struct platform_device *platform_devs[] __initdata = { static struct platform_device *of_platform_devs[] __initdata = { &u8500_dma40_device, &db8500_pmu_device, - &db8500_prcmu_device, }; static resource_size_t __initdata db8500_gpio_base[] = { @@ -222,6 +221,28 @@ struct device * __init u8500_init_devices(void) platform_device_register_data(parent, "cpufreq-u8500", -1, NULL, 0); + for (i = 0; i < ARRAY_SIZE(platform_devs); i++) + platform_devs[i]->dev.parent = parent; + + platform_add_devices(platform_devs, ARRAY_SIZE(platform_devs)); + + return parent; +} + +/* TODO: Once all pieces are DT:ed, remove completely. */ +struct device * __init u8500_of_init_devices(void) +{ + struct device *parent; + int i; + + parent = db8500_soc_device_init(); + + db8500_add_rtc(parent); + db8500_add_usb(parent, usb_db8500_rx_dma_cfg, usb_db8500_tx_dma_cfg); + + platform_device_register_data(parent, + "cpufreq-u8500", -1, NULL, 0); + for (i = 0; i < ARRAY_SIZE(of_platform_devs); i++) of_platform_devs[i]->dev.parent = parent; @@ -229,7 +250,7 @@ struct device * __init u8500_init_devices(void) * Devices to be DT:ed: * u8500_dma40_device = todo * db8500_pmu_device = todo - * db8500_prcmu_device = todo + * db8500_prcmu_device = done */ platform_add_devices(of_platform_devs, ARRAY_SIZE(of_platform_devs)); diff --git a/arch/arm/plat-omap/include/plat/gpmc.h b/arch/arm/plat-omap/include/plat/gpmc.h index 1527929b445a..f37764a36072 100644 --- a/arch/arm/plat-omap/include/plat/gpmc.h +++ b/arch/arm/plat-omap/include/plat/gpmc.h @@ -92,6 +92,8 @@ enum omap_ecc { OMAP_ECC_HAMMING_CODE_HW, /* gpmc to detect the error */ /* 1-bit ecc: stored at beginning of spare area as romcode */ OMAP_ECC_HAMMING_CODE_HW_ROMCODE, /* gpmc method & romcode layout */ + OMAP_ECC_BCH4_CODE_HW, /* 4-bit BCH ecc code */ + OMAP_ECC_BCH8_CODE_HW, /* 8-bit BCH ecc code */ }; /* @@ -157,4 +159,13 @@ extern int gpmc_nand_write(int cs, int cmd, int wval); int gpmc_enable_hwecc(int cs, int mode, int dev_width, int ecc_size); int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code); + +#ifdef CONFIG_ARCH_OMAP3 +int gpmc_init_hwecc_bch(int cs, int nsectors, int nerrors); +int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors, + int nerrors); +int gpmc_calculate_ecc_bch4(int cs, const u_char *dat, u_char *ecc); +int gpmc_calculate_ecc_bch8(int cs, const u_char *dat, u_char *ecc); +#endif /* CONFIG_ARCH_OMAP3 */ + #endif diff --git a/arch/arm/plat-samsung/include/plat/fb.h b/arch/arm/plat-samsung/include/plat/fb.h index 0fedf47fa502..536002ff2ab8 100644 --- a/arch/arm/plat-samsung/include/plat/fb.h +++ b/arch/arm/plat-samsung/include/plat/fb.h @@ -24,15 +24,16 @@ /** * struct s3c_fb_pd_win - per window setup data - * @win_mode: The display parameters to initialise (not for window 0) + * @xres : The window X size. + * @yres : The window Y size. * @virtual_x: The virtual X size. * @virtual_y: The virtual Y size. */ struct s3c_fb_pd_win { - struct fb_videomode win_mode; - unsigned short default_bpp; unsigned short max_bpp; + unsigned short xres; + unsigned short yres; unsigned short virtual_x; unsigned short virtual_y; }; @@ -45,6 +46,7 @@ struct s3c_fb_pd_win { * @default_win: default window layer number to be used for UI layer. * @vidcon0: The base vidcon0 values to control the panel data format. * @vidcon1: The base vidcon1 values to control the panel data output. + * @vtiming: Video timing when connected to a RGB type panel. * @win: The setup data for each hardware window, or NULL for unused. * @display_mode: The LCD output display mode. * @@ -58,8 +60,7 @@ struct s3c_fb_platdata { void (*setup_gpio)(void); struct s3c_fb_pd_win *win[S3C_FB_MAX_WIN]; - - u32 default_win; + struct fb_videomode *vtiming; u32 vidcon0; u32 vidcon1; diff --git a/arch/blackfin/mach-bf561/boards/acvilon.c b/arch/blackfin/mach-bf561/boards/acvilon.c index f6ffd6f054c3..0b74218fdd3a 100644 --- a/arch/blackfin/mach-bf561/boards/acvilon.c +++ b/arch/blackfin/mach-bf561/boards/acvilon.c @@ -248,8 +248,6 @@ static struct platform_device bfin_uart0_device = { #if defined(CONFIG_MTD_NAND_PLATFORM) || defined(CONFIG_MTD_NAND_PLATFORM_MODULE) -const char *part_probes[] = { "cmdlinepart", NULL }; - static struct mtd_partition bfin_plat_nand_partitions[] = { { .name = "params(nand)", @@ -289,7 +287,6 @@ static struct platform_nand_data bfin_plat_nand_data = { .chip = { .nr_chips = 1, .chip_delay = 30, - .part_probe_types = part_probes, .partitions = bfin_plat_nand_partitions, .nr_partitions = ARRAY_SIZE(bfin_plat_nand_partitions), }, diff --git a/arch/mips/alchemy/devboards/db1200.c b/arch/mips/alchemy/devboards/db1200.c index 7dde01642d6b..bf2248474fa8 100644 --- a/arch/mips/alchemy/devboards/db1200.c +++ b/arch/mips/alchemy/devboards/db1200.c @@ -213,8 +213,6 @@ static int au1200_nand_device_ready(struct mtd_info *mtd) return __raw_readl((void __iomem *)MEM_STSTAT) & 1; } -static const char *db1200_part_probes[] = { "cmdlinepart", NULL }; - static struct mtd_partition db1200_nand_parts[] = { { .name = "NAND FS 0", @@ -235,7 +233,6 @@ struct platform_nand_data db1200_nand_platdata = { .nr_partitions = ARRAY_SIZE(db1200_nand_parts), .partitions = db1200_nand_parts, .chip_delay = 20, - .part_probe_types = db1200_part_probes, }, .ctrl = { .dev_ready = au1200_nand_device_ready, diff --git a/arch/mips/alchemy/devboards/db1300.c b/arch/mips/alchemy/devboards/db1300.c index 0893f2af0d01..c56e0246694e 100644 --- a/arch/mips/alchemy/devboards/db1300.c +++ b/arch/mips/alchemy/devboards/db1300.c @@ -145,8 +145,6 @@ static int au1300_nand_device_ready(struct mtd_info *mtd) return __raw_readl((void __iomem *)MEM_STSTAT) & 1; } -static const char *db1300_part_probes[] = { "cmdlinepart", NULL }; - static struct mtd_partition db1300_nand_parts[] = { { .name = "NAND FS 0", @@ -167,7 +165,6 @@ struct platform_nand_data db1300_nand_platdata = { .nr_partitions = ARRAY_SIZE(db1300_nand_parts), .partitions = db1300_nand_parts, .chip_delay = 20, - .part_probe_types = db1300_part_probes, }, .ctrl = { .dev_ready = au1300_nand_device_ready, diff --git a/arch/mips/alchemy/devboards/db1550.c b/arch/mips/alchemy/devboards/db1550.c index 6815d0783cd8..9eb79062f46e 100644 --- a/arch/mips/alchemy/devboards/db1550.c +++ b/arch/mips/alchemy/devboards/db1550.c @@ -149,8 +149,6 @@ static int au1550_nand_device_ready(struct mtd_info *mtd) return __raw_readl((void __iomem *)MEM_STSTAT) & 1; } -static const char *db1550_part_probes[] = { "cmdlinepart", NULL }; - static struct mtd_partition db1550_nand_parts[] = { { .name = "NAND FS 0", @@ -171,7 +169,6 @@ struct platform_nand_data db1550_nand_platdata = { .nr_partitions = ARRAY_SIZE(db1550_nand_parts), .partitions = db1550_nand_parts, .chip_delay = 20, - .part_probe_types = db1550_part_probes, }, .ctrl = { .dev_ready = au1550_nand_device_ready, diff --git a/arch/mips/pnx833x/common/platform.c b/arch/mips/pnx833x/common/platform.c index 87167dcc79fa..05a1d922cd60 100644 --- a/arch/mips/pnx833x/common/platform.c +++ b/arch/mips/pnx833x/common/platform.c @@ -244,11 +244,6 @@ static struct platform_device pnx833x_sata_device = { .resource = pnx833x_sata_resources, }; -static const char *part_probes[] = { - "cmdlinepart", - NULL -}; - static void pnx833x_flash_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) { @@ -268,7 +263,6 @@ static struct platform_nand_data pnx833x_flash_nand_data = { .chip = { .nr_chips = 1, .chip_delay = 25, - .part_probe_types = part_probes, }, .ctrl = { .cmd_ctrl = pnx833x_flash_nand_cmd_ctrl diff --git a/arch/mips/rb532/devices.c b/arch/mips/rb532/devices.c index ea774285e6c5..716e9a12f0e7 100644 --- a/arch/mips/rb532/devices.c +++ b/arch/mips/rb532/devices.c @@ -293,7 +293,6 @@ static void __init rb532_nand_setup(void) rb532_nand_data.chip.nr_partitions = ARRAY_SIZE(rb532_partition_info); rb532_nand_data.chip.partitions = rb532_partition_info; rb532_nand_data.chip.chip_delay = NAND_CHIP_DELAY; - rb532_nand_data.chip.options = NAND_NO_AUTOINCR; } diff --git a/arch/powerpc/include/asm/stat.h b/arch/powerpc/include/asm/stat.h index 10cfb558e0fd..84880b80cc1c 100644 --- a/arch/powerpc/include/asm/stat.h +++ b/arch/powerpc/include/asm/stat.h @@ -30,7 +30,7 @@ struct stat { unsigned long st_dev; ino_t st_ino; #ifdef __powerpc64__ - unsigned short st_nlink; + unsigned long st_nlink; mode_t st_mode; #else mode_t st_mode; diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c index 34cd0c5ff2e1..a8a1ca741c85 100644 --- a/arch/sh/boards/mach-migor/setup.c +++ b/arch/sh/boards/mach-migor/setup.c @@ -188,7 +188,6 @@ static struct platform_nand_data migor_nand_flash_data = { .partitions = migor_nand_flash_partitions, .nr_partitions = ARRAY_SIZE(migor_nand_flash_partitions), .chip_delay = 20, - .part_probe_types = (const char *[]) { "cmdlinepart", NULL }, }, .ctrl = { .dev_ready = migor_nand_flash_ready, diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index d7038230b71e..7053140c6596 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -35,9 +35,28 @@ static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { {0,} }; + +static void cirrus_kick_out_firmware_fb(struct pci_dev *pdev) +{ + struct apertures_struct *ap; + bool primary = false; + + ap = alloc_apertures(1); + ap->ranges[0].base = pci_resource_start(pdev, 0); + ap->ranges[0].size = pci_resource_len(pdev, 0); + +#ifdef CONFIG_X86 + primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; +#endif + remove_conflicting_framebuffers(ap, "cirrusdrmfb", primary); + kfree(ap); +} + static int __devinit cirrus_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + cirrus_kick_out_firmware_fb(pdev); + return drm_get_pci_dev(pdev, ent, &driver); } diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index 21bdfa8836f7..64ea597cb6d3 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.h +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h @@ -145,7 +145,7 @@ struct cirrus_device { struct ttm_bo_device bdev; atomic_t validate_sequence; } ttm; - + bool mm_inited; }; diff --git a/drivers/gpu/drm/cirrus/cirrus_ttm.c b/drivers/gpu/drm/cirrus/cirrus_ttm.c index 2ebcd11a5023..50e170f879de 100644 --- a/drivers/gpu/drm/cirrus/cirrus_ttm.c +++ b/drivers/gpu/drm/cirrus/cirrus_ttm.c @@ -275,12 +275,17 @@ int cirrus_mm_init(struct cirrus_device *cirrus) pci_resource_len(dev->pdev, 0), DRM_MTRR_WC); + cirrus->mm_inited = true; return 0; } void cirrus_mm_fini(struct cirrus_device *cirrus) { struct drm_device *dev = cirrus->dev; + + if (!cirrus->mm_inited) + return; + ttm_bo_device_release(&cirrus->ttm.bdev); cirrus_ttm_global_release(cirrus); diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index c3b5139eba7f..eb92fe257a39 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -30,7 +30,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/i2c.h> -#include <linux/export.h> +#include <linux/module.h> #include "drmP.h" #include "drm_edid.h" #include "drm_edid_modes.h" @@ -149,6 +149,10 @@ int drm_edid_header_is_valid(const u8 *raw_edid) } EXPORT_SYMBOL(drm_edid_header_is_valid); +static int edid_fixup __read_mostly = 6; +module_param_named(edid_fixup, edid_fixup, int, 0400); +MODULE_PARM_DESC(edid_fixup, + "Minimum number of valid EDID header bytes (0-8, default 6)"); /* * Sanity check the EDID block (base or extension). Return 0 if the block @@ -160,10 +164,13 @@ bool drm_edid_block_valid(u8 *raw_edid, int block) u8 csum = 0; struct edid *edid = (struct edid *)raw_edid; + if (edid_fixup > 8 || edid_fixup < 0) + edid_fixup = 6; + if (block == 0) { int score = drm_edid_header_is_valid(raw_edid); if (score == 8) ; - else if (score >= 6) { + else if (score >= edid_fixup) { DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); memcpy(raw_edid, edid_header, sizeof(edid_header)); } else { diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 3c8e04f54713..93e832d6c328 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -41,9 +41,28 @@ static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { MODULE_DEVICE_TABLE(pci, pciidlist); +static void mgag200_kick_out_firmware_fb(struct pci_dev *pdev) +{ + struct apertures_struct *ap; + bool primary = false; + + ap = alloc_apertures(1); + ap->ranges[0].base = pci_resource_start(pdev, 0); + ap->ranges[0].size = pci_resource_len(pdev, 0); + +#ifdef CONFIG_X86 + primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; +#endif + remove_conflicting_framebuffers(ap, "mgag200drmfb", primary); + kfree(ap); +} + + static int __devinit mga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + mgag200_kick_out_firmware_fb(pdev); + return drm_get_pci_dev(pdev, ent, &driver); } diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 58991af90502..01550d05e273 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -1029,6 +1029,11 @@ int evergreen_pcie_gart_enable(struct radeon_device *rdev) WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp); WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp); WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp); + if ((rdev->family == CHIP_JUNIPER) || + (rdev->family == CHIP_CYPRESS) || + (rdev->family == CHIP_HEMLOCK) || + (rdev->family == CHIP_BARTS)) + WREG32(MC_VM_MD_L1_TLB3_CNTL, tmp); } WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp); WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp); @@ -1553,163 +1558,10 @@ int evergreen_cp_resume(struct radeon_device *rdev) /* * Core functions */ -static u32 evergreen_get_tile_pipe_to_backend_map(struct radeon_device *rdev, - u32 num_tile_pipes, - u32 num_backends, - u32 backend_disable_mask) -{ - u32 backend_map = 0; - u32 enabled_backends_mask = 0; - u32 enabled_backends_count = 0; - u32 cur_pipe; - u32 swizzle_pipe[EVERGREEN_MAX_PIPES]; - u32 cur_backend = 0; - u32 i; - bool force_no_swizzle; - - if (num_tile_pipes > EVERGREEN_MAX_PIPES) - num_tile_pipes = EVERGREEN_MAX_PIPES; - if (num_tile_pipes < 1) - num_tile_pipes = 1; - if (num_backends > EVERGREEN_MAX_BACKENDS) - num_backends = EVERGREEN_MAX_BACKENDS; - if (num_backends < 1) - num_backends = 1; - - for (i = 0; i < EVERGREEN_MAX_BACKENDS; ++i) { - if (((backend_disable_mask >> i) & 1) == 0) { - enabled_backends_mask |= (1 << i); - ++enabled_backends_count; - } - if (enabled_backends_count == num_backends) - break; - } - - if (enabled_backends_count == 0) { - enabled_backends_mask = 1; - enabled_backends_count = 1; - } - - if (enabled_backends_count != num_backends) - num_backends = enabled_backends_count; - - memset((uint8_t *)&swizzle_pipe[0], 0, sizeof(u32) * EVERGREEN_MAX_PIPES); - switch (rdev->family) { - case CHIP_CEDAR: - case CHIP_REDWOOD: - case CHIP_PALM: - case CHIP_SUMO: - case CHIP_SUMO2: - case CHIP_TURKS: - case CHIP_CAICOS: - force_no_swizzle = false; - break; - case CHIP_CYPRESS: - case CHIP_HEMLOCK: - case CHIP_JUNIPER: - case CHIP_BARTS: - default: - force_no_swizzle = true; - break; - } - if (force_no_swizzle) { - bool last_backend_enabled = false; - - force_no_swizzle = false; - for (i = 0; i < EVERGREEN_MAX_BACKENDS; ++i) { - if (((enabled_backends_mask >> i) & 1) == 1) { - if (last_backend_enabled) - force_no_swizzle = true; - last_backend_enabled = true; - } else - last_backend_enabled = false; - } - } - - switch (num_tile_pipes) { - case 1: - case 3: - case 5: - case 7: - DRM_ERROR("odd number of pipes!\n"); - break; - case 2: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - break; - case 4: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 1; - swizzle_pipe[3] = 3; - } - break; - case 6: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - swizzle_pipe[5] = 5; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 1; - swizzle_pipe[4] = 3; - swizzle_pipe[5] = 5; - } - break; - case 8: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - swizzle_pipe[5] = 5; - swizzle_pipe[6] = 6; - swizzle_pipe[7] = 7; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 6; - swizzle_pipe[4] = 1; - swizzle_pipe[5] = 3; - swizzle_pipe[6] = 5; - swizzle_pipe[7] = 7; - } - break; - } - - for (cur_pipe = 0; cur_pipe < num_tile_pipes; ++cur_pipe) { - while (((1 << cur_backend) & enabled_backends_mask) == 0) - cur_backend = (cur_backend + 1) % EVERGREEN_MAX_BACKENDS; - - backend_map |= (((cur_backend & 0xf) << (swizzle_pipe[cur_pipe] * 4))); - - cur_backend = (cur_backend + 1) % EVERGREEN_MAX_BACKENDS; - } - - return backend_map; -} - static void evergreen_gpu_init(struct radeon_device *rdev) { - u32 cc_rb_backend_disable = 0; - u32 cc_gc_shader_pipe_config; - u32 gb_addr_config = 0; + u32 gb_addr_config; u32 mc_shared_chmap, mc_arb_ramcfg; - u32 gb_backend_map; - u32 grbm_gfx_index; u32 sx_debug_1; u32 smx_dc_ctl0; u32 sq_config; @@ -1724,6 +1576,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) u32 sq_stack_resource_mgmt_3; u32 vgt_cache_invalidation; u32 hdp_host_path_cntl, tmp; + u32 disabled_rb_mask; int i, j, num_shader_engines, ps_thread_count; switch (rdev->family) { @@ -1748,6 +1601,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x100; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = CYPRESS_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_JUNIPER: rdev->config.evergreen.num_ses = 1; @@ -1769,6 +1623,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x100; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = JUNIPER_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_REDWOOD: rdev->config.evergreen.num_ses = 1; @@ -1790,6 +1645,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x100; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = REDWOOD_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_CEDAR: default: @@ -1812,6 +1668,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x40; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = CEDAR_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_PALM: rdev->config.evergreen.num_ses = 1; @@ -1833,6 +1690,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x40; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = CEDAR_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_SUMO: rdev->config.evergreen.num_ses = 1; @@ -1860,6 +1718,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x40; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = REDWOOD_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_SUMO2: rdev->config.evergreen.num_ses = 1; @@ -1881,6 +1740,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x40; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = REDWOOD_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_BARTS: rdev->config.evergreen.num_ses = 2; @@ -1902,6 +1762,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x100; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = BARTS_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_TURKS: rdev->config.evergreen.num_ses = 1; @@ -1923,6 +1784,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x100; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = TURKS_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_CAICOS: rdev->config.evergreen.num_ses = 1; @@ -1944,6 +1806,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sc_prim_fifo_size = 0x40; rdev->config.evergreen.sc_hiz_tile_fifo_size = 0x30; rdev->config.evergreen.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = CAICOS_GB_ADDR_CONFIG_GOLDEN; break; } @@ -1960,20 +1823,6 @@ static void evergreen_gpu_init(struct radeon_device *rdev) evergreen_fix_pci_max_read_req_size(rdev); - cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & ~2; - - cc_gc_shader_pipe_config |= - INACTIVE_QD_PIPES((EVERGREEN_MAX_PIPES_MASK << rdev->config.evergreen.max_pipes) - & EVERGREEN_MAX_PIPES_MASK); - cc_gc_shader_pipe_config |= - INACTIVE_SIMDS((EVERGREEN_MAX_SIMDS_MASK << rdev->config.evergreen.max_simds) - & EVERGREEN_MAX_SIMDS_MASK); - - cc_rb_backend_disable = - BACKEND_DISABLE((EVERGREEN_MAX_BACKENDS_MASK << rdev->config.evergreen.max_backends) - & EVERGREEN_MAX_BACKENDS_MASK); - - mc_shared_chmap = RREG32(MC_SHARED_CHMAP); if ((rdev->family == CHIP_PALM) || (rdev->family == CHIP_SUMO) || @@ -1982,134 +1831,6 @@ static void evergreen_gpu_init(struct radeon_device *rdev) else mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG); - switch (rdev->config.evergreen.max_tile_pipes) { - case 1: - default: - gb_addr_config |= NUM_PIPES(0); - break; - case 2: - gb_addr_config |= NUM_PIPES(1); - break; - case 4: - gb_addr_config |= NUM_PIPES(2); - break; - case 8: - gb_addr_config |= NUM_PIPES(3); - break; - } - - gb_addr_config |= PIPE_INTERLEAVE_SIZE((mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT); - gb_addr_config |= BANK_INTERLEAVE_SIZE(0); - gb_addr_config |= NUM_SHADER_ENGINES(rdev->config.evergreen.num_ses - 1); - gb_addr_config |= SHADER_ENGINE_TILE_SIZE(1); - gb_addr_config |= NUM_GPUS(0); /* Hemlock? */ - gb_addr_config |= MULTI_GPU_TILE_SIZE(2); - - if (((mc_arb_ramcfg & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT) > 2) - gb_addr_config |= ROW_SIZE(2); - else - gb_addr_config |= ROW_SIZE((mc_arb_ramcfg & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT); - - if (rdev->ddev->pdev->device == 0x689e) { - u32 efuse_straps_4; - u32 efuse_straps_3; - u8 efuse_box_bit_131_124; - - WREG32(RCU_IND_INDEX, 0x204); - efuse_straps_4 = RREG32(RCU_IND_DATA); - WREG32(RCU_IND_INDEX, 0x203); - efuse_straps_3 = RREG32(RCU_IND_DATA); - efuse_box_bit_131_124 = (u8)(((efuse_straps_4 & 0xf) << 4) | ((efuse_straps_3 & 0xf0000000) >> 28)); - - switch(efuse_box_bit_131_124) { - case 0x00: - gb_backend_map = 0x76543210; - break; - case 0x55: - gb_backend_map = 0x77553311; - break; - case 0x56: - gb_backend_map = 0x77553300; - break; - case 0x59: - gb_backend_map = 0x77552211; - break; - case 0x66: - gb_backend_map = 0x77443300; - break; - case 0x99: - gb_backend_map = 0x66552211; - break; - case 0x5a: - gb_backend_map = 0x77552200; - break; - case 0xaa: - gb_backend_map = 0x66442200; - break; - case 0x95: - gb_backend_map = 0x66553311; - break; - default: - DRM_ERROR("bad backend map, using default\n"); - gb_backend_map = - evergreen_get_tile_pipe_to_backend_map(rdev, - rdev->config.evergreen.max_tile_pipes, - rdev->config.evergreen.max_backends, - ((EVERGREEN_MAX_BACKENDS_MASK << - rdev->config.evergreen.max_backends) & - EVERGREEN_MAX_BACKENDS_MASK)); - break; - } - } else if (rdev->ddev->pdev->device == 0x68b9) { - u32 efuse_straps_3; - u8 efuse_box_bit_127_124; - - WREG32(RCU_IND_INDEX, 0x203); - efuse_straps_3 = RREG32(RCU_IND_DATA); - efuse_box_bit_127_124 = (u8)((efuse_straps_3 & 0xF0000000) >> 28); - - switch(efuse_box_bit_127_124) { - case 0x0: - gb_backend_map = 0x00003210; - break; - case 0x5: - case 0x6: - case 0x9: - case 0xa: - gb_backend_map = 0x00003311; - break; - default: - DRM_ERROR("bad backend map, using default\n"); - gb_backend_map = - evergreen_get_tile_pipe_to_backend_map(rdev, - rdev->config.evergreen.max_tile_pipes, - rdev->config.evergreen.max_backends, - ((EVERGREEN_MAX_BACKENDS_MASK << - rdev->config.evergreen.max_backends) & - EVERGREEN_MAX_BACKENDS_MASK)); - break; - } - } else { - switch (rdev->family) { - case CHIP_CYPRESS: - case CHIP_HEMLOCK: - case CHIP_BARTS: - gb_backend_map = 0x66442200; - break; - case CHIP_JUNIPER: - gb_backend_map = 0x00002200; - break; - default: - gb_backend_map = - evergreen_get_tile_pipe_to_backend_map(rdev, - rdev->config.evergreen.max_tile_pipes, - rdev->config.evergreen.max_backends, - ((EVERGREEN_MAX_BACKENDS_MASK << - rdev->config.evergreen.max_backends) & - EVERGREEN_MAX_BACKENDS_MASK)); - } - } - /* setup tiling info dword. gb_addr_config is not adequate since it does * not have bank info, so create a custom tiling dword. * bits 3:0 num_pipes @@ -2136,45 +1857,54 @@ static void evergreen_gpu_init(struct radeon_device *rdev) /* num banks is 8 on all fusion asics. 0 = 4, 1 = 8, 2 = 16 */ if (rdev->flags & RADEON_IS_IGP) rdev->config.evergreen.tile_config |= 1 << 4; - else - rdev->config.evergreen.tile_config |= - ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) << 4; - rdev->config.evergreen.tile_config |= - ((mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT) << 8; + else { + if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) + rdev->config.evergreen.tile_config |= 1 << 4; + else + rdev->config.evergreen.tile_config |= 0 << 4; + } + rdev->config.evergreen.tile_config |= 0 << 8; rdev->config.evergreen.tile_config |= ((gb_addr_config & 0x30000000) >> 28) << 12; - rdev->config.evergreen.backend_map = gb_backend_map; - WREG32(GB_BACKEND_MAP, gb_backend_map); - WREG32(GB_ADDR_CONFIG, gb_addr_config); - WREG32(DMIF_ADDR_CONFIG, gb_addr_config); - WREG32(HDP_ADDR_CONFIG, gb_addr_config); - - num_shader_engines = ((RREG32(GB_ADDR_CONFIG) & NUM_SHADER_ENGINES(3)) >> 12) + 1; - grbm_gfx_index = INSTANCE_BROADCAST_WRITES; + num_shader_engines = (gb_addr_config & NUM_SHADER_ENGINES(3) >> 12) + 1; - for (i = 0; i < rdev->config.evergreen.num_ses; i++) { - u32 rb = cc_rb_backend_disable | (0xf0 << 16); - u32 sp = cc_gc_shader_pipe_config; - u32 gfx = grbm_gfx_index | SE_INDEX(i); + if ((rdev->family >= CHIP_CEDAR) && (rdev->family <= CHIP_HEMLOCK)) { + u32 efuse_straps_4; + u32 efuse_straps_3; - if (i == num_shader_engines) { - rb |= BACKEND_DISABLE(EVERGREEN_MAX_BACKENDS_MASK); - sp |= INACTIVE_SIMDS(EVERGREEN_MAX_SIMDS_MASK); + WREG32(RCU_IND_INDEX, 0x204); + efuse_straps_4 = RREG32(RCU_IND_DATA); + WREG32(RCU_IND_INDEX, 0x203); + efuse_straps_3 = RREG32(RCU_IND_DATA); + tmp = (((efuse_straps_4 & 0xf) << 4) | + ((efuse_straps_3 & 0xf0000000) >> 28)); + } else { + tmp = 0; + for (i = (rdev->config.evergreen.num_ses - 1); i >= 0; i--) { + u32 rb_disable_bitmap; + + WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i)); + WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i)); + rb_disable_bitmap = (RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000) >> 16; + tmp <<= 4; + tmp |= rb_disable_bitmap; } + } + /* enabled rb are just the one not disabled :) */ + disabled_rb_mask = tmp; - WREG32(GRBM_GFX_INDEX, gfx); - WREG32(RLC_GFX_INDEX, gfx); + WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); + WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); - WREG32(CC_RB_BACKEND_DISABLE, rb); - WREG32(CC_SYS_RB_BACKEND_DISABLE, rb); - WREG32(GC_USER_RB_BACKEND_DISABLE, rb); - WREG32(CC_GC_SHADER_PIPE_CONFIG, sp); - } + WREG32(GB_ADDR_CONFIG, gb_addr_config); + WREG32(DMIF_ADDR_CONFIG, gb_addr_config); + WREG32(HDP_ADDR_CONFIG, gb_addr_config); - grbm_gfx_index |= SE_BROADCAST_WRITES; - WREG32(GRBM_GFX_INDEX, grbm_gfx_index); - WREG32(RLC_GFX_INDEX, grbm_gfx_index); + tmp = gb_addr_config & NUM_PIPES_MASK; + tmp = r6xx_remap_render_backend(rdev, tmp, rdev->config.evergreen.max_backends, + EVERGREEN_MAX_BACKENDS, disabled_rb_mask); + WREG32(GB_BACKEND_MAP, tmp); WREG32(CGTS_SYS_TCC_DISABLE, 0); WREG32(CGTS_TCC_DISABLE, 0); diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h index 79130bfd1d6f..2773039b4902 100644 --- a/drivers/gpu/drm/radeon/evergreend.h +++ b/drivers/gpu/drm/radeon/evergreend.h @@ -37,6 +37,15 @@ #define EVERGREEN_MAX_PIPES_MASK 0xFF #define EVERGREEN_MAX_LDS_NUM 0xFFFF +#define CYPRESS_GB_ADDR_CONFIG_GOLDEN 0x02011003 +#define BARTS_GB_ADDR_CONFIG_GOLDEN 0x02011003 +#define CAYMAN_GB_ADDR_CONFIG_GOLDEN 0x02011003 +#define JUNIPER_GB_ADDR_CONFIG_GOLDEN 0x02010002 +#define REDWOOD_GB_ADDR_CONFIG_GOLDEN 0x02010002 +#define TURKS_GB_ADDR_CONFIG_GOLDEN 0x02010002 +#define CEDAR_GB_ADDR_CONFIG_GOLDEN 0x02010001 +#define CAICOS_GB_ADDR_CONFIG_GOLDEN 0x02010001 + /* Registers */ #define RCU_IND_INDEX 0x100 @@ -54,6 +63,7 @@ #define BACKEND_DISABLE(x) ((x) << 16) #define GB_ADDR_CONFIG 0x98F8 #define NUM_PIPES(x) ((x) << 0) +#define NUM_PIPES_MASK 0x0000000f #define PIPE_INTERLEAVE_SIZE(x) ((x) << 4) #define BANK_INTERLEAVE_SIZE(x) ((x) << 8) #define NUM_SHADER_ENGINES(x) ((x) << 12) @@ -452,6 +462,7 @@ #define MC_VM_MD_L1_TLB0_CNTL 0x2654 #define MC_VM_MD_L1_TLB1_CNTL 0x2658 #define MC_VM_MD_L1_TLB2_CNTL 0x265C +#define MC_VM_MD_L1_TLB3_CNTL 0x2698 #define FUS_MC_VM_MD_L1_TLB0_CNTL 0x265C #define FUS_MC_VM_MD_L1_TLB1_CNTL 0x2660 diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index ce4e7cc6c905..3df4efa11942 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -417,215 +417,17 @@ out: /* * Core functions */ -static u32 cayman_get_tile_pipe_to_backend_map(struct radeon_device *rdev, - u32 num_tile_pipes, - u32 num_backends_per_asic, - u32 *backend_disable_mask_per_asic, - u32 num_shader_engines) -{ - u32 backend_map = 0; - u32 enabled_backends_mask = 0; - u32 enabled_backends_count = 0; - u32 num_backends_per_se; - u32 cur_pipe; - u32 swizzle_pipe[CAYMAN_MAX_PIPES]; - u32 cur_backend = 0; - u32 i; - bool force_no_swizzle; - - /* force legal values */ - if (num_tile_pipes < 1) - num_tile_pipes = 1; - if (num_tile_pipes > rdev->config.cayman.max_tile_pipes) - num_tile_pipes = rdev->config.cayman.max_tile_pipes; - if (num_shader_engines < 1) - num_shader_engines = 1; - if (num_shader_engines > rdev->config.cayman.max_shader_engines) - num_shader_engines = rdev->config.cayman.max_shader_engines; - if (num_backends_per_asic < num_shader_engines) - num_backends_per_asic = num_shader_engines; - if (num_backends_per_asic > (rdev->config.cayman.max_backends_per_se * num_shader_engines)) - num_backends_per_asic = rdev->config.cayman.max_backends_per_se * num_shader_engines; - - /* make sure we have the same number of backends per se */ - num_backends_per_asic = ALIGN(num_backends_per_asic, num_shader_engines); - /* set up the number of backends per se */ - num_backends_per_se = num_backends_per_asic / num_shader_engines; - if (num_backends_per_se > rdev->config.cayman.max_backends_per_se) { - num_backends_per_se = rdev->config.cayman.max_backends_per_se; - num_backends_per_asic = num_backends_per_se * num_shader_engines; - } - - /* create enable mask and count for enabled backends */ - for (i = 0; i < CAYMAN_MAX_BACKENDS; ++i) { - if (((*backend_disable_mask_per_asic >> i) & 1) == 0) { - enabled_backends_mask |= (1 << i); - ++enabled_backends_count; - } - if (enabled_backends_count == num_backends_per_asic) - break; - } - - /* force the backends mask to match the current number of backends */ - if (enabled_backends_count != num_backends_per_asic) { - u32 this_backend_enabled; - u32 shader_engine; - u32 backend_per_se; - - enabled_backends_mask = 0; - enabled_backends_count = 0; - *backend_disable_mask_per_asic = CAYMAN_MAX_BACKENDS_MASK; - for (i = 0; i < CAYMAN_MAX_BACKENDS; ++i) { - /* calc the current se */ - shader_engine = i / rdev->config.cayman.max_backends_per_se; - /* calc the backend per se */ - backend_per_se = i % rdev->config.cayman.max_backends_per_se; - /* default to not enabled */ - this_backend_enabled = 0; - if ((shader_engine < num_shader_engines) && - (backend_per_se < num_backends_per_se)) - this_backend_enabled = 1; - if (this_backend_enabled) { - enabled_backends_mask |= (1 << i); - *backend_disable_mask_per_asic &= ~(1 << i); - ++enabled_backends_count; - } - } - } - - - memset((uint8_t *)&swizzle_pipe[0], 0, sizeof(u32) * CAYMAN_MAX_PIPES); - switch (rdev->family) { - case CHIP_CAYMAN: - case CHIP_ARUBA: - force_no_swizzle = true; - break; - default: - force_no_swizzle = false; - break; - } - if (force_no_swizzle) { - bool last_backend_enabled = false; - - force_no_swizzle = false; - for (i = 0; i < CAYMAN_MAX_BACKENDS; ++i) { - if (((enabled_backends_mask >> i) & 1) == 1) { - if (last_backend_enabled) - force_no_swizzle = true; - last_backend_enabled = true; - } else - last_backend_enabled = false; - } - } - - switch (num_tile_pipes) { - case 1: - case 3: - case 5: - case 7: - DRM_ERROR("odd number of pipes!\n"); - break; - case 2: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - break; - case 4: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 1; - swizzle_pipe[3] = 3; - } - break; - case 6: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - swizzle_pipe[5] = 5; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 1; - swizzle_pipe[4] = 3; - swizzle_pipe[5] = 5; - } - break; - case 8: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - swizzle_pipe[5] = 5; - swizzle_pipe[6] = 6; - swizzle_pipe[7] = 7; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 6; - swizzle_pipe[4] = 1; - swizzle_pipe[5] = 3; - swizzle_pipe[6] = 5; - swizzle_pipe[7] = 7; - } - break; - } - - for (cur_pipe = 0; cur_pipe < num_tile_pipes; ++cur_pipe) { - while (((1 << cur_backend) & enabled_backends_mask) == 0) - cur_backend = (cur_backend + 1) % CAYMAN_MAX_BACKENDS; - - backend_map |= (((cur_backend & 0xf) << (swizzle_pipe[cur_pipe] * 4))); - - cur_backend = (cur_backend + 1) % CAYMAN_MAX_BACKENDS; - } - - return backend_map; -} - -static u32 cayman_get_disable_mask_per_asic(struct radeon_device *rdev, - u32 disable_mask_per_se, - u32 max_disable_mask_per_se, - u32 num_shader_engines) -{ - u32 disable_field_width_per_se = r600_count_pipe_bits(disable_mask_per_se); - u32 disable_mask_per_asic = disable_mask_per_se & max_disable_mask_per_se; - - if (num_shader_engines == 1) - return disable_mask_per_asic; - else if (num_shader_engines == 2) - return disable_mask_per_asic | (disable_mask_per_asic << disable_field_width_per_se); - else - return 0xffffffff; -} - static void cayman_gpu_init(struct radeon_device *rdev) { - u32 cc_rb_backend_disable = 0; - u32 cc_gc_shader_pipe_config; u32 gb_addr_config = 0; u32 mc_shared_chmap, mc_arb_ramcfg; - u32 gb_backend_map; u32 cgts_tcc_disable; u32 sx_debug_1; u32 smx_dc_ctl0; - u32 gc_user_shader_pipe_config; - u32 gc_user_rb_backend_disable; - u32 cgts_user_tcc_disable; u32 cgts_sm_ctrl_reg; u32 hdp_host_path_cntl; u32 tmp; + u32 disabled_rb_mask; int i, j; switch (rdev->family) { @@ -650,6 +452,7 @@ static void cayman_gpu_init(struct radeon_device *rdev) rdev->config.cayman.sc_prim_fifo_size = 0x100; rdev->config.cayman.sc_hiz_tile_fifo_size = 0x30; rdev->config.cayman.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = CAYMAN_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_ARUBA: default: @@ -687,6 +490,7 @@ static void cayman_gpu_init(struct radeon_device *rdev) rdev->config.cayman.sc_prim_fifo_size = 0x40; rdev->config.cayman.sc_hiz_tile_fifo_size = 0x30; rdev->config.cayman.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = ARUBA_GB_ADDR_CONFIG_GOLDEN; break; } @@ -706,39 +510,6 @@ static void cayman_gpu_init(struct radeon_device *rdev) mc_shared_chmap = RREG32(MC_SHARED_CHMAP); mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG); - cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE); - cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG); - cgts_tcc_disable = 0xffff0000; - for (i = 0; i < rdev->config.cayman.max_texture_channel_caches; i++) - cgts_tcc_disable &= ~(1 << (16 + i)); - gc_user_rb_backend_disable = RREG32(GC_USER_RB_BACKEND_DISABLE); - gc_user_shader_pipe_config = RREG32(GC_USER_SHADER_PIPE_CONFIG); - cgts_user_tcc_disable = RREG32(CGTS_USER_TCC_DISABLE); - - rdev->config.cayman.num_shader_engines = rdev->config.cayman.max_shader_engines; - tmp = ((~gc_user_shader_pipe_config) & INACTIVE_QD_PIPES_MASK) >> INACTIVE_QD_PIPES_SHIFT; - rdev->config.cayman.num_shader_pipes_per_simd = r600_count_pipe_bits(tmp); - rdev->config.cayman.num_tile_pipes = rdev->config.cayman.max_tile_pipes; - tmp = ((~gc_user_shader_pipe_config) & INACTIVE_SIMDS_MASK) >> INACTIVE_SIMDS_SHIFT; - rdev->config.cayman.num_simds_per_se = r600_count_pipe_bits(tmp); - tmp = ((~gc_user_rb_backend_disable) & BACKEND_DISABLE_MASK) >> BACKEND_DISABLE_SHIFT; - rdev->config.cayman.num_backends_per_se = r600_count_pipe_bits(tmp); - tmp = (gc_user_rb_backend_disable & BACKEND_DISABLE_MASK) >> BACKEND_DISABLE_SHIFT; - rdev->config.cayman.backend_disable_mask_per_asic = - cayman_get_disable_mask_per_asic(rdev, tmp, CAYMAN_MAX_BACKENDS_PER_SE_MASK, - rdev->config.cayman.num_shader_engines); - rdev->config.cayman.backend_map = - cayman_get_tile_pipe_to_backend_map(rdev, rdev->config.cayman.num_tile_pipes, - rdev->config.cayman.num_backends_per_se * - rdev->config.cayman.num_shader_engines, - &rdev->config.cayman.backend_disable_mask_per_asic, - rdev->config.cayman.num_shader_engines); - tmp = ((~cgts_user_tcc_disable) & TCC_DISABLE_MASK) >> TCC_DISABLE_SHIFT; - rdev->config.cayman.num_texture_channel_caches = r600_count_pipe_bits(tmp); - tmp = (mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT; - rdev->config.cayman.mem_max_burst_length_bytes = (tmp + 1) * 256; - if (rdev->config.cayman.mem_max_burst_length_bytes > 512) - rdev->config.cayman.mem_max_burst_length_bytes = 512; tmp = (mc_arb_ramcfg & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT; rdev->config.cayman.mem_row_size_in_kb = (4 * (1 << (8 + tmp))) / 1024; if (rdev->config.cayman.mem_row_size_in_kb > 4) @@ -748,73 +519,6 @@ static void cayman_gpu_init(struct radeon_device *rdev) rdev->config.cayman.num_gpus = 1; rdev->config.cayman.multi_gpu_tile_size = 64; - //gb_addr_config = 0x02011003 -#if 0 - gb_addr_config = RREG32(GB_ADDR_CONFIG); -#else - gb_addr_config = 0; - switch (rdev->config.cayman.num_tile_pipes) { - case 1: - default: - gb_addr_config |= NUM_PIPES(0); - break; - case 2: - gb_addr_config |= NUM_PIPES(1); - break; - case 4: - gb_addr_config |= NUM_PIPES(2); - break; - case 8: - gb_addr_config |= NUM_PIPES(3); - break; - } - - tmp = (rdev->config.cayman.mem_max_burst_length_bytes / 256) - 1; - gb_addr_config |= PIPE_INTERLEAVE_SIZE(tmp); - gb_addr_config |= NUM_SHADER_ENGINES(rdev->config.cayman.num_shader_engines - 1); - tmp = (rdev->config.cayman.shader_engine_tile_size / 16) - 1; - gb_addr_config |= SHADER_ENGINE_TILE_SIZE(tmp); - switch (rdev->config.cayman.num_gpus) { - case 1: - default: - gb_addr_config |= NUM_GPUS(0); - break; - case 2: - gb_addr_config |= NUM_GPUS(1); - break; - case 4: - gb_addr_config |= NUM_GPUS(2); - break; - } - switch (rdev->config.cayman.multi_gpu_tile_size) { - case 16: - gb_addr_config |= MULTI_GPU_TILE_SIZE(0); - break; - case 32: - default: - gb_addr_config |= MULTI_GPU_TILE_SIZE(1); - break; - case 64: - gb_addr_config |= MULTI_GPU_TILE_SIZE(2); - break; - case 128: - gb_addr_config |= MULTI_GPU_TILE_SIZE(3); - break; - } - switch (rdev->config.cayman.mem_row_size_in_kb) { - case 1: - default: - gb_addr_config |= ROW_SIZE(0); - break; - case 2: - gb_addr_config |= ROW_SIZE(1); - break; - case 4: - gb_addr_config |= ROW_SIZE(2); - break; - } -#endif - tmp = (gb_addr_config & NUM_PIPES_MASK) >> NUM_PIPES_SHIFT; rdev->config.cayman.num_tile_pipes = (1 << tmp); tmp = (gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT; @@ -828,17 +532,7 @@ static void cayman_gpu_init(struct radeon_device *rdev) tmp = (gb_addr_config & ROW_SIZE_MASK) >> ROW_SIZE_SHIFT; rdev->config.cayman.mem_row_size_in_kb = 1 << tmp; - //gb_backend_map = 0x76541032; -#if 0 - gb_backend_map = RREG32(GB_BACKEND_MAP); -#else - gb_backend_map = - cayman_get_tile_pipe_to_backend_map(rdev, rdev->config.cayman.num_tile_pipes, - rdev->config.cayman.num_backends_per_se * - rdev->config.cayman.num_shader_engines, - &rdev->config.cayman.backend_disable_mask_per_asic, - rdev->config.cayman.num_shader_engines); -#endif + /* setup tiling info dword. gb_addr_config is not adequate since it does * not have bank info, so create a custom tiling dword. * bits 3:0 num_pipes @@ -866,33 +560,49 @@ static void cayman_gpu_init(struct radeon_device *rdev) /* num banks is 8 on all fusion asics. 0 = 4, 1 = 8, 2 = 16 */ if (rdev->flags & RADEON_IS_IGP) rdev->config.cayman.tile_config |= 1 << 4; - else - rdev->config.cayman.tile_config |= - ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) << 4; + else { + if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) + rdev->config.cayman.tile_config |= 1 << 4; + else + rdev->config.cayman.tile_config |= 0 << 4; + } rdev->config.cayman.tile_config |= ((gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT) << 8; rdev->config.cayman.tile_config |= ((gb_addr_config & ROW_SIZE_MASK) >> ROW_SIZE_SHIFT) << 12; - rdev->config.cayman.backend_map = gb_backend_map; - WREG32(GB_BACKEND_MAP, gb_backend_map); + tmp = 0; + for (i = (rdev->config.cayman.max_shader_engines - 1); i >= 0; i--) { + u32 rb_disable_bitmap; + + WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i)); + WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i)); + rb_disable_bitmap = (RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000) >> 16; + tmp <<= 4; + tmp |= rb_disable_bitmap; + } + /* enabled rb are just the one not disabled :) */ + disabled_rb_mask = tmp; + + WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); + WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_BROADCAST_WRITES); + WREG32(GB_ADDR_CONFIG, gb_addr_config); WREG32(DMIF_ADDR_CONFIG, gb_addr_config); WREG32(HDP_ADDR_CONFIG, gb_addr_config); - /* primary versions */ - WREG32(CC_RB_BACKEND_DISABLE, cc_rb_backend_disable); - WREG32(CC_SYS_RB_BACKEND_DISABLE, cc_rb_backend_disable); - WREG32(CC_GC_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config); + tmp = gb_addr_config & NUM_PIPES_MASK; + tmp = r6xx_remap_render_backend(rdev, tmp, + rdev->config.cayman.max_backends_per_se * + rdev->config.cayman.max_shader_engines, + CAYMAN_MAX_BACKENDS, disabled_rb_mask); + WREG32(GB_BACKEND_MAP, tmp); + cgts_tcc_disable = 0xffff0000; + for (i = 0; i < rdev->config.cayman.max_texture_channel_caches; i++) + cgts_tcc_disable &= ~(1 << (16 + i)); WREG32(CGTS_TCC_DISABLE, cgts_tcc_disable); WREG32(CGTS_SYS_TCC_DISABLE, cgts_tcc_disable); - - /* user versions */ - WREG32(GC_USER_RB_BACKEND_DISABLE, cc_rb_backend_disable); - WREG32(GC_USER_SYS_RB_BACKEND_DISABLE, cc_rb_backend_disable); - WREG32(GC_USER_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config); - WREG32(CGTS_USER_SYS_TCC_DISABLE, cgts_tcc_disable); WREG32(CGTS_USER_TCC_DISABLE, cgts_tcc_disable); diff --git a/drivers/gpu/drm/radeon/nid.h b/drivers/gpu/drm/radeon/nid.h index 2aa7046ada56..a0b98066e207 100644 --- a/drivers/gpu/drm/radeon/nid.h +++ b/drivers/gpu/drm/radeon/nid.h @@ -41,6 +41,9 @@ #define CAYMAN_MAX_TCC 16 #define CAYMAN_MAX_TCC_MASK 0xFF +#define CAYMAN_GB_ADDR_CONFIG_GOLDEN 0x02011003 +#define ARUBA_GB_ADDR_CONFIG_GOLDEN 0x12010001 + #define DMIF_ADDR_CONFIG 0xBD4 #define SRBM_GFX_CNTL 0x0E44 #define RINGID(x) (((x) & 0x3) << 0) @@ -148,6 +151,8 @@ #define CGTS_SYS_TCC_DISABLE 0x3F90 #define CGTS_USER_SYS_TCC_DISABLE 0x3F94 +#define RLC_GFX_INDEX 0x3FC4 + #define CONFIG_MEMSIZE 0x5428 #define HDP_MEM_COHERENCY_FLUSH_CNTL 0x5480 @@ -212,6 +217,12 @@ #define SOFT_RESET_VGT (1 << 14) #define SOFT_RESET_IA (1 << 15) +#define GRBM_GFX_INDEX 0x802C +#define INSTANCE_INDEX(x) ((x) << 0) +#define SE_INDEX(x) ((x) << 16) +#define INSTANCE_BROADCAST_WRITES (1 << 30) +#define SE_BROADCAST_WRITES (1 << 31) + #define SCRATCH_REG0 0x8500 #define SCRATCH_REG1 0x8504 #define SCRATCH_REG2 0x8508 diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index f388a1d73b63..45cfcea63507 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -1376,113 +1376,51 @@ int r600_asic_reset(struct radeon_device *rdev) return r600_gpu_soft_reset(rdev); } -static u32 r600_get_tile_pipe_to_backend_map(u32 num_tile_pipes, - u32 num_backends, - u32 backend_disable_mask) -{ - u32 backend_map = 0; - u32 enabled_backends_mask; - u32 enabled_backends_count; - u32 cur_pipe; - u32 swizzle_pipe[R6XX_MAX_PIPES]; - u32 cur_backend; - u32 i; - - if (num_tile_pipes > R6XX_MAX_PIPES) - num_tile_pipes = R6XX_MAX_PIPES; - if (num_tile_pipes < 1) - num_tile_pipes = 1; - if (num_backends > R6XX_MAX_BACKENDS) - num_backends = R6XX_MAX_BACKENDS; - if (num_backends < 1) - num_backends = 1; - - enabled_backends_mask = 0; - enabled_backends_count = 0; - for (i = 0; i < R6XX_MAX_BACKENDS; ++i) { - if (((backend_disable_mask >> i) & 1) == 0) { - enabled_backends_mask |= (1 << i); - ++enabled_backends_count; - } - if (enabled_backends_count == num_backends) - break; - } - - if (enabled_backends_count == 0) { - enabled_backends_mask = 1; - enabled_backends_count = 1; - } - - if (enabled_backends_count != num_backends) - num_backends = enabled_backends_count; - - memset((uint8_t *)&swizzle_pipe[0], 0, sizeof(u32) * R6XX_MAX_PIPES); - switch (num_tile_pipes) { - case 1: - swizzle_pipe[0] = 0; - break; - case 2: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - break; - case 3: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - break; - case 4: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - break; - case 5: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - break; - case 6: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 5; - swizzle_pipe[4] = 1; - swizzle_pipe[5] = 3; - break; - case 7: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 6; - swizzle_pipe[4] = 1; - swizzle_pipe[5] = 3; - swizzle_pipe[6] = 5; - break; - case 8: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 6; - swizzle_pipe[4] = 1; - swizzle_pipe[5] = 3; - swizzle_pipe[6] = 5; - swizzle_pipe[7] = 7; - break; +u32 r6xx_remap_render_backend(struct radeon_device *rdev, + u32 tiling_pipe_num, + u32 max_rb_num, + u32 total_max_rb_num, + u32 disabled_rb_mask) +{ + u32 rendering_pipe_num, rb_num_width, req_rb_num; + u32 pipe_rb_ratio, pipe_rb_remain; + u32 data = 0, mask = 1 << (max_rb_num - 1); + unsigned i, j; + + /* mask out the RBs that don't exist on that asic */ + disabled_rb_mask |= (0xff << max_rb_num) & 0xff; + + rendering_pipe_num = 1 << tiling_pipe_num; + req_rb_num = total_max_rb_num - r600_count_pipe_bits(disabled_rb_mask); + BUG_ON(rendering_pipe_num < req_rb_num); + + pipe_rb_ratio = rendering_pipe_num / req_rb_num; + pipe_rb_remain = rendering_pipe_num - pipe_rb_ratio * req_rb_num; + + if (rdev->family <= CHIP_RV740) { + /* r6xx/r7xx */ + rb_num_width = 2; + } else { + /* eg+ */ + rb_num_width = 4; } - cur_backend = 0; - for (cur_pipe = 0; cur_pipe < num_tile_pipes; ++cur_pipe) { - while (((1 << cur_backend) & enabled_backends_mask) == 0) - cur_backend = (cur_backend + 1) % R6XX_MAX_BACKENDS; - - backend_map |= (u32)(((cur_backend & 3) << (swizzle_pipe[cur_pipe] * 2))); - - cur_backend = (cur_backend + 1) % R6XX_MAX_BACKENDS; + for (i = 0; i < max_rb_num; i++) { + if (!(mask & disabled_rb_mask)) { + for (j = 0; j < pipe_rb_ratio; j++) { + data <<= rb_num_width; + data |= max_rb_num - i - 1; + } + if (pipe_rb_remain) { + data <<= rb_num_width; + data |= max_rb_num - i - 1; + pipe_rb_remain--; + } + } + mask >>= 1; } - return backend_map; + return data; } int r600_count_pipe_bits(uint32_t val) @@ -1500,7 +1438,6 @@ void r600_gpu_init(struct radeon_device *rdev) { u32 tiling_config; u32 ramcfg; - u32 backend_map; u32 cc_rb_backend_disable; u32 cc_gc_shader_pipe_config; u32 tmp; @@ -1511,8 +1448,9 @@ void r600_gpu_init(struct radeon_device *rdev) u32 sq_thread_resource_mgmt = 0; u32 sq_stack_resource_mgmt_1 = 0; u32 sq_stack_resource_mgmt_2 = 0; + u32 disabled_rb_mask; - /* FIXME: implement */ + rdev->config.r600.tiling_group_size = 256; switch (rdev->family) { case CHIP_R600: rdev->config.r600.max_pipes = 4; @@ -1616,10 +1554,7 @@ void r600_gpu_init(struct radeon_device *rdev) rdev->config.r600.tiling_nbanks = 4 << ((ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT); tiling_config |= BANK_TILING((ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT); tiling_config |= GROUP_SIZE((ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT); - if ((ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT) - rdev->config.r600.tiling_group_size = 512; - else - rdev->config.r600.tiling_group_size = 256; + tmp = (ramcfg & NOOFROWS_MASK) >> NOOFROWS_SHIFT; if (tmp > 3) { tiling_config |= ROW_TILING(3); @@ -1631,32 +1566,36 @@ void r600_gpu_init(struct radeon_device *rdev) tiling_config |= BANK_SWAPS(1); cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000; - cc_rb_backend_disable |= - BACKEND_DISABLE((R6XX_MAX_BACKENDS_MASK << rdev->config.r600.max_backends) & R6XX_MAX_BACKENDS_MASK); - - cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffffff00; - cc_gc_shader_pipe_config |= - INACTIVE_QD_PIPES((R6XX_MAX_PIPES_MASK << rdev->config.r600.max_pipes) & R6XX_MAX_PIPES_MASK); - cc_gc_shader_pipe_config |= - INACTIVE_SIMDS((R6XX_MAX_SIMDS_MASK << rdev->config.r600.max_simds) & R6XX_MAX_SIMDS_MASK); - - backend_map = r600_get_tile_pipe_to_backend_map(rdev->config.r600.max_tile_pipes, - (R6XX_MAX_BACKENDS - - r600_count_pipe_bits((cc_rb_backend_disable & - R6XX_MAX_BACKENDS_MASK) >> 16)), - (cc_rb_backend_disable >> 16)); + tmp = R6XX_MAX_BACKENDS - + r600_count_pipe_bits((cc_rb_backend_disable >> 16) & R6XX_MAX_BACKENDS_MASK); + if (tmp < rdev->config.r600.max_backends) { + rdev->config.r600.max_backends = tmp; + } + + cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0x00ffff00; + tmp = R6XX_MAX_PIPES - + r600_count_pipe_bits((cc_gc_shader_pipe_config >> 8) & R6XX_MAX_PIPES_MASK); + if (tmp < rdev->config.r600.max_pipes) { + rdev->config.r600.max_pipes = tmp; + } + tmp = R6XX_MAX_SIMDS - + r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R6XX_MAX_SIMDS_MASK); + if (tmp < rdev->config.r600.max_simds) { + rdev->config.r600.max_simds = tmp; + } + + disabled_rb_mask = (RREG32(CC_RB_BACKEND_DISABLE) >> 16) & R6XX_MAX_BACKENDS_MASK; + tmp = (tiling_config & PIPE_TILING__MASK) >> PIPE_TILING__SHIFT; + tmp = r6xx_remap_render_backend(rdev, tmp, rdev->config.r600.max_backends, + R6XX_MAX_BACKENDS, disabled_rb_mask); + tiling_config |= tmp << 16; + rdev->config.r600.backend_map = tmp; + rdev->config.r600.tile_config = tiling_config; - rdev->config.r600.backend_map = backend_map; - tiling_config |= BACKEND_MAP(backend_map); WREG32(GB_TILING_CONFIG, tiling_config); WREG32(DCP_TILING_CONFIG, tiling_config & 0xffff); WREG32(HDP_TILING_CONFIG, tiling_config & 0xffff); - /* Setup pipes */ - WREG32(CC_RB_BACKEND_DISABLE, cc_rb_backend_disable); - WREG32(CC_GC_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config); - WREG32(GC_USER_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config); - tmp = R6XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config & INACTIVE_QD_PIPES_MASK) >> 8); WREG32(VGT_OUT_DEALLOC_CNTL, (tmp * 4) & DEALLOC_DIST_MASK); WREG32(VGT_VERTEX_REUSE_BLOCK_CNTL, ((tmp * 4) - 2) & VTX_REUSE_DEPTH_MASK); diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index 15bd3b216243..a0dbf1fe6a40 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -219,6 +219,8 @@ #define BACKEND_MAP(x) ((x) << 16) #define GB_TILING_CONFIG 0x98F0 +#define PIPE_TILING__SHIFT 1 +#define PIPE_TILING__MASK 0x0000000e #define GC_USER_SHADER_PIPE_CONFIG 0x8954 #define INACTIVE_QD_PIPES(x) ((x) << 8) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 2e24022b389a..85dac33e3cce 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1848,6 +1848,11 @@ extern struct radeon_hdmi_acr r600_hdmi_acr(uint32_t clock); extern void r600_hdmi_enable(struct drm_encoder *encoder); extern void r600_hdmi_disable(struct drm_encoder *encoder); extern void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode); +extern u32 r6xx_remap_render_backend(struct radeon_device *rdev, + u32 tiling_pipe_num, + u32 max_rb_num, + u32 total_max_rb_num, + u32 enabled_rb_mask); /* * evergreen functions used by radeon_encoder.c diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index 0137689ed461..142f89462aa4 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -147,6 +147,7 @@ static int radeon_cs_sync_rings(struct radeon_cs_parser *p) sync_to_ring, p->ring); } +/* XXX: note that this is called from the legacy UMS CS ioctl as well */ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) { struct drm_radeon_cs *cs = data; @@ -245,22 +246,24 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data) } } - if ((p->cs_flags & RADEON_CS_USE_VM) && - !p->rdev->vm_manager.enabled) { - DRM_ERROR("VM not active on asic!\n"); - return -EINVAL; - } - - /* we only support VM on SI+ */ - if ((p->rdev->family >= CHIP_TAHITI) && - ((p->cs_flags & RADEON_CS_USE_VM) == 0)) { - DRM_ERROR("VM required on SI+!\n"); - return -EINVAL; - } + /* these are KMS only */ + if (p->rdev) { + if ((p->cs_flags & RADEON_CS_USE_VM) && + !p->rdev->vm_manager.enabled) { + DRM_ERROR("VM not active on asic!\n"); + return -EINVAL; + } - if (radeon_cs_get_ring(p, ring, priority)) - return -EINVAL; + /* we only support VM on SI+ */ + if ((p->rdev->family >= CHIP_TAHITI) && + ((p->cs_flags & RADEON_CS_USE_VM) == 0)) { + DRM_ERROR("VM required on SI+!\n"); + return -EINVAL; + } + if (radeon_cs_get_ring(p, ring, priority)) + return -EINVAL; + } /* deal with non-vm */ if ((p->chunk_ib_idx != -1) && diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index c2f473bc13b8..04ddc365a908 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -151,6 +151,8 @@ int rv770_pcie_gart_enable(struct radeon_device *rdev) WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp); WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp); WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp); + if (rdev->family == CHIP_RV740) + WREG32(MC_VM_MD_L1_TLB3_CNTL, tmp); WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp); WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp); WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp); @@ -363,180 +365,6 @@ void r700_cp_fini(struct radeon_device *rdev) /* * Core functions */ -static u32 r700_get_tile_pipe_to_backend_map(struct radeon_device *rdev, - u32 num_tile_pipes, - u32 num_backends, - u32 backend_disable_mask) -{ - u32 backend_map = 0; - u32 enabled_backends_mask; - u32 enabled_backends_count; - u32 cur_pipe; - u32 swizzle_pipe[R7XX_MAX_PIPES]; - u32 cur_backend; - u32 i; - bool force_no_swizzle; - - if (num_tile_pipes > R7XX_MAX_PIPES) - num_tile_pipes = R7XX_MAX_PIPES; - if (num_tile_pipes < 1) - num_tile_pipes = 1; - if (num_backends > R7XX_MAX_BACKENDS) - num_backends = R7XX_MAX_BACKENDS; - if (num_backends < 1) - num_backends = 1; - - enabled_backends_mask = 0; - enabled_backends_count = 0; - for (i = 0; i < R7XX_MAX_BACKENDS; ++i) { - if (((backend_disable_mask >> i) & 1) == 0) { - enabled_backends_mask |= (1 << i); - ++enabled_backends_count; - } - if (enabled_backends_count == num_backends) - break; - } - - if (enabled_backends_count == 0) { - enabled_backends_mask = 1; - enabled_backends_count = 1; - } - - if (enabled_backends_count != num_backends) - num_backends = enabled_backends_count; - - switch (rdev->family) { - case CHIP_RV770: - case CHIP_RV730: - force_no_swizzle = false; - break; - case CHIP_RV710: - case CHIP_RV740: - default: - force_no_swizzle = true; - break; - } - - memset((uint8_t *)&swizzle_pipe[0], 0, sizeof(u32) * R7XX_MAX_PIPES); - switch (num_tile_pipes) { - case 1: - swizzle_pipe[0] = 0; - break; - case 2: - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - break; - case 3: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 1; - } - break; - case 4: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 3; - swizzle_pipe[3] = 1; - } - break; - case 5: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 1; - swizzle_pipe[4] = 3; - } - break; - case 6: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - swizzle_pipe[5] = 5; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 5; - swizzle_pipe[4] = 3; - swizzle_pipe[5] = 1; - } - break; - case 7: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - swizzle_pipe[5] = 5; - swizzle_pipe[6] = 6; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 6; - swizzle_pipe[4] = 3; - swizzle_pipe[5] = 1; - swizzle_pipe[6] = 5; - } - break; - case 8: - if (force_no_swizzle) { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 1; - swizzle_pipe[2] = 2; - swizzle_pipe[3] = 3; - swizzle_pipe[4] = 4; - swizzle_pipe[5] = 5; - swizzle_pipe[6] = 6; - swizzle_pipe[7] = 7; - } else { - swizzle_pipe[0] = 0; - swizzle_pipe[1] = 2; - swizzle_pipe[2] = 4; - swizzle_pipe[3] = 6; - swizzle_pipe[4] = 3; - swizzle_pipe[5] = 1; - swizzle_pipe[6] = 7; - swizzle_pipe[7] = 5; - } - break; - } - - cur_backend = 0; - for (cur_pipe = 0; cur_pipe < num_tile_pipes; ++cur_pipe) { - while (((1 << cur_backend) & enabled_backends_mask) == 0) - cur_backend = (cur_backend + 1) % R7XX_MAX_BACKENDS; - - backend_map |= (u32)(((cur_backend & 3) << (swizzle_pipe[cur_pipe] * 2))); - - cur_backend = (cur_backend + 1) % R7XX_MAX_BACKENDS; - } - - return backend_map; -} - static void rv770_gpu_init(struct radeon_device *rdev) { int i, j, num_qd_pipes; @@ -552,14 +380,17 @@ static void rv770_gpu_init(struct radeon_device *rdev) u32 sq_thread_resource_mgmt; u32 hdp_host_path_cntl; u32 sq_dyn_gpr_size_simd_ab_0; - u32 backend_map; u32 gb_tiling_config = 0; u32 cc_rb_backend_disable = 0; u32 cc_gc_shader_pipe_config = 0; u32 mc_arb_ramcfg; - u32 db_debug4; + u32 db_debug4, tmp; + u32 inactive_pipes, shader_pipe_config; + u32 disabled_rb_mask; + unsigned active_number; /* setup chip specs */ + rdev->config.rv770.tiling_group_size = 256; switch (rdev->family) { case CHIP_RV770: rdev->config.rv770.max_pipes = 4; @@ -670,33 +501,70 @@ static void rv770_gpu_init(struct radeon_device *rdev) /* setup tiling, simd, pipe config */ mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG); + shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG); + inactive_pipes = (shader_pipe_config & INACTIVE_QD_PIPES_MASK) >> INACTIVE_QD_PIPES_SHIFT; + for (i = 0, tmp = 1, active_number = 0; i < R7XX_MAX_PIPES; i++) { + if (!(inactive_pipes & tmp)) { + active_number++; + } + tmp <<= 1; + } + if (active_number == 1) { + WREG32(SPI_CONFIG_CNTL, DISABLE_INTERP_1); + } else { + WREG32(SPI_CONFIG_CNTL, 0); + } + + cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000; + tmp = R7XX_MAX_BACKENDS - r600_count_pipe_bits(cc_rb_backend_disable >> 16); + if (tmp < rdev->config.rv770.max_backends) { + rdev->config.rv770.max_backends = tmp; + } + + cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffffff00; + tmp = R7XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config >> 8) & R7XX_MAX_PIPES_MASK); + if (tmp < rdev->config.rv770.max_pipes) { + rdev->config.rv770.max_pipes = tmp; + } + tmp = R7XX_MAX_SIMDS - r600_count_pipe_bits((cc_gc_shader_pipe_config >> 16) & R7XX_MAX_SIMDS_MASK); + if (tmp < rdev->config.rv770.max_simds) { + rdev->config.rv770.max_simds = tmp; + } + switch (rdev->config.rv770.max_tile_pipes) { case 1: default: - gb_tiling_config |= PIPE_TILING(0); + gb_tiling_config = PIPE_TILING(0); break; case 2: - gb_tiling_config |= PIPE_TILING(1); + gb_tiling_config = PIPE_TILING(1); break; case 4: - gb_tiling_config |= PIPE_TILING(2); + gb_tiling_config = PIPE_TILING(2); break; case 8: - gb_tiling_config |= PIPE_TILING(3); + gb_tiling_config = PIPE_TILING(3); break; } rdev->config.rv770.tiling_npipes = rdev->config.rv770.max_tile_pipes; + disabled_rb_mask = (RREG32(CC_RB_BACKEND_DISABLE) >> 16) & R7XX_MAX_BACKENDS_MASK; + tmp = (gb_tiling_config & PIPE_TILING__MASK) >> PIPE_TILING__SHIFT; + tmp = r6xx_remap_render_backend(rdev, tmp, rdev->config.rv770.max_backends, + R7XX_MAX_BACKENDS, disabled_rb_mask); + gb_tiling_config |= tmp << 16; + rdev->config.rv770.backend_map = tmp; + if (rdev->family == CHIP_RV770) gb_tiling_config |= BANK_TILING(1); - else - gb_tiling_config |= BANK_TILING((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT); + else { + if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) + gb_tiling_config |= BANK_TILING(1); + else + gb_tiling_config |= BANK_TILING(0); + } rdev->config.rv770.tiling_nbanks = 4 << ((gb_tiling_config >> 4) & 0x3); gb_tiling_config |= GROUP_SIZE((mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT); - if ((mc_arb_ramcfg & BURSTLENGTH_MASK) >> BURSTLENGTH_SHIFT) - rdev->config.rv770.tiling_group_size = 512; - else - rdev->config.rv770.tiling_group_size = 256; if (((mc_arb_ramcfg & NOOFROWS_MASK) >> NOOFROWS_SHIFT) > 3) { gb_tiling_config |= ROW_TILING(3); gb_tiling_config |= SAMPLE_SPLIT(3); @@ -708,47 +576,19 @@ static void rv770_gpu_init(struct radeon_device *rdev) } gb_tiling_config |= BANK_SWAPS(1); - - cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000; - cc_rb_backend_disable |= - BACKEND_DISABLE((R7XX_MAX_BACKENDS_MASK << rdev->config.rv770.max_backends) & R7XX_MAX_BACKENDS_MASK); - - cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & 0xffffff00; - cc_gc_shader_pipe_config |= - INACTIVE_QD_PIPES((R7XX_MAX_PIPES_MASK << rdev->config.rv770.max_pipes) & R7XX_MAX_PIPES_MASK); - cc_gc_shader_pipe_config |= - INACTIVE_SIMDS((R7XX_MAX_SIMDS_MASK << rdev->config.rv770.max_simds) & R7XX_MAX_SIMDS_MASK); - - if (rdev->family == CHIP_RV740) - backend_map = 0x28; - else - backend_map = r700_get_tile_pipe_to_backend_map(rdev, - rdev->config.rv770.max_tile_pipes, - (R7XX_MAX_BACKENDS - - r600_count_pipe_bits((cc_rb_backend_disable & - R7XX_MAX_BACKENDS_MASK) >> 16)), - (cc_rb_backend_disable >> 16)); - rdev->config.rv770.tile_config = gb_tiling_config; - rdev->config.rv770.backend_map = backend_map; - gb_tiling_config |= BACKEND_MAP(backend_map); WREG32(GB_TILING_CONFIG, gb_tiling_config); WREG32(DCP_TILING_CONFIG, (gb_tiling_config & 0xffff)); WREG32(HDP_TILING_CONFIG, (gb_tiling_config & 0xffff)); - WREG32(CC_RB_BACKEND_DISABLE, cc_rb_backend_disable); - WREG32(CC_GC_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config); - WREG32(GC_USER_SHADER_PIPE_CONFIG, cc_gc_shader_pipe_config); - WREG32(CC_SYS_RB_BACKEND_DISABLE, cc_rb_backend_disable); - WREG32(CGTS_SYS_TCC_DISABLE, 0); WREG32(CGTS_TCC_DISABLE, 0); WREG32(CGTS_USER_SYS_TCC_DISABLE, 0); WREG32(CGTS_USER_TCC_DISABLE, 0); - num_qd_pipes = - R7XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config & INACTIVE_QD_PIPES_MASK) >> 8); + + num_qd_pipes = R7XX_MAX_PIPES - r600_count_pipe_bits((cc_gc_shader_pipe_config & INACTIVE_QD_PIPES_MASK) >> 8); WREG32(VGT_OUT_DEALLOC_CNTL, (num_qd_pipes * 4) & DEALLOC_DIST_MASK); WREG32(VGT_VERTEX_REUSE_BLOCK_CNTL, ((num_qd_pipes * 4) - 2) & VTX_REUSE_DEPTH_MASK); @@ -809,8 +649,6 @@ static void rv770_gpu_init(struct radeon_device *rdev) WREG32(VGT_NUM_INSTANCES, 1); - WREG32(SPI_CONFIG_CNTL, GPR_WRITE_PRIORITY(0)); - WREG32(SPI_CONFIG_CNTL_1, VTX_DONE_DELAY(4)); WREG32(CP_PERFMON_CNTL, 0); diff --git a/drivers/gpu/drm/radeon/rv770d.h b/drivers/gpu/drm/radeon/rv770d.h index 9c549f702f2f..fdc089896011 100644 --- a/drivers/gpu/drm/radeon/rv770d.h +++ b/drivers/gpu/drm/radeon/rv770d.h @@ -106,10 +106,13 @@ #define BACKEND_MAP(x) ((x) << 16) #define GB_TILING_CONFIG 0x98F0 +#define PIPE_TILING__SHIFT 1 +#define PIPE_TILING__MASK 0x0000000e #define GC_USER_SHADER_PIPE_CONFIG 0x8954 #define INACTIVE_QD_PIPES(x) ((x) << 8) #define INACTIVE_QD_PIPES_MASK 0x0000FF00 +#define INACTIVE_QD_PIPES_SHIFT 8 #define INACTIVE_SIMDS(x) ((x) << 16) #define INACTIVE_SIMDS_MASK 0x00FF0000 @@ -174,6 +177,7 @@ #define MC_VM_MD_L1_TLB0_CNTL 0x2654 #define MC_VM_MD_L1_TLB1_CNTL 0x2658 #define MC_VM_MD_L1_TLB2_CNTL 0x265C +#define MC_VM_MD_L1_TLB3_CNTL 0x2698 #define MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x203C #define MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2038 #define MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2034 diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 36792bd4da77..b67cfcaa661f 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -1834,6 +1834,7 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink) spin_unlock(&glob->lru_lock); (void) ttm_bo_cleanup_refs(bo, false, false, false); kref_put(&bo->list_kref, ttm_bo_release_list); + spin_lock(&glob->lru_lock); continue; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c index 51c9ba5cd2fb..21ee78226560 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c @@ -66,7 +66,7 @@ static int vmw_gmr2_bind(struct vmw_private *dev_priv, cmd += sizeof(remap_cmd) / sizeof(uint32); for (i = 0; i < num_pages; ++i) { - if (VMW_PPN_SIZE > 4) + if (VMW_PPN_SIZE <= 4) *cmd = page_to_pfn(*pages++); else *((uint64_t *)cmd) = page_to_pfn(*pages++); diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 671c8bc14bbc..50e83dc5dc49 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2735,6 +2735,7 @@ static struct regulator_consumer_supply db8500_vape_consumers[] = { REGULATOR_SUPPLY("vcore", "uart2"), REGULATOR_SUPPLY("v-ape", "nmk-ske-keypad.0"), REGULATOR_SUPPLY("v-hsi", "ste_hsi.0"), + REGULATOR_SUPPLY("vddvario", "smsc911x.0"), }; static struct regulator_consumer_supply db8500_vsmps2_consumers[] = { diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 5760c1a4b3f6..27143e042af5 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -128,7 +128,7 @@ config MTD_AFS_PARTS config MTD_OF_PARTS tristate "OpenFirmware partitioning information support" - default Y + default y depends on OF help This provides a partition parsing function which derives diff --git a/drivers/mtd/bcm63xxpart.c b/drivers/mtd/bcm63xxpart.c index 608321ee056e..63d2a64331f7 100644 --- a/drivers/mtd/bcm63xxpart.c +++ b/drivers/mtd/bcm63xxpart.c @@ -4,7 +4,7 @@ * Copyright © 2006-2008 Florian Fainelli <florian@openwrt.org> * Mike Albon <malbon@openwrt.org> * Copyright © 2009-2010 Daniel Dickinson <openwrt@cshore.neomailbox.net> - * Copyright © 2011 Jonas Gorski <jonas.gorski@gmail.com> + * Copyright © 2011-2012 Jonas Gorski <jonas.gorski@gmail.com> * * 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 @@ -82,6 +82,7 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, int namelen = 0; int i; u32 computed_crc; + bool rootfs_first = false; if (bcm63xx_detect_cfe(master)) return -EINVAL; @@ -109,6 +110,7 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, char *boardid = &(buf->board_id[0]); char *tagversion = &(buf->tag_version[0]); + sscanf(buf->flash_image_start, "%u", &rootfsaddr); sscanf(buf->kernel_address, "%u", &kerneladdr); sscanf(buf->kernel_length, "%u", &kernellen); sscanf(buf->total_length, "%u", &totallen); @@ -117,10 +119,19 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, tagversion, boardid); kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE; - rootfsaddr = kerneladdr + kernellen; + rootfsaddr = rootfsaddr - BCM63XX_EXTENDED_SIZE; spareaddr = roundup(totallen, master->erasesize) + cfelen; sparelen = master->size - spareaddr - nvramlen; - rootfslen = spareaddr - rootfsaddr; + + if (rootfsaddr < kerneladdr) { + /* default Broadcom layout */ + rootfslen = kerneladdr - rootfsaddr; + rootfs_first = true; + } else { + /* OpenWrt layout */ + rootfsaddr = kerneladdr + kernellen; + rootfslen = spareaddr - rootfsaddr; + } } else { pr_warn("CFE boot tag CRC invalid (expected %08x, actual %08x)\n", buf->header_crc, computed_crc); @@ -156,18 +167,26 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, curpart++; if (kernellen > 0) { - parts[curpart].name = "kernel"; - parts[curpart].offset = kerneladdr; - parts[curpart].size = kernellen; + int kernelpart = curpart; + + if (rootfslen > 0 && rootfs_first) + kernelpart++; + parts[kernelpart].name = "kernel"; + parts[kernelpart].offset = kerneladdr; + parts[kernelpart].size = kernellen; curpart++; } if (rootfslen > 0) { - parts[curpart].name = "rootfs"; - parts[curpart].offset = rootfsaddr; - parts[curpart].size = rootfslen; - if (sparelen > 0) - parts[curpart].size += sparelen; + int rootfspart = curpart; + + if (kernellen > 0 && rootfs_first) + rootfspart--; + parts[rootfspart].name = "rootfs"; + parts[rootfspart].offset = rootfsaddr; + parts[rootfspart].size = rootfslen; + if (sparelen > 0 && !rootfs_first) + parts[rootfspart].size += sparelen; curpart++; } diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index d02592e6a0f0..22d0493a026f 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -317,7 +317,7 @@ static void fixup_s29gl064n_sectors(struct mtd_info *mtd) if ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0x003f) { cfi->cfiq->EraseRegionInfo[0] |= 0x0040; - pr_warning("%s: Bad S29GL064N CFI data, adjust from 64 to 128 sectors\n", mtd->name); + pr_warning("%s: Bad S29GL064N CFI data; adjust from 64 to 128 sectors\n", mtd->name); } } @@ -328,10 +328,23 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd) if ((cfi->cfiq->EraseRegionInfo[1] & 0xffff) == 0x007e) { cfi->cfiq->EraseRegionInfo[1] &= ~0x0040; - pr_warning("%s: Bad S29GL032N CFI data, adjust from 127 to 63 sectors\n", mtd->name); + pr_warning("%s: Bad S29GL032N CFI data; adjust from 127 to 63 sectors\n", mtd->name); } } +static void fixup_s29ns512p_sectors(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + /* + * S29NS512P flash uses more than 8bits to report number of sectors, + * which is not permitted by CFI. + */ + cfi->cfiq->EraseRegionInfo[0] = 0x020001ff; + pr_warning("%s: Bad S29NS512P CFI data; adjust to 512 sectors\n", mtd->name); +} + /* Used to fix CFI-Tables of chips without Extended Query Tables */ static struct cfi_fixup cfi_nopri_fixup_table[] = { { CFI_MFR_SST, 0x234a, fixup_sst39vf }, /* SST39VF1602 */ @@ -362,6 +375,7 @@ static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors }, { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors }, { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors }, + { CFI_MFR_AMD, 0x3f00, fixup_s29ns512p_sectors }, { CFI_MFR_SST, 0x536a, fixup_sst38vf640x_sectorsize }, /* SST38VF6402 */ { CFI_MFR_SST, 0x536b, fixup_sst38vf640x_sectorsize }, /* SST38VF6401 */ { CFI_MFR_SST, 0x536c, fixup_sst38vf640x_sectorsize }, /* SST38VF6404 */ diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index ddf9ec6d9168..4558e0f4d07f 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -70,7 +70,7 @@ struct cmdline_mtd_partition { /* mtdpart_setup() parses into here */ static struct cmdline_mtd_partition *partitions; -/* the command line passed to mtdpart_setupd() */ +/* the command line passed to mtdpart_setup() */ static char *cmdline; static int cmdline_parsed = 0; diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index a4a80b742e65..681e2ee0f2d6 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -52,8 +52,6 @@ static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len) while (pages) { page = page_read(mapping, index); - if (!page) - return -ENOMEM; if (IS_ERR(page)) return PTR_ERR(page); @@ -112,8 +110,6 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len, len = len - cpylen; page = page_read(dev->blkdev->bd_inode->i_mapping, index); - if (!page) - return -ENOMEM; if (IS_ERR(page)) return PTR_ERR(page); @@ -148,8 +144,6 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf, len = len - cpylen; page = page_read(mapping, index); - if (!page) - return -ENOMEM; if (IS_ERR(page)) return PTR_ERR(page); @@ -271,7 +265,6 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) dev->mtd.flags = MTD_CAP_RAM; dev->mtd._erase = block2mtd_erase; dev->mtd._write = block2mtd_write; - dev->mtd._writev = mtd_writev; dev->mtd._sync = block2mtd_sync; dev->mtd._read = block2mtd_read; dev->mtd.priv = dev; diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 50aa90aa7a7f..f70854d728fe 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -227,7 +227,7 @@ static void doc_read_data_area(struct docg3 *docg3, void *buf, int len, u8 data8, *dst8; doc_dbg("doc_read_data_area(buf=%p, len=%d)\n", buf, len); - cdr = len & 0x3; + cdr = len & 0x1; len4 = len - cdr; if (first) @@ -732,12 +732,24 @@ err: * @len: the number of bytes to be read (must be a multiple of 4) * @buf: the buffer to be filled in (or NULL is forget bytes) * @first: 1 if first time read, DOC_READADDRESS should be set + * @last_odd: 1 if last read ended up on an odd byte + * + * Reads bytes from a prepared page. There is a trickery here : if the last read + * ended up on an odd offset in the 1024 bytes double page, ie. between the 2 + * planes, the first byte must be read apart. If a word (16bit) read was used, + * the read would return the byte of plane 2 as low *and* high endian, which + * will mess the read. * */ static int doc_read_page_getbytes(struct docg3 *docg3, int len, u_char *buf, - int first) + int first, int last_odd) { - doc_read_data_area(docg3, buf, len, first); + if (last_odd && len > 0) { + doc_read_data_area(docg3, buf, 1, first); + doc_read_data_area(docg3, buf ? buf + 1 : buf, len - 1, 0); + } else { + doc_read_data_area(docg3, buf, len, first); + } doc_delay(docg3, 2); return len; } @@ -850,6 +862,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, u8 *buf = ops->datbuf; size_t len, ooblen, nbdata, nboob; u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1; + int max_bitflips = 0; if (buf) len = ops->len; @@ -876,7 +889,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, ret = 0; skip = from % DOC_LAYOUT_PAGE_SIZE; mutex_lock(&docg3->cascade->lock); - while (!ret && (len > 0 || ooblen > 0)) { + while (ret >= 0 && (len > 0 || ooblen > 0)) { calc_block_sector(from - skip, &block0, &block1, &page, &ofs, docg3->reliable); nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip); @@ -887,20 +900,20 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES); if (ret < 0) goto err_in_read; - ret = doc_read_page_getbytes(docg3, skip, NULL, 1); + ret = doc_read_page_getbytes(docg3, skip, NULL, 1, 0); if (ret < skip) goto err_in_read; - ret = doc_read_page_getbytes(docg3, nbdata, buf, 0); + ret = doc_read_page_getbytes(docg3, nbdata, buf, 0, skip % 2); if (ret < nbdata) goto err_in_read; doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE - nbdata - skip, - NULL, 0); - ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0); + NULL, 0, (skip + nbdata) % 2); + ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0, 0); if (ret < nboob) goto err_in_read; doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob, - NULL, 0); + NULL, 0, nboob % 2); doc_get_bch_hw_ecc(docg3, hwecc); eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1); @@ -936,7 +949,8 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, } if (ret > 0) { mtd->ecc_stats.corrected += ret; - ret = -EUCLEAN; + max_bitflips = max(max_bitflips, ret); + ret = max_bitflips; } } @@ -1004,7 +1018,7 @@ static int doc_reload_bbt(struct docg3 *docg3) DOC_LAYOUT_PAGE_SIZE); if (!ret) doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE, - buf, 1); + buf, 1, 0); buf += DOC_LAYOUT_PAGE_SIZE; } doc_read_page_finish(docg3); @@ -1064,10 +1078,10 @@ static int doc_get_erase_count(struct docg3 *docg3, loff_t from) ret = doc_reset_seq(docg3); if (!ret) ret = doc_read_page_prepare(docg3, block0, block1, page, - ofs + DOC_LAYOUT_WEAR_OFFSET); + ofs + DOC_LAYOUT_WEAR_OFFSET, 0); if (!ret) ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_WEAR_SIZE, - buf, 1); + buf, 1, 0); doc_read_page_finish(docg3); if (ret || (buf[0] != DOC_ERASE_MARK) || (buf[2] != DOC_ERASE_MARK)) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 1924d247c1cb..5d0d68c3fe27 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -639,12 +639,16 @@ static const struct spi_device_id m25p_ids[] = { { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, + /* Everspin */ + { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) }, + /* Intel/Numonyx -- xxxs33b */ { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, /* Macronix */ + { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) }, { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, @@ -728,6 +732,7 @@ static const struct spi_device_id m25p_ids[] = { { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, /* Catalyst / On Semiconductor -- non-JEDEC */ { "cat25c11", CAT25_INFO( 16, 8, 16, 1) }, diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index 797d43cd3550..67960362681e 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -990,9 +990,9 @@ static int __devinit spear_smi_probe(struct platform_device *pdev) goto err_clk; } - ret = clk_enable(dev->clk); + ret = clk_prepare_enable(dev->clk); if (ret) - goto err_clk_enable; + goto err_clk_prepare_enable; ret = request_irq(irq, spear_smi_int_handler, 0, pdev->name, dev); if (ret) { @@ -1020,8 +1020,8 @@ err_bank_setup: free_irq(irq, dev); platform_set_drvdata(pdev, NULL); err_irq: - clk_disable(dev->clk); -err_clk_enable: + clk_disable_unprepare(dev->clk); +err_clk_prepare_enable: clk_put(dev->clk); err_clk: iounmap(dev->io_base); @@ -1074,7 +1074,7 @@ static int __devexit spear_smi_remove(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); free_irq(irq, dev); - clk_disable(dev->clk); + clk_disable_unprepare(dev->clk); clk_put(dev->clk); iounmap(dev->io_base); kfree(dev); @@ -1091,7 +1091,7 @@ int spear_smi_suspend(struct platform_device *pdev, pm_message_t state) struct spear_smi *dev = platform_get_drvdata(pdev); if (dev && dev->clk) - clk_disable(dev->clk); + clk_disable_unprepare(dev->clk); return 0; } @@ -1102,7 +1102,7 @@ int spear_smi_resume(struct platform_device *pdev) int ret = -EPERM; if (dev && dev->clk) - ret = clk_enable(dev->clk); + ret = clk_prepare_enable(dev->clk); if (!ret) spear_smi_hw_init(dev); diff --git a/drivers/mtd/lpddr/qinfo_probe.c b/drivers/mtd/lpddr/qinfo_probe.c index dbfe17baf046..45abed67f1ef 100644 --- a/drivers/mtd/lpddr/qinfo_probe.c +++ b/drivers/mtd/lpddr/qinfo_probe.c @@ -57,7 +57,7 @@ static struct qinfo_query_info qinfo_array[] = { static long lpddr_get_qinforec_pos(struct map_info *map, char *id_str) { - int qinfo_lines = sizeof(qinfo_array)/sizeof(struct qinfo_query_info); + int qinfo_lines = ARRAY_SIZE(qinfo_array); int i; int bankwidth = map_bankwidth(map) * 8; int major, minor; diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 8af67cfd671a..5ba2458e799a 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -224,7 +224,7 @@ config MTD_CK804XROM config MTD_SCB2_FLASH tristate "BIOS flash chip on Intel SCB2 boards" - depends on X86 && MTD_JEDECPROBE + depends on X86 && MTD_JEDECPROBE && PCI help Support for treating the BIOS flash chip on Intel SCB2 boards as an MTD device - with this you can reprogram your BIOS. diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c index 92e1f41634c7..93f03175c82d 100644 --- a/drivers/mtd/maps/intel_vr_nor.c +++ b/drivers/mtd/maps/intel_vr_nor.c @@ -260,18 +260,7 @@ static struct pci_driver vr_nor_pci_driver = { .id_table = vr_nor_pci_ids, }; -static int __init vr_nor_mtd_init(void) -{ - return pci_register_driver(&vr_nor_pci_driver); -} - -static void __exit vr_nor_mtd_exit(void) -{ - pci_unregister_driver(&vr_nor_pci_driver); -} - -module_init(vr_nor_mtd_init); -module_exit(vr_nor_mtd_exit); +module_pci_driver(vr_nor_pci_driver); MODULE_AUTHOR("Andy Lowe"); MODULE_DESCRIPTION("MTD map driver for NOR flash on Intel Vermilion Range"); diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index 1d005a3e9b41..f14ce0af763f 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -352,18 +352,7 @@ static struct pci_driver mtd_pci_driver = { .id_table = mtd_pci_ids, }; -static int __init mtd_pci_maps_init(void) -{ - return pci_register_driver(&mtd_pci_driver); -} - -static void __exit mtd_pci_maps_exit(void) -{ - pci_unregister_driver(&mtd_pci_driver); -} - -module_init(mtd_pci_maps_init); -module_exit(mtd_pci_maps_exit); +module_pci_driver(mtd_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index 934a72c80078..9dcbc684abdb 100644 --- a/drivers/mtd/maps/scb2_flash.c +++ b/drivers/mtd/maps/scb2_flash.c @@ -234,20 +234,7 @@ static struct pci_driver scb2_flash_driver = { .remove = __devexit_p(scb2_flash_remove), }; -static int __init -scb2_flash_init(void) -{ - return pci_register_driver(&scb2_flash_driver); -} - -static void __exit -scb2_flash_exit(void) -{ - pci_unregister_driver(&scb2_flash_driver); -} - -module_init(scb2_flash_init); -module_exit(scb2_flash_exit); +module_pci_driver(scb2_flash_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Tim Hockin <thockin@sun.com>"); diff --git a/drivers/mtd/maps/wr_sbc82xx_flash.c b/drivers/mtd/maps/wr_sbc82xx_flash.c index 71b0ba797912..e7534c82f93a 100644 --- a/drivers/mtd/maps/wr_sbc82xx_flash.c +++ b/drivers/mtd/maps/wr_sbc82xx_flash.c @@ -59,7 +59,7 @@ static struct mtd_partition bigflash_parts[] = { } }; -static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL}; +static const char *part_probes[] __initconst = {"cmdlinepart", "RedBoot", NULL}; #define init_sbc82xx_one_flash(map, br, or) \ do { \ diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index c837507dfb1c..575730744fdb 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -250,6 +250,43 @@ static ssize_t mtd_name_show(struct device *dev, } static DEVICE_ATTR(name, S_IRUGO, mtd_name_show, NULL); +static ssize_t mtd_ecc_strength_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", mtd->ecc_strength); +} +static DEVICE_ATTR(ecc_strength, S_IRUGO, mtd_ecc_strength_show, NULL); + +static ssize_t mtd_bitflip_threshold_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", mtd->bitflip_threshold); +} + +static ssize_t mtd_bitflip_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + unsigned int bitflip_threshold; + int retval; + + retval = kstrtouint(buf, 0, &bitflip_threshold); + if (retval) + return retval; + + mtd->bitflip_threshold = bitflip_threshold; + return count; +} +static DEVICE_ATTR(bitflip_threshold, S_IRUGO | S_IWUSR, + mtd_bitflip_threshold_show, + mtd_bitflip_threshold_store); + static struct attribute *mtd_attrs[] = { &dev_attr_type.attr, &dev_attr_flags.attr, @@ -260,6 +297,8 @@ static struct attribute *mtd_attrs[] = { &dev_attr_oobsize.attr, &dev_attr_numeraseregions.attr, &dev_attr_name.attr, + &dev_attr_ecc_strength.attr, + &dev_attr_bitflip_threshold.attr, NULL, }; @@ -322,6 +361,10 @@ int add_mtd_device(struct mtd_info *mtd) mtd->index = i; mtd->usecount = 0; + /* default value if not set by driver */ + if (mtd->bitflip_threshold == 0) + mtd->bitflip_threshold = mtd->ecc_strength; + if (is_power_of_2(mtd->erasesize)) mtd->erasesize_shift = ffs(mtd->erasesize) - 1; else @@ -757,12 +800,24 @@ EXPORT_SYMBOL_GPL(mtd_get_unmapped_area); int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { + int ret_code; *retlen = 0; if (from < 0 || from > mtd->size || len > mtd->size - from) return -EINVAL; if (!len) return 0; - return mtd->_read(mtd, from, len, retlen, buf); + + /* + * In the absence of an error, drivers return a non-negative integer + * representing the maximum number of bitflips that were corrected on + * any one ecc region (if applicable; zero otherwise). + */ + ret_code = mtd->_read(mtd, from, len, retlen, buf); + if (unlikely(ret_code < 0)) + return ret_code; + if (mtd->ecc_strength == 0) + return 0; /* device lacks ecc */ + return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0; } EXPORT_SYMBOL_GPL(mtd_read); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 9651c06de0a9..d518e4db8a0b 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -67,12 +67,12 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len, stats = part->master->ecc_stats; res = part->master->_read(part->master, from + part->offset, len, retlen, buf); - if (unlikely(res)) { - if (mtd_is_bitflip(res)) - mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected; - if (mtd_is_eccerr(res)) - mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed; - } + if (unlikely(mtd_is_eccerr(res))) + mtd->ecc_stats.failed += + part->master->ecc_stats.failed - stats.failed; + else + mtd->ecc_stats.corrected += + part->master->ecc_stats.corrected - stats.corrected; return res; } @@ -517,6 +517,8 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, slave->mtd.ecclayout = master->ecclayout; slave->mtd.ecc_strength = master->ecc_strength; + slave->mtd.bitflip_threshold = master->bitflip_threshold; + if (master->_block_isbad) { uint64_t offs = 0; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 7d17cecad69d..31bb7e5b504a 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -115,6 +115,46 @@ config MTD_NAND_OMAP2 Support for NAND flash on Texas Instruments OMAP2, OMAP3 and OMAP4 platforms. +config MTD_NAND_OMAP_BCH + depends on MTD_NAND && MTD_NAND_OMAP2 && ARCH_OMAP3 + bool "Enable support for hardware BCH error correction" + default n + select BCH + select BCH_CONST_PARAMS + help + Support for hardware BCH error correction. + +choice + prompt "BCH error correction capability" + depends on MTD_NAND_OMAP_BCH + +config MTD_NAND_OMAP_BCH8 + bool "8 bits / 512 bytes (recommended)" + help + Support correcting up to 8 bitflips per 512-byte block. + This will use 13 bytes of spare area per 512 bytes of page data. + This is the recommended mode, as 4-bit mode does not work + on some OMAP3 revisions, due to a hardware bug. + +config MTD_NAND_OMAP_BCH4 + bool "4 bits / 512 bytes" + help + Support correcting up to 4 bitflips per 512-byte block. + This will use 7 bytes of spare area per 512 bytes of page data. + Note that this mode does not work on some OMAP3 revisions, due to a + hardware bug. Please check your OMAP datasheet before selecting this + mode. + +endchoice + +if MTD_NAND_OMAP_BCH +config BCH_CONST_M + default 13 +config BCH_CONST_T + default 4 if MTD_NAND_OMAP_BCH4 + default 8 if MTD_NAND_OMAP_BCH8 +endif + config MTD_NAND_IDS tristate @@ -440,7 +480,7 @@ config MTD_NAND_NANDSIM config MTD_NAND_GPMI_NAND bool "GPMI NAND Flash Controller driver" - depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28) + depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28 || SOC_IMX6Q) help Enables NAND Flash support for IMX23 or IMX28. The GPMI controller is very powerful, with the help of BCH diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c index 4f20e1d8bef1..60a0dfdb0808 100644 --- a/drivers/mtd/nand/alauda.c +++ b/drivers/mtd/nand/alauda.c @@ -414,7 +414,7 @@ static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len, } err = 0; if (corrected) - err = -EUCLEAN; + err = 1; /* return max_bitflips per ecc step */ if (uncorrected) err = -EBADMSG; out: @@ -446,7 +446,7 @@ static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len, } err = 0; if (corrected) - err = -EUCLEAN; + err = 1; /* return max_bitflips per ecc step */ if (uncorrected) err = -EBADMSG; return err; diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 2165576a1c67..97ac6712bb19 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -324,9 +324,10 @@ static int atmel_nand_calculate(struct mtd_info *mtd, * mtd: mtd info structure * chip: nand chip info structure * buf: buffer to store read data + * oob_required: caller expects OOB data read to chip->oob_poi */ -static int atmel_nand_read_page(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int page) +static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -335,6 +336,7 @@ static int atmel_nand_read_page(struct mtd_info *mtd, uint8_t *oob = chip->oob_poi; uint8_t *ecc_pos; int stat; + unsigned int max_bitflips = 0; /* * Errata: ALE is incorrectly wired up to the ECC controller @@ -371,10 +373,12 @@ static int atmel_nand_read_page(struct mtd_info *mtd, /* check if there's an error */ stat = chip->ecc.correct(mtd, p, oob, NULL); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } /* get back to oob start (end of page) */ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); @@ -382,7 +386,7 @@ static int atmel_nand_read_page(struct mtd_info *mtd, /* read the oob */ chip->read_buf(mtd, oob, mtd->oobsize); - return 0; + return max_bitflips; } /* diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 73abbc3e093e..9f609d2dcf62 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -508,8 +508,6 @@ static int __devinit au1550nd_probe(struct platform_device *pdev) this->chip_delay = 30; this->ecc.mode = NAND_ECC_SOFT; - this->options = NAND_NO_AUTOINCR; - if (pd->devwidth) this->options |= NAND_BUSWIDTH_16; diff --git a/drivers/mtd/nand/bcm_umi_bch.c b/drivers/mtd/nand/bcm_umi_bch.c index a930666d0687..5914bb32e001 100644 --- a/drivers/mtd/nand/bcm_umi_bch.c +++ b/drivers/mtd/nand/bcm_umi_bch.c @@ -22,9 +22,9 @@ /* ---- Private Function Prototypes -------------------------------------- */ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int page); + struct nand_chip *chip, uint8_t *buf, int oob_required, int page); static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf); + struct nand_chip *chip, const uint8_t *buf, int oob_required); /* ---- Private Variables ------------------------------------------------ */ @@ -103,11 +103,12 @@ static struct nand_ecclayout nand_hw_eccoob_4096 = { * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data +* @oob_required: caller expects OOB data read to chip->oob_poi * ***************************************************************************/ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf, - int page) + int oob_required, int page) { int sectorIdx = 0; int eccsize = chip->ecc.size; @@ -116,6 +117,7 @@ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, uint8_t eccCalc[NAND_ECC_NUM_BYTES]; int sectorOobSize = mtd->oobsize / eccsteps; int stat; + unsigned int max_bitflips = 0; for (sectorIdx = 0; sectorIdx < eccsteps; sectorIdx++, datap += eccsize) { @@ -177,9 +179,10 @@ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, } #endif mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); } } - return 0; + return max_bitflips; } /**************************************************************************** @@ -188,10 +191,11 @@ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer +* @oob_required: must write chip->oob_poi to OOB * ***************************************************************************/ static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) + struct nand_chip *chip, const uint8_t *buf, int oob_required) { int sectorIdx = 0; int eccsize = chip->ecc.size; diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c index 6908cdde3065..c855e7cd337b 100644 --- a/drivers/mtd/nand/bcm_umi_nand.c +++ b/drivers/mtd/nand/bcm_umi_nand.c @@ -341,7 +341,7 @@ static int bcm_umi_nand_verify_buf(struct mtd_info *mtd, const u_char * buf, * for MLC parts which may have permanently stuck bits. */ struct nand_chip *chip = mtd->priv; - int ret = chip->ecc.read_page(mtd, chip, readbackbuf, 0); + int ret = chip->ecc.read_page(mtd, chip, readbackbuf, 0, 0); if (ret < 0) return -EFAULT; else { @@ -476,12 +476,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev) this->badblock_pattern = &largepage_bbt; } - /* - * FIXME: ecc strength value of 6 bits per 512 bytes of data is a - * conservative guess, given 13 ecc bytes and using bch alg. - * (Assume Galois field order m=15 to allow a margin of error.) - */ - this->ecc.strength = 6; + this->ecc.strength = 8; #endif diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index d7b86b925de5..3f1c18599cbd 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -558,7 +558,7 @@ static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd, } static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { bf5xx_nand_read_buf(mtd, buf, mtd->writesize); bf5xx_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -567,7 +567,7 @@ static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip } static void bf5xx_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { bf5xx_nand_write_buf(mtd, buf, mtd->writesize); bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 2a96e1a12062..41371ba1a811 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -364,25 +364,27 @@ static int cafe_nand_write_oob(struct mtd_info *mtd, /* Don't use -- use nand_read_oob_std for now */ static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return 1; + return 0; } /** * cafe_nand_read_page_syndrome - [REPLACEABLE] hardware ecc syndrome based page read * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller expects OOB data read to chip->oob_poi * * The hw generator calculates the error syndrome automatically. Therefor * we need a special oob layout and handling. */ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { struct cafe_priv *cafe = mtd->priv; + unsigned int max_bitflips = 0; cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", cafe_readl(cafe, NAND_ECC_RESULT), @@ -449,10 +451,11 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, } else { dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n); mtd->ecc_stats.corrected += n; + max_bitflips = max_t(unsigned int, max_bitflips, n); } } - return 0; + return max_bitflips; } static struct nand_ecclayout cafe_oobinfo_2048 = { @@ -518,7 +521,8 @@ static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = { static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) + struct nand_chip *chip, + const uint8_t *buf, int oob_required) { struct cafe_priv *cafe = mtd->priv; @@ -530,16 +534,17 @@ static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, } static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int page, int cached, int raw) + const uint8_t *buf, int oob_required, int page, + int cached, int raw) { int status; chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) - chip->ecc.write_page_raw(mtd, chip, buf); + chip->ecc.write_page_raw(mtd, chip, buf, oob_required); else - chip->ecc.write_page(mtd, chip, buf); + chip->ecc.write_page(mtd, chip, buf, oob_required); /* * Cached progamming disabled for now, Not sure if its worth the @@ -685,7 +690,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, /* Enable the following for a flash based bad block table */ cafe->nand.bbt_options = NAND_BBT_USE_FLASH; - cafe->nand.options = NAND_NO_AUTOINCR | NAND_OWN_BUFFERS; + cafe->nand.options = NAND_OWN_BUFFERS; if (skipbbt) { cafe->nand.options |= NAND_SKIP_BBTSCAN; @@ -888,17 +893,7 @@ static struct pci_driver cafe_nand_pci_driver = { .resume = cafe_nand_resume, }; -static int __init cafe_nand_init(void) -{ - return pci_register_driver(&cafe_nand_pci_driver); -} - -static void __exit cafe_nand_exit(void) -{ - pci_unregister_driver(&cafe_nand_pci_driver); -} -module_init(cafe_nand_init); -module_exit(cafe_nand_exit); +module_pci_driver(cafe_nand_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index 821c34c62500..adb6c3ef37fb 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -240,7 +240,6 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) /* Enable the following for a flash based bad block table */ this->bbt_options = NAND_BBT_USE_FLASH; - this->options = NAND_NO_AUTOINCR; /* Scan to find existence of the device */ if (nand_scan(new_mtd, 1)) { diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index a9e57d686297..0650aafa0dd2 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -924,9 +924,10 @@ bool is_erased(uint8_t *buf, int len) #define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO) static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, - uint32_t irq_status) + uint32_t irq_status, unsigned int *max_bitflips) { bool check_erased_page = false; + unsigned int bitflips = 0; if (irq_status & INTR_STATUS__ECC_ERR) { /* read the ECC errors. we'll ignore them for now */ @@ -965,6 +966,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, /* correct the ECC error */ buf[offset] ^= err_correction_value; denali->mtd.ecc_stats.corrected++; + bitflips++; } } else { /* if the error is not correctable, need to @@ -984,6 +986,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, clear_interrupts(denali); denali_set_intr_modes(denali, true); } + *max_bitflips = bitflips; return check_erased_page; } @@ -1084,7 +1087,7 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip, * by write_page above. * */ static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { /* for regular page writes, we let HW handle all the ECC * data written to the device. */ @@ -1096,7 +1099,7 @@ static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, * write_page() function above. */ static void denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { /* for raw page writes, we want to disable ECC and simply write whatever data is in the buffer. */ @@ -1110,17 +1113,17 @@ static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, } static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { read_oob_data(mtd, chip->oob_poi, page); - return 0; /* notify NAND core to send command to - NAND device. */ + return 0; } static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { + unsigned int max_bitflips; struct denali_nand_info *denali = mtd_to_denali(mtd); dma_addr_t addr = denali->buf.dma_buf; @@ -1153,7 +1156,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, memcpy(buf, denali->buf.buf, mtd->writesize); - check_erased_page = handle_ecc(denali, buf, irq_status); + check_erased_page = handle_ecc(denali, buf, irq_status, &max_bitflips); denali_enable_dma(denali, false); if (check_erased_page) { @@ -1167,11 +1170,11 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, denali->mtd.ecc_stats.failed++; } } - return 0; + return max_bitflips; } static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { struct denali_nand_info *denali = mtd_to_denali(mtd); @@ -1702,17 +1705,4 @@ static struct pci_driver denali_pci_driver = { .remove = denali_pci_remove, }; -static int __devinit denali_init(void) -{ - printk(KERN_INFO "Spectra MTD driver\n"); - return pci_register_driver(&denali_pci_driver); -} - -/* Free memory */ -static void __devexit denali_exit(void) -{ - pci_unregister_driver(&denali_pci_driver); -} - -module_init(denali_init); -module_exit(denali_exit); +module_pci_driver(denali_pci_driver); diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index b08202664543..a225e49a5623 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -720,6 +720,7 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand, struct docg4_priv *doc = nand->priv; void __iomem *docptr = doc->virtadr; uint16_t status, edc_err, *buf16; + int bits_corrected = 0; dev_dbg(doc->dev, "%s: page %08x\n", __func__, page); @@ -772,7 +773,7 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand, /* If bitflips are reported, attempt to correct with ecc */ if (edc_err & DOC_ECCCONF1_BCH_SYNDROM_ERR) { - int bits_corrected = correct_data(mtd, buf, page); + bits_corrected = correct_data(mtd, buf, page); if (bits_corrected == -EBADMSG) mtd->ecc_stats.failed++; else @@ -781,24 +782,24 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand, } writew(0, docptr + DOC_DATAEND); - return 0; + return bits_corrected; } static int docg4_read_page_raw(struct mtd_info *mtd, struct nand_chip *nand, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { return read_page(mtd, nand, buf, page, false); } static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { return read_page(mtd, nand, buf, page, true); } static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand, - int page, int sndcmd) + int page) { struct docg4_priv *doc = nand->priv; void __iomem *docptr = doc->virtadr; @@ -952,13 +953,13 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *nand, } static void docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { return write_page(mtd, nand, buf, false); } static void docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { return write_page(mtd, nand, buf, true); } @@ -1002,7 +1003,7 @@ static int __init read_factory_bbt(struct mtd_info *mtd) return -ENOMEM; read_page_prologue(mtd, g4_addr); - status = docg4_read_page(mtd, nand, buf, DOCG4_FACTORY_BBT_PAGE); + status = docg4_read_page(mtd, nand, buf, 0, DOCG4_FACTORY_BBT_PAGE); if (status) goto exit; @@ -1079,7 +1080,7 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) /* write first page of block */ write_page_prologue(mtd, g4_addr); - docg4_write_page(mtd, nand, buf); + docg4_write_page(mtd, nand, buf, 1); ret = pageprog(mtd); if (!ret) mtd->ecc_stats.badblocks++; @@ -1192,8 +1193,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd) nand->ecc.prepad = 8; nand->ecc.bytes = 8; nand->ecc.strength = DOCG4_T; - nand->options = - NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE | NAND_NO_AUTOINCR; + nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE; nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA; nand->controller = &nand->hwcontrol; spin_lock_init(&nand->controller->lock); diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 80b5264f0a32..784293806110 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -75,6 +75,7 @@ struct fsl_elbc_fcm_ctrl { unsigned int use_mdr; /* Non zero if the MDR is to be set */ unsigned int oob; /* Non zero if operating on OOB data */ unsigned int counter; /* counter for the initializations */ + unsigned int max_bitflips; /* Saved during READ0 cmd */ }; /* These map to the positions used by the FCM hardware ECC generator */ @@ -253,6 +254,8 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) if (chip->ecc.mode != NAND_ECC_HW) return 0; + elbc_fcm_ctrl->max_bitflips = 0; + if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) { uint32_t lteccr = in_be32(&lbc->lteccr); /* @@ -262,11 +265,16 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) * bits 28-31 are uncorrectable errors, marked elsewhere. * for small page nand only 1 bit is used. * if the ELBC doesn't have the lteccr register it reads 0 + * FIXME: 4 bits can be corrected on NANDs with 2k pages, so + * count the number of sub-pages with bitflips and update + * ecc_stats.corrected accordingly. */ if (lteccr & 0x000F000F) out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */ - if (lteccr & 0x000F0000) + if (lteccr & 0x000F0000) { mtd->ecc_stats.corrected++; + elbc_fcm_ctrl->max_bitflips = 1; + } } return 0; @@ -738,26 +746,28 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) return 0; } -static int fsl_elbc_read_page(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, - int page) +static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) { + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_lbc_ctrl *ctrl = priv->ctrl; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; + fsl_elbc_read_buf(mtd, buf, mtd->writesize); - fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + if (oob_required) + fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL) mtd->ecc_stats.failed++; - return 0; + return elbc_fcm_ctrl->max_bitflips; } /* ECC will be calculated automatically, and errors will be detected in * waitfunc. */ -static void fsl_elbc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf) +static void fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { fsl_elbc_write_buf(mtd, buf, mtd->writesize); fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -795,7 +805,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) chip->bbt_md = &bbt_mirror_descr; /* set up nand options */ - chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR; + chip->options = NAND_NO_READRDY; chip->bbt_options = NAND_BBT_USE_FLASH; chip->controller = &elbc_fcm_ctrl->controller; @@ -814,11 +824,6 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) chip->ecc.size = 512; chip->ecc.bytes = 3; chip->ecc.strength = 1; - /* - * FIXME: can hardware ecc correct 4 bitflips if page size is - * 2k? Then does hardware report number of corrections for this - * case? If so, ecc_stats reporting needs to be fixed as well. - */ } else { /* otherwise fall back to default software ECC */ chip->ecc.mode = NAND_ECC_SOFT; diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index c30ac7b83d28..9602c1b7e27e 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -63,6 +63,7 @@ struct fsl_ifc_nand_ctrl { unsigned int oob; /* Non zero if operating on OOB data */ unsigned int eccread; /* Non zero for a full-page ECC read */ unsigned int counter; /* counter for the initializations */ + unsigned int max_bitflips; /* Saved during READ0 cmd */ }; static struct fsl_ifc_nand_ctrl *ifc_nand_ctrl; @@ -262,6 +263,8 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_WPER) dev_err(priv->dev, "NAND Flash Write Protect Error\n"); + nctrl->max_bitflips = 0; + if (nctrl->eccread) { int errors; int bufnum = nctrl->page & priv->bufnum_mask; @@ -290,6 +293,9 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) } mtd->ecc_stats.corrected += errors; + nctrl->max_bitflips = max_t(unsigned int, + nctrl->max_bitflips, + errors); } nctrl->eccread = 0; @@ -375,21 +381,31 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, return; - /* READID must read all 8 possible bytes */ case NAND_CMD_READID: + case NAND_CMD_PARAM: { + int timing = IFC_FIR_OP_RB; + if (command == NAND_CMD_PARAM) + timing = IFC_FIR_OP_RBCD; + out_be32(&ifc->ifc_nand.nand_fir0, (IFC_FIR_OP_CMD0 << IFC_NAND_FIR0_OP0_SHIFT) | (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT)); + (timing << IFC_NAND_FIR0_OP2_SHIFT)); out_be32(&ifc->ifc_nand.nand_fcr0, - NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT); - /* 8 bytes for manuf, device and exts */ - out_be32(&ifc->ifc_nand.nand_fbcr, 8); - ifc_nand_ctrl->read_bytes = 8; + command << IFC_NAND_FCR0_CMD0_SHIFT); + out_be32(&ifc->ifc_nand.row3, column); + + /* + * although currently it's 8 bytes for READID, we always read + * the maximum 256 bytes(for PARAM) + */ + out_be32(&ifc->ifc_nand.nand_fbcr, 256); + ifc_nand_ctrl->read_bytes = 256; set_addr(mtd, 0, 0, 0); fsl_ifc_run_command(mtd); return; + } /* ERASE1 stores the block and page address */ case NAND_CMD_ERASE1: @@ -682,15 +698,16 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) return nand_fsr | NAND_STATUS_WP; } -static int fsl_ifc_read_page(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, int page) +static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) { struct fsl_ifc_mtd *priv = chip->priv; struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; fsl_ifc_read_buf(mtd, buf, mtd->writesize); - fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + if (oob_required) + fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_ECCER) dev_err(priv->dev, "NAND Flash ECC Uncorrectable Error\n"); @@ -698,15 +715,14 @@ static int fsl_ifc_read_page(struct mtd_info *mtd, if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) mtd->ecc_stats.failed++; - return 0; + return nctrl->max_bitflips; } /* ECC will be calculated automatically, and errors will be detected in * waitfunc. */ -static void fsl_ifc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf) +static void fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { fsl_ifc_write_buf(mtd, buf, mtd->writesize); fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -789,7 +805,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) out_be32(&ifc->ifc_nand.ncfgr, 0x0); /* set up nand options */ - chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR; + chip->options = NAND_NO_READRDY; chip->bbt_options = NAND_BBT_USE_FLASH; @@ -811,6 +827,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) /* Hardware generates ECC per 512 Bytes */ chip->ecc.size = 512; chip->ecc.bytes = 8; + chip->ecc.strength = 4; switch (csor & CSOR_NAND_PGS_MASK) { case CSOR_NAND_PGS_512: diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 1b8330e1155a..38d26240d8b1 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -692,6 +692,7 @@ static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller expects OOB data read to chip->oob_poi * @page: page number to read * * This routine is needed for fsmc version 8 as reading from NAND chip has to be @@ -701,7 +702,7 @@ static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf, * max of 8 bits) */ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); @@ -720,6 +721,7 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, */ uint16_t ecc_oob[7]; uint8_t *oob = (uint8_t *)&ecc_oob[0]; + unsigned int max_bitflips = 0; for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page); @@ -748,13 +750,15 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, chip->ecc.calculate(mtd, p, &ecc_calc[i]); stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /* @@ -994,9 +998,9 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) return PTR_ERR(host->clk); } - ret = clk_enable(host->clk); + ret = clk_prepare_enable(host->clk); if (ret) - goto err_clk_enable; + goto err_clk_prepare_enable; /* * This device ID is actually a common AMBA ID as used on the @@ -1176,8 +1180,8 @@ err_req_write_chnl: if (host->mode == USE_DMA_ACCESS) dma_release_channel(host->read_dma_chan); err_req_read_chnl: - clk_disable(host->clk); -err_clk_enable: + clk_disable_unprepare(host->clk); +err_clk_prepare_enable: clk_put(host->clk); return ret; } @@ -1198,7 +1202,7 @@ static int fsmc_nand_remove(struct platform_device *pdev) dma_release_channel(host->write_dma_chan); dma_release_channel(host->read_dma_chan); } - clk_disable(host->clk); + clk_disable_unprepare(host->clk); clk_put(host->clk); } @@ -1210,7 +1214,7 @@ static int fsmc_nand_suspend(struct device *dev) { struct fsmc_nand_data *host = dev_get_drvdata(dev); if (host) - clk_disable(host->clk); + clk_disable_unprepare(host->clk); return 0; } @@ -1218,7 +1222,7 @@ static int fsmc_nand_resume(struct device *dev) { struct fsmc_nand_data *host = dev_get_drvdata(dev); if (host) { - clk_enable(host->clk); + clk_prepare_enable(host->clk); fsmc_nand_setup(host->regs_va, host->bank, host->nand.options & NAND_BUSWIDTH_16, host->dev_timings); diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h b/drivers/mtd/nand/gpmi-nand/bch-regs.h index 4effb8c579db..a0924515c396 100644 --- a/drivers/mtd/nand/gpmi-nand/bch-regs.h +++ b/drivers/mtd/nand/gpmi-nand/bch-regs.h @@ -51,15 +51,26 @@ #define BP_BCH_FLASH0LAYOUT0_ECC0 12 #define BM_BCH_FLASH0LAYOUT0_ECC0 (0xf << BP_BCH_FLASH0LAYOUT0_ECC0) -#define BF_BCH_FLASH0LAYOUT0_ECC0(v) \ - (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) & BM_BCH_FLASH0LAYOUT0_ECC0) +#define MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0 11 +#define MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0 (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) +#define BF_BCH_FLASH0LAYOUT0_ECC0(v, x) \ + (GPMI_IS_MX6Q(x) \ + ? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) \ + & MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0) \ + : (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) \ + & BM_BCH_FLASH0LAYOUT0_ECC0) \ + ) #define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 #define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ (0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) -#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v) \ - (((v) << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)\ - & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) +#define MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ + (0x3ff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) +#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v, x) \ + (GPMI_IS_MX6Q(x) \ + ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ + : ((v) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ + ) #define HW_BCH_FLASH0LAYOUT1 0x00000090 @@ -72,13 +83,24 @@ #define BP_BCH_FLASH0LAYOUT1_ECCN 12 #define BM_BCH_FLASH0LAYOUT1_ECCN (0xf << BP_BCH_FLASH0LAYOUT1_ECCN) -#define BF_BCH_FLASH0LAYOUT1_ECCN(v) \ - (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) & BM_BCH_FLASH0LAYOUT1_ECCN) +#define MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN 11 +#define MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) +#define BF_BCH_FLASH0LAYOUT1_ECCN(v, x) \ + (GPMI_IS_MX6Q(x) \ + ? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) \ + & MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN) \ + : (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) \ + & BM_BCH_FLASH0LAYOUT1_ECCN) \ + ) #define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 #define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ (0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) -#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v) \ - (((v) << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ - & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) +#define MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ + (0x3ff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) +#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v, x) \ + (GPMI_IS_MX6Q(x) \ + ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ + : ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ + ) #endif diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index e8ea7107932e..a1f43329ad43 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -21,7 +21,6 @@ #include <linux/mtd/gpmi-nand.h> #include <linux/delay.h> #include <linux/clk.h> -#include <mach/mxs.h> #include "gpmi-nand.h" #include "gpmi-regs.h" @@ -37,6 +36,8 @@ struct timing_threshod timing_default_threshold = { .max_dll_delay_in_ns = 16, }; +#define MXS_SET_ADDR 0x4 +#define MXS_CLR_ADDR 0x8 /* * Clear the bit and poll it cleared. This is usually called with * a reset address and mask being either SFTRST(bit 31) or CLKGATE @@ -47,7 +48,7 @@ static int clear_poll_bit(void __iomem *addr, u32 mask) int timeout = 0x400; /* clear the bit */ - __mxs_clrl(mask, addr); + writel(mask, addr + MXS_CLR_ADDR); /* * SFTRST needs 3 GPMI clocks to settle, the reference manual @@ -92,11 +93,11 @@ static int gpmi_reset_block(void __iomem *reset_addr, bool just_enable) goto error; /* clear CLKGATE */ - __mxs_clrl(MODULE_CLKGATE, reset_addr); + writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR); if (!just_enable) { /* set SFTRST to reset the block */ - __mxs_setl(MODULE_SFTRST, reset_addr); + writel(MODULE_SFTRST, reset_addr + MXS_SET_ADDR); udelay(1); /* poll CLKGATE becoming set */ @@ -223,13 +224,13 @@ int bch_set_geometry(struct gpmi_nand_data *this) /* Configure layout 0. */ writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) - | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength) - | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size), + | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) + | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this), r->bch_regs + HW_BCH_FLASH0LAYOUT0); writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) - | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength) - | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size), + | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) + | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this), r->bch_regs + HW_BCH_FLASH0LAYOUT1); /* Set *all* chip selects to use layout 0. */ @@ -255,11 +256,12 @@ static unsigned int ns_to_cycles(unsigned int time, return max(k, min); } +#define DEF_MIN_PROP_DELAY 5 +#define DEF_MAX_PROP_DELAY 9 /* Apply timing to current hardware conditions. */ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, struct gpmi_nfc_hardware_timing *hw) { - struct gpmi_nand_platform_data *pdata = this->pdata; struct timing_threshod *nfc = &timing_default_threshold; struct nand_chip *nand = &this->nand; struct nand_timing target = this->timing; @@ -276,8 +278,8 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, int ideal_sample_delay_in_ns; unsigned int sample_delay_factor; int tEYE; - unsigned int min_prop_delay_in_ns = pdata->min_prop_delay_in_ns; - unsigned int max_prop_delay_in_ns = pdata->max_prop_delay_in_ns; + unsigned int min_prop_delay_in_ns = DEF_MIN_PROP_DELAY; + unsigned int max_prop_delay_in_ns = DEF_MAX_PROP_DELAY; /* * If there are multiple chips, we need to relax the timings to allow @@ -803,7 +805,8 @@ int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip) if (GPMI_IS_MX23(this)) { mask = MX23_BM_GPMI_DEBUG_READY0 << chip; reg = readl(r->gpmi_regs + HW_GPMI_DEBUG); - } else if (GPMI_IS_MX28(this)) { + } else if (GPMI_IS_MX28(this) || GPMI_IS_MX6Q(this)) { + /* MX28 shares the same R/B register as MX6Q. */ mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip); reg = readl(r->gpmi_regs + HW_GPMI_STAT); } else diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index b68e04310bd8..a05b7b444d4f 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -25,6 +25,8 @@ #include <linux/mtd/gpmi-nand.h> #include <linux/mtd/partitions.h> #include <linux/pinctrl/consumer.h> +#include <linux/of.h> +#include <linux/of_device.h> #include "gpmi-nand.h" /* add our owner bbt descriptor */ @@ -387,7 +389,7 @@ static void release_bch_irq(struct gpmi_nand_data *this) static bool gpmi_dma_filter(struct dma_chan *chan, void *param) { struct gpmi_nand_data *this = param; - struct resource *r = this->private; + int dma_channel = (int)this->private; if (!mxs_dma_is_apbh(chan)) return false; @@ -399,7 +401,7 @@ static bool gpmi_dma_filter(struct dma_chan *chan, void *param) * for mx28 : MX28_DMA_GPMI0 ~ MX28_DMA_GPMI7 * (These eight channels share the same IRQ!) */ - if (r->start <= chan->chan_id && chan->chan_id <= r->end) { + if (dma_channel == chan->chan_id) { chan->private = &this->dma_data; return true; } @@ -419,57 +421,45 @@ static void release_dma_channels(struct gpmi_nand_data *this) static int __devinit acquire_dma_channels(struct gpmi_nand_data *this) { struct platform_device *pdev = this->pdev; - struct gpmi_nand_platform_data *pdata = this->pdata; - struct resources *res = &this->resources; - struct resource *r, *r_dma; - unsigned int i; + struct resource *r_dma; + struct device_node *dn; + int dma_channel; + unsigned int ret; + struct dma_chan *dma_chan; + dma_cap_mask_t mask; + + /* dma channel, we only use the first one. */ + dn = pdev->dev.of_node; + ret = of_property_read_u32(dn, "fsl,gpmi-dma-channel", &dma_channel); + if (ret) { + pr_err("unable to get DMA channel from dt.\n"); + goto acquire_err; + } + this->private = (void *)dma_channel; - r = platform_get_resource_byname(pdev, IORESOURCE_DMA, - GPMI_NAND_DMA_CHANNELS_RES_NAME); + /* gpmi dma interrupt */ r_dma = platform_get_resource_byname(pdev, IORESOURCE_IRQ, GPMI_NAND_DMA_INTERRUPT_RES_NAME); - if (!r || !r_dma) { + if (!r_dma) { pr_err("Can't get resource for DMA\n"); - return -ENXIO; + goto acquire_err; } + this->dma_data.chan_irq = r_dma->start; - /* used in gpmi_dma_filter() */ - this->private = r; - - for (i = r->start; i <= r->end; i++) { - struct dma_chan *dma_chan; - dma_cap_mask_t mask; + /* request dma channel */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); - if (i - r->start >= pdata->max_chip_count) - break; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - /* get the DMA interrupt */ - if (r_dma->start == r_dma->end) { - /* only register the first. */ - if (i == r->start) - this->dma_data.chan_irq = r_dma->start; - else - this->dma_data.chan_irq = NO_IRQ; - } else - this->dma_data.chan_irq = r_dma->start + (i - r->start); - - dma_chan = dma_request_channel(mask, gpmi_dma_filter, this); - if (!dma_chan) - goto acquire_err; - - /* fill the first empty item */ - this->dma_chans[i - r->start] = dma_chan; + dma_chan = dma_request_channel(mask, gpmi_dma_filter, this); + if (!dma_chan) { + pr_err("dma_request_channel failed.\n"); + goto acquire_err; } - res->dma_low_channel = r->start; - res->dma_high_channel = i; + this->dma_chans[0] = dma_chan; return 0; acquire_err: - pr_err("Can't acquire DMA channel %u\n", i); release_dma_channels(this); return -EINVAL; } @@ -851,7 +841,7 @@ static void block_mark_swapping(struct gpmi_nand_data *this, } static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { struct gpmi_nand_data *this = chip->priv; struct bch_geometry *nfc_geo = &this->bch_geometry; @@ -917,28 +907,31 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, mtd->ecc_stats.corrected += corrected; } - /* - * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() for - * details about our policy for delivering the OOB. - * - * We fill the caller's buffer with set bits, and then copy the block - * mark to th caller's buffer. Note that, if block mark swapping was - * necessary, it has already been done, so we can rely on the first - * byte of the auxiliary buffer to contain the block mark. - */ - memset(chip->oob_poi, ~0, mtd->oobsize); - chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; + if (oob_required) { + /* + * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() + * for details about our policy for delivering the OOB. + * + * We fill the caller's buffer with set bits, and then copy the + * block mark to th caller's buffer. Note that, if block mark + * swapping was necessary, it has already been done, so we can + * rely on the first byte of the auxiliary buffer to contain + * the block mark. + */ + memset(chip->oob_poi, ~0, mtd->oobsize); + chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; - read_page_swap_end(this, buf, mtd->writesize, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - payload_virt, payload_phys); + read_page_swap_end(this, buf, mtd->writesize, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size, + payload_virt, payload_phys); + } exit_nfc: return ret; } -static void gpmi_ecc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) +static void gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { struct gpmi_nand_data *this = chip->priv; struct bch_geometry *nfc_geo = &this->bch_geometry; @@ -1077,7 +1070,7 @@ exit_auxiliary: * this driver. */ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { struct gpmi_nand_data *this = chip->priv; @@ -1100,11 +1093,7 @@ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, chip->oob_poi[0] = chip->read_byte(mtd); } - /* - * Return true, indicating that the next call to this function must send - * a command. - */ - return true; + return 0; } static int @@ -1318,7 +1307,7 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) /* Write the first page of the current stride. */ dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page); chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - chip->ecc.write_page_raw(mtd, chip, buffer); + chip->ecc.write_page_raw(mtd, chip, buffer, 0); chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); /* Wait for the write to finish. */ @@ -1444,6 +1433,10 @@ static int gpmi_pre_bbt_scan(struct gpmi_nand_data *this) if (ret) return ret; + /* Adjust the ECC strength according to the chip. */ + this->nand.ecc.strength = this->bch_geometry.ecc_strength; + this->mtd.ecc_strength = this->bch_geometry.ecc_strength; + /* NAND boot init, depends on the gpmi_set_geometry(). */ return nand_boot_init(this); } @@ -1471,9 +1464,9 @@ void gpmi_nfc_exit(struct gpmi_nand_data *this) static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this) { - struct gpmi_nand_platform_data *pdata = this->pdata; struct mtd_info *mtd = &this->mtd; struct nand_chip *chip = &this->nand; + struct mtd_part_parser_data ppdata = {}; int ret; /* init current chip */ @@ -1502,6 +1495,7 @@ static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this) chip->options |= NAND_NO_SUBPAGE_WRITE; chip->ecc.mode = NAND_ECC_HW; chip->ecc.size = 1; + chip->ecc.strength = 8; chip->ecc.layout = &gpmi_hw_ecclayout; /* Allocate a temporary DMA buffer for reading ID in the nand_scan() */ @@ -1511,14 +1505,14 @@ static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this) if (ret) goto err_out; - ret = nand_scan(mtd, pdata->max_chip_count); + ret = nand_scan(mtd, 1); if (ret) { pr_err("Chip scan failed\n"); goto err_out; } - ret = mtd_device_parse_register(mtd, NULL, NULL, - pdata->partitions, pdata->partition_count); + ppdata.of_node = this->pdev->dev.of_node; + ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); if (ret) goto err_out; return 0; @@ -1528,12 +1522,41 @@ err_out: return ret; } +static const struct platform_device_id gpmi_ids[] = { + { .name = "imx23-gpmi-nand", .driver_data = IS_MX23, }, + { .name = "imx28-gpmi-nand", .driver_data = IS_MX28, }, + { .name = "imx6q-gpmi-nand", .driver_data = IS_MX6Q, }, + {}, +}; + +static const struct of_device_id gpmi_nand_id_table[] = { + { + .compatible = "fsl,imx23-gpmi-nand", + .data = (void *)&gpmi_ids[IS_MX23] + }, { + .compatible = "fsl,imx28-gpmi-nand", + .data = (void *)&gpmi_ids[IS_MX28] + }, { + .compatible = "fsl,imx6q-gpmi-nand", + .data = (void *)&gpmi_ids[IS_MX6Q] + }, {} +}; +MODULE_DEVICE_TABLE(of, gpmi_nand_id_table); + static int __devinit gpmi_nand_probe(struct platform_device *pdev) { - struct gpmi_nand_platform_data *pdata = pdev->dev.platform_data; struct gpmi_nand_data *this; + const struct of_device_id *of_id; int ret; + of_id = of_match_device(gpmi_nand_id_table, &pdev->dev); + if (of_id) { + pdev->id_entry = of_id->data; + } else { + pr_err("Failed to find the right device id.\n"); + return -ENOMEM; + } + this = kzalloc(sizeof(*this), GFP_KERNEL); if (!this) { pr_err("Failed to allocate per-device memory\n"); @@ -1543,13 +1566,6 @@ static int __devinit gpmi_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, this); this->pdev = pdev; this->dev = &pdev->dev; - this->pdata = pdata; - - if (pdata->platform_init) { - ret = pdata->platform_init(); - if (ret) - goto platform_init_error; - } ret = acquire_resources(this); if (ret) @@ -1567,7 +1583,6 @@ static int __devinit gpmi_nand_probe(struct platform_device *pdev) exit_nfc_init: release_resources(this); -platform_init_error: exit_acquire_resources: platform_set_drvdata(pdev, NULL); kfree(this); @@ -1585,19 +1600,10 @@ static int __exit gpmi_nand_remove(struct platform_device *pdev) return 0; } -static const struct platform_device_id gpmi_ids[] = { - { - .name = "imx23-gpmi-nand", - .driver_data = IS_MX23, - }, { - .name = "imx28-gpmi-nand", - .driver_data = IS_MX28, - }, {}, -}; - static struct platform_driver gpmi_nand_driver = { .driver = { .name = "gpmi-nand", + .of_match_table = gpmi_nand_id_table, }, .probe = gpmi_nand_probe, .remove = __exit_p(gpmi_nand_remove), diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h index ec6180d4ff8f..ce5daa160920 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h @@ -266,8 +266,10 @@ extern int gpmi_read_page(struct gpmi_nand_data *, #define STATUS_UNCORRECTABLE 0xfe /* Use the platform_id to distinguish different Archs. */ -#define IS_MX23 0x1 -#define IS_MX28 0x2 +#define IS_MX23 0x0 +#define IS_MX28 0x1 +#define IS_MX6Q 0x2 #define GPMI_IS_MX23(x) ((x)->pdev->id_entry->driver_data == IS_MX23) #define GPMI_IS_MX28(x) ((x)->pdev->id_entry->driver_data == IS_MX28) +#define GPMI_IS_MX6Q(x) ((x)->pdev->id_entry->driver_data == IS_MX6Q) #endif diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c index 9bf5ce5fa22d..50166e93ba96 100644 --- a/drivers/mtd/nand/h1910.c +++ b/drivers/mtd/nand/h1910.c @@ -124,7 +124,6 @@ static int __init h1910_init(void) /* 15 us command delay time */ this->chip_delay = 50; this->ecc.mode = NAND_ECC_SOFT; - this->options = NAND_NO_AUTOINCR; /* Scan to find existence of the device */ if (nand_scan(h1910_nand_mtd, 1)) { diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index e4147e8acb7c..a6fa884ae49b 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -332,11 +332,7 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; chip->ecc.size = 512; chip->ecc.bytes = 9; - chip->ecc.strength = 2; - /* - * FIXME: ecc_strength value of 2 bits per 512 bytes of data is a - * conservative guess, given 9 ecc bytes and reed-solomon alg. - */ + chip->ecc.strength = 4; if (pdata) chip->ecc.layout = pdata->ecc_layout; diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index c240cf1af961..c259c24d7986 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -734,7 +734,6 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op) chip->write_buf = mpc5121_nfc_write_buf; chip->verify_buf = mpc5121_nfc_verify_buf; chip->select_chip = mpc5121_nfc_select_chip; - chip->options = NAND_NO_AUTOINCR; chip->bbt_options = NAND_BBT_USE_FLASH; chip->ecc.mode = NAND_ECC_SOFT; diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 9e374e9bd296..c58e6a93f445 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -32,6 +32,8 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/completion.h> +#include <linux/of_device.h> +#include <linux/of_mtd.h> #include <asm/mach/flash.h> #include <mach/mxc_nand.h> @@ -140,13 +142,47 @@ #define NFC_V3_DELAY_LINE (host->regs_ip + 0x34) +struct mxc_nand_host; + +struct mxc_nand_devtype_data { + void (*preset)(struct mtd_info *); + void (*send_cmd)(struct mxc_nand_host *, uint16_t, int); + void (*send_addr)(struct mxc_nand_host *, uint16_t, int); + void (*send_page)(struct mtd_info *, unsigned int); + void (*send_read_id)(struct mxc_nand_host *); + uint16_t (*get_dev_status)(struct mxc_nand_host *); + int (*check_int)(struct mxc_nand_host *); + void (*irq_control)(struct mxc_nand_host *, int); + u32 (*get_ecc_status)(struct mxc_nand_host *); + struct nand_ecclayout *ecclayout_512, *ecclayout_2k, *ecclayout_4k; + void (*select_chip)(struct mtd_info *mtd, int chip); + int (*correct_data)(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc); + + /* + * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked + * (CONFIG1:INT_MSK is set). To handle this the driver uses + * enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK + */ + int irqpending_quirk; + int needs_ip; + + size_t regs_offset; + size_t spare0_offset; + size_t axi_offset; + + int spare_len; + int eccbytes; + int eccsize; +}; + struct mxc_nand_host { struct mtd_info mtd; struct nand_chip nand; struct device *dev; - void *spare0; - void *main_area0; + void __iomem *spare0; + void __iomem *main_area0; void __iomem *base; void __iomem *regs; @@ -163,16 +199,9 @@ struct mxc_nand_host { uint8_t *data_buf; unsigned int buf_start; - int spare_len; - - void (*preset)(struct mtd_info *); - void (*send_cmd)(struct mxc_nand_host *, uint16_t, int); - void (*send_addr)(struct mxc_nand_host *, uint16_t, int); - void (*send_page)(struct mtd_info *, unsigned int); - void (*send_read_id)(struct mxc_nand_host *); - uint16_t (*get_dev_status)(struct mxc_nand_host *); - int (*check_int)(struct mxc_nand_host *); - void (*irq_control)(struct mxc_nand_host *, int); + + const struct mxc_nand_devtype_data *devtype_data; + struct mxc_nand_platform_data pdata; }; /* OOB placement block for use with hardware ecc generation */ @@ -242,21 +271,7 @@ static struct nand_ecclayout nandv2_hw_eccoob_4k = { } }; -static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL }; - -static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) -{ - struct mxc_nand_host *host = dev_id; - - if (!host->check_int(host)) - return IRQ_NONE; - - host->irq_control(host, 0); - - complete(&host->op_completion); - - return IRQ_HANDLED; -} +static const char *part_probes[] = { "RedBoot", "cmdlinepart", "ofpart", NULL }; static int check_int_v3(struct mxc_nand_host *host) { @@ -280,26 +295,12 @@ static int check_int_v1_v2(struct mxc_nand_host *host) if (!(tmp & NFC_V1_V2_CONFIG2_INT)) return 0; - if (!cpu_is_mx21()) + if (!host->devtype_data->irqpending_quirk) writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); return 1; } -/* - * It has been observed that the i.MX21 cannot read the CONFIG2:INT bit - * if interrupts are masked (CONFIG1:INT_MSK is set). To handle this, the - * driver can enable/disable the irq line rather than simply masking the - * interrupts. - */ -static void irq_control_mx21(struct mxc_nand_host *host, int activate) -{ - if (activate) - enable_irq(host->irq); - else - disable_irq_nosync(host->irq); -} - static void irq_control_v1_v2(struct mxc_nand_host *host, int activate) { uint16_t tmp; @@ -328,6 +329,47 @@ static void irq_control_v3(struct mxc_nand_host *host, int activate) writel(tmp, NFC_V3_CONFIG2); } +static void irq_control(struct mxc_nand_host *host, int activate) +{ + if (host->devtype_data->irqpending_quirk) { + if (activate) + enable_irq(host->irq); + else + disable_irq_nosync(host->irq); + } else { + host->devtype_data->irq_control(host, activate); + } +} + +static u32 get_ecc_status_v1(struct mxc_nand_host *host) +{ + return readw(NFC_V1_V2_ECC_STATUS_RESULT); +} + +static u32 get_ecc_status_v2(struct mxc_nand_host *host) +{ + return readl(NFC_V1_V2_ECC_STATUS_RESULT); +} + +static u32 get_ecc_status_v3(struct mxc_nand_host *host) +{ + return readl(NFC_V3_ECC_STATUS_RESULT); +} + +static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) +{ + struct mxc_nand_host *host = dev_id; + + if (!host->devtype_data->check_int(host)) + return IRQ_NONE; + + irq_control(host, 0); + + complete(&host->op_completion); + + return IRQ_HANDLED; +} + /* This function polls the NANDFC to wait for the basic operation to * complete by checking the INT bit of config2 register. */ @@ -336,14 +378,14 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq) int max_retries = 8000; if (useirq) { - if (!host->check_int(host)) { + if (!host->devtype_data->check_int(host)) { INIT_COMPLETION(host->op_completion); - host->irq_control(host, 1); + irq_control(host, 1); wait_for_completion(&host->op_completion); } } else { while (max_retries-- > 0) { - if (host->check_int(host)) + if (host->devtype_data->check_int(host)) break; udelay(1); @@ -374,7 +416,7 @@ static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq) writew(cmd, NFC_V1_V2_FLASH_CMD); writew(NFC_CMD, NFC_V1_V2_CONFIG2); - if (cpu_is_mx21() && (cmd == NAND_CMD_RESET)) { + if (host->devtype_data->irqpending_quirk && (cmd == NAND_CMD_RESET)) { int max_retries = 100; /* Reset completion is indicated by NFC_CONFIG2 */ /* being set to 0 */ @@ -433,13 +475,27 @@ static void send_page_v3(struct mtd_info *mtd, unsigned int ops) wait_op_done(host, false); } -static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops) +static void send_page_v2(struct mtd_info *mtd, unsigned int ops) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + /* NANDFC buffer 0 is used for page read/write */ + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); + + writew(ops, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, true); +} + +static void send_page_v1(struct mtd_info *mtd, unsigned int ops) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; int bufs, i; - if (nfc_is_v1() && mtd->writesize > 512) + if (mtd->writesize > 512) bufs = 4; else bufs = 1; @@ -463,7 +519,7 @@ static void send_read_id_v3(struct mxc_nand_host *host) wait_op_done(host, true); - memcpy(host->data_buf, host->main_area0, 16); + memcpy_fromio(host->data_buf, host->main_area0, 16); } /* Request the NANDFC to perform a read of the NAND device ID. */ @@ -479,7 +535,7 @@ static void send_read_id_v1_v2(struct mxc_nand_host *host) /* Wait for operation to complete */ wait_op_done(host, true); - memcpy(host->data_buf, host->main_area0, 16); + memcpy_fromio(host->data_buf, host->main_area0, 16); if (this->options & NAND_BUSWIDTH_16) { /* compress the ID info */ @@ -555,7 +611,7 @@ static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat, * additional correction. 2-Bit errors cannot be corrected by * HW ECC, so we need to return failure */ - uint16_t ecc_status = readw(NFC_V1_V2_ECC_STATUS_RESULT); + uint16_t ecc_status = get_ecc_status_v1(host); if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { pr_debug("MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); @@ -580,10 +636,7 @@ static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, no_subpages = mtd->writesize >> 9; - if (nfc_is_v21()) - ecc_stat = readl(NFC_V1_V2_ECC_STATUS_RESULT); - else - ecc_stat = readl(NFC_V3_ECC_STATUS_RESULT); + ecc_stat = host->devtype_data->get_ecc_status(host); do { err = ecc_stat & ecc_bit_mask; @@ -616,7 +669,7 @@ static u_char mxc_nand_read_byte(struct mtd_info *mtd) /* Check for status request */ if (host->status_request) - return host->get_dev_status(host) & 0xFF; + return host->devtype_data->get_dev_status(host) & 0xFF; ret = *(uint8_t *)(host->data_buf + host->buf_start); host->buf_start++; @@ -682,7 +735,7 @@ static int mxc_nand_verify_buf(struct mtd_info *mtd, /* This function is used by upper layer for select and * deselect of the NAND chip */ -static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) +static void mxc_nand_select_chip_v1_v3(struct mtd_info *mtd, int chip) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; @@ -701,11 +754,30 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) clk_prepare_enable(host->clk); host->clk_act = 1; } +} - if (nfc_is_v21()) { - host->active_cs = chip; - writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); +static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + if (chip == -1) { + /* Disable the NFC clock */ + if (host->clk_act) { + clk_disable(host->clk); + host->clk_act = 0; + } + return; + } + + if (!host->clk_act) { + /* Enable the NFC clock */ + clk_enable(host->clk); + host->clk_act = 1; } + + host->active_cs = chip; + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); } /* @@ -718,23 +790,23 @@ static void copy_spare(struct mtd_info *mtd, bool bfrom) u16 i, j; u16 n = mtd->writesize >> 9; u8 *d = host->data_buf + mtd->writesize; - u8 *s = host->spare0; - u16 t = host->spare_len; + u8 __iomem *s = host->spare0; + u16 t = host->devtype_data->spare_len; j = (mtd->oobsize / n >> 1) << 1; if (bfrom) { for (i = 0; i < n - 1; i++) - memcpy(d + i * j, s + i * t, j); + memcpy_fromio(d + i * j, s + i * t, j); /* the last section */ - memcpy(d + i * j, s + i * t, mtd->oobsize - i * j); + memcpy_fromio(d + i * j, s + i * t, mtd->oobsize - i * j); } else { for (i = 0; i < n - 1; i++) - memcpy(&s[i * t], &d[i * j], j); + memcpy_toio(&s[i * t], &d[i * j], j); /* the last section */ - memcpy(&s[i * t], &d[i * j], mtd->oobsize - i * j); + memcpy_toio(&s[i * t], &d[i * j], mtd->oobsize - i * j); } } @@ -751,34 +823,44 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) * perform a read/write buf operation, the saved column * address is used to index into the full page. */ - host->send_addr(host, 0, page_addr == -1); + host->devtype_data->send_addr(host, 0, page_addr == -1); if (mtd->writesize > 512) /* another col addr cycle for 2k page */ - host->send_addr(host, 0, false); + host->devtype_data->send_addr(host, 0, false); } /* Write out page address, if necessary */ if (page_addr != -1) { /* paddr_0 - p_addr_7 */ - host->send_addr(host, (page_addr & 0xff), false); + host->devtype_data->send_addr(host, (page_addr & 0xff), false); if (mtd->writesize > 512) { if (mtd->size >= 0x10000000) { /* paddr_8 - paddr_15 */ - host->send_addr(host, (page_addr >> 8) & 0xff, false); - host->send_addr(host, (page_addr >> 16) & 0xff, true); + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, + false); + host->devtype_data->send_addr(host, + (page_addr >> 16) & 0xff, + true); } else /* paddr_8 - paddr_15 */ - host->send_addr(host, (page_addr >> 8) & 0xff, true); + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, true); } else { /* One more address cycle for higher density devices */ if (mtd->size >= 0x4000000) { /* paddr_8 - paddr_15 */ - host->send_addr(host, (page_addr >> 8) & 0xff, false); - host->send_addr(host, (page_addr >> 16) & 0xff, true); + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, + false); + host->devtype_data->send_addr(host, + (page_addr >> 16) & 0xff, + true); } else /* paddr_8 - paddr_15 */ - host->send_addr(host, (page_addr >> 8) & 0xff, true); + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, true); } } } @@ -800,7 +882,35 @@ static int get_eccsize(struct mtd_info *mtd) return 8; } -static void preset_v1_v2(struct mtd_info *mtd) +static void preset_v1(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint16_t config1 = 0; + + if (nand_chip->ecc.mode == NAND_ECC_HW) + config1 |= NFC_V1_V2_CONFIG1_ECC_EN; + + if (!host->devtype_data->irqpending_quirk) + config1 |= NFC_V1_V2_CONFIG1_INT_MSK; + + host->eccsize = 1; + + writew(config1, NFC_V1_V2_CONFIG1); + /* preset operation */ + + /* Unlock the internal RAM Buffer */ + writew(0x2, NFC_V1_V2_CONFIG); + + /* Blocks to be unlocked */ + writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR); + writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR); + + /* Unlock Block Command for given address range */ + writew(0x4, NFC_V1_V2_WRPROT); +} + +static void preset_v2(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; @@ -809,13 +919,12 @@ static void preset_v1_v2(struct mtd_info *mtd) if (nand_chip->ecc.mode == NAND_ECC_HW) config1 |= NFC_V1_V2_CONFIG1_ECC_EN; - if (nfc_is_v21()) - config1 |= NFC_V2_CONFIG1_FP_INT; + config1 |= NFC_V2_CONFIG1_FP_INT; - if (!cpu_is_mx21()) + if (!host->devtype_data->irqpending_quirk) config1 |= NFC_V1_V2_CONFIG1_INT_MSK; - if (nfc_is_v21() && mtd->writesize) { + if (mtd->writesize) { uint16_t pages_per_block = mtd->erasesize / mtd->writesize; host->eccsize = get_eccsize(mtd); @@ -834,20 +943,14 @@ static void preset_v1_v2(struct mtd_info *mtd) writew(0x2, NFC_V1_V2_CONFIG); /* Blocks to be unlocked */ - if (nfc_is_v21()) { - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0); - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1); - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2); - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3); - } else if (nfc_is_v1()) { - writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR); - writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR); - } else - BUG(); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3); /* Unlock Block Command for given address range */ writew(0x4, NFC_V1_V2_WRPROT); @@ -937,15 +1040,15 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, /* Command pre-processing step */ switch (command) { case NAND_CMD_RESET: - host->preset(mtd); - host->send_cmd(host, command, false); + host->devtype_data->preset(mtd); + host->devtype_data->send_cmd(host, command, false); break; case NAND_CMD_STATUS: host->buf_start = 0; host->status_request = true; - host->send_cmd(host, command, true); + host->devtype_data->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); break; @@ -958,15 +1061,16 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, command = NAND_CMD_READ0; /* only READ0 is valid */ - host->send_cmd(host, command, false); + host->devtype_data->send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); if (mtd->writesize > 512) - host->send_cmd(host, NAND_CMD_READSTART, true); + host->devtype_data->send_cmd(host, + NAND_CMD_READSTART, true); - host->send_page(mtd, NFC_OUTPUT); + host->devtype_data->send_page(mtd, NFC_OUTPUT); - memcpy(host->data_buf, host->main_area0, mtd->writesize); + memcpy_fromio(host->data_buf, host->main_area0, mtd->writesize); copy_spare(mtd, true); break; @@ -977,28 +1081,28 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, host->buf_start = column; - host->send_cmd(host, command, false); + host->devtype_data->send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); break; case NAND_CMD_PAGEPROG: - memcpy(host->main_area0, host->data_buf, mtd->writesize); + memcpy_toio(host->main_area0, host->data_buf, mtd->writesize); copy_spare(mtd, false); - host->send_page(mtd, NFC_INPUT); - host->send_cmd(host, command, true); + host->devtype_data->send_page(mtd, NFC_INPUT); + host->devtype_data->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); break; case NAND_CMD_READID: - host->send_cmd(host, command, true); + host->devtype_data->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); - host->send_read_id(host); + host->devtype_data->send_read_id(host); host->buf_start = column; break; case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: - host->send_cmd(host, command, false); + host->devtype_data->send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); break; @@ -1032,15 +1136,191 @@ static struct nand_bbt_descr bbt_mirror_descr = { .pattern = mirror_pattern, }; +/* v1 + irqpending_quirk: i.MX21 */ +static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { + .preset = preset_v1, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v1, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v1, + .ecclayout_512 = &nandv1_hw_eccoob_smallpage, + .ecclayout_2k = &nandv1_hw_eccoob_largepage, + .ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */ + .select_chip = mxc_nand_select_chip_v1_v3, + .correct_data = mxc_nand_correct_data_v1, + .irqpending_quirk = 1, + .needs_ip = 0, + .regs_offset = 0xe00, + .spare0_offset = 0x800, + .spare_len = 16, + .eccbytes = 3, + .eccsize = 1, +}; + +/* v1 + !irqpending_quirk: i.MX27, i.MX31 */ +static const struct mxc_nand_devtype_data imx27_nand_devtype_data = { + .preset = preset_v1, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v1, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v1, + .ecclayout_512 = &nandv1_hw_eccoob_smallpage, + .ecclayout_2k = &nandv1_hw_eccoob_largepage, + .ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */ + .select_chip = mxc_nand_select_chip_v1_v3, + .correct_data = mxc_nand_correct_data_v1, + .irqpending_quirk = 0, + .needs_ip = 0, + .regs_offset = 0xe00, + .spare0_offset = 0x800, + .axi_offset = 0, + .spare_len = 16, + .eccbytes = 3, + .eccsize = 1, +}; + +/* v21: i.MX25, i.MX35 */ +static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { + .preset = preset_v2, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v2, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v2, + .ecclayout_512 = &nandv2_hw_eccoob_smallpage, + .ecclayout_2k = &nandv2_hw_eccoob_largepage, + .ecclayout_4k = &nandv2_hw_eccoob_4k, + .select_chip = mxc_nand_select_chip_v2, + .correct_data = mxc_nand_correct_data_v2_v3, + .irqpending_quirk = 0, + .needs_ip = 0, + .regs_offset = 0x1e00, + .spare0_offset = 0x1000, + .axi_offset = 0, + .spare_len = 64, + .eccbytes = 9, + .eccsize = 0, +}; + +/* v3: i.MX51, i.MX53 */ +static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { + .preset = preset_v3, + .send_cmd = send_cmd_v3, + .send_addr = send_addr_v3, + .send_page = send_page_v3, + .send_read_id = send_read_id_v3, + .get_dev_status = get_dev_status_v3, + .check_int = check_int_v3, + .irq_control = irq_control_v3, + .get_ecc_status = get_ecc_status_v3, + .ecclayout_512 = &nandv2_hw_eccoob_smallpage, + .ecclayout_2k = &nandv2_hw_eccoob_largepage, + .ecclayout_4k = &nandv2_hw_eccoob_smallpage, /* XXX: needs fix */ + .select_chip = mxc_nand_select_chip_v1_v3, + .correct_data = mxc_nand_correct_data_v2_v3, + .irqpending_quirk = 0, + .needs_ip = 1, + .regs_offset = 0, + .spare0_offset = 0x1000, + .axi_offset = 0x1e00, + .spare_len = 64, + .eccbytes = 0, + .eccsize = 0, +}; + +#ifdef CONFIG_OF_MTD +static const struct of_device_id mxcnd_dt_ids[] = { + { + .compatible = "fsl,imx21-nand", + .data = &imx21_nand_devtype_data, + }, { + .compatible = "fsl,imx27-nand", + .data = &imx27_nand_devtype_data, + }, { + .compatible = "fsl,imx25-nand", + .data = &imx25_nand_devtype_data, + }, { + .compatible = "fsl,imx51-nand", + .data = &imx51_nand_devtype_data, + }, + { /* sentinel */ } +}; + +static int __init mxcnd_probe_dt(struct mxc_nand_host *host) +{ + struct device_node *np = host->dev->of_node; + struct mxc_nand_platform_data *pdata = &host->pdata; + const struct of_device_id *of_id = + of_match_device(mxcnd_dt_ids, host->dev); + int buswidth; + + if (!np) + return 1; + + if (of_get_nand_ecc_mode(np) >= 0) + pdata->hw_ecc = 1; + + pdata->flash_bbt = of_get_nand_on_flash_bbt(np); + + buswidth = of_get_nand_bus_width(np); + if (buswidth < 0) + return buswidth; + + pdata->width = buswidth / 8; + + host->devtype_data = of_id->data; + + return 0; +} +#else +static int __init mxcnd_probe_dt(struct mxc_nand_host *host) +{ + return 1; +} +#endif + +static int __init mxcnd_probe_pdata(struct mxc_nand_host *host) +{ + struct mxc_nand_platform_data *pdata = host->dev->platform_data; + + if (!pdata) + return -ENODEV; + + host->pdata = *pdata; + + if (nfc_is_v1()) { + if (cpu_is_mx21()) + host->devtype_data = &imx21_nand_devtype_data; + else + host->devtype_data = &imx27_nand_devtype_data; + } else if (nfc_is_v21()) { + host->devtype_data = &imx25_nand_devtype_data; + } else if (nfc_is_v3_2()) { + host->devtype_data = &imx51_nand_devtype_data; + } else + BUG(); + + return 0; +} + static int __init mxcnd_probe(struct platform_device *pdev) { struct nand_chip *this; struct mtd_info *mtd; - struct mxc_nand_platform_data *pdata = pdev->dev.platform_data; struct mxc_nand_host *host; struct resource *res; int err = 0; - struct nand_ecclayout *oob_smallpage, *oob_largepage; /* Allocate memory for MTD device structure and private data */ host = kzalloc(sizeof(struct mxc_nand_host) + NAND_MAX_PAGESIZE + @@ -1065,7 +1345,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->priv = host; this->dev_ready = mxc_nand_dev_ready; this->cmdfunc = mxc_nand_command; - this->select_chip = mxc_nand_select_chip; this->read_byte = mxc_nand_read_byte; this->read_word = mxc_nand_read_word; this->write_buf = mxc_nand_write_buf; @@ -1095,36 +1374,26 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->main_area0 = host->base; - if (nfc_is_v1() || nfc_is_v21()) { - host->preset = preset_v1_v2; - host->send_cmd = send_cmd_v1_v2; - host->send_addr = send_addr_v1_v2; - host->send_page = send_page_v1_v2; - host->send_read_id = send_read_id_v1_v2; - host->get_dev_status = get_dev_status_v1_v2; - host->check_int = check_int_v1_v2; - if (cpu_is_mx21()) - host->irq_control = irq_control_mx21; - else - host->irq_control = irq_control_v1_v2; - } + err = mxcnd_probe_dt(host); + if (err > 0) + err = mxcnd_probe_pdata(host); + if (err < 0) + goto eirq; - if (nfc_is_v21()) { - host->regs = host->base + 0x1e00; - host->spare0 = host->base + 0x1000; - host->spare_len = 64; - oob_smallpage = &nandv2_hw_eccoob_smallpage; - oob_largepage = &nandv2_hw_eccoob_largepage; - this->ecc.bytes = 9; - } else if (nfc_is_v1()) { - host->regs = host->base + 0xe00; - host->spare0 = host->base + 0x800; - host->spare_len = 16; - oob_smallpage = &nandv1_hw_eccoob_smallpage; - oob_largepage = &nandv1_hw_eccoob_largepage; - this->ecc.bytes = 3; - host->eccsize = 1; - } else if (nfc_is_v3_2()) { + if (host->devtype_data->regs_offset) + host->regs = host->base + host->devtype_data->regs_offset; + host->spare0 = host->base + host->devtype_data->spare0_offset; + if (host->devtype_data->axi_offset) + host->regs_axi = host->base + host->devtype_data->axi_offset; + + this->ecc.bytes = host->devtype_data->eccbytes; + host->eccsize = host->devtype_data->eccsize; + + this->select_chip = host->devtype_data->select_chip; + this->ecc.size = 512; + this->ecc.layout = host->devtype_data->ecclayout_512; + + if (host->devtype_data->needs_ip) { res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) { err = -ENODEV; @@ -1135,42 +1404,22 @@ static int __init mxcnd_probe(struct platform_device *pdev) err = -ENOMEM; goto eirq; } - host->regs_axi = host->base + 0x1e00; - host->spare0 = host->base + 0x1000; - host->spare_len = 64; - host->preset = preset_v3; - host->send_cmd = send_cmd_v3; - host->send_addr = send_addr_v3; - host->send_page = send_page_v3; - host->send_read_id = send_read_id_v3; - host->check_int = check_int_v3; - host->get_dev_status = get_dev_status_v3; - host->irq_control = irq_control_v3; - oob_smallpage = &nandv2_hw_eccoob_smallpage; - oob_largepage = &nandv2_hw_eccoob_largepage; - } else - BUG(); - - this->ecc.size = 512; - this->ecc.layout = oob_smallpage; + } - if (pdata->hw_ecc) { + if (host->pdata.hw_ecc) { this->ecc.calculate = mxc_nand_calculate_ecc; this->ecc.hwctl = mxc_nand_enable_hwecc; - if (nfc_is_v1()) - this->ecc.correct = mxc_nand_correct_data_v1; - else - this->ecc.correct = mxc_nand_correct_data_v2_v3; + this->ecc.correct = host->devtype_data->correct_data; this->ecc.mode = NAND_ECC_HW; } else { this->ecc.mode = NAND_ECC_SOFT; } - /* NAND bus width determines access funtions used by upper layer */ - if (pdata->width == 2) + /* NAND bus width determines access functions used by upper layer */ + if (host->pdata.width == 2) this->options |= NAND_BUSWIDTH_16; - if (pdata->flash_bbt) { + if (host->pdata.flash_bbt) { this->bbt_td = &bbt_main_descr; this->bbt_md = &bbt_mirror_descr; /* update flash based bbt */ @@ -1182,28 +1431,25 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->irq = platform_get_irq(pdev, 0); /* - * mask the interrupt. For i.MX21 explicitely call - * irq_control_v1_v2 to use the mask bit. We can't call - * disable_irq_nosync() for an interrupt we do not own yet. + * Use host->devtype_data->irq_control() here instead of irq_control() + * because we must not disable_irq_nosync without having requested the + * irq. */ - if (cpu_is_mx21()) - irq_control_v1_v2(host, 0); - else - host->irq_control(host, 0); + host->devtype_data->irq_control(host, 0); err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host); if (err) goto eirq; - host->irq_control(host, 0); - /* - * Now that the interrupt is disabled make sure the interrupt - * mask bit is cleared on i.MX21. Otherwise we can't read - * the interrupt status bit on this machine. + * Now that we "own" the interrupt make sure the interrupt mask bit is + * cleared on i.MX21. Otherwise we can't read the interrupt status bit + * on this machine. */ - if (cpu_is_mx21()) - irq_control_v1_v2(host, 1); + if (host->devtype_data->irqpending_quirk) { + disable_irq_nosync(host->irq); + host->devtype_data->irq_control(host, 1); + } /* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, nfc_is_v21() ? 4 : 1, NULL)) { @@ -1212,18 +1458,12 @@ static int __init mxcnd_probe(struct platform_device *pdev) } /* Call preset again, with correct writesize this time */ - host->preset(mtd); + host->devtype_data->preset(mtd); if (mtd->writesize == 2048) - this->ecc.layout = oob_largepage; - if (nfc_is_v21() && mtd->writesize == 4096) - this->ecc.layout = &nandv2_hw_eccoob_4k; - - /* second phase scan */ - if (nand_scan_tail(mtd)) { - err = -ENXIO; - goto escan; - } + this->ecc.layout = host->devtype_data->ecclayout_2k; + else if (mtd->writesize == 4096) + this->ecc.layout = host->devtype_data->ecclayout_4k; if (this->ecc.mode == NAND_ECC_HW) { if (nfc_is_v1()) @@ -1232,9 +1472,19 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->ecc.strength = (host->eccsize == 4) ? 4 : 8; } + /* second phase scan */ + if (nand_scan_tail(mtd)) { + err = -ENXIO; + goto escan; + } + /* Register the partitions */ - mtd_device_parse_register(mtd, part_probes, NULL, pdata->parts, - pdata->nr_parts); + mtd_device_parse_register(mtd, part_probes, + &(struct mtd_part_parser_data){ + .of_node = pdev->dev.of_node, + }, + host->pdata.parts, + host->pdata.nr_parts); platform_set_drvdata(pdev, host); @@ -1275,6 +1525,8 @@ static int __devexit mxcnd_remove(struct platform_device *pdev) static struct platform_driver mxcnd_driver = { .driver = { .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(mxcnd_dt_ids), }, .remove = __devexit_p(mxcnd_remove), }; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 47b19c0bb070..d47586cf64ce 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1066,15 +1066,17 @@ EXPORT_SYMBOL(nand_lock); * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * Not for syndrome calculating ECC controllers, which use a special oob layout. */ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { chip->read_buf(mtd, buf, mtd->writesize); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + if (oob_required) + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; } @@ -1083,13 +1085,14 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * We need a special oob layout and handling even when OOB isn't used. */ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, int page) + struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1126,10 +1129,11 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read */ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1138,8 +1142,9 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; + unsigned int max_bitflips = 0; - chip->ecc.read_page_raw(mtd, chip, buf, page); + chip->ecc.read_page_raw(mtd, chip, buf, 1, page); for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) chip->ecc.calculate(mtd, p, &ecc_calc[i]); @@ -1154,12 +1159,14 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, int stat; stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** @@ -1180,6 +1187,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, int datafrag_len, eccfrag_len, aligned_len, aligned_pos; int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; int index = 0; + unsigned int max_bitflips = 0; /* Column address within the page aligned to ECC size (256bytes) */ start_step = data_offs / chip->ecc.size; @@ -1244,12 +1252,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** @@ -1257,12 +1267,13 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * Not for syndrome calculating ECC controllers which need a special oob layout. */ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1271,6 +1282,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; + unsigned int max_bitflips = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_READ); @@ -1289,12 +1301,14 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, int stat; stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** @@ -1302,6 +1316,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * Hardware ECC for large page chips, require OOB to be read first. For this @@ -1311,7 +1326,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * the data area, by overwriting the NAND manufacturer bad block markings. */ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int page) + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1320,6 +1335,7 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; uint8_t *ecc_calc = chip->buffers->ecccalc; + unsigned int max_bitflips = 0; /* Read the OOB area first */ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); @@ -1337,12 +1353,14 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, chip->ecc.calculate(mtd, p, &ecc_calc[i]); stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** @@ -1350,19 +1368,21 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * The hw generator calculates the error syndrome automatically. Therefore we * need a special oob layout and handling. */ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; uint8_t *oob = chip->oob_poi; + unsigned int max_bitflips = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; @@ -1379,10 +1399,12 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, chip->read_buf(mtd, oob, eccbytes); stat = chip->ecc.correct(mtd, p, oob, NULL); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } oob += eccbytes; @@ -1397,7 +1419,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, if (i) chip->read_buf(mtd, oob, i); - return 0; + return max_bitflips; } /** @@ -1459,11 +1481,9 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - int chipnr, page, realpage, col, bytes, aligned; + int chipnr, page, realpage, col, bytes, aligned, oob_required; struct nand_chip *chip = mtd->priv; struct mtd_ecc_stats stats; - int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; - int sndcmd = 1; int ret = 0; uint32_t readlen = ops->len; uint32_t oobreadlen = ops->ooblen; @@ -1471,6 +1491,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, mtd->oobavail : mtd->oobsize; uint8_t *bufpoi, *oob, *buf; + unsigned int max_bitflips = 0; stats = mtd->ecc_stats; @@ -1484,6 +1505,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, buf = ops->datbuf; oob = ops->oobbuf; + oob_required = oob ? 1 : 0; while (1) { bytes = min(mtd->writesize - col, readlen); @@ -1493,21 +1515,22 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, if (realpage != chip->pagebuf || oob) { bufpoi = aligned ? buf : chip->buffers->databuf; - if (likely(sndcmd)) { - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); - sndcmd = 0; - } + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); - /* Now read the page into the buffer */ + /* + * Now read the page into the buffer. Absent an error, + * the read methods return max bitflips per ecc step. + */ if (unlikely(ops->mode == MTD_OPS_RAW)) - ret = chip->ecc.read_page_raw(mtd, chip, - bufpoi, page); + ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, + oob_required, + page); else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); else ret = chip->ecc.read_page(mtd, chip, bufpoi, - page); + oob_required, page); if (ret < 0) { if (!aligned) /* Invalidate page cache */ @@ -1515,22 +1538,25 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, break; } + max_bitflips = max_t(unsigned int, max_bitflips, ret); + /* Transfer not aligned data */ if (!aligned) { if (!NAND_SUBPAGE_READ(chip) && !oob && !(mtd->ecc_stats.failed - stats.failed) && - (ops->mode != MTD_OPS_RAW)) + (ops->mode != MTD_OPS_RAW)) { chip->pagebuf = realpage; - else + chip->pagebuf_bitflips = ret; + } else { /* Invalidate page cache */ chip->pagebuf = -1; + } memcpy(buf, chip->buffers->databuf + col, bytes); } buf += bytes; if (unlikely(oob)) { - int toread = min(oobreadlen, max_oobsize); if (toread) { @@ -1541,13 +1567,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, } if (!(chip->options & NAND_NO_READRDY)) { - /* - * Apply delay or wait for ready/busy pin. Do - * this before the AUTOINCR check, so no - * problems arise if a chip which does auto - * increment is marked as NOAUTOINCR by the - * board driver. - */ + /* Apply delay or wait for ready/busy pin */ if (!chip->dev_ready) udelay(chip->chip_delay); else @@ -1556,6 +1576,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, } else { memcpy(buf, chip->buffers->databuf + col, bytes); buf += bytes; + max_bitflips = max_t(unsigned int, max_bitflips, + chip->pagebuf_bitflips); } readlen -= bytes; @@ -1575,26 +1597,19 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, chip->select_chip(mtd, -1); chip->select_chip(mtd, chipnr); } - - /* - * Check, if the chip supports auto page increment or if we - * have hit a block boundary. - */ - if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) - sndcmd = 1; } ops->retlen = ops->len - (size_t) readlen; if (oob) ops->oobretlen = ops->ooblen - oobreadlen; - if (ret) + if (ret < 0) return ret; if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; + return max_bitflips; } /** @@ -1630,17 +1645,13 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, * @mtd: mtd info structure * @chip: nand chip info structure * @page: page number to read - * @sndcmd: flag whether to issue read command or not */ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { - if (sndcmd) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - sndcmd = 0; - } + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return sndcmd; + return 0; } /** @@ -1649,10 +1660,9 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @page: page number to read - * @sndcmd: flag whether to issue read command or not */ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { uint8_t *buf = chip->oob_poi; int length = mtd->oobsize; @@ -1679,7 +1689,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, if (length > 0) chip->read_buf(mtd, bufpoi, length); - return 1; + return 0; } /** @@ -1775,13 +1785,13 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - int page, realpage, chipnr, sndcmd = 1; + int page, realpage, chipnr; struct nand_chip *chip = mtd->priv; struct mtd_ecc_stats stats; - int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; int readlen = ops->ooblen; int len; uint8_t *buf = ops->oobbuf; + int ret = 0; pr_debug("%s: from = 0x%08Lx, len = %i\n", __func__, (unsigned long long)from, readlen); @@ -1817,20 +1827,18 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, while (1) { if (ops->mode == MTD_OPS_RAW) - sndcmd = chip->ecc.read_oob_raw(mtd, chip, page, sndcmd); + ret = chip->ecc.read_oob_raw(mtd, chip, page); else - sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); + ret = chip->ecc.read_oob(mtd, chip, page); + + if (ret < 0) + break; len = min(len, readlen); buf = nand_transfer_oob(chip, buf, ops, len); if (!(chip->options & NAND_NO_READRDY)) { - /* - * Apply delay or wait for ready/busy pin. Do this - * before the AUTOINCR check, so no problems arise if a - * chip which does auto increment is marked as - * NOAUTOINCR by the board driver. - */ + /* Apply delay or wait for ready/busy pin */ if (!chip->dev_ready) udelay(chip->chip_delay); else @@ -1851,16 +1859,12 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, chip->select_chip(mtd, -1); chip->select_chip(mtd, chipnr); } - - /* - * Check, if the chip supports auto page increment or if we - * have hit a block boundary. - */ - if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) - sndcmd = 1; } - ops->oobretlen = ops->ooblen; + ops->oobretlen = ops->ooblen - readlen; + + if (ret < 0) + return ret; if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; @@ -1919,14 +1923,16 @@ out: * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB * * Not for syndrome calculating ECC controllers, which use a special oob layout. */ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { chip->write_buf(mtd, buf, mtd->writesize); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + if (oob_required) + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); } /** @@ -1934,12 +1940,13 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB * * We need a special oob layout and handling even when ECC isn't checked. */ static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1973,9 +1980,10 @@ static void nand_write_page_raw_syndrome(struct mtd_info *mtd, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB */ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1991,7 +1999,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, for (i = 0; i < chip->ecc.total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; - chip->ecc.write_page_raw(mtd, chip, buf); + chip->ecc.write_page_raw(mtd, chip, buf, 1); } /** @@ -1999,9 +2007,10 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB */ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2027,12 +2036,14 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB * * The hw generator calculates the error syndrome automatically. Therefore we * need a special oob layout and handling. */ static void nand_write_page_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) + struct nand_chip *chip, + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2071,21 +2082,23 @@ static void nand_write_page_syndrome(struct mtd_info *mtd, * @mtd: MTD device structure * @chip: NAND chip descriptor * @buf: the data to write + * @oob_required: must write chip->oob_poi to OOB * @page: page number to write * @cached: cached programming * @raw: use _raw version of write_page */ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int page, int cached, int raw) + const uint8_t *buf, int oob_required, int page, + int cached, int raw) { int status; chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) - chip->ecc.write_page_raw(mtd, chip, buf); + chip->ecc.write_page_raw(mtd, chip, buf, oob_required); else - chip->ecc.write_page(mtd, chip, buf); + chip->ecc.write_page(mtd, chip, buf, oob_required); /* * Cached progamming disabled for now. Not sure if it's worth the @@ -2118,6 +2131,9 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, if (chip->verify_buf(mtd, buf, mtd->writesize)) return -EIO; + + /* Make sure the next page prog is preceded by a status read */ + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); #endif return 0; } @@ -2202,6 +2218,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, uint8_t *oob = ops->oobbuf; uint8_t *buf = ops->datbuf; int ret, subpage; + int oob_required = oob ? 1 : 0; ops->retlen = 0; if (!writelen) @@ -2264,8 +2281,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, memset(chip->oob_poi, 0xff, mtd->oobsize); } - ret = chip->write_page(mtd, chip, wbuf, page, cached, - (ops->mode == MTD_OPS_RAW)); + ret = chip->write_page(mtd, chip, wbuf, oob_required, page, + cached, (ops->mode == MTD_OPS_RAW)); if (ret) break; @@ -2898,8 +2915,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, *busw = NAND_BUSWIDTH_16; chip->options &= ~NAND_CHIPOPTIONS_MSK; - chip->options |= (NAND_NO_READRDY | - NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK; + chip->options |= NAND_NO_READRDY & NAND_CHIPOPTIONS_MSK; pr_info("ONFI flash detected\n"); return 1; @@ -3076,11 +3092,6 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; ident_done: - /* - * Set chip as a default. Board drivers can override it, if necessary. - */ - chip->options |= NAND_NO_AUTOINCR; - /* Try to identify manufacturer */ for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { if (nand_manuf_ids[maf_idx].id == *maf_id) @@ -3154,10 +3165,11 @@ ident_done: if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp; - pr_info("NAND device: Manufacturer ID:" - " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id, - nand_manuf_ids[maf_idx].name, - chip->onfi_version ? chip->onfi_params.model : type->name); + pr_info("NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s)," + " page size: %d, OOB size: %d\n", + *maf_id, *dev_id, nand_manuf_ids[maf_idx].name, + chip->onfi_version ? chip->onfi_params.model : type->name, + mtd->writesize, mtd->oobsize); return type; } @@ -3329,8 +3341,13 @@ int nand_scan_tail(struct mtd_info *mtd) if (!chip->ecc.write_oob) chip->ecc.write_oob = nand_write_oob_syndrome; - if (mtd->writesize >= chip->ecc.size) + if (mtd->writesize >= chip->ecc.size) { + if (!chip->ecc.strength) { + pr_warn("Driver must set ecc.strength when using hardware ECC\n"); + BUG(); + } break; + } pr_warn("%d byte HW ECC not possible on " "%d byte page size, fallback to SW ECC\n", chip->ecc.size, mtd->writesize); @@ -3385,7 +3402,7 @@ int nand_scan_tail(struct mtd_info *mtd) BUG(); } chip->ecc.strength = - chip->ecc.bytes*8 / fls(8*chip->ecc.size); + chip->ecc.bytes * 8 / fls(8 * chip->ecc.size); break; case NAND_ECC_NONE: @@ -3483,7 +3500,7 @@ int nand_scan_tail(struct mtd_info *mtd) /* propagate ecc info to mtd_info */ mtd->ecclayout = chip->ecc.layout; - mtd->ecc_strength = chip->ecc.strength * chip->ecc.steps; + mtd->ecc_strength = chip->ecc.strength; /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 20a112f591fe..30d1319ff065 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -324,6 +324,7 @@ static int scan_read_raw_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs, buf += mtd->oobsize + mtd->writesize; len -= mtd->writesize; + offs += mtd->writesize; } return 0; } diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index af4fe8ca7b5e..621b70b7a159 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -70,7 +70,7 @@ struct nand_flash_dev nand_flash_ids[] = { * These are the new chips with large page size. The pagesize and the * erasesize is determined from the extended id bytes */ -#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR) +#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY) #define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) /* 512 Megabit */ @@ -157,9 +157,7 @@ struct nand_flash_dev nand_flash_ids[] = { * writes possible, but not implemented now */ {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, - NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY | - BBT_AUTO_REFRESH - }, + NAND_IS_AND | NAND_NO_READRDY | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH}, {NULL,} }; diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 261f478f8cc3..6cc8fbfabb8e 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -268,7 +268,6 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " #define OPT_PAGE512 0x00000002 /* 512-byte page chips */ #define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */ #define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */ -#define OPT_AUTOINCR 0x00000020 /* page number auto incrementation is possible */ #define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */ #define OPT_PAGE4096 0x00000080 /* 4096-byte page chips */ #define OPT_LARGEPAGE (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */ @@ -594,7 +593,7 @@ static int init_nandsim(struct mtd_info *mtd) ns->options |= OPT_PAGE256; } else if (ns->geom.pgsz == 512) { - ns->options |= (OPT_PAGE512 | OPT_AUTOINCR); + ns->options |= OPT_PAGE512; if (ns->busw == 8) ns->options |= OPT_PAGE512_8BIT; } else if (ns->geom.pgsz == 2048) { @@ -663,8 +662,6 @@ static int init_nandsim(struct mtd_info *mtd) for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (second_id_byte != nand_flash_ids[i].id) continue; - if (!(nand_flash_ids[i].options & NAND_NO_AUTOINCR)) - ns->options |= OPT_AUTOINCR; } if (ns->busw == 16) @@ -1936,20 +1933,8 @@ static u_char ns_nand_read_byte(struct mtd_info *mtd) if (ns->regs.count == ns->regs.num) { NS_DBG("read_byte: all bytes were read\n"); - /* - * The OPT_AUTOINCR allows to read next consecutive pages without - * new read operation cycle. - */ - if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { - ns->regs.count = 0; - if (ns->regs.row + 1 < ns->geom.pgnum) - ns->regs.row += 1; - NS_DBG("read_byte: switch to the next page (%#x)\n", ns->regs.row); - do_state_action(ns, ACTION_CPY); - } - else if (NS_STATE(ns->nxstate) == STATE_READY) + if (NS_STATE(ns->nxstate) == STATE_READY) switch_state(ns); - } return outb; @@ -2203,14 +2188,7 @@ static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ns->regs.count += len; if (ns->regs.count == ns->regs.num) { - if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { - ns->regs.count = 0; - if (ns->regs.row + 1 < ns->geom.pgnum) - ns->regs.row += 1; - NS_DBG("read_buf: switch to the next page (%#x)\n", ns->regs.row); - do_state_action(ns, ACTION_CPY); - } - else if (NS_STATE(ns->nxstate) == STATE_READY) + if (NS_STATE(ns->nxstate) == STATE_READY) switch_state(ns); } diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index c2b0bba9d8b3..d7f681d0c9b9 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -21,6 +21,10 @@ #include <linux/io.h> #include <linux/slab.h> +#ifdef CONFIG_MTD_NAND_OMAP_BCH +#include <linux/bch.h> +#endif + #include <plat/dma.h> #include <plat/gpmc.h> #include <plat/nand.h> @@ -127,6 +131,11 @@ struct omap_nand_info { } iomode; u_char *buf; int buf_len; + +#ifdef CONFIG_MTD_NAND_OMAP_BCH + struct bch_control *bch; + struct nand_ecclayout ecclayout; +#endif }; /** @@ -402,7 +411,7 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write); if (ret) /* PFPW engine is busy, use cpu copy method */ - goto out_copy; + goto out_copy_unmap; init_completion(&info->comp); @@ -421,6 +430,8 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, dma_unmap_single(&info->pdev->dev, dma_addr, len, dir); return 0; +out_copy_unmap: + dma_unmap_single(&info->pdev->dev, dma_addr, len, dir); out_copy: if (info->nand.options & NAND_BUSWIDTH_16) is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len) @@ -879,7 +890,7 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); unsigned long timeo = jiffies; - int status = NAND_STATUS_FAIL, state = this->state; + int status, state = this->state; if (state == FL_ERASING) timeo += (HZ * 400) / 1000; @@ -894,6 +905,8 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) break; cond_resched(); } + + status = gpmc_nand_read(info->gpmc_cs, GPMC_NAND_DATA); return status; } @@ -925,6 +938,226 @@ static int omap_dev_ready(struct mtd_info *mtd) return 1; } +#ifdef CONFIG_MTD_NAND_OMAP_BCH + +/** + * omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) +{ + int nerrors; + unsigned int dev_width; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + struct nand_chip *chip = mtd->priv; + + nerrors = (info->nand.ecc.bytes == 13) ? 8 : 4; + dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; + /* + * Program GPMC to perform correction on one 512-byte sector at a time. + * Using 4 sectors at a time (i.e. ecc.size = 2048) is also possible and + * gives a slight (5%) performance gain (but requires additional code). + */ + (void)gpmc_enable_hwecc_bch(info->gpmc_cs, mode, dev_width, 1, nerrors); +} + +/** + * omap3_calculate_ecc_bch4 - Generate 7 bytes of ECC bytes + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed + * @ecc_code: The ecc_code buffer + */ +static int omap3_calculate_ecc_bch4(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + return gpmc_calculate_ecc_bch4(info->gpmc_cs, dat, ecc_code); +} + +/** + * omap3_calculate_ecc_bch8 - Generate 13 bytes of ECC bytes + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed + * @ecc_code: The ecc_code buffer + */ +static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + return gpmc_calculate_ecc_bch8(info->gpmc_cs, dat, ecc_code); +} + +/** + * omap3_correct_data_bch - Decode received data and correct errors + * @mtd: MTD device structure + * @data: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from HW ECC registers + */ +static int omap3_correct_data_bch(struct mtd_info *mtd, u_char *data, + u_char *read_ecc, u_char *calc_ecc) +{ + int i, count; + /* cannot correct more than 8 errors */ + unsigned int errloc[8]; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + + count = decode_bch(info->bch, NULL, 512, read_ecc, calc_ecc, NULL, + errloc); + if (count > 0) { + /* correct errors */ + for (i = 0; i < count; i++) { + /* correct data only, not ecc bytes */ + if (errloc[i] < 8*512) + data[errloc[i]/8] ^= 1 << (errloc[i] & 7); + pr_debug("corrected bitflip %u\n", errloc[i]); + } + } else if (count < 0) { + pr_err("ecc unrecoverable error\n"); + } + return count; +} + +/** + * omap3_free_bch - Release BCH ecc resources + * @mtd: MTD device structure + */ +static void omap3_free_bch(struct mtd_info *mtd) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + if (info->bch) { + free_bch(info->bch); + info->bch = NULL; + } +} + +/** + * omap3_init_bch - Initialize BCH ECC + * @mtd: MTD device structure + * @ecc_opt: OMAP ECC mode (OMAP_ECC_BCH4_CODE_HW or OMAP_ECC_BCH8_CODE_HW) + */ +static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) +{ + int ret, max_errors; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); +#ifdef CONFIG_MTD_NAND_OMAP_BCH8 + const int hw_errors = 8; +#else + const int hw_errors = 4; +#endif + info->bch = NULL; + + max_errors = (ecc_opt == OMAP_ECC_BCH8_CODE_HW) ? 8 : 4; + if (max_errors != hw_errors) { + pr_err("cannot configure %d-bit BCH ecc, only %d-bit supported", + max_errors, hw_errors); + goto fail; + } + + /* initialize GPMC BCH engine */ + ret = gpmc_init_hwecc_bch(info->gpmc_cs, 1, max_errors); + if (ret) + goto fail; + + /* software bch library is only used to detect and locate errors */ + info->bch = init_bch(13, max_errors, 0x201b /* hw polynomial */); + if (!info->bch) + goto fail; + + info->nand.ecc.size = 512; + info->nand.ecc.hwctl = omap3_enable_hwecc_bch; + info->nand.ecc.correct = omap3_correct_data_bch; + info->nand.ecc.mode = NAND_ECC_HW; + + /* + * The number of corrected errors in an ecc block that will trigger + * block scrubbing defaults to the ecc strength (4 or 8). + * Set mtd->bitflip_threshold here to define a custom threshold. + */ + + if (max_errors == 8) { + info->nand.ecc.strength = 8; + info->nand.ecc.bytes = 13; + info->nand.ecc.calculate = omap3_calculate_ecc_bch8; + } else { + info->nand.ecc.strength = 4; + info->nand.ecc.bytes = 7; + info->nand.ecc.calculate = omap3_calculate_ecc_bch4; + } + + pr_info("enabling NAND BCH ecc with %d-bit correction\n", max_errors); + return 0; +fail: + omap3_free_bch(mtd); + return -1; +} + +/** + * omap3_init_bch_tail - Build an oob layout for BCH ECC correction. + * @mtd: MTD device structure + */ +static int omap3_init_bch_tail(struct mtd_info *mtd) +{ + int i, steps; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + struct nand_ecclayout *layout = &info->ecclayout; + + /* build oob layout */ + steps = mtd->writesize/info->nand.ecc.size; + layout->eccbytes = steps*info->nand.ecc.bytes; + + /* do not bother creating special oob layouts for small page devices */ + if (mtd->oobsize < 64) { + pr_err("BCH ecc is not supported on small page devices\n"); + goto fail; + } + + /* reserve 2 bytes for bad block marker */ + if (layout->eccbytes+2 > mtd->oobsize) { + pr_err("no oob layout available for oobsize %d eccbytes %u\n", + mtd->oobsize, layout->eccbytes); + goto fail; + } + + /* put ecc bytes at oob tail */ + for (i = 0; i < layout->eccbytes; i++) + layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i; + + layout->oobfree[0].offset = 2; + layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; + info->nand.ecc.layout = layout; + + if (!(info->nand.options & NAND_BUSWIDTH_16)) + info->nand.badblock_pattern = &bb_descrip_flashbased; + return 0; +fail: + omap3_free_bch(mtd); + return -1; +} + +#else +static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) +{ + pr_err("CONFIG_MTD_NAND_OMAP_BCH is not enabled\n"); + return -1; +} +static int omap3_init_bch_tail(struct mtd_info *mtd) +{ + return -1; +} +static void omap3_free_bch(struct mtd_info *mtd) +{ +} +#endif /* CONFIG_MTD_NAND_OMAP_BCH */ + static int __devinit omap_nand_probe(struct platform_device *pdev) { struct omap_nand_info *info; @@ -1063,6 +1296,13 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) info->nand.ecc.hwctl = omap_enable_hwecc; info->nand.ecc.correct = omap_correct_data; info->nand.ecc.mode = NAND_ECC_HW; + } else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) || + (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) { + err = omap3_init_bch(&info->mtd, pdata->ecc_opt); + if (err) { + err = -EINVAL; + goto out_release_mem_region; + } } /* DIP switches on some boards change between 8 and 16 bit @@ -1094,6 +1334,14 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) (offset + omap_oobinfo.eccbytes); info->nand.ecc.layout = &omap_oobinfo; + } else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) || + (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) { + /* build OOB layout for BCH ECC correction */ + err = omap3_init_bch_tail(&info->mtd); + if (err) { + err = -EINVAL; + goto out_release_mem_region; + } } /* second phase scan */ @@ -1122,6 +1370,7 @@ static int omap_nand_remove(struct platform_device *pdev) struct mtd_info *mtd = platform_get_drvdata(pdev); struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); + omap3_free_bch(&info->mtd); platform_set_drvdata(pdev, NULL); if (info->dma_ch != -1) diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index 974dbf8251c9..1440e51cedcc 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -155,7 +155,6 @@ static int __devinit pasemi_nand_probe(struct platform_device *ofdev) chip->ecc.mode = NAND_ECC_SOFT; /* Enable the following for a flash based bad block table */ - chip->options = NAND_NO_AUTOINCR; chip->bbt_options = NAND_BBT_USE_FLASH; /* Scan to find existence of the device */ diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 6404e6e81b10..1bcb52040422 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -23,14 +23,18 @@ struct plat_nand_data { void __iomem *io_base; }; +static const char *part_probe_types[] = { "cmdlinepart", NULL }; + /* * Probe for the NAND device. */ static int __devinit plat_nand_probe(struct platform_device *pdev) { struct platform_nand_data *pdata = pdev->dev.platform_data; + struct mtd_part_parser_data ppdata; struct plat_nand_data *data; struct resource *res; + const char **part_types; int err = 0; if (pdata->chip.nr_chips < 1) { @@ -75,6 +79,7 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) data->chip.select_chip = pdata->ctrl.select_chip; data->chip.write_buf = pdata->ctrl.write_buf; data->chip.read_buf = pdata->ctrl.read_buf; + data->chip.read_byte = pdata->ctrl.read_byte; data->chip.chip_delay = pdata->chip.chip_delay; data->chip.options |= pdata->chip.options; data->chip.bbt_options |= pdata->chip.bbt_options; @@ -98,8 +103,10 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) goto out; } - err = mtd_device_parse_register(&data->mtd, - pdata->chip.part_probe_types, NULL, + part_types = pdata->chip.part_probe_types ? : part_probe_types; + + ppdata.of_node = pdev->dev.of_node; + err = mtd_device_parse_register(&data->mtd, part_types, &ppdata, pdata->chip.partitions, pdata->chip.nr_partitions); @@ -140,12 +147,19 @@ static int __devexit plat_nand_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id plat_nand_match[] = { + { .compatible = "gen_nand" }, + {}, +}; +MODULE_DEVICE_TABLE(of, plat_nand_match); + static struct platform_driver plat_nand_driver = { - .probe = plat_nand_probe, - .remove = __devexit_p(plat_nand_remove), - .driver = { - .name = "gen_nand", - .owner = THIS_MODULE, + .probe = plat_nand_probe, + .remove = __devexit_p(plat_nand_remove), + .driver = { + .name = "gen_nand", + .owner = THIS_MODULE, + .of_match_table = plat_nand_match, }, }; diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index def50caa6f84..252aaefcacfa 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -682,14 +682,15 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, } static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) + struct nand_chip *chip, const uint8_t *buf, int oob_required) { chip->write_buf(mtd, buf, mtd->writesize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); } static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int page) + struct nand_chip *chip, uint8_t *buf, int oob_required, + int page) { struct pxa3xx_nand_host *host = mtd->priv; struct pxa3xx_nand_info *info = host->info_data; @@ -1004,7 +1005,6 @@ KEEP_CONFIG: chip->ecc.size = host->page_size; chip->ecc.strength = 1; - chip->options = NAND_NO_AUTOINCR; chip->options |= NAND_NO_READRDY; if (host->reg_ndcr & NDCR_DWIDTH_M) chip->options |= NAND_BUSWIDTH_16; diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index c2040187c813..8cb627751c9c 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -539,14 +539,11 @@ exit: * nand_read_oob_syndrome assumes we can send column address - we can't */ static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { - if (sndcmd) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - sndcmd = 0; - } + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return sndcmd; + return 0; } /* @@ -1104,18 +1101,7 @@ static struct pci_driver r852_pci_driver = { .driver.pm = &r852_pm_ops, }; -static __init int r852_module_init(void) -{ - return pci_register_driver(&r852_pci_driver); -} - -static void __exit r852_module_exit(void) -{ - pci_unregister_driver(&r852_pci_driver); -} - -module_init(r852_module_init); -module_exit(r852_module_exit); +module_pci_driver(r852_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>"); diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index e9b2b260de3a..aa9b8a5e0b8f 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -344,7 +344,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va } static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -359,14 +359,14 @@ static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, if (flctl->hwecc_cant_correct[i]) mtd->ecc_stats.failed++; else - mtd->ecc_stats.corrected += 0; + mtd->ecc_stats.corrected += 0; /* FIXME */ } return 0; } static void flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -881,8 +881,6 @@ static int __devinit flctl_probe(struct platform_device *pdev) flctl->hwecc = pdata->has_hwecc; flctl->holden = pdata->use_holden; - nand->options = NAND_NO_AUTOINCR; - /* Set address of hardware control function */ /* 20 us command delay time */ nand->chip_delay = 20; diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c index 774c3c266713..082bcdcd6bcf 100644 --- a/drivers/mtd/nand/sm_common.c +++ b/drivers/mtd/nand/sm_common.c @@ -94,17 +94,16 @@ static struct nand_flash_dev nand_smartmedia_flash_ids[] = { {NULL,} }; -#define XD_TYPEM (NAND_NO_AUTOINCR | NAND_BROKEN_XD) static struct nand_flash_dev nand_xd_flash_ids[] = { {"xD 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0}, {"xD 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0}, {"xD 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0}, {"xD 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0}, - {"xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, XD_TYPEM}, - {"xD 512MiB 3,3V", 0xdc, 512, 512, 0x4000, XD_TYPEM}, - {"xD 1GiB 3,3V", 0xd3, 512, 1024, 0x4000, XD_TYPEM}, - {"xD 2GiB 3,3V", 0xd5, 512, 2048, 0x4000, XD_TYPEM}, + {"xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, NAND_BROKEN_XD}, + {"xD 512MiB 3,3V", 0xdc, 512, 512, 0x4000, NAND_BROKEN_XD}, + {"xD 1GiB 3,3V", 0xd3, 512, 1024, 0x4000, NAND_BROKEN_XD}, + {"xD 2GiB 3,3V", 0xd5, 512, 2048, 0x4000, NAND_BROKEN_XD}, {NULL,} }; diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index b3ce12ef359e..7153e0d27101 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1201,7 +1201,8 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from, if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; + /* return max bitflips per ecc step; ONENANDs correct 1 bit only */ + return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0; } /** @@ -1333,7 +1334,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; + /* return max bitflips per ecc step; ONENANDs correct 1 bit only */ + return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0; } /** diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index b8e01c3eaa95..b26395d16347 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -24,6 +24,7 @@ #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/slab.h> +#include <linux/of_device.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> #include <linux/pinctrl/pinconf.h> @@ -1688,18 +1689,34 @@ static struct pinctrl_desc nmk_pinctrl_desc = { .owner = THIS_MODULE, }; +static const struct of_device_id nmk_pinctrl_match[] = { + { + .compatible = "stericsson,nmk_pinctrl", + .data = (void *)PINCTRL_NMK_DB8500, + }, + {}, +}; + static int __devinit nmk_pinctrl_probe(struct platform_device *pdev) { const struct platform_device_id *platid = platform_get_device_id(pdev); + struct device_node *np = pdev->dev.of_node; struct nmk_pinctrl *npct; + unsigned int version = 0; int i; npct = devm_kzalloc(&pdev->dev, sizeof(*npct), GFP_KERNEL); if (!npct) return -ENOMEM; + if (platid) + version = platid->driver_data; + else if (np) + version = (unsigned int) + of_match_device(nmk_pinctrl_match, &pdev->dev)->data; + /* Poke in other ASIC variants here */ - if (platid->driver_data == PINCTRL_NMK_DB8500) + if (version == PINCTRL_NMK_DB8500) nmk_pinctrl_db8500_init(&npct->soc); /* @@ -1758,6 +1775,7 @@ static struct platform_driver nmk_pinctrl_driver = { .driver = { .owner = THIS_MODULE, .name = "pinctrl-nomadik", + .of_match_table = nmk_pinctrl_match, }, .probe = nmk_pinctrl_probe, .id_table = nmk_pinctrl_id, diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index c1a3fd8e1243..ce875dc365e5 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -523,6 +523,30 @@ static const struct dmi_system_id video_vendor_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4750"), }, }, + { + .callback = video_set_backlight_video_vendor, + .ident = "Acer Extensa 5235", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Extensa 5235"), + }, + }, + { + .callback = video_set_backlight_video_vendor, + .ident = "Acer TravelMate 5760", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 5760"), + }, + }, + { + .callback = video_set_backlight_video_vendor, + .ident = "Acer Aspire 5750", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5750"), + }, + }, {} }; diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 8a582bdfdc76..694a15a56230 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -87,6 +87,9 @@ static int gmux_update_status(struct backlight_device *bd) struct apple_gmux_data *gmux_data = bl_get_data(bd); u32 brightness = bd->props.brightness; + if (bd->props.state & BL_CORE_SUSPENDED) + return 0; + /* * Older gmux versions require writing out lower bytes first then * setting the upper byte to 0 to flush the values. Newer versions @@ -102,6 +105,7 @@ static int gmux_update_status(struct backlight_device *bd) } static const struct backlight_ops gmux_bl_ops = { + .options = BL_CORE_SUSPENDRESUME, .get_brightness = gmux_get_brightness, .update_status = gmux_update_status, }; diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index e6c08ee8d46c..5f78aac9b163 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -21,7 +21,6 @@ #include <linux/err.h> #include <linux/dmi.h> #include <linux/io.h> -#include <linux/rfkill.h> #include <linux/power_supply.h> #include <linux/acpi.h> #include <linux/mm.h> @@ -90,11 +89,8 @@ static struct platform_driver platform_driver = { static struct platform_device *platform_device; static struct backlight_device *dell_backlight_device; -static struct rfkill *wifi_rfkill; -static struct rfkill *bluetooth_rfkill; -static struct rfkill *wwan_rfkill; -static const struct dmi_system_id __initdata dell_device_table[] = { +static const struct dmi_system_id dell_device_table[] __initconst = { { .ident = "Dell laptop", .matches = { @@ -119,96 +115,94 @@ static const struct dmi_system_id __initdata dell_device_table[] = { }; MODULE_DEVICE_TABLE(dmi, dell_device_table); -static struct dmi_system_id __devinitdata dell_blacklist[] = { - /* Supported by compal-laptop */ - { - .ident = "Dell Mini 9", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"), - }, - }, +static struct dmi_system_id __devinitdata dell_quirks[] = { { - .ident = "Dell Mini 10", + .callback = dmi_matched, + .ident = "Dell Vostro V130", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"), }, + .driver_data = &quirk_dell_vostro_v130, }, { - .ident = "Dell Mini 10v", + .callback = dmi_matched, + .ident = "Dell Vostro V131", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"), }, + .driver_data = &quirk_dell_vostro_v130, }, { - .ident = "Dell Mini 1012", + .callback = dmi_matched, + .ident = "Dell Vostro 3350", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3350"), }, + .driver_data = &quirk_dell_vostro_v130, }, { - .ident = "Dell Inspiron 11z", + .callback = dmi_matched, + .ident = "Dell Vostro 3555", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3555"), }, + .driver_data = &quirk_dell_vostro_v130, }, { - .ident = "Dell Mini 12", + .callback = dmi_matched, + .ident = "Dell Inspiron N311z", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N311z"), }, + .driver_data = &quirk_dell_vostro_v130, }, - {} -}; - -static struct dmi_system_id __devinitdata dell_quirks[] = { { .callback = dmi_matched, - .ident = "Dell Vostro V130", + .ident = "Dell Inspiron M5110", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"), }, .driver_data = &quirk_dell_vostro_v130, }, { .callback = dmi_matched, - .ident = "Dell Vostro V131", + .ident = "Dell Vostro 3360", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3360"), }, .driver_data = &quirk_dell_vostro_v130, }, { .callback = dmi_matched, - .ident = "Dell Vostro 3555", + .ident = "Dell Vostro 3460", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3555"), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3460"), }, .driver_data = &quirk_dell_vostro_v130, }, { .callback = dmi_matched, - .ident = "Dell Inspiron N311z", + .ident = "Dell Vostro 3560", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N311z"), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3560"), }, .driver_data = &quirk_dell_vostro_v130, }, { .callback = dmi_matched, - .ident = "Dell Inspiron M5110", + .ident = "Dell Vostro 3450", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell System Vostro 3450"), }, .driver_data = &quirk_dell_vostro_v130, }, @@ -305,94 +299,6 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, return buffer; } -/* Derived from information in DellWirelessCtl.cpp: - Class 17, select 11 is radio control. It returns an array of 32-bit values. - - Input byte 0 = 0: Wireless information - - result[0]: return code - result[1]: - Bit 0: Hardware switch supported - Bit 1: Wifi locator supported - Bit 2: Wifi is supported - Bit 3: Bluetooth is supported - Bit 4: WWAN is supported - Bit 5: Wireless keyboard supported - Bits 6-7: Reserved - Bit 8: Wifi is installed - Bit 9: Bluetooth is installed - Bit 10: WWAN is installed - Bits 11-15: Reserved - Bit 16: Hardware switch is on - Bit 17: Wifi is blocked - Bit 18: Bluetooth is blocked - Bit 19: WWAN is blocked - Bits 20-31: Reserved - result[2]: NVRAM size in bytes - result[3]: NVRAM format version number - - Input byte 0 = 2: Wireless switch configuration - result[0]: return code - result[1]: - Bit 0: Wifi controlled by switch - Bit 1: Bluetooth controlled by switch - Bit 2: WWAN controlled by switch - Bits 3-6: Reserved - Bit 7: Wireless switch config locked - Bit 8: Wifi locator enabled - Bits 9-14: Reserved - Bit 15: Wifi locator setting locked - Bits 16-31: Reserved -*/ - -static int dell_rfkill_set(void *data, bool blocked) -{ - int disable = blocked ? 1 : 0; - unsigned long radio = (unsigned long)data; - int hwswitch_bit = (unsigned long)data - 1; - int ret = 0; - - get_buffer(); - dell_send_request(buffer, 17, 11); - - /* If the hardware switch controls this radio, and the hardware - switch is disabled, don't allow changing the software state */ - if ((hwswitch_state & BIT(hwswitch_bit)) && - !(buffer->output[1] & BIT(16))) { - ret = -EINVAL; - goto out; - } - - buffer->input[0] = (1 | (radio<<8) | (disable << 16)); - dell_send_request(buffer, 17, 11); - -out: - release_buffer(); - return ret; -} - -static void dell_rfkill_query(struct rfkill *rfkill, void *data) -{ - int status; - int bit = (unsigned long)data + 16; - int hwswitch_bit = (unsigned long)data - 1; - - get_buffer(); - dell_send_request(buffer, 17, 11); - status = buffer->output[1]; - release_buffer(); - - rfkill_set_sw_state(rfkill, !!(status & BIT(bit))); - - if (hwswitch_state & (BIT(hwswitch_bit))) - rfkill_set_hw_state(rfkill, !(status & BIT(16))); -} - -static const struct rfkill_ops dell_rfkill_ops = { - .set_block = dell_rfkill_set, - .query = dell_rfkill_query, -}; - static struct dentry *dell_laptop_dir; static int dell_debugfs_show(struct seq_file *s, void *data) @@ -462,108 +368,6 @@ static const struct file_operations dell_debugfs_fops = { .release = single_release, }; -static void dell_update_rfkill(struct work_struct *ignored) -{ - if (wifi_rfkill) - dell_rfkill_query(wifi_rfkill, (void *)1); - if (bluetooth_rfkill) - dell_rfkill_query(bluetooth_rfkill, (void *)2); - if (wwan_rfkill) - dell_rfkill_query(wwan_rfkill, (void *)3); -} -static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill); - - -static int __init dell_setup_rfkill(void) -{ - int status; - int ret; - - if (dmi_check_system(dell_blacklist)) { - pr_info("Blacklisted hardware detected - not enabling rfkill\n"); - return 0; - } - - get_buffer(); - dell_send_request(buffer, 17, 11); - status = buffer->output[1]; - buffer->input[0] = 0x2; - dell_send_request(buffer, 17, 11); - hwswitch_state = buffer->output[1]; - release_buffer(); - - if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { - wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev, - RFKILL_TYPE_WLAN, - &dell_rfkill_ops, (void *) 1); - if (!wifi_rfkill) { - ret = -ENOMEM; - goto err_wifi; - } - ret = rfkill_register(wifi_rfkill); - if (ret) - goto err_wifi; - } - - if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) { - bluetooth_rfkill = rfkill_alloc("dell-bluetooth", - &platform_device->dev, - RFKILL_TYPE_BLUETOOTH, - &dell_rfkill_ops, (void *) 2); - if (!bluetooth_rfkill) { - ret = -ENOMEM; - goto err_bluetooth; - } - ret = rfkill_register(bluetooth_rfkill); - if (ret) - goto err_bluetooth; - } - - if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) { - wwan_rfkill = rfkill_alloc("dell-wwan", - &platform_device->dev, - RFKILL_TYPE_WWAN, - &dell_rfkill_ops, (void *) 3); - if (!wwan_rfkill) { - ret = -ENOMEM; - goto err_wwan; - } - ret = rfkill_register(wwan_rfkill); - if (ret) - goto err_wwan; - } - - return 0; -err_wwan: - rfkill_destroy(wwan_rfkill); - if (bluetooth_rfkill) - rfkill_unregister(bluetooth_rfkill); -err_bluetooth: - rfkill_destroy(bluetooth_rfkill); - if (wifi_rfkill) - rfkill_unregister(wifi_rfkill); -err_wifi: - rfkill_destroy(wifi_rfkill); - - return ret; -} - -static void dell_cleanup_rfkill(void) -{ - if (wifi_rfkill) { - rfkill_unregister(wifi_rfkill); - rfkill_destroy(wifi_rfkill); - } - if (bluetooth_rfkill) { - rfkill_unregister(bluetooth_rfkill); - rfkill_destroy(bluetooth_rfkill); - } - if (wwan_rfkill) { - rfkill_unregister(wwan_rfkill); - rfkill_destroy(wwan_rfkill); - } -} - static int dell_send_intensity(struct backlight_device *bd) { int ret = 0; @@ -655,30 +459,6 @@ static void touchpad_led_exit(void) led_classdev_unregister(&touchpad_led); } -static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, - struct serio *port) -{ - static bool extended; - - if (str & 0x20) - return false; - - if (unlikely(data == 0xe0)) { - extended = true; - return false; - } else if (unlikely(extended)) { - switch (data) { - case 0x8: - schedule_delayed_work(&dell_rfkill_work, - round_jiffies_relative(HZ)); - break; - } - extended = false; - } - - return false; -} - static int __init dell_init(void) { int max_intensity = 0; @@ -720,26 +500,10 @@ static int __init dell_init(void) goto fail_buffer; buffer = page_address(bufferpage); - ret = dell_setup_rfkill(); - - if (ret) { - pr_warn("Unable to setup rfkill\n"); - goto fail_rfkill; - } - - ret = i8042_install_filter(dell_laptop_i8042_filter); - if (ret) { - pr_warn("Unable to install key filter\n"); - goto fail_filter; - } - if (quirks && quirks->touchpad_led) touchpad_led_init(&platform_device->dev); dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); - if (dell_laptop_dir != NULL) - debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, - &dell_debugfs_fops); #ifdef CONFIG_ACPI /* In the event of an ACPI backlight being available, don't @@ -782,11 +546,6 @@ static int __init dell_init(void) return 0; fail_backlight: - i8042_remove_filter(dell_laptop_i8042_filter); - cancel_delayed_work_sync(&dell_rfkill_work); -fail_filter: - dell_cleanup_rfkill(); -fail_rfkill: free_page((unsigned long)bufferpage); fail_buffer: platform_device_del(platform_device); @@ -804,10 +563,7 @@ static void __exit dell_exit(void) debugfs_remove_recursive(dell_laptop_dir); if (quirks && quirks->touchpad_led) touchpad_led_exit(); - i8042_remove_filter(dell_laptop_i8042_filter); - cancel_delayed_work_sync(&dell_rfkill_work); backlight_device_unregister(dell_backlight_device); - dell_cleanup_rfkill(); if (platform_device) { platform_device_unregister(platform_device); platform_driver_unregister(&platform_driver); diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index 580d80a73c3a..da267eae8ba8 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -16,6 +16,8 @@ * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -34,7 +36,8 @@ #define ACPI_FUJITSU_CLASS "fujitsu" #define INVERT_TABLET_MODE_BIT 0x01 -#define FORCE_TABLET_MODE_IF_UNDOCK 0x02 +#define INVERT_DOCK_STATE_BIT 0x02 +#define FORCE_TABLET_MODE_IF_UNDOCK 0x04 #define KEYMAP_LEN 16 @@ -161,6 +164,8 @@ static void fujitsu_send_state(void) state = fujitsu_read_register(0xdd); dock = state & 0x02; + if (fujitsu.config.quirks & INVERT_DOCK_STATE_BIT) + dock = !dock; if ((fujitsu.config.quirks & FORCE_TABLET_MODE_IF_UNDOCK) && (!dock)) { tablet_mode = 1; @@ -221,9 +226,6 @@ static int __devinit input_fujitsu_setup(struct device *parent, input_set_capability(idev, EV_SW, SW_DOCK); input_set_capability(idev, EV_SW, SW_TABLET_MODE); - input_set_capability(idev, EV_SW, SW_DOCK); - input_set_capability(idev, EV_SW, SW_TABLET_MODE); - error = input_register_device(idev); if (error) { input_free_device(idev); @@ -275,25 +277,31 @@ static irqreturn_t fujitsu_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit fujitsu_dmi_default(const struct dmi_system_id *dmi) +static void __devinit fujitsu_dmi_common(const struct dmi_system_id *dmi) { - printk(KERN_INFO MODULENAME ": %s\n", dmi->ident); + pr_info("%s\n", dmi->ident); memcpy(fujitsu.config.keymap, dmi->driver_data, sizeof(fujitsu.config.keymap)); +} + +static int __devinit fujitsu_dmi_lifebook(const struct dmi_system_id *dmi) +{ + fujitsu_dmi_common(dmi); + fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT; return 1; } static int __devinit fujitsu_dmi_stylistic(const struct dmi_system_id *dmi) { - fujitsu_dmi_default(dmi); + fujitsu_dmi_common(dmi); fujitsu.config.quirks |= FORCE_TABLET_MODE_IF_UNDOCK; - fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT; + fujitsu.config.quirks |= INVERT_DOCK_STATE_BIT; return 1; } static struct dmi_system_id dmi_ids[] __initconst = { { - .callback = fujitsu_dmi_default, + .callback = fujitsu_dmi_lifebook, .ident = "Fujitsu Siemens P/T Series", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), @@ -302,7 +310,7 @@ static struct dmi_system_id dmi_ids[] __initconst = { .driver_data = keymap_Lifebook_Tseries }, { - .callback = fujitsu_dmi_default, + .callback = fujitsu_dmi_lifebook, .ident = "Fujitsu Lifebook T Series", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), @@ -320,7 +328,7 @@ static struct dmi_system_id dmi_ids[] __initconst = { .driver_data = keymap_Stylistic_Tseries }, { - .callback = fujitsu_dmi_default, + .callback = fujitsu_dmi_lifebook, .ident = "Fujitsu LifeBook U810", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), @@ -347,7 +355,7 @@ static struct dmi_system_id dmi_ids[] __initconst = { .driver_data = keymap_Stylistic_ST5xxx }, { - .callback = fujitsu_dmi_default, + .callback = fujitsu_dmi_lifebook, .ident = "Unknown (using defaults)", .matches = { DMI_MATCH(DMI_SYS_VENDOR, ""), @@ -473,6 +481,6 @@ module_exit(fujitsu_module_exit); MODULE_AUTHOR("Robert Gerlach <khnz@gmx.de>"); MODULE_DESCRIPTION("Fujitsu tablet pc extras driver"); MODULE_LICENSE("GPL"); -MODULE_VERSION("2.4"); +MODULE_VERSION("2.5"); MODULE_DEVICE_TABLE(acpi, fujitsu_ids); diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c index 7387f97a2941..24a3ae065f1b 100644 --- a/drivers/platform/x86/hdaps.c +++ b/drivers/platform/x86/hdaps.c @@ -2,7 +2,7 @@ * hdaps.c - driver for IBM's Hard Drive Active Protection System * * Copyright (C) 2005 Robert Love <rml@novell.com> - * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com> + * Copyright (C) 2005 Jesper Juhl <jj@chaosbits.net> * * The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads * starting with the R40, T41, and X40. It provides a basic two-axis diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index e2faa3cbb792..387183a2d6dd 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -634,6 +634,8 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) RFKILL_TYPE_WLAN, &hp_wmi_rfkill_ops, (void *) HPWMI_WIFI); + if (!wifi_rfkill) + return -ENOMEM; rfkill_init_sw_state(wifi_rfkill, hp_wmi_get_sw_state(HPWMI_WIFI)); rfkill_set_hw_state(wifi_rfkill, @@ -648,6 +650,10 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) RFKILL_TYPE_BLUETOOTH, &hp_wmi_rfkill_ops, (void *) HPWMI_BLUETOOTH); + if (!bluetooth_rfkill) { + err = -ENOMEM; + goto register_wifi_error; + } rfkill_init_sw_state(bluetooth_rfkill, hp_wmi_get_sw_state(HPWMI_BLUETOOTH)); rfkill_set_hw_state(bluetooth_rfkill, @@ -662,6 +668,10 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) RFKILL_TYPE_WWAN, &hp_wmi_rfkill_ops, (void *) HPWMI_WWAN); + if (!wwan_rfkill) { + err = -ENOMEM; + goto register_bluetooth_error; + } rfkill_init_sw_state(wwan_rfkill, hp_wmi_get_sw_state(HPWMI_WWAN)); rfkill_set_hw_state(wwan_rfkill, diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index ac902f7a9baa..4f20f8dd3d7c 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -194,7 +194,6 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) /* * debugfs */ -#define DEBUGFS_EVENT_LEN (4096) static int debugfs_status_show(struct seq_file *s, void *data) { unsigned long value; @@ -315,7 +314,7 @@ static int __devinit ideapad_debugfs_init(struct ideapad_private *priv) node = debugfs_create_file("status", S_IRUGO, priv->debug, NULL, &debugfs_status_fops); if (!node) { - pr_err("failed to create event in debugfs"); + pr_err("failed to create status in debugfs"); goto errout; } @@ -785,6 +784,10 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) case 9: ideapad_sync_rfk_state(priv); break; + case 13: + case 6: + ideapad_input_report(priv, vpc_bit); + break; case 4: ideapad_backlight_notify_brightness(priv); break; @@ -795,7 +798,7 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) ideapad_backlight_notify_power(priv); break; default: - ideapad_input_report(priv, vpc_bit); + pr_info("Unknown event: %lu\n", vpc_bit); } } } diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 8a51795aa02a..210d4ae547c2 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -141,6 +141,27 @@ MODULE_PARM_DESC(kbd_backlight_timeout, "(default: 0)"); static void sony_nc_kbd_backlight_resume(void); +static int sony_nc_kbd_backlight_setup(struct platform_device *pd, + unsigned int handle); +static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd); + +static int sony_nc_battery_care_setup(struct platform_device *pd, + unsigned int handle); +static void sony_nc_battery_care_cleanup(struct platform_device *pd); + +static int sony_nc_thermal_setup(struct platform_device *pd); +static void sony_nc_thermal_cleanup(struct platform_device *pd); +static void sony_nc_thermal_resume(void); + +static int sony_nc_lid_resume_setup(struct platform_device *pd); +static void sony_nc_lid_resume_cleanup(struct platform_device *pd); + +static int sony_nc_highspeed_charging_setup(struct platform_device *pd); +static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd); + +static int sony_nc_touchpad_setup(struct platform_device *pd, + unsigned int handle); +static void sony_nc_touchpad_cleanup(struct platform_device *pd); enum sony_nc_rfkill { SONY_WIFI, @@ -153,6 +174,9 @@ enum sony_nc_rfkill { static int sony_rfkill_handle; static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL]; static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900}; +static int sony_nc_rfkill_setup(struct acpi_device *device, + unsigned int handle); +static void sony_nc_rfkill_cleanup(void); static void sony_nc_rfkill_update(void); /*********** Input Devices ***********/ @@ -691,59 +715,97 @@ static struct acpi_device *sony_nc_acpi_device = NULL; /* * acpi_evaluate_object wrappers + * all useful calls into SNC methods take one or zero parameters and return + * integers or arrays. */ -static int acpi_callgetfunc(acpi_handle handle, char *name, int *result) +static union acpi_object *__call_snc_method(acpi_handle handle, char *method, + u64 *value) { - struct acpi_buffer output; - union acpi_object out_obj; + union acpi_object *result = NULL; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_status status; - output.length = sizeof(out_obj); - output.pointer = &out_obj; + if (value) { + struct acpi_object_list params; + union acpi_object in; + in.type = ACPI_TYPE_INTEGER; + in.integer.value = *value; + params.count = 1; + params.pointer = ∈ + status = acpi_evaluate_object(handle, method, ¶ms, &output); + dprintk("__call_snc_method: [%s:0x%.8x%.8x]\n", method, + (unsigned int)(*value >> 32), + (unsigned int)*value & 0xffffffff); + } else { + status = acpi_evaluate_object(handle, method, NULL, &output); + dprintk("__call_snc_method: [%s]\n", method); + } - status = acpi_evaluate_object(handle, name, NULL, &output); - if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) { - *result = out_obj.integer.value; - return 0; + if (ACPI_FAILURE(status)) { + pr_err("Failed to evaluate [%s]\n", method); + return NULL; } - pr_warn("acpi_callreadfunc failed\n"); + result = (union acpi_object *) output.pointer; + if (!result) + dprintk("No return object [%s]\n", method); - return -1; + return result; } -static int acpi_callsetfunc(acpi_handle handle, char *name, int value, - int *result) +static int sony_nc_int_call(acpi_handle handle, char *name, int *value, + int *result) { - struct acpi_object_list params; - union acpi_object in_obj; - struct acpi_buffer output; - union acpi_object out_obj; - acpi_status status; - - params.count = 1; - params.pointer = &in_obj; - in_obj.type = ACPI_TYPE_INTEGER; - in_obj.integer.value = value; + union acpi_object *object = NULL; + if (value) { + u64 v = *value; + object = __call_snc_method(handle, name, &v); + } else + object = __call_snc_method(handle, name, NULL); - output.length = sizeof(out_obj); - output.pointer = &out_obj; + if (!object) + return -EINVAL; - status = acpi_evaluate_object(handle, name, ¶ms, &output); - if (status == AE_OK) { - if (result != NULL) { - if (out_obj.type != ACPI_TYPE_INTEGER) { - pr_warn("acpi_evaluate_object bad return type\n"); - return -1; - } - *result = out_obj.integer.value; - } - return 0; + if (object->type != ACPI_TYPE_INTEGER) { + pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n", + ACPI_TYPE_INTEGER, object->type); + kfree(object); + return -EINVAL; } - pr_warn("acpi_evaluate_object failed\n"); + if (result) + *result = object->integer.value; + + kfree(object); + return 0; +} + +#define MIN(a, b) (a > b ? b : a) +static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value, + void *buffer, size_t buflen) +{ + size_t len = len; + union acpi_object *object = __call_snc_method(handle, name, value); + + if (!object) + return -EINVAL; + + if (object->type == ACPI_TYPE_BUFFER) + len = MIN(buflen, object->buffer.length); + + else if (object->type == ACPI_TYPE_INTEGER) + len = MIN(buflen, sizeof(object->integer.value)); + + else { + pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n", + ACPI_TYPE_BUFFER, object->type); + kfree(object); + return -EINVAL; + } - return -1; + memcpy(buffer, object->buffer.pointer, len); + kfree(object); + return 0; } struct sony_nc_handles { @@ -770,16 +832,17 @@ static ssize_t sony_nc_handles_show(struct device *dev, static int sony_nc_handles_setup(struct platform_device *pd) { - int i; - int result; + int i, r, result, arg; handles = kzalloc(sizeof(*handles), GFP_KERNEL); if (!handles) return -ENOMEM; for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { - if (!acpi_callsetfunc(sony_nc_acpi_handle, - "SN00", i + 0x20, &result)) { + arg = i + 0x20; + r = sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, + &result); + if (!r) { dprintk("caching handle 0x%.4x (offset: 0x%.2x)\n", result, i); handles->cap[i] = result; @@ -819,8 +882,8 @@ static int sony_find_snc_handle(int handle) int i; /* not initialized yet, return early */ - if (!handles) - return -1; + if (!handles || !handle) + return -EINVAL; for (i = 0; i < 0x10; i++) { if (handles->cap[i] == handle) { @@ -830,21 +893,20 @@ static int sony_find_snc_handle(int handle) } } dprintk("handle 0x%.4x not found\n", handle); - return -1; + return -EINVAL; } static int sony_call_snc_handle(int handle, int argument, int *result) { - int ret = 0; + int arg, ret = 0; int offset = sony_find_snc_handle(handle); if (offset < 0) - return -1; + return offset; - ret = acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument, - result); - dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", offset | argument, - *result); + arg = offset | argument; + ret = sony_nc_int_call(sony_nc_acpi_handle, "SN07", &arg, result); + dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", arg, *result); return ret; } @@ -889,14 +951,16 @@ static int boolean_validate(const int direction, const int value) static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr, char *buffer) { - int value; + int value, ret = 0; struct sony_nc_value *item = container_of(attr, struct sony_nc_value, devattr); if (!*item->acpiget) return -EIO; - if (acpi_callgetfunc(sony_nc_acpi_handle, *item->acpiget, &value) < 0) + ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiget, NULL, + &value); + if (ret < 0) return -EIO; if (item->validate) @@ -909,7 +973,8 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, struct device_attribute *attr, const char *buffer, size_t count) { - int value; + unsigned long value = 0; + int ret = 0; struct sony_nc_value *item = container_of(attr, struct sony_nc_value, devattr); @@ -919,7 +984,8 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, if (count > 31) return -EINVAL; - value = simple_strtoul(buffer, NULL, 10); + if (kstrtoul(buffer, 10, &value)) + return -EINVAL; if (item->validate) value = item->validate(SNC_VALIDATE_IN, value); @@ -927,8 +993,11 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, if (value < 0) return value; - if (acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, value, NULL) < 0) + ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiset, + (int *)&value, NULL); + if (ret < 0) return -EIO; + item->value = value; item->valid = 1; return count; @@ -948,15 +1017,15 @@ struct sony_backlight_props sony_bl_props; static int sony_backlight_update_status(struct backlight_device *bd) { - return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", - bd->props.brightness + 1, NULL); + int arg = bd->props.brightness + 1; + return sony_nc_int_call(sony_nc_acpi_handle, "SBRT", &arg, NULL); } static int sony_backlight_get_brightness(struct backlight_device *bd) { int value; - if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) + if (sony_nc_int_call(sony_nc_acpi_handle, "GBRT", NULL, &value)) return 0; /* brightness levels are 1-based, while backlight ones are 0-based */ return value - 1; @@ -1024,10 +1093,14 @@ static struct sony_nc_event sony_100_events[] = { { 0x06, SONYPI_EVENT_FNKEY_RELEASED }, { 0x87, SONYPI_EVENT_FNKEY_F7 }, { 0x07, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x88, SONYPI_EVENT_FNKEY_F8 }, + { 0x08, SONYPI_EVENT_FNKEY_RELEASED }, { 0x89, SONYPI_EVENT_FNKEY_F9 }, { 0x09, SONYPI_EVENT_FNKEY_RELEASED }, { 0x8A, SONYPI_EVENT_FNKEY_F10 }, { 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x8B, SONYPI_EVENT_FNKEY_F11 }, + { 0x0B, SONYPI_EVENT_FNKEY_RELEASED }, { 0x8C, SONYPI_EVENT_FNKEY_F12 }, { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, { 0x9d, SONYPI_EVENT_ZOOM_PRESSED }, @@ -1063,63 +1136,116 @@ static struct sony_nc_event sony_127_events[] = { { 0, 0 }, }; +static int sony_nc_hotkeys_decode(u32 event, unsigned int handle) +{ + int ret = -EINVAL; + unsigned int result = 0; + struct sony_nc_event *key_event; + + if (sony_call_snc_handle(handle, 0x200, &result)) { + dprintk("Unable to decode event 0x%.2x 0x%.2x\n", handle, + event); + return -EINVAL; + } + + result &= 0xFF; + + if (handle == 0x0100) + key_event = sony_100_events; + else + key_event = sony_127_events; + + for (; key_event->data; key_event++) { + if (key_event->data == result) { + ret = key_event->event; + break; + } + } + + if (!key_event->data) + pr_info("Unknown hotkey 0x%.2x/0x%.2x (handle 0x%.2x)\n", + event, result, handle); + + return ret; +} + /* * ACPI callbacks */ static void sony_nc_notify(struct acpi_device *device, u32 event) { - u32 ev = event; + u32 real_ev = event; + u8 ev_type = 0; + dprintk("sony_nc_notify, event: 0x%.2x\n", event); + + if (event >= 0x90) { + unsigned int result = 0; + unsigned int arg = 0; + unsigned int handle = 0; + unsigned int offset = event - 0x90; + + if (offset >= ARRAY_SIZE(handles->cap)) { + pr_err("Event 0x%x outside of capabilities list\n", + event); + return; + } + handle = handles->cap[offset]; + + /* list of handles known for generating events */ + switch (handle) { + /* hotkey event */ + case 0x0100: + case 0x0127: + ev_type = 1; + real_ev = sony_nc_hotkeys_decode(event, handle); + + if (real_ev > 0) + sony_laptop_report_input_event(real_ev); + else + /* restore the original event for reporting */ + real_ev = event; - if (ev >= 0x90) { - /* New-style event */ - int result; - int key_handle = 0; - ev -= 0x90; - - if (sony_find_snc_handle(0x100) == ev) - key_handle = 0x100; - if (sony_find_snc_handle(0x127) == ev) - key_handle = 0x127; - - if (key_handle) { - struct sony_nc_event *key_event; - - if (sony_call_snc_handle(key_handle, 0x200, &result)) { - dprintk("sony_nc_notify, unable to decode" - " event 0x%.2x 0x%.2x\n", key_handle, - ev); - /* restore the original event */ - ev = event; - } else { - ev = result & 0xFF; - - if (key_handle == 0x100) - key_event = sony_100_events; - else - key_event = sony_127_events; - - for (; key_event->data; key_event++) { - if (key_event->data == ev) { - ev = key_event->event; - break; - } - } + break; - if (!key_event->data) - pr_info("Unknown event: 0x%x 0x%x\n", - key_handle, ev); - else - sony_laptop_report_input_event(ev); - } - } else if (sony_find_snc_handle(sony_rfkill_handle) == ev) { - sony_nc_rfkill_update(); - return; + /* wlan switch */ + case 0x0124: + case 0x0135: + /* events on this handle are reported when the + * switch changes position or for battery + * events. We'll notify both of them but only + * update the rfkill device status when the + * switch is moved. + */ + ev_type = 2; + sony_call_snc_handle(handle, 0x0100, &result); + real_ev = result & 0x03; + + /* hw switch event */ + if (real_ev == 1) + sony_nc_rfkill_update(); + + break; + + default: + dprintk("Unknown event 0x%x for handle 0x%x\n", + event, handle); + break; } - } else - sony_laptop_report_input_event(ev); - dprintk("sony_nc_notify, event: 0x%.2x\n", ev); - acpi_bus_generate_proc_event(sony_nc_acpi_device, 1, ev); + /* clear the event (and the event reason when present) */ + arg = 1 << offset; + sony_nc_int_call(sony_nc_acpi_handle, "SN05", &arg, &result); + + } else { + /* old style event */ + ev_type = 1; + sony_laptop_report_input_event(real_ev); + } + + acpi_bus_generate_proc_event(sony_nc_acpi_device, ev_type, real_ev); + + acpi_bus_generate_netlink_event(sony_nc_acpi_device->pnp.device_class, + dev_name(&sony_nc_acpi_device->dev), ev_type, real_ev); } static acpi_status sony_walk_callback(acpi_handle handle, u32 level, @@ -1140,20 +1266,190 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level, /* * ACPI device */ -static int sony_nc_function_setup(struct acpi_device *device) +static void sony_nc_function_setup(struct acpi_device *device, + struct platform_device *pf_device) { - int result; + unsigned int i, result, bitmask, arg; + + if (!handles) + return; + + /* setup found handles here */ + for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { + unsigned int handle = handles->cap[i]; + + if (!handle) + continue; + + dprintk("setting up handle 0x%.4x\n", handle); + + switch (handle) { + case 0x0100: + case 0x0101: + case 0x0127: + /* setup hotkeys */ + sony_call_snc_handle(handle, 0, &result); + break; + case 0x0102: + /* setup hotkeys */ + sony_call_snc_handle(handle, 0x100, &result); + break; + case 0x0105: + case 0x0148: + /* touchpad enable/disable */ + result = sony_nc_touchpad_setup(pf_device, handle); + if (result) + pr_err("couldn't set up touchpad control function (%d)\n", + result); + break; + case 0x0115: + case 0x0136: + case 0x013f: + result = sony_nc_battery_care_setup(pf_device, handle); + if (result) + pr_err("couldn't set up battery care function (%d)\n", + result); + break; + case 0x0119: + result = sony_nc_lid_resume_setup(pf_device); + if (result) + pr_err("couldn't set up lid resume function (%d)\n", + result); + break; + case 0x0122: + result = sony_nc_thermal_setup(pf_device); + if (result) + pr_err("couldn't set up thermal profile function (%d)\n", + result); + break; + case 0x0131: + result = sony_nc_highspeed_charging_setup(pf_device); + if (result) + pr_err("couldn't set up high speed charging function (%d)\n", + result); + break; + case 0x0124: + case 0x0135: + result = sony_nc_rfkill_setup(device, handle); + if (result) + pr_err("couldn't set up rfkill support (%d)\n", + result); + break; + case 0x0137: + case 0x0143: + result = sony_nc_kbd_backlight_setup(pf_device, handle); + if (result) + pr_err("couldn't set up keyboard backlight function (%d)\n", + result); + break; + default: + continue; + } + } /* Enable all events */ - acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0xffff, &result); + arg = 0x10; + if (!sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, &bitmask)) + sony_nc_int_call(sony_nc_acpi_handle, "SN02", &bitmask, + &result); +} + +static void sony_nc_function_cleanup(struct platform_device *pd) +{ + unsigned int i, result, bitmask, handle; - /* Setup hotkeys */ - sony_call_snc_handle(0x0100, 0, &result); - sony_call_snc_handle(0x0101, 0, &result); - sony_call_snc_handle(0x0102, 0x100, &result); - sony_call_snc_handle(0x0127, 0, &result); + /* get enabled events and disable them */ + sony_nc_int_call(sony_nc_acpi_handle, "SN01", NULL, &bitmask); + sony_nc_int_call(sony_nc_acpi_handle, "SN03", &bitmask, &result); - return 0; + /* cleanup handles here */ + for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { + + handle = handles->cap[i]; + + if (!handle) + continue; + + switch (handle) { + case 0x0105: + case 0x0148: + sony_nc_touchpad_cleanup(pd); + break; + case 0x0115: + case 0x0136: + case 0x013f: + sony_nc_battery_care_cleanup(pd); + break; + case 0x0119: + sony_nc_lid_resume_cleanup(pd); + break; + case 0x0122: + sony_nc_thermal_cleanup(pd); + break; + case 0x0131: + sony_nc_highspeed_charging_cleanup(pd); + break; + case 0x0124: + case 0x0135: + sony_nc_rfkill_cleanup(); + break; + case 0x0137: + case 0x0143: + sony_nc_kbd_backlight_cleanup(pd); + break; + default: + continue; + } + } + + /* finally cleanup the handles list */ + sony_nc_handles_cleanup(pd); +} + +static void sony_nc_function_resume(void) +{ + unsigned int i, result, bitmask, arg; + + dprintk("Resuming SNC device\n"); + + for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { + unsigned int handle = handles->cap[i]; + + if (!handle) + continue; + + switch (handle) { + case 0x0100: + case 0x0101: + case 0x0127: + /* re-enable hotkeys */ + sony_call_snc_handle(handle, 0, &result); + break; + case 0x0102: + /* re-enable hotkeys */ + sony_call_snc_handle(handle, 0x100, &result); + break; + case 0x0122: + sony_nc_thermal_resume(); + break; + case 0x0124: + case 0x0135: + sony_nc_rfkill_update(); + break; + case 0x0137: + case 0x0143: + sony_nc_kbd_backlight_resume(); + break; + default: + continue; + } + } + + /* Enable all events */ + arg = 0x10; + if (!sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, &bitmask)) + sony_nc_int_call(sony_nc_acpi_handle, "SN02", &bitmask, + &result); } static int sony_nc_resume(struct acpi_device *device) @@ -1166,8 +1462,8 @@ static int sony_nc_resume(struct acpi_device *device) if (!item->valid) continue; - ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, - item->value, NULL); + ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiset, + &item->value, NULL); if (ret < 0) { pr_err("%s: %d\n", __func__, ret); break; @@ -1176,21 +1472,14 @@ static int sony_nc_resume(struct acpi_device *device) if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", &handle))) { - if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL)) + int arg = 1; + if (sony_nc_int_call(sony_nc_acpi_handle, "ECON", &arg, NULL)) dprintk("ECON Method failed\n"); } if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", - &handle))) { - dprintk("Doing SNC setup\n"); - sony_nc_function_setup(device); - } - - /* re-read rfkill state */ - sony_nc_rfkill_update(); - - /* restore kbd backlight states */ - sony_nc_kbd_backlight_resume(); + &handle))) + sony_nc_function_resume(); return 0; } @@ -1213,7 +1502,7 @@ static int sony_nc_rfkill_set(void *data, bool blocked) int argument = sony_rfkill_address[(long) data] + 0x100; if (!blocked) - argument |= 0xff0000; + argument |= 0x030000; return sony_call_snc_handle(sony_rfkill_handle, argument, &result); } @@ -1230,7 +1519,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, enum rfkill_type type; const char *name; int result; - bool hwblock; + bool hwblock, swblock; switch (nc_type) { case SONY_WIFI: @@ -1258,8 +1547,21 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, if (!rfk) return -ENOMEM; - sony_call_snc_handle(sony_rfkill_handle, 0x200, &result); + if (sony_call_snc_handle(sony_rfkill_handle, 0x200, &result) < 0) { + rfkill_destroy(rfk); + return -1; + } hwblock = !(result & 0x1); + + if (sony_call_snc_handle(sony_rfkill_handle, + sony_rfkill_address[nc_type], + &result) < 0) { + rfkill_destroy(rfk); + return -1; + } + swblock = !(result & 0x2); + + rfkill_init_sw_state(rfk, swblock); rfkill_set_hw_state(rfk, hwblock); err = rfkill_register(rfk); @@ -1295,101 +1597,79 @@ static void sony_nc_rfkill_update(void) sony_call_snc_handle(sony_rfkill_handle, argument, &result); rfkill_set_states(sony_rfkill_devices[i], - !(result & 0xf), false); + !(result & 0x2), false); } } -static void sony_nc_rfkill_setup(struct acpi_device *device) +static int sony_nc_rfkill_setup(struct acpi_device *device, + unsigned int handle) { - int offset; - u8 dev_code, i; - acpi_status status; - struct acpi_object_list params; - union acpi_object in_obj; - union acpi_object *device_enum; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - - offset = sony_find_snc_handle(0x124); - if (offset == -1) { - offset = sony_find_snc_handle(0x135); - if (offset == -1) - return; - else - sony_rfkill_handle = 0x135; - } else - sony_rfkill_handle = 0x124; - dprintk("Found rkfill handle: 0x%.4x\n", sony_rfkill_handle); - - /* need to read the whole buffer returned by the acpi call to SN06 - * here otherwise we may miss some features - */ - params.count = 1; - params.pointer = &in_obj; - in_obj.type = ACPI_TYPE_INTEGER; - in_obj.integer.value = offset; - status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", ¶ms, - &buffer); - if (ACPI_FAILURE(status)) { - dprintk("Radio device enumeration failed\n"); - return; - } - - device_enum = (union acpi_object *) buffer.pointer; - if (!device_enum) { - pr_err("No SN06 return object\n"); - goto out_no_enum; - } - if (device_enum->type != ACPI_TYPE_BUFFER) { - pr_err("Invalid SN06 return object 0x%.2x\n", - device_enum->type); - goto out_no_enum; - } + u64 offset; + int i; + unsigned char buffer[32] = { 0 }; - /* the buffer is filled with magic numbers describing the devices - * available, 0xff terminates the enumeration + offset = sony_find_snc_handle(handle); + sony_rfkill_handle = handle; + + i = sony_nc_buffer_call(sony_nc_acpi_handle, "SN06", &offset, buffer, + 32); + if (i < 0) + return i; + + /* The buffer is filled with magic numbers describing the devices + * available, 0xff terminates the enumeration. + * Known codes: + * 0x00 WLAN + * 0x10 BLUETOOTH + * 0x20 WWAN GPRS-EDGE + * 0x21 WWAN HSDPA + * 0x22 WWAN EV-DO + * 0x23 WWAN GPS + * 0x25 Gobi WWAN no GPS + * 0x26 Gobi WWAN + GPS + * 0x28 Gobi WWAN no GPS + * 0x29 Gobi WWAN + GPS + * 0x30 WIMAX + * 0x50 Gobi WWAN no GPS + * 0x51 Gobi WWAN + GPS + * 0x70 no SIM card slot + * 0x71 SIM card slot */ - for (i = 0; i < device_enum->buffer.length; i++) { + for (i = 0; i < ARRAY_SIZE(buffer); i++) { - dev_code = *(device_enum->buffer.pointer + i); - if (dev_code == 0xff) + if (buffer[i] == 0xff) break; - dprintk("Radio devices, looking at 0x%.2x\n", dev_code); + dprintk("Radio devices, found 0x%.2x\n", buffer[i]); - if (dev_code == 0 && !sony_rfkill_devices[SONY_WIFI]) + if (buffer[i] == 0 && !sony_rfkill_devices[SONY_WIFI]) sony_nc_setup_rfkill(device, SONY_WIFI); - if (dev_code == 0x10 && !sony_rfkill_devices[SONY_BLUETOOTH]) + if (buffer[i] == 0x10 && !sony_rfkill_devices[SONY_BLUETOOTH]) sony_nc_setup_rfkill(device, SONY_BLUETOOTH); - if ((0xf0 & dev_code) == 0x20 && + if (((0xf0 & buffer[i]) == 0x20 || + (0xf0 & buffer[i]) == 0x50) && !sony_rfkill_devices[SONY_WWAN]) sony_nc_setup_rfkill(device, SONY_WWAN); - if (dev_code == 0x30 && !sony_rfkill_devices[SONY_WIMAX]) + if (buffer[i] == 0x30 && !sony_rfkill_devices[SONY_WIMAX]) sony_nc_setup_rfkill(device, SONY_WIMAX); } - -out_no_enum: - kfree(buffer.pointer); - return; + return 0; } /* Keyboard backlight feature */ -#define KBDBL_HANDLER 0x137 -#define KBDBL_PRESENT 0xB00 -#define SET_MODE 0xC00 -#define SET_STATE 0xD00 -#define SET_TIMEOUT 0xE00 - struct kbd_backlight { - int mode; - int timeout; + unsigned int handle; + unsigned int base; + unsigned int mode; + unsigned int timeout; struct device_attribute mode_attr; struct device_attribute timeout_attr; }; -static struct kbd_backlight *kbdbl_handle; +static struct kbd_backlight *kbdbl_ctl; static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) { @@ -1398,15 +1678,15 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) if (value > 1) return -EINVAL; - if (sony_call_snc_handle(KBDBL_HANDLER, - (value << 0x10) | SET_MODE, &result)) + if (sony_call_snc_handle(kbdbl_ctl->handle, + (value << 0x10) | (kbdbl_ctl->base), &result)) return -EIO; /* Try to turn the light on/off immediately */ - sony_call_snc_handle(KBDBL_HANDLER, (value << 0x10) | SET_STATE, - &result); + sony_call_snc_handle(kbdbl_ctl->handle, + (value << 0x10) | (kbdbl_ctl->base + 0x100), &result); - kbdbl_handle->mode = value; + kbdbl_ctl->mode = value; return 0; } @@ -1421,7 +1701,7 @@ static ssize_t sony_nc_kbd_backlight_mode_store(struct device *dev, if (count > 31) return -EINVAL; - if (strict_strtoul(buffer, 10, &value)) + if (kstrtoul(buffer, 10, &value)) return -EINVAL; ret = __sony_nc_kbd_backlight_mode_set(value); @@ -1435,7 +1715,7 @@ static ssize_t sony_nc_kbd_backlight_mode_show(struct device *dev, struct device_attribute *attr, char *buffer) { ssize_t count = 0; - count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->mode); + count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_ctl->mode); return count; } @@ -1446,11 +1726,11 @@ static int __sony_nc_kbd_backlight_timeout_set(u8 value) if (value > 3) return -EINVAL; - if (sony_call_snc_handle(KBDBL_HANDLER, - (value << 0x10) | SET_TIMEOUT, &result)) + if (sony_call_snc_handle(kbdbl_ctl->handle, (value << 0x10) | + (kbdbl_ctl->base + 0x200), &result)) return -EIO; - kbdbl_handle->timeout = value; + kbdbl_ctl->timeout = value; return 0; } @@ -1465,7 +1745,7 @@ static ssize_t sony_nc_kbd_backlight_timeout_store(struct device *dev, if (count > 31) return -EINVAL; - if (strict_strtoul(buffer, 10, &value)) + if (kstrtoul(buffer, 10, &value)) return -EINVAL; ret = __sony_nc_kbd_backlight_timeout_set(value); @@ -1479,39 +1759,58 @@ static ssize_t sony_nc_kbd_backlight_timeout_show(struct device *dev, struct device_attribute *attr, char *buffer) { ssize_t count = 0; - count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->timeout); + count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_ctl->timeout); return count; } -static int sony_nc_kbd_backlight_setup(struct platform_device *pd) +static int sony_nc_kbd_backlight_setup(struct platform_device *pd, + unsigned int handle) { int result; + int ret = 0; - if (sony_call_snc_handle(KBDBL_HANDLER, KBDBL_PRESENT, &result)) - return 0; - if (!(result & 0x02)) + /* verify the kbd backlight presence, these handles are not used for + * keyboard backlight only + */ + ret = sony_call_snc_handle(handle, handle == 0x0137 ? 0x0B00 : 0x0100, + &result); + if (ret) + return ret; + + if ((handle == 0x0137 && !(result & 0x02)) || + !(result & 0x01)) { + dprintk("no backlight keyboard found\n"); return 0; + } - kbdbl_handle = kzalloc(sizeof(*kbdbl_handle), GFP_KERNEL); - if (!kbdbl_handle) + kbdbl_ctl = kzalloc(sizeof(*kbdbl_ctl), GFP_KERNEL); + if (!kbdbl_ctl) return -ENOMEM; - sysfs_attr_init(&kbdbl_handle->mode_attr.attr); - kbdbl_handle->mode_attr.attr.name = "kbd_backlight"; - kbdbl_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR; - kbdbl_handle->mode_attr.show = sony_nc_kbd_backlight_mode_show; - kbdbl_handle->mode_attr.store = sony_nc_kbd_backlight_mode_store; + kbdbl_ctl->handle = handle; + if (handle == 0x0137) + kbdbl_ctl->base = 0x0C00; + else + kbdbl_ctl->base = 0x4000; + + sysfs_attr_init(&kbdbl_ctl->mode_attr.attr); + kbdbl_ctl->mode_attr.attr.name = "kbd_backlight"; + kbdbl_ctl->mode_attr.attr.mode = S_IRUGO | S_IWUSR; + kbdbl_ctl->mode_attr.show = sony_nc_kbd_backlight_mode_show; + kbdbl_ctl->mode_attr.store = sony_nc_kbd_backlight_mode_store; - sysfs_attr_init(&kbdbl_handle->timeout_attr.attr); - kbdbl_handle->timeout_attr.attr.name = "kbd_backlight_timeout"; - kbdbl_handle->timeout_attr.attr.mode = S_IRUGO | S_IWUSR; - kbdbl_handle->timeout_attr.show = sony_nc_kbd_backlight_timeout_show; - kbdbl_handle->timeout_attr.store = sony_nc_kbd_backlight_timeout_store; + sysfs_attr_init(&kbdbl_ctl->timeout_attr.attr); + kbdbl_ctl->timeout_attr.attr.name = "kbd_backlight_timeout"; + kbdbl_ctl->timeout_attr.attr.mode = S_IRUGO | S_IWUSR; + kbdbl_ctl->timeout_attr.show = sony_nc_kbd_backlight_timeout_show; + kbdbl_ctl->timeout_attr.store = sony_nc_kbd_backlight_timeout_store; - if (device_create_file(&pd->dev, &kbdbl_handle->mode_attr)) + ret = device_create_file(&pd->dev, &kbdbl_ctl->mode_attr); + if (ret) goto outkzalloc; - if (device_create_file(&pd->dev, &kbdbl_handle->timeout_attr)) + ret = device_create_file(&pd->dev, &kbdbl_ctl->timeout_attr); + if (ret) goto outmode; __sony_nc_kbd_backlight_mode_set(kbd_backlight); @@ -1520,57 +1819,661 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd) return 0; outmode: - device_remove_file(&pd->dev, &kbdbl_handle->mode_attr); + device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr); outkzalloc: - kfree(kbdbl_handle); - kbdbl_handle = NULL; - return -1; + kfree(kbdbl_ctl); + kbdbl_ctl = NULL; + return ret; } -static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd) +static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd) { - if (kbdbl_handle) { + if (kbdbl_ctl) { int result; - device_remove_file(&pd->dev, &kbdbl_handle->mode_attr); - device_remove_file(&pd->dev, &kbdbl_handle->timeout_attr); + device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr); + device_remove_file(&pd->dev, &kbdbl_ctl->timeout_attr); /* restore the default hw behaviour */ - sony_call_snc_handle(KBDBL_HANDLER, 0x1000 | SET_MODE, &result); - sony_call_snc_handle(KBDBL_HANDLER, SET_TIMEOUT, &result); + sony_call_snc_handle(kbdbl_ctl->handle, + kbdbl_ctl->base | 0x10000, &result); + sony_call_snc_handle(kbdbl_ctl->handle, + kbdbl_ctl->base + 0x200, &result); - kfree(kbdbl_handle); + kfree(kbdbl_ctl); + kbdbl_ctl = NULL; } - return 0; } static void sony_nc_kbd_backlight_resume(void) { int ignore = 0; - if (!kbdbl_handle) + if (!kbdbl_ctl) return; - if (kbdbl_handle->mode == 0) - sony_call_snc_handle(KBDBL_HANDLER, SET_MODE, &ignore); - - if (kbdbl_handle->timeout != 0) - sony_call_snc_handle(KBDBL_HANDLER, - (kbdbl_handle->timeout << 0x10) | SET_TIMEOUT, + if (kbdbl_ctl->mode == 0) + sony_call_snc_handle(kbdbl_ctl->handle, kbdbl_ctl->base, &ignore); + + if (kbdbl_ctl->timeout != 0) + sony_call_snc_handle(kbdbl_ctl->handle, + (kbdbl_ctl->base + 0x200) | + (kbdbl_ctl->timeout << 0x10), &ignore); +} + +struct battery_care_control { + struct device_attribute attrs[2]; + unsigned int handle; +}; +static struct battery_care_control *bcare_ctl; + +static ssize_t sony_nc_battery_care_limit_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned int result, cmd; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (kstrtoul(buffer, 10, &value)) + return -EINVAL; + + /* limit values (2 bits): + * 00 - none + * 01 - 80% + * 10 - 50% + * 11 - 100% + * + * bit 0: 0 disable BCL, 1 enable BCL + * bit 1: 1 tell to store the battery limit (see bits 6,7) too + * bits 2,3: reserved + * bits 4,5: store the limit into the EC + * bits 6,7: store the limit into the battery + */ + + /* + * handle 0x0115 should allow storing on battery too; + * handle 0x0136 same as 0x0115 + health status; + * handle 0x013f, same as 0x0136 but no storing on the battery + * + * Store only inside the EC for now, regardless the handle number + */ + if (value == 0) + /* disable limits */ + cmd = 0x0; + + else if (value <= 50) + cmd = 0x21; + + else if (value <= 80) + cmd = 0x11; + + else if (value <= 100) + cmd = 0x31; + + else + return -EINVAL; + + if (sony_call_snc_handle(bcare_ctl->handle, (cmd << 0x10) | 0x0100, + &result)) + return -EIO; + + return count; +} + +static ssize_t sony_nc_battery_care_limit_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int result, status; + + if (sony_call_snc_handle(bcare_ctl->handle, 0x0000, &result)) + return -EIO; + + status = (result & 0x01) ? ((result & 0x30) >> 0x04) : 0; + switch (status) { + case 1: + status = 80; + break; + case 2: + status = 50; + break; + case 3: + status = 100; + break; + default: + status = 0; + break; + } + + return snprintf(buffer, PAGE_SIZE, "%d\n", status); +} + +static ssize_t sony_nc_battery_care_health_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + ssize_t count = 0; + unsigned int health; + + if (sony_call_snc_handle(bcare_ctl->handle, 0x0200, &health)) + return -EIO; + + count = snprintf(buffer, PAGE_SIZE, "%d\n", health & 0xff); + + return count; +} + +static int sony_nc_battery_care_setup(struct platform_device *pd, + unsigned int handle) +{ + int ret = 0; + + bcare_ctl = kzalloc(sizeof(struct battery_care_control), GFP_KERNEL); + if (!bcare_ctl) + return -ENOMEM; + + bcare_ctl->handle = handle; + + sysfs_attr_init(&bcare_ctl->attrs[0].attr); + bcare_ctl->attrs[0].attr.name = "battery_care_limiter"; + bcare_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR; + bcare_ctl->attrs[0].show = sony_nc_battery_care_limit_show; + bcare_ctl->attrs[0].store = sony_nc_battery_care_limit_store; + + ret = device_create_file(&pd->dev, &bcare_ctl->attrs[0]); + if (ret) + goto outkzalloc; + + /* 0x0115 is for models with no health reporting capability */ + if (handle == 0x0115) + return 0; + + sysfs_attr_init(&bcare_ctl->attrs[1].attr); + bcare_ctl->attrs[1].attr.name = "battery_care_health"; + bcare_ctl->attrs[1].attr.mode = S_IRUGO; + bcare_ctl->attrs[1].show = sony_nc_battery_care_health_show; + + ret = device_create_file(&pd->dev, &bcare_ctl->attrs[1]); + if (ret) + goto outlimiter; + + return 0; + +outlimiter: + device_remove_file(&pd->dev, &bcare_ctl->attrs[0]); + +outkzalloc: + kfree(bcare_ctl); + bcare_ctl = NULL; + + return ret; +} + +static void sony_nc_battery_care_cleanup(struct platform_device *pd) +{ + if (bcare_ctl) { + device_remove_file(&pd->dev, &bcare_ctl->attrs[0]); + if (bcare_ctl->handle != 0x0115) + device_remove_file(&pd->dev, &bcare_ctl->attrs[1]); + + kfree(bcare_ctl); + bcare_ctl = NULL; + } +} + +struct snc_thermal_ctrl { + unsigned int mode; + unsigned int profiles; + struct device_attribute mode_attr; + struct device_attribute profiles_attr; +}; +static struct snc_thermal_ctrl *th_handle; + +#define THM_PROFILE_MAX 3 +static const char * const snc_thermal_profiles[] = { + "balanced", + "silent", + "performance" +}; + +static int sony_nc_thermal_mode_set(unsigned short mode) +{ + unsigned int result; + + /* the thermal profile seems to be a two bit bitmask: + * lsb -> silent + * msb -> performance + * no bit set is the normal operation and is always valid + * Some vaio models only have "balanced" and "performance" + */ + if ((mode && !(th_handle->profiles & mode)) || mode >= THM_PROFILE_MAX) + return -EINVAL; + + if (sony_call_snc_handle(0x0122, mode << 0x10 | 0x0200, &result)) + return -EIO; + + th_handle->mode = mode; + + return 0; +} + +static int sony_nc_thermal_mode_get(void) +{ + unsigned int result; + + if (sony_call_snc_handle(0x0122, 0x0100, &result)) + return -EIO; + + return result & 0xff; +} + +static ssize_t sony_nc_thermal_profiles_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + short cnt; + size_t idx = 0; + + for (cnt = 0; cnt < THM_PROFILE_MAX; cnt++) { + if (!cnt || (th_handle->profiles & cnt)) + idx += snprintf(buffer + idx, PAGE_SIZE - idx, "%s ", + snc_thermal_profiles[cnt]); + } + idx += snprintf(buffer + idx, PAGE_SIZE - idx, "\n"); + + return idx; +} + +static ssize_t sony_nc_thermal_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned short cmd; + size_t len = count; + + if (count == 0) + return -EINVAL; + + /* skip the newline if present */ + if (buffer[len - 1] == '\n') + len--; + + for (cmd = 0; cmd < THM_PROFILE_MAX; cmd++) + if (strncmp(buffer, snc_thermal_profiles[cmd], len) == 0) + break; + + if (sony_nc_thermal_mode_set(cmd)) + return -EIO; + + return count; +} + +static ssize_t sony_nc_thermal_mode_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + ssize_t count = 0; + unsigned int mode = sony_nc_thermal_mode_get(); + + if (mode < 0) + return mode; + + count = snprintf(buffer, PAGE_SIZE, "%s\n", snc_thermal_profiles[mode]); + + return count; +} + +static int sony_nc_thermal_setup(struct platform_device *pd) +{ + int ret = 0; + th_handle = kzalloc(sizeof(struct snc_thermal_ctrl), GFP_KERNEL); + if (!th_handle) + return -ENOMEM; + + ret = sony_call_snc_handle(0x0122, 0x0000, &th_handle->profiles); + if (ret) { + pr_warn("couldn't to read the thermal profiles\n"); + goto outkzalloc; + } + + ret = sony_nc_thermal_mode_get(); + if (ret < 0) { + pr_warn("couldn't to read the current thermal profile"); + goto outkzalloc; + } + th_handle->mode = ret; + + sysfs_attr_init(&th_handle->profiles_attr.attr); + th_handle->profiles_attr.attr.name = "thermal_profiles"; + th_handle->profiles_attr.attr.mode = S_IRUGO; + th_handle->profiles_attr.show = sony_nc_thermal_profiles_show; + + sysfs_attr_init(&th_handle->mode_attr.attr); + th_handle->mode_attr.attr.name = "thermal_control"; + th_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR; + th_handle->mode_attr.show = sony_nc_thermal_mode_show; + th_handle->mode_attr.store = sony_nc_thermal_mode_store; + + ret = device_create_file(&pd->dev, &th_handle->profiles_attr); + if (ret) + goto outkzalloc; + + ret = device_create_file(&pd->dev, &th_handle->mode_attr); + if (ret) + goto outprofiles; + + return 0; + +outprofiles: + device_remove_file(&pd->dev, &th_handle->profiles_attr); +outkzalloc: + kfree(th_handle); + th_handle = NULL; + return ret; +} + +static void sony_nc_thermal_cleanup(struct platform_device *pd) +{ + if (th_handle) { + device_remove_file(&pd->dev, &th_handle->profiles_attr); + device_remove_file(&pd->dev, &th_handle->mode_attr); + kfree(th_handle); + th_handle = NULL; + } +} + +static void sony_nc_thermal_resume(void) +{ + unsigned int status = sony_nc_thermal_mode_get(); + + if (status != th_handle->mode) + sony_nc_thermal_mode_set(th_handle->mode); +} + +/* resume on LID open */ +struct snc_lid_resume_control { + struct device_attribute attrs[3]; + unsigned int status; +}; +static struct snc_lid_resume_control *lid_ctl; + +static ssize_t sony_nc_lid_resume_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned int result, pos; + unsigned long value; + if (count > 31) + return -EINVAL; + + if (kstrtoul(buffer, 10, &value) || value > 1) + return -EINVAL; + + /* the value we have to write to SNC is a bitmask: + * +--------------+ + * | S3 | S4 | S5 | + * +--------------+ + * 2 1 0 + */ + if (strcmp(attr->attr.name, "lid_resume_S3") == 0) + pos = 2; + else if (strcmp(attr->attr.name, "lid_resume_S4") == 0) + pos = 1; + else if (strcmp(attr->attr.name, "lid_resume_S5") == 0) + pos = 0; + else + return -EINVAL; + + if (value) + value = lid_ctl->status | (1 << pos); + else + value = lid_ctl->status & ~(1 << pos); + + if (sony_call_snc_handle(0x0119, value << 0x10 | 0x0100, &result)) + return -EIO; + + lid_ctl->status = value; + + return count; +} + +static ssize_t sony_nc_lid_resume_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int pos; + + if (strcmp(attr->attr.name, "lid_resume_S3") == 0) + pos = 2; + else if (strcmp(attr->attr.name, "lid_resume_S4") == 0) + pos = 1; + else if (strcmp(attr->attr.name, "lid_resume_S5") == 0) + pos = 0; + else + return -EINVAL; + + return snprintf(buffer, PAGE_SIZE, "%d\n", + (lid_ctl->status >> pos) & 0x01); +} + +static int sony_nc_lid_resume_setup(struct platform_device *pd) +{ + unsigned int result; + int i; + + if (sony_call_snc_handle(0x0119, 0x0000, &result)) + return -EIO; + + lid_ctl = kzalloc(sizeof(struct snc_lid_resume_control), GFP_KERNEL); + if (!lid_ctl) + return -ENOMEM; + + lid_ctl->status = result & 0x7; + + sysfs_attr_init(&lid_ctl->attrs[0].attr); + lid_ctl->attrs[0].attr.name = "lid_resume_S3"; + lid_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR; + lid_ctl->attrs[0].show = sony_nc_lid_resume_show; + lid_ctl->attrs[0].store = sony_nc_lid_resume_store; + + sysfs_attr_init(&lid_ctl->attrs[1].attr); + lid_ctl->attrs[1].attr.name = "lid_resume_S4"; + lid_ctl->attrs[1].attr.mode = S_IRUGO | S_IWUSR; + lid_ctl->attrs[1].show = sony_nc_lid_resume_show; + lid_ctl->attrs[1].store = sony_nc_lid_resume_store; + + sysfs_attr_init(&lid_ctl->attrs[2].attr); + lid_ctl->attrs[2].attr.name = "lid_resume_S5"; + lid_ctl->attrs[2].attr.mode = S_IRUGO | S_IWUSR; + lid_ctl->attrs[2].show = sony_nc_lid_resume_show; + lid_ctl->attrs[2].store = sony_nc_lid_resume_store; + + for (i = 0; i < 3; i++) { + result = device_create_file(&pd->dev, &lid_ctl->attrs[i]); + if (result) + goto liderror; + } + + return 0; + +liderror: + for (; i > 0; i--) + device_remove_file(&pd->dev, &lid_ctl->attrs[i]); + + kfree(lid_ctl); + lid_ctl = NULL; + + return result; +} + +static void sony_nc_lid_resume_cleanup(struct platform_device *pd) +{ + int i; + + if (lid_ctl) { + for (i = 0; i < 3; i++) + device_remove_file(&pd->dev, &lid_ctl->attrs[i]); + + kfree(lid_ctl); + lid_ctl = NULL; + } +} + +/* High speed charging function */ +static struct device_attribute *hsc_handle; + +static ssize_t sony_nc_highspeed_charging_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned int result; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (kstrtoul(buffer, 10, &value) || value > 1) + return -EINVAL; + + if (sony_call_snc_handle(0x0131, value << 0x10 | 0x0200, &result)) + return -EIO; + + return count; +} + +static ssize_t sony_nc_highspeed_charging_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int result; + + if (sony_call_snc_handle(0x0131, 0x0100, &result)) + return -EIO; + + return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01); +} + +static int sony_nc_highspeed_charging_setup(struct platform_device *pd) +{ + unsigned int result; + + if (sony_call_snc_handle(0x0131, 0x0000, &result) || !(result & 0x01)) { + /* some models advertise the handle but have no implementation + * for it + */ + pr_info("No High Speed Charging capability found\n"); + return 0; + } + + hsc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); + if (!hsc_handle) + return -ENOMEM; + + sysfs_attr_init(&hsc_handle->attr); + hsc_handle->attr.name = "battery_highspeed_charging"; + hsc_handle->attr.mode = S_IRUGO | S_IWUSR; + hsc_handle->show = sony_nc_highspeed_charging_show; + hsc_handle->store = sony_nc_highspeed_charging_store; + + result = device_create_file(&pd->dev, hsc_handle); + if (result) { + kfree(hsc_handle); + hsc_handle = NULL; + return result; + } + + return 0; +} + +static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd) +{ + if (hsc_handle) { + device_remove_file(&pd->dev, hsc_handle); + kfree(hsc_handle); + hsc_handle = NULL; + } +} + +/* Touchpad enable/disable */ +struct touchpad_control { + struct device_attribute attr; + int handle; +}; +static struct touchpad_control *tp_ctl; + +static ssize_t sony_nc_touchpad_store(struct device *dev, + struct device_attribute *attr, const char *buffer, size_t count) +{ + unsigned int result; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (kstrtoul(buffer, 10, &value) || value > 1) + return -EINVAL; + + /* sysfs: 0 disabled, 1 enabled + * EC: 0 enabled, 1 disabled + */ + if (sony_call_snc_handle(tp_ctl->handle, + (!value << 0x10) | 0x100, &result)) + return -EIO; + + return count; +} + +static ssize_t sony_nc_touchpad_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int result; + + if (sony_call_snc_handle(tp_ctl->handle, 0x000, &result)) + return -EINVAL; + + return snprintf(buffer, PAGE_SIZE, "%d\n", !(result & 0x01)); +} + +static int sony_nc_touchpad_setup(struct platform_device *pd, + unsigned int handle) +{ + int ret = 0; + + tp_ctl = kzalloc(sizeof(struct touchpad_control), GFP_KERNEL); + if (!tp_ctl) + return -ENOMEM; + + tp_ctl->handle = handle; + + sysfs_attr_init(&tp_ctl->attr.attr); + tp_ctl->attr.attr.name = "touchpad"; + tp_ctl->attr.attr.mode = S_IRUGO | S_IWUSR; + tp_ctl->attr.show = sony_nc_touchpad_show; + tp_ctl->attr.store = sony_nc_touchpad_store; + + ret = device_create_file(&pd->dev, &tp_ctl->attr); + if (ret) { + kfree(tp_ctl); + tp_ctl = NULL; + } + + return ret; +} + +static void sony_nc_touchpad_cleanup(struct platform_device *pd) +{ + if (tp_ctl) { + device_remove_file(&pd->dev, &tp_ctl->attr); + kfree(tp_ctl); + tp_ctl = NULL; + } } static void sony_nc_backlight_ng_read_limits(int handle, struct sony_backlight_props *props) { - int offset; - acpi_status status; - u8 brlvl, i; + u64 offset; + int i; u8 min = 0xff, max = 0x00; - struct acpi_object_list params; - union acpi_object in_obj; - union acpi_object *lvl_enum; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + unsigned char buffer[32] = { 0 }; props->handle = handle; props->offset = 0; @@ -1583,50 +2486,31 @@ static void sony_nc_backlight_ng_read_limits(int handle, /* try to read the boundaries from ACPI tables, if we fail the above * defaults should be reasonable */ - params.count = 1; - params.pointer = &in_obj; - in_obj.type = ACPI_TYPE_INTEGER; - in_obj.integer.value = offset; - status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", ¶ms, - &buffer); - if (ACPI_FAILURE(status)) + i = sony_nc_buffer_call(sony_nc_acpi_handle, "SN06", &offset, buffer, + 32); + if (i < 0) return; - lvl_enum = (union acpi_object *) buffer.pointer; - if (!lvl_enum) { - pr_err("No SN06 return object."); - return; - } - if (lvl_enum->type != ACPI_TYPE_BUFFER) { - pr_err("Invalid SN06 return object 0x%.2x\n", - lvl_enum->type); - goto out_invalid; - } - /* the buffer lists brightness levels available, brightness levels are - * from 0 to 8 in the array, other values are used by ALS control. + * from position 0 to 8 in the array, other values are used by ALS + * control. */ - for (i = 0; i < 9 && i < lvl_enum->buffer.length; i++) { + for (i = 0; i < 9 && i < ARRAY_SIZE(buffer); i++) { - brlvl = *(lvl_enum->buffer.pointer + i); - dprintk("Brightness level: %d\n", brlvl); + dprintk("Brightness level: %d\n", buffer[i]); - if (!brlvl) + if (!buffer[i]) break; - if (brlvl > max) - max = brlvl; - if (brlvl < min) - min = brlvl; + if (buffer[i] > max) + max = buffer[i]; + if (buffer[i] < min) + min = buffer[i]; } props->offset = min; props->maxlvl = max; dprintk("Brightness levels: min=%d max=%d\n", props->offset, props->maxlvl); - -out_invalid: - kfree(buffer.pointer); - return; } static void sony_nc_backlight_setup(void) @@ -1715,28 +2599,25 @@ static int sony_nc_add(struct acpi_device *device) if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", &handle))) { - if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL)) + int arg = 1; + if (sony_nc_int_call(sony_nc_acpi_handle, "ECON", &arg, NULL)) dprintk("ECON Method failed\n"); } if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", &handle))) { dprintk("Doing SNC setup\n"); + /* retrieve the available handles */ result = sony_nc_handles_setup(sony_pf_device); - if (result) - goto outpresent; - result = sony_nc_kbd_backlight_setup(sony_pf_device); - if (result) - goto outsnc; - sony_nc_function_setup(device); - sony_nc_rfkill_setup(device); + if (!result) + sony_nc_function_setup(device, sony_pf_device); } /* setup input devices and helper fifo */ result = sony_laptop_setup_input(device); if (result) { pr_err("Unable to create input devices\n"); - goto outkbdbacklight; + goto outsnc; } if (acpi_video_backlight_support()) { @@ -1794,10 +2675,8 @@ static int sony_nc_add(struct acpi_device *device) sony_laptop_remove_input(); - outkbdbacklight: - sony_nc_kbd_backlight_cleanup(sony_pf_device); - outsnc: + sony_nc_function_cleanup(sony_pf_device); sony_nc_handles_cleanup(sony_pf_device); outpresent: @@ -1820,11 +2699,10 @@ static int sony_nc_remove(struct acpi_device *device, int type) device_remove_file(&sony_pf_device->dev, &item->devattr); } - sony_nc_kbd_backlight_cleanup(sony_pf_device); + sony_nc_function_cleanup(sony_pf_device); sony_nc_handles_cleanup(sony_pf_device); sony_pf_remove(); sony_laptop_remove_input(); - sony_nc_rfkill_cleanup(); dprintk(SONY_NC_DRIVER_NAME " removed.\n"); return 0; @@ -2437,7 +3315,9 @@ static ssize_t sony_pic_wwanpower_store(struct device *dev, if (count > 31) return -EINVAL; - value = simple_strtoul(buffer, NULL, 10); + if (kstrtoul(buffer, 10, &value)) + return -EINVAL; + mutex_lock(&spic_dev.lock); __sony_pic_set_wwanpower(value); mutex_unlock(&spic_dev.lock); @@ -2474,7 +3354,9 @@ static ssize_t sony_pic_bluetoothpower_store(struct device *dev, if (count > 31) return -EINVAL; - value = simple_strtoul(buffer, NULL, 10); + if (kstrtoul(buffer, 10, &value)) + return -EINVAL; + mutex_lock(&spic_dev.lock); __sony_pic_set_bluetoothpower(value); mutex_unlock(&spic_dev.lock); @@ -2513,7 +3395,9 @@ static ssize_t sony_pic_fanspeed_store(struct device *dev, if (count > 31) return -EINVAL; - value = simple_strtoul(buffer, NULL, 10); + if (kstrtoul(buffer, 10, &value)) + return -EINVAL; + if (sony_pic_set_fanspeed(value)) return -EIO; @@ -2671,7 +3555,8 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd, ret = -EIO; break; } - if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) { + if (sony_nc_int_call(sony_nc_acpi_handle, "GBRT", NULL, + &value)) { ret = -EIO; break; } @@ -2688,8 +3573,9 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd, ret = -EFAULT; break; } - if (acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", - (val8 >> 5) + 1, NULL)) { + value = (val8 >> 5) + 1; + if (sony_nc_int_call(sony_nc_acpi_handle, "SBRT", &value, + NULL)) { ret = -EIO; break; } diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index d68c0002f4a2..8b5610d88418 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3402,7 +3402,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) /* Do not issue duplicate brightness change events to * userspace. tpacpi_detect_brightness_capabilities() must have * been called before this point */ - if (tp_features.bright_acpimode && acpi_video_backlight_support()) { + if (acpi_video_backlight_support()) { pr_info("This ThinkPad has standard ACPI backlight " "brightness control, supported by the ACPI " "video driver\n"); diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 57787d87d9a4..dab10f6edcd4 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -95,6 +95,7 @@ MODULE_LICENSE("GPL"); /* registers */ #define HCI_FAN 0x0004 +#define HCI_TR_BACKLIGHT 0x0005 #define HCI_SYSTEM_EVENT 0x0016 #define HCI_VIDEO_OUT 0x001c #define HCI_HOTKEY_EVENT 0x001e @@ -134,6 +135,7 @@ struct toshiba_acpi_dev { unsigned int system_event_supported:1; unsigned int ntfy_supported:1; unsigned int info_supported:1; + unsigned int tr_backlight_supported:1; struct mutex mutex; }; @@ -478,34 +480,70 @@ static const struct rfkill_ops toshiba_rfk_ops = { .poll = bt_rfkill_poll, }; +static int get_tr_backlight_status(struct toshiba_acpi_dev *dev, bool *enabled) +{ + u32 hci_result; + u32 status; + + hci_read1(dev, HCI_TR_BACKLIGHT, &status, &hci_result); + *enabled = !status; + return hci_result == HCI_SUCCESS ? 0 : -EIO; +} + +static int set_tr_backlight_status(struct toshiba_acpi_dev *dev, bool enable) +{ + u32 hci_result; + u32 value = !enable; + + hci_write1(dev, HCI_TR_BACKLIGHT, value, &hci_result); + return hci_result == HCI_SUCCESS ? 0 : -EIO; +} + static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; -static int get_lcd(struct backlight_device *bd) +static int __get_lcd_brightness(struct toshiba_acpi_dev *dev) { - struct toshiba_acpi_dev *dev = bl_get_data(bd); u32 hci_result; u32 value; + int brightness = 0; + + if (dev->tr_backlight_supported) { + bool enabled; + int ret = get_tr_backlight_status(dev, &enabled); + if (ret) + return ret; + if (enabled) + return 0; + brightness++; + } hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result); if (hci_result == HCI_SUCCESS) - return (value >> HCI_LCD_BRIGHTNESS_SHIFT); + return brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT); return -EIO; } +static int get_lcd_brightness(struct backlight_device *bd) +{ + struct toshiba_acpi_dev *dev = bl_get_data(bd); + return __get_lcd_brightness(dev); +} + static int lcd_proc_show(struct seq_file *m, void *v) { struct toshiba_acpi_dev *dev = m->private; int value; + int levels; if (!dev->backlight_dev) return -ENODEV; - value = get_lcd(dev->backlight_dev); + levels = dev->backlight_dev->props.max_brightness + 1; + value = get_lcd_brightness(dev->backlight_dev); if (value >= 0) { seq_printf(m, "brightness: %d\n", value); - seq_printf(m, "brightness_levels: %d\n", - HCI_LCD_BRIGHTNESS_LEVELS); + seq_printf(m, "brightness_levels: %d\n", levels); return 0; } @@ -518,10 +556,19 @@ static int lcd_proc_open(struct inode *inode, struct file *file) return single_open(file, lcd_proc_show, PDE(inode)->data); } -static int set_lcd(struct toshiba_acpi_dev *dev, int value) +static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value) { u32 hci_result; + if (dev->tr_backlight_supported) { + bool enable = !value; + int ret = set_tr_backlight_status(dev, enable); + if (ret) + return ret; + if (value) + value--; + } + value = value << HCI_LCD_BRIGHTNESS_SHIFT; hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result); return hci_result == HCI_SUCCESS ? 0 : -EIO; @@ -530,7 +577,7 @@ static int set_lcd(struct toshiba_acpi_dev *dev, int value) static int set_lcd_status(struct backlight_device *bd) { struct toshiba_acpi_dev *dev = bl_get_data(bd); - return set_lcd(dev, bd->props.brightness); + return set_lcd_brightness(dev, bd->props.brightness); } static ssize_t lcd_proc_write(struct file *file, const char __user *buf, @@ -541,6 +588,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf, size_t len; int value; int ret; + int levels = dev->backlight_dev->props.max_brightness + 1; len = min(count, sizeof(cmd) - 1); if (copy_from_user(cmd, buf, len)) @@ -548,8 +596,8 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf, cmd[len] = '\0'; if (sscanf(cmd, " brightness : %i", &value) == 1 && - value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { - ret = set_lcd(dev, value); + value >= 0 && value < levels) { + ret = set_lcd_brightness(dev, value); if (ret == 0) ret = count; } else { @@ -860,8 +908,9 @@ static void remove_toshiba_proc_entries(struct toshiba_acpi_dev *dev) } static const struct backlight_ops toshiba_backlight_data = { - .get_brightness = get_lcd, - .update_status = set_lcd_status, + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = get_lcd_brightness, + .update_status = set_lcd_status, }; static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, @@ -1020,6 +1069,56 @@ static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) return error; } +static int __devinit toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev) +{ + struct backlight_properties props; + int brightness; + int ret; + bool enabled; + + /* + * Some machines don't support the backlight methods at all, and + * others support it read-only. Either of these is pretty useless, + * so only register the backlight device if the backlight method + * supports both reads and writes. + */ + brightness = __get_lcd_brightness(dev); + if (brightness < 0) + return 0; + ret = set_lcd_brightness(dev, brightness); + if (ret) { + pr_debug("Backlight method is read-only, disabling backlight support\n"); + return 0; + } + + /* Determine whether or not BIOS supports transflective backlight */ + ret = get_tr_backlight_status(dev, &enabled); + dev->tr_backlight_supported = !ret; + + memset(&props, 0, sizeof(props)); + props.type = BACKLIGHT_PLATFORM; + props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; + + /* adding an extra level and having 0 change to transflective mode */ + if (dev->tr_backlight_supported) + props.max_brightness++; + + dev->backlight_dev = backlight_device_register("toshiba", + &dev->acpi_dev->dev, + dev, + &toshiba_backlight_data, + &props); + if (IS_ERR(dev->backlight_dev)) { + ret = PTR_ERR(dev->backlight_dev); + pr_err("Could not register toshiba backlight device\n"); + dev->backlight_dev = NULL; + return ret; + } + + dev->backlight_dev->props.brightness = brightness; + return 0; +} + static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) { struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); @@ -1078,7 +1177,6 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) u32 dummy; bool bt_present; int ret = 0; - struct backlight_properties props; if (toshiba_acpi) return -EBUSY; @@ -1104,22 +1202,9 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) mutex_init(&dev->mutex); - memset(&props, 0, sizeof(props)); - props.type = BACKLIGHT_PLATFORM; - props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; - dev->backlight_dev = backlight_device_register("toshiba", - &acpi_dev->dev, - dev, - &toshiba_backlight_data, - &props); - if (IS_ERR(dev->backlight_dev)) { - ret = PTR_ERR(dev->backlight_dev); - - pr_err("Could not register toshiba backlight device\n"); - dev->backlight_dev = NULL; + ret = toshiba_acpi_setup_backlight(dev); + if (ret) goto error; - } - dev->backlight_dev->props.brightness = get_lcd(dev->backlight_dev); /* Register rfkill switch for Bluetooth */ if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) { diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c index 41781ed8301c..b57ad8641480 100644 --- a/drivers/platform/x86/xo1-rfkill.c +++ b/drivers/platform/x86/xo1-rfkill.c @@ -15,15 +15,26 @@ #include <asm/olpc.h> +static bool card_blocked; + static int rfkill_set_block(void *data, bool blocked) { unsigned char cmd; + int r; + + if (blocked == card_blocked) + return 0; + if (blocked) cmd = EC_WLAN_ENTER_RESET; else cmd = EC_WLAN_LEAVE_RESET; - return olpc_ec_cmd(cmd, NULL, 0, NULL, 0); + r = olpc_ec_cmd(cmd, NULL, 0, NULL, 0); + if (r == 0) + card_blocked = blocked; + + return r; } static const struct rfkill_ops rfkill_ops = { diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index a290be51a1f4..0217f7415ef5 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2210,7 +2210,7 @@ config FB_XILINX config FB_COBALT tristate "Cobalt server LCD frame buffer support" - depends on FB && MIPS_COBALT + depends on FB && (MIPS_COBALT || MIPS_SEAD3) config FB_SH7760 bool "SH7760/SH7763/SH7720/SH7721 LCDC support" @@ -2382,6 +2382,39 @@ config FB_BROADSHEET and could also have been called by other names when coupled with a bridge adapter. +config FB_AUO_K190X + tristate "AUO-K190X EPD controller support" + depends on FB + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_SYS_FOPS + select FB_DEFERRED_IO + help + Provides support for epaper controllers from the K190X series + of AUO. These controllers can be used to drive epaper displays + from Sipix. + + This option enables the common support, shared by the individual + controller drivers. You will also have to enable the driver + for the controller type used in your device. + +config FB_AUO_K1900 + tristate "AUO-K1900 EPD controller support" + depends on FB && FB_AUO_K190X + help + This driver implements support for the AUO K1900 epd-controller. + This controller can drive Sipix epaper displays but can only do + serial updates, reducing the number of possible frames per second. + +config FB_AUO_K1901 + tristate "AUO-K1901 EPD controller support" + depends on FB && FB_AUO_K190X + help + This driver implements support for the AUO K1901 epd-controller. + This controller can drive Sipix epaper displays and supports + concurrent updates, making higher frames per second possible. + config FB_JZ4740 tristate "JZ4740 LCD framebuffer support" depends on FB && MACH_JZ4740 diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 9356add945b3..ee8dafb69e36 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -118,6 +118,9 @@ obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o obj-$(CONFIG_FB_MAXINE) += maxinefb.o obj-$(CONFIG_FB_METRONOME) += metronomefb.o obj-$(CONFIG_FB_BROADSHEET) += broadsheetfb.o +obj-$(CONFIG_FB_AUO_K190X) += auo_k190x.o +obj-$(CONFIG_FB_AUO_K1900) += auo_k1900fb.o +obj-$(CONFIG_FB_AUO_K1901) += auo_k1901fb.o obj-$(CONFIG_FB_S1D13XXX) += s1d13xxxfb.o obj-$(CONFIG_FB_SH7760) += sh7760fb.o obj-$(CONFIG_FB_IMX) += imxfb.o diff --git a/drivers/video/auo_k1900fb.c b/drivers/video/auo_k1900fb.c new file mode 100644 index 000000000000..c36cf961dcb2 --- /dev/null +++ b/drivers/video/auo_k1900fb.c @@ -0,0 +1,198 @@ +/* + * auok190xfb.c -- FB driver for AUO-K1900 controllers + * + * Copyright (C) 2011, 2012 Heiko Stuebner <heiko@sntech.de> + * + * based on broadsheetfb.c + * + * Copyright (C) 2008, Jaya Kumar + * + * 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. + * + * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. + * + * This driver is written to be used with the AUO-K1900 display controller. + * + * It is intended to be architecture independent. A board specific driver + * must be used to perform all the physical IO interactions. + * + * The controller supports different update modes: + * mode0+1 16 step gray (4bit) + * mode2 4 step gray (2bit) - FIXME: add strange refresh + * mode3 2 step gray (1bit) - FIXME: add strange refresh + * mode4 handwriting mode (strange behaviour) + * mode5 automatic selection of update mode + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/list.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <linux/pm_runtime.h> + +#include <video/auo_k190xfb.h> + +#include "auo_k190x.h" + +/* + * AUO-K1900 specific commands + */ + +#define AUOK1900_CMD_PARTIALDISP 0x1001 +#define AUOK1900_CMD_ROTATION 0x1006 +#define AUOK1900_CMD_LUT_STOP 0x1009 + +#define AUOK1900_INIT_TEMP_AVERAGE (1 << 13) +#define AUOK1900_INIT_ROTATE(_x) ((_x & 0x3) << 10) +#define AUOK1900_INIT_RESOLUTION(_res) ((_res & 0x7) << 2) + +static void auok1900_init(struct auok190xfb_par *par) +{ + struct auok190x_board *board = par->board; + u16 init_param = 0; + + init_param |= AUOK1900_INIT_TEMP_AVERAGE; + init_param |= AUOK1900_INIT_ROTATE(par->rotation); + init_param |= AUOK190X_INIT_INVERSE_WHITE; + init_param |= AUOK190X_INIT_FORMAT0; + init_param |= AUOK1900_INIT_RESOLUTION(par->resolution); + init_param |= AUOK190X_INIT_SHIFT_RIGHT; + + auok190x_send_cmdargs(par, AUOK190X_CMD_INIT, 1, &init_param); + + /* let the controller finish */ + board->wait_for_rdy(par); +} + +static void auok1900_update_region(struct auok190xfb_par *par, int mode, + u16 y1, u16 y2) +{ + struct device *dev = par->info->device; + unsigned char *buf = (unsigned char *)par->info->screen_base; + int xres = par->info->var.xres; + u16 args[4]; + + pm_runtime_get_sync(dev); + + mutex_lock(&(par->io_lock)); + + /* y1 and y2 must be a multiple of 2 so drop the lowest bit */ + y1 &= 0xfffe; + y2 &= 0xfffe; + + dev_dbg(dev, "update (x,y,w,h,mode)=(%d,%d,%d,%d,%d)\n", + 1, y1+1, xres, y2-y1, mode); + + /* to FIX handle different partial update modes */ + args[0] = mode | 1; + args[1] = y1 + 1; + args[2] = xres; + args[3] = y2 - y1; + buf += y1 * xres; + auok190x_send_cmdargs_pixels(par, AUOK1900_CMD_PARTIALDISP, 4, args, + ((y2 - y1) * xres)/2, (u16 *) buf); + auok190x_send_command(par, AUOK190X_CMD_DATA_STOP); + + par->update_cnt++; + + mutex_unlock(&(par->io_lock)); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} + +static void auok1900fb_dpy_update_pages(struct auok190xfb_par *par, + u16 y1, u16 y2) +{ + int mode; + + if (par->update_mode < 0) { + mode = AUOK190X_UPDATE_MODE(1); + par->last_mode = -1; + } else { + mode = AUOK190X_UPDATE_MODE(par->update_mode); + par->last_mode = par->update_mode; + } + + if (par->flash) + mode |= AUOK190X_UPDATE_NONFLASH; + + auok1900_update_region(par, mode, y1, y2); +} + +static void auok1900fb_dpy_update(struct auok190xfb_par *par) +{ + int mode; + + if (par->update_mode < 0) { + mode = AUOK190X_UPDATE_MODE(0); + par->last_mode = -1; + } else { + mode = AUOK190X_UPDATE_MODE(par->update_mode); + par->last_mode = par->update_mode; + } + + if (par->flash) + mode |= AUOK190X_UPDATE_NONFLASH; + + auok1900_update_region(par, mode, 0, par->info->var.yres); + par->update_cnt = 0; +} + +static bool auok1900fb_need_refresh(struct auok190xfb_par *par) +{ + return (par->update_cnt > 10); +} + +static int __devinit auok1900fb_probe(struct platform_device *pdev) +{ + struct auok190x_init_data init; + struct auok190x_board *board; + + /* pick up board specific routines */ + board = pdev->dev.platform_data; + if (!board) + return -EINVAL; + + /* fill temporary init struct for common init */ + init.id = "auo_k1900fb"; + init.board = board; + init.update_partial = auok1900fb_dpy_update_pages; + init.update_all = auok1900fb_dpy_update; + init.need_refresh = auok1900fb_need_refresh; + init.init = auok1900_init; + + return auok190x_common_probe(pdev, &init); +} + +static int __devexit auok1900fb_remove(struct platform_device *pdev) +{ + return auok190x_common_remove(pdev); +} + +static struct platform_driver auok1900fb_driver = { + .probe = auok1900fb_probe, + .remove = __devexit_p(auok1900fb_remove), + .driver = { + .owner = THIS_MODULE, + .name = "auo_k1900fb", + .pm = &auok190x_pm, + }, +}; +module_platform_driver(auok1900fb_driver); + +MODULE_DESCRIPTION("framebuffer driver for the AUO-K1900 EPD controller"); +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/auo_k1901fb.c b/drivers/video/auo_k1901fb.c new file mode 100644 index 000000000000..1c054c18616e --- /dev/null +++ b/drivers/video/auo_k1901fb.c @@ -0,0 +1,251 @@ +/* + * auok190xfb.c -- FB driver for AUO-K1901 controllers + * + * Copyright (C) 2011, 2012 Heiko Stuebner <heiko@sntech.de> + * + * based on broadsheetfb.c + * + * Copyright (C) 2008, Jaya Kumar + * + * 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. + * + * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. + * + * This driver is written to be used with the AUO-K1901 display controller. + * + * It is intended to be architecture independent. A board specific driver + * must be used to perform all the physical IO interactions. + * + * The controller supports different update modes: + * mode0+1 16 step gray (4bit) + * mode2+3 4 step gray (2bit) + * mode4+5 2 step gray (1bit) + * - mode4 is described as "without LUT" + * mode7 automatic selection of update mode + * + * The most interesting difference to the K1900 is the ability to do screen + * updates in an asynchronous fashion. Where the K1900 needs to wait for the + * current update to complete, the K1901 can process later updates already. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/list.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <linux/pm_runtime.h> + +#include <video/auo_k190xfb.h> + +#include "auo_k190x.h" + +/* + * AUO-K1901 specific commands + */ + +#define AUOK1901_CMD_LUT_INTERFACE 0x0005 +#define AUOK1901_CMD_DMA_START 0x1001 +#define AUOK1901_CMD_CURSOR_START 0x1007 +#define AUOK1901_CMD_CURSOR_STOP AUOK190X_CMD_DATA_STOP +#define AUOK1901_CMD_DDMA_START 0x1009 + +#define AUOK1901_INIT_GATE_PULSE_LOW (0 << 14) +#define AUOK1901_INIT_GATE_PULSE_HIGH (1 << 14) +#define AUOK1901_INIT_SINGLE_GATE (0 << 13) +#define AUOK1901_INIT_DOUBLE_GATE (1 << 13) + +/* Bits to pixels + * Mode 15-12 11-8 7-4 3-0 + * format2 2 T 1 T + * format3 1 T 2 T + * format4 T 2 T 1 + * format5 T 1 T 2 + * + * halftone modes: + * format6 2 2 1 1 + * format7 1 1 2 2 + */ +#define AUOK1901_INIT_FORMAT2 (1 << 7) +#define AUOK1901_INIT_FORMAT3 ((1 << 7) | (1 << 6)) +#define AUOK1901_INIT_FORMAT4 (1 << 8) +#define AUOK1901_INIT_FORMAT5 ((1 << 8) | (1 << 6)) +#define AUOK1901_INIT_FORMAT6 ((1 << 8) | (1 << 7)) +#define AUOK1901_INIT_FORMAT7 ((1 << 8) | (1 << 7) | (1 << 6)) + +/* res[4] to bit 10 + * res[3-0] to bits 5-2 + */ +#define AUOK1901_INIT_RESOLUTION(_res) (((_res & (1 << 4)) << 6) \ + | ((_res & 0xf) << 2)) + +/* + * portrait / landscape orientation in AUOK1901_CMD_DMA_START + */ +#define AUOK1901_DMA_ROTATE90(_rot) ((_rot & 1) << 13) + +/* + * equivalent to 1 << 11, needs the ~ to have same rotation like K1900 + */ +#define AUOK1901_DDMA_ROTATE180(_rot) ((~_rot & 2) << 10) + +static void auok1901_init(struct auok190xfb_par *par) +{ + struct auok190x_board *board = par->board; + u16 init_param = 0; + + init_param |= AUOK190X_INIT_INVERSE_WHITE; + init_param |= AUOK190X_INIT_FORMAT0; + init_param |= AUOK1901_INIT_RESOLUTION(par->resolution); + init_param |= AUOK190X_INIT_SHIFT_LEFT; + + auok190x_send_cmdargs(par, AUOK190X_CMD_INIT, 1, &init_param); + + /* let the controller finish */ + board->wait_for_rdy(par); +} + +static void auok1901_update_region(struct auok190xfb_par *par, int mode, + u16 y1, u16 y2) +{ + struct device *dev = par->info->device; + unsigned char *buf = (unsigned char *)par->info->screen_base; + int xres = par->info->var.xres; + u16 args[5]; + + pm_runtime_get_sync(dev); + + mutex_lock(&(par->io_lock)); + + /* y1 and y2 must be a multiple of 2 so drop the lowest bit */ + y1 &= 0xfffe; + y2 &= 0xfffe; + + dev_dbg(dev, "update (x,y,w,h,mode)=(%d,%d,%d,%d,%d)\n", + 1, y1+1, xres, y2-y1, mode); + + /* K1901: first transfer the region data */ + args[0] = AUOK1901_DMA_ROTATE90(par->rotation) | 1; + args[1] = y1 + 1; + args[2] = xres; + args[3] = y2 - y1; + buf += y1 * xres; + auok190x_send_cmdargs_pixels_nowait(par, AUOK1901_CMD_DMA_START, 4, + args, ((y2 - y1) * xres)/2, + (u16 *) buf); + auok190x_send_command_nowait(par, AUOK190X_CMD_DATA_STOP); + + /* K1901: second tell the controller to update the region with mode */ + args[0] = mode | AUOK1901_DDMA_ROTATE180(par->rotation); + args[1] = 1; + args[2] = y1 + 1; + args[3] = xres; + args[4] = y2 - y1; + auok190x_send_cmdargs_nowait(par, AUOK1901_CMD_DDMA_START, 5, args); + + par->update_cnt++; + + mutex_unlock(&(par->io_lock)); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} + +static void auok1901fb_dpy_update_pages(struct auok190xfb_par *par, + u16 y1, u16 y2) +{ + int mode; + + if (par->update_mode < 0) { + mode = AUOK190X_UPDATE_MODE(1); + par->last_mode = -1; + } else { + mode = AUOK190X_UPDATE_MODE(par->update_mode); + par->last_mode = par->update_mode; + } + + if (par->flash) + mode |= AUOK190X_UPDATE_NONFLASH; + + auok1901_update_region(par, mode, y1, y2); +} + +static void auok1901fb_dpy_update(struct auok190xfb_par *par) +{ + int mode; + + /* When doing full updates, wait for the controller to be ready + * This will hopefully catch some hangs of the K1901 + */ + par->board->wait_for_rdy(par); + + if (par->update_mode < 0) { + mode = AUOK190X_UPDATE_MODE(0); + par->last_mode = -1; + } else { + mode = AUOK190X_UPDATE_MODE(par->update_mode); + par->last_mode = par->update_mode; + } + + if (par->flash) + mode |= AUOK190X_UPDATE_NONFLASH; + + auok1901_update_region(par, mode, 0, par->info->var.yres); + par->update_cnt = 0; +} + +static bool auok1901fb_need_refresh(struct auok190xfb_par *par) +{ + return (par->update_cnt > 10); +} + +static int __devinit auok1901fb_probe(struct platform_device *pdev) +{ + struct auok190x_init_data init; + struct auok190x_board *board; + + /* pick up board specific routines */ + board = pdev->dev.platform_data; + if (!board) + return -EINVAL; + + /* fill temporary init struct for common init */ + init.id = "auo_k1901fb"; + init.board = board; + init.update_partial = auok1901fb_dpy_update_pages; + init.update_all = auok1901fb_dpy_update; + init.need_refresh = auok1901fb_need_refresh; + init.init = auok1901_init; + + return auok190x_common_probe(pdev, &init); +} + +static int __devexit auok1901fb_remove(struct platform_device *pdev) +{ + return auok190x_common_remove(pdev); +} + +static struct platform_driver auok1901fb_driver = { + .probe = auok1901fb_probe, + .remove = __devexit_p(auok1901fb_remove), + .driver = { + .owner = THIS_MODULE, + .name = "auo_k1901fb", + .pm = &auok190x_pm, + }, +}; +module_platform_driver(auok1901fb_driver); + +MODULE_DESCRIPTION("framebuffer driver for the AUO-K1901 EPD controller"); +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/auo_k190x.c b/drivers/video/auo_k190x.c new file mode 100644 index 000000000000..77da6a2f43dc --- /dev/null +++ b/drivers/video/auo_k190x.c @@ -0,0 +1,1046 @@ +/* + * Common code for AUO-K190X framebuffer drivers + * + * Copyright (C) 2012 Heiko Stuebner <heiko@sntech.de> + * + * 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/kernel.h> +#include <linux/gpio.h> +#include <linux/pm_runtime.h> +#include <linux/fb.h> +#include <linux/delay.h> +#include <linux/uaccess.h> +#include <linux/vmalloc.h> +#include <linux/regulator/consumer.h> + +#include <video/auo_k190xfb.h> + +#include "auo_k190x.h" + +struct panel_info { + int w; + int h; +}; + +/* table of panel specific parameters to be indexed into by the board drivers */ +static struct panel_info panel_table[] = { + /* standard 6" */ + [AUOK190X_RESOLUTION_800_600] = { + .w = 800, + .h = 600, + }, + /* standard 9" */ + [AUOK190X_RESOLUTION_1024_768] = { + .w = 1024, + .h = 768, + }, +}; + +/* + * private I80 interface to the board driver + */ + +static void auok190x_issue_data(struct auok190xfb_par *par, u16 data) +{ + par->board->set_ctl(par, AUOK190X_I80_WR, 0); + par->board->set_hdb(par, data); + par->board->set_ctl(par, AUOK190X_I80_WR, 1); +} + +static void auok190x_issue_cmd(struct auok190xfb_par *par, u16 data) +{ + par->board->set_ctl(par, AUOK190X_I80_DC, 0); + auok190x_issue_data(par, data); + par->board->set_ctl(par, AUOK190X_I80_DC, 1); +} + +static int auok190x_issue_pixels(struct auok190xfb_par *par, int size, + u16 *data) +{ + struct device *dev = par->info->device; + int i; + u16 tmp; + + if (size & 3) { + dev_err(dev, "issue_pixels: size %d must be a multiple of 4\n", + size); + return -EINVAL; + } + + for (i = 0; i < (size >> 1); i++) { + par->board->set_ctl(par, AUOK190X_I80_WR, 0); + + /* simple reduction of 8bit staticgray to 4bit gray + * combines 4 * 4bit pixel values into a 16bit value + */ + tmp = (data[2*i] & 0xF0) >> 4; + tmp |= (data[2*i] & 0xF000) >> 8; + tmp |= (data[2*i+1] & 0xF0) << 4; + tmp |= (data[2*i+1] & 0xF000); + + par->board->set_hdb(par, tmp); + par->board->set_ctl(par, AUOK190X_I80_WR, 1); + } + + return 0; +} + +static u16 auok190x_read_data(struct auok190xfb_par *par) +{ + u16 data; + + par->board->set_ctl(par, AUOK190X_I80_OE, 0); + data = par->board->get_hdb(par); + par->board->set_ctl(par, AUOK190X_I80_OE, 1); + + return data; +} + +/* + * Command interface for the controller drivers + */ + +void auok190x_send_command_nowait(struct auok190xfb_par *par, u16 data) +{ + par->board->set_ctl(par, AUOK190X_I80_CS, 0); + auok190x_issue_cmd(par, data); + par->board->set_ctl(par, AUOK190X_I80_CS, 1); +} +EXPORT_SYMBOL_GPL(auok190x_send_command_nowait); + +void auok190x_send_cmdargs_nowait(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv) +{ + int i; + + par->board->set_ctl(par, AUOK190X_I80_CS, 0); + auok190x_issue_cmd(par, cmd); + + for (i = 0; i < argc; i++) + auok190x_issue_data(par, argv[i]); + par->board->set_ctl(par, AUOK190X_I80_CS, 1); +} +EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_nowait); + +int auok190x_send_command(struct auok190xfb_par *par, u16 data) +{ + int ret; + + ret = par->board->wait_for_rdy(par); + if (ret) + return ret; + + auok190x_send_command_nowait(par, data); + return 0; +} +EXPORT_SYMBOL_GPL(auok190x_send_command); + +int auok190x_send_cmdargs(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv) +{ + int ret; + + ret = par->board->wait_for_rdy(par); + if (ret) + return ret; + + auok190x_send_cmdargs_nowait(par, cmd, argc, argv); + return 0; +} +EXPORT_SYMBOL_GPL(auok190x_send_cmdargs); + +int auok190x_read_cmdargs(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv) +{ + int i, ret; + + ret = par->board->wait_for_rdy(par); + if (ret) + return ret; + + par->board->set_ctl(par, AUOK190X_I80_CS, 0); + auok190x_issue_cmd(par, cmd); + + for (i = 0; i < argc; i++) + argv[i] = auok190x_read_data(par); + par->board->set_ctl(par, AUOK190X_I80_CS, 1); + + return 0; +} +EXPORT_SYMBOL_GPL(auok190x_read_cmdargs); + +void auok190x_send_cmdargs_pixels_nowait(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv, int size, u16 *data) +{ + int i; + + par->board->set_ctl(par, AUOK190X_I80_CS, 0); + + auok190x_issue_cmd(par, cmd); + + for (i = 0; i < argc; i++) + auok190x_issue_data(par, argv[i]); + + auok190x_issue_pixels(par, size, data); + + par->board->set_ctl(par, AUOK190X_I80_CS, 1); +} +EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_pixels_nowait); + +int auok190x_send_cmdargs_pixels(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv, int size, u16 *data) +{ + int ret; + + ret = par->board->wait_for_rdy(par); + if (ret) + return ret; + + auok190x_send_cmdargs_pixels_nowait(par, cmd, argc, argv, size, data); + + return 0; +} +EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_pixels); + +/* + * fbdefio callbacks - common on both controllers. + */ + +static void auok190xfb_dpy_first_io(struct fb_info *info) +{ + /* tell runtime-pm that we wish to use the device in a short time */ + pm_runtime_get(info->device); +} + +/* this is called back from the deferred io workqueue */ +static void auok190xfb_dpy_deferred_io(struct fb_info *info, + struct list_head *pagelist) +{ + struct fb_deferred_io *fbdefio = info->fbdefio; + struct auok190xfb_par *par = info->par; + u16 yres = info->var.yres; + u16 xres = info->var.xres; + u16 y1 = 0, h = 0; + int prev_index = -1; + struct page *cur; + int h_inc; + int threshold; + + if (!list_empty(pagelist)) + /* the device resume should've been requested through first_io, + * if the resume did not finish until now, wait for it. + */ + pm_runtime_barrier(info->device); + else + /* We reached this via the fsync or some other way. + * In either case the first_io function did not run, + * so we runtime_resume the device here synchronously. + */ + pm_runtime_get_sync(info->device); + + /* Do a full screen update every n updates to prevent + * excessive darkening of the Sipix display. + * If we do this, there is no need to walk the pages. + */ + if (par->need_refresh(par)) { + par->update_all(par); + goto out; + } + + /* height increment is fixed per page */ + h_inc = DIV_ROUND_UP(PAGE_SIZE , xres); + + /* calculate number of pages from pixel height */ + threshold = par->consecutive_threshold / h_inc; + if (threshold < 1) + threshold = 1; + + /* walk the written page list and swizzle the data */ + list_for_each_entry(cur, &fbdefio->pagelist, lru) { + if (prev_index < 0) { + /* just starting so assign first page */ + y1 = (cur->index << PAGE_SHIFT) / xres; + h = h_inc; + } else if ((cur->index - prev_index) <= threshold) { + /* page is within our threshold for single updates */ + h += h_inc * (cur->index - prev_index); + } else { + /* page not consecutive, issue previous update first */ + par->update_partial(par, y1, y1 + h); + + /* start over with our non consecutive page */ + y1 = (cur->index << PAGE_SHIFT) / xres; + h = h_inc; + } + prev_index = cur->index; + } + + /* if we still have any pages to update we do so now */ + if (h >= yres) + /* its a full screen update, just do it */ + par->update_all(par); + else + par->update_partial(par, y1, min((u16) (y1 + h), yres)); + +out: + pm_runtime_mark_last_busy(info->device); + pm_runtime_put_autosuspend(info->device); +} + +/* + * framebuffer operations + */ + +/* + * this is the slow path from userspace. they can seek and write to + * the fb. it's inefficient to do anything less than a full screen draw + */ +static ssize_t auok190xfb_write(struct fb_info *info, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct auok190xfb_par *par = info->par; + unsigned long p = *ppos; + void *dst; + int err = 0; + unsigned long total_size; + + if (info->state != FBINFO_STATE_RUNNING) + return -EPERM; + + total_size = info->fix.smem_len; + + if (p > total_size) + return -EFBIG; + + if (count > total_size) { + err = -EFBIG; + count = total_size; + } + + if (count + p > total_size) { + if (!err) + err = -ENOSPC; + + count = total_size - p; + } + + dst = (void *)(info->screen_base + p); + + if (copy_from_user(dst, buf, count)) + err = -EFAULT; + + if (!err) + *ppos += count; + + par->update_all(par); + + return (err) ? err : count; +} + +static void auok190xfb_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + struct auok190xfb_par *par = info->par; + + sys_fillrect(info, rect); + + par->update_all(par); +} + +static void auok190xfb_copyarea(struct fb_info *info, + const struct fb_copyarea *area) +{ + struct auok190xfb_par *par = info->par; + + sys_copyarea(info, area); + + par->update_all(par); +} + +static void auok190xfb_imageblit(struct fb_info *info, + const struct fb_image *image) +{ + struct auok190xfb_par *par = info->par; + + sys_imageblit(info, image); + + par->update_all(par); +} + +static int auok190xfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + if (info->var.xres != var->xres || info->var.yres != var->yres || + info->var.xres_virtual != var->xres_virtual || + info->var.yres_virtual != var->yres_virtual) { + pr_info("%s: Resolution not supported: X%u x Y%u\n", + __func__, var->xres, var->yres); + return -EINVAL; + } + + /* + * Memory limit + */ + + if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) { + pr_info("%s: Memory Limit requested yres_virtual = %u\n", + __func__, var->yres_virtual); + return -ENOMEM; + } + + return 0; +} + +static struct fb_ops auok190xfb_ops = { + .owner = THIS_MODULE, + .fb_read = fb_sys_read, + .fb_write = auok190xfb_write, + .fb_fillrect = auok190xfb_fillrect, + .fb_copyarea = auok190xfb_copyarea, + .fb_imageblit = auok190xfb_imageblit, + .fb_check_var = auok190xfb_check_var, +}; + +/* + * Controller-functions common to both K1900 and K1901 + */ + +static int auok190x_read_temperature(struct auok190xfb_par *par) +{ + struct device *dev = par->info->device; + u16 data[4]; + int temp; + + pm_runtime_get_sync(dev); + + mutex_lock(&(par->io_lock)); + + auok190x_read_cmdargs(par, AUOK190X_CMD_READ_VERSION, 4, data); + + mutex_unlock(&(par->io_lock)); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + /* sanitize and split of half-degrees for now */ + temp = ((data[0] & AUOK190X_VERSION_TEMP_MASK) >> 1); + + /* handle positive and negative temperatures */ + if (temp >= 201) + return (255 - temp + 1) * (-1); + else + return temp; +} + +static void auok190x_identify(struct auok190xfb_par *par) +{ + struct device *dev = par->info->device; + u16 data[4]; + + pm_runtime_get_sync(dev); + + mutex_lock(&(par->io_lock)); + + auok190x_read_cmdargs(par, AUOK190X_CMD_READ_VERSION, 4, data); + + mutex_unlock(&(par->io_lock)); + + par->epd_type = data[1] & AUOK190X_VERSION_TEMP_MASK; + + par->panel_size_int = AUOK190X_VERSION_SIZE_INT(data[2]); + par->panel_size_float = AUOK190X_VERSION_SIZE_FLOAT(data[2]); + par->panel_model = AUOK190X_VERSION_MODEL(data[2]); + + par->tcon_version = AUOK190X_VERSION_TCON(data[3]); + par->lut_version = AUOK190X_VERSION_LUT(data[3]); + + dev_dbg(dev, "panel %d.%din, model 0x%x, EPD 0x%x TCON-rev 0x%x, LUT-rev 0x%x", + par->panel_size_int, par->panel_size_float, par->panel_model, + par->epd_type, par->tcon_version, par->lut_version); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} + +/* + * Sysfs functions + */ + +static ssize_t update_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct auok190xfb_par *par = info->par; + + return sprintf(buf, "%d\n", par->update_mode); +} + +static ssize_t update_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct auok190xfb_par *par = info->par; + int mode, ret; + + ret = kstrtoint(buf, 10, &mode); + if (ret) + return ret; + + par->update_mode = mode; + + /* if we enter a better mode, do a full update */ + if (par->last_mode > 1 && mode < par->last_mode) + par->update_all(par); + + return count; +} + +static ssize_t flash_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct auok190xfb_par *par = info->par; + + return sprintf(buf, "%d\n", par->flash); +} + +static ssize_t flash_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct auok190xfb_par *par = info->par; + int flash, ret; + + ret = kstrtoint(buf, 10, &flash); + if (ret) + return ret; + + if (flash > 0) + par->flash = 1; + else + par->flash = 0; + + return count; +} + +static ssize_t temp_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct auok190xfb_par *par = info->par; + int temp; + + temp = auok190x_read_temperature(par); + return sprintf(buf, "%d\n", temp); +} + +static DEVICE_ATTR(update_mode, 0644, update_mode_show, update_mode_store); +static DEVICE_ATTR(flash, 0644, flash_show, flash_store); +static DEVICE_ATTR(temp, 0644, temp_show, NULL); + +static struct attribute *auok190x_attributes[] = { + &dev_attr_update_mode.attr, + &dev_attr_flash.attr, + &dev_attr_temp.attr, + NULL +}; + +static const struct attribute_group auok190x_attr_group = { + .attrs = auok190x_attributes, +}; + +static int auok190x_power(struct auok190xfb_par *par, bool on) +{ + struct auok190x_board *board = par->board; + int ret; + + if (on) { + /* We should maintain POWER up for at least 80ms before set + * RST_N and SLP_N to high (TCON spec 20100803_v35 p59) + */ + ret = regulator_enable(par->regulator); + if (ret) + return ret; + + msleep(200); + gpio_set_value(board->gpio_nrst, 1); + gpio_set_value(board->gpio_nsleep, 1); + msleep(200); + } else { + regulator_disable(par->regulator); + gpio_set_value(board->gpio_nrst, 0); + gpio_set_value(board->gpio_nsleep, 0); + } + + return 0; +} + +/* + * Recovery - powercycle the controller + */ + +static void auok190x_recover(struct auok190xfb_par *par) +{ + auok190x_power(par, 0); + msleep(100); + auok190x_power(par, 1); + + par->init(par); + + /* wait for init to complete */ + par->board->wait_for_rdy(par); +} + +/* + * Power-management + */ + +#ifdef CONFIG_PM +static int auok190x_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fb_info *info = platform_get_drvdata(pdev); + struct auok190xfb_par *par = info->par; + struct auok190x_board *board = par->board; + u16 standby_param; + + /* take and keep the lock until we are resumed, as the controller + * will never reach the non-busy state when in standby mode + */ + mutex_lock(&(par->io_lock)); + + if (par->standby) { + dev_warn(dev, "already in standby, runtime-pm pairing mismatch\n"); + mutex_unlock(&(par->io_lock)); + return 0; + } + + /* according to runtime_pm.txt runtime_suspend only means, that the + * device will not process data and will not communicate with the CPU + * As we hold the lock, this stays true even without standby + */ + if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { + dev_dbg(dev, "runtime suspend without standby\n"); + goto finish; + } else if (board->quirks & AUOK190X_QUIRK_STANDBYPARAM) { + /* for some TCON versions STANDBY expects a parameter (0) but + * it seems the real tcon version has to be determined yet. + */ + dev_dbg(dev, "runtime suspend with additional empty param\n"); + standby_param = 0; + auok190x_send_cmdargs(par, AUOK190X_CMD_STANDBY, 1, + &standby_param); + } else { + dev_dbg(dev, "runtime suspend without param\n"); + auok190x_send_command(par, AUOK190X_CMD_STANDBY); + } + + msleep(64); + +finish: + par->standby = 1; + + return 0; +} + +static int auok190x_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fb_info *info = platform_get_drvdata(pdev); + struct auok190xfb_par *par = info->par; + struct auok190x_board *board = par->board; + + if (!par->standby) { + dev_warn(dev, "not in standby, runtime-pm pairing mismatch\n"); + return 0; + } + + if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { + dev_dbg(dev, "runtime resume without standby\n"); + } else { + /* when in standby, controller is always busy + * and only accepts the wakeup command + */ + dev_dbg(dev, "runtime resume from standby\n"); + auok190x_send_command_nowait(par, AUOK190X_CMD_WAKEUP); + + msleep(160); + + /* wait for the controller to be ready and release the lock */ + board->wait_for_rdy(par); + } + + par->standby = 0; + + mutex_unlock(&(par->io_lock)); + + return 0; +} + +static int auok190x_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fb_info *info = platform_get_drvdata(pdev); + struct auok190xfb_par *par = info->par; + struct auok190x_board *board = par->board; + int ret; + + dev_dbg(dev, "suspend\n"); + if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { + /* suspend via powering off the ic */ + dev_dbg(dev, "suspend with broken standby\n"); + + auok190x_power(par, 0); + } else { + dev_dbg(dev, "suspend using sleep\n"); + + /* the sleep state can only be entered from the standby state. + * pm_runtime_get_noresume gets called before the suspend call. + * So the devices usage count is >0 but it is not necessarily + * active. + */ + if (!pm_runtime_status_suspended(dev)) { + ret = auok190x_runtime_suspend(dev); + if (ret < 0) { + dev_err(dev, "auok190x_runtime_suspend failed with %d\n", + ret); + return ret; + } + par->manual_standby = 1; + } + + gpio_direction_output(board->gpio_nsleep, 0); + } + + msleep(100); + + return 0; +} + +static int auok190x_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fb_info *info = platform_get_drvdata(pdev); + struct auok190xfb_par *par = info->par; + struct auok190x_board *board = par->board; + + dev_dbg(dev, "resume\n"); + if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { + dev_dbg(dev, "resume with broken standby\n"); + + auok190x_power(par, 1); + + par->init(par); + } else { + dev_dbg(dev, "resume from sleep\n"); + + /* device should be in runtime suspend when we were suspended + * and pm_runtime_put_sync gets called after this function. + * So there is no need to touch the standby mode here at all. + */ + gpio_direction_output(board->gpio_nsleep, 1); + msleep(100); + + /* an additional init call seems to be necessary after sleep */ + auok190x_runtime_resume(dev); + par->init(par); + + /* if we were runtime-suspended before, suspend again*/ + if (!par->manual_standby) + auok190x_runtime_suspend(dev); + else + par->manual_standby = 0; + } + + return 0; +} +#endif + +const struct dev_pm_ops auok190x_pm = { + SET_RUNTIME_PM_OPS(auok190x_runtime_suspend, auok190x_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(auok190x_suspend, auok190x_resume) +}; +EXPORT_SYMBOL_GPL(auok190x_pm); + +/* + * Common probe and remove code + */ + +int __devinit auok190x_common_probe(struct platform_device *pdev, + struct auok190x_init_data *init) +{ + struct auok190x_board *board = init->board; + struct auok190xfb_par *par; + struct fb_info *info; + struct panel_info *panel; + int videomemorysize, ret; + unsigned char *videomemory; + + /* check board contents */ + if (!board->init || !board->cleanup || !board->wait_for_rdy + || !board->set_ctl || !board->set_hdb || !board->get_hdb + || !board->setup_irq) + return -EINVAL; + + info = framebuffer_alloc(sizeof(struct auok190xfb_par), &pdev->dev); + if (!info) + return -ENOMEM; + + par = info->par; + par->info = info; + par->board = board; + par->recover = auok190x_recover; + par->update_partial = init->update_partial; + par->update_all = init->update_all; + par->need_refresh = init->need_refresh; + par->init = init->init; + + /* init update modes */ + par->update_cnt = 0; + par->update_mode = -1; + par->last_mode = -1; + par->flash = 0; + + par->regulator = regulator_get(info->device, "vdd"); + if (IS_ERR(par->regulator)) { + ret = PTR_ERR(par->regulator); + dev_err(info->device, "Failed to get regulator: %d\n", ret); + goto err_reg; + } + + ret = board->init(par); + if (ret) { + dev_err(info->device, "board init failed, %d\n", ret); + goto err_board; + } + + ret = gpio_request(board->gpio_nsleep, "AUOK190x sleep"); + if (ret) { + dev_err(info->device, "could not request sleep gpio, %d\n", + ret); + goto err_gpio1; + } + + ret = gpio_direction_output(board->gpio_nsleep, 0); + if (ret) { + dev_err(info->device, "could not set sleep gpio, %d\n", ret); + goto err_gpio2; + } + + ret = gpio_request(board->gpio_nrst, "AUOK190x reset"); + if (ret) { + dev_err(info->device, "could not request reset gpio, %d\n", + ret); + goto err_gpio2; + } + + ret = gpio_direction_output(board->gpio_nrst, 0); + if (ret) { + dev_err(info->device, "could not set reset gpio, %d\n", ret); + goto err_gpio3; + } + + ret = auok190x_power(par, 1); + if (ret) { + dev_err(info->device, "could not power on the device, %d\n", + ret); + goto err_gpio3; + } + + mutex_init(&par->io_lock); + + init_waitqueue_head(&par->waitq); + + ret = par->board->setup_irq(par->info); + if (ret) { + dev_err(info->device, "could not setup ready-irq, %d\n", ret); + goto err_irq; + } + + /* wait for init to complete */ + par->board->wait_for_rdy(par); + + /* + * From here on the controller can talk to us + */ + + /* initialise fix, var, resolution and rotation */ + + strlcpy(info->fix.id, init->id, 16); + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; + info->fix.xpanstep = 0; + info->fix.ypanstep = 0; + info->fix.ywrapstep = 0; + info->fix.accel = FB_ACCEL_NONE; + + info->var.bits_per_pixel = 8; + info->var.grayscale = 1; + info->var.red.length = 8; + info->var.green.length = 8; + info->var.blue.length = 8; + + panel = &panel_table[board->resolution]; + + /* if 90 degree rotation, switch width and height */ + if (board->rotation & 1) { + info->var.xres = panel->h; + info->var.yres = panel->w; + info->var.xres_virtual = panel->h; + info->var.yres_virtual = panel->w; + info->fix.line_length = panel->h; + } else { + info->var.xres = panel->w; + info->var.yres = panel->h; + info->var.xres_virtual = panel->w; + info->var.yres_virtual = panel->h; + info->fix.line_length = panel->w; + } + + par->resolution = board->resolution; + par->rotation = board->rotation; + + /* videomemory handling */ + + videomemorysize = roundup((panel->w * panel->h), PAGE_SIZE); + videomemory = vmalloc(videomemorysize); + if (!videomemory) { + ret = -ENOMEM; + goto err_irq; + } + + memset(videomemory, 0, videomemorysize); + info->screen_base = (char *)videomemory; + info->fix.smem_len = videomemorysize; + + info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; + info->fbops = &auok190xfb_ops; + + /* deferred io init */ + + info->fbdefio = devm_kzalloc(info->device, + sizeof(struct fb_deferred_io), + GFP_KERNEL); + if (!info->fbdefio) { + dev_err(info->device, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto err_defio; + } + + dev_dbg(info->device, "targetting %d frames per second\n", board->fps); + info->fbdefio->delay = HZ / board->fps; + info->fbdefio->first_io = auok190xfb_dpy_first_io, + info->fbdefio->deferred_io = auok190xfb_dpy_deferred_io, + fb_deferred_io_init(info); + + /* color map */ + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret < 0) { + dev_err(info->device, "Failed to allocate colormap\n"); + goto err_cmap; + } + + /* controller init */ + + par->consecutive_threshold = 100; + par->init(par); + auok190x_identify(par); + + platform_set_drvdata(pdev, info); + + ret = register_framebuffer(info); + if (ret < 0) + goto err_regfb; + + ret = sysfs_create_group(&info->device->kobj, &auok190x_attr_group); + if (ret) + goto err_sysfs; + + dev_info(info->device, "fb%d: %dx%d using %dK of video memory\n", + info->node, info->var.xres, info->var.yres, + videomemorysize >> 10); + + /* increase autosuspend_delay when we use alternative methods + * for runtime_pm + */ + par->autosuspend_delay = (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) + ? 1000 : 200; + + pm_runtime_set_active(info->device); + pm_runtime_enable(info->device); + pm_runtime_set_autosuspend_delay(info->device, par->autosuspend_delay); + pm_runtime_use_autosuspend(info->device); + + return 0; + +err_sysfs: + unregister_framebuffer(info); +err_regfb: + fb_dealloc_cmap(&info->cmap); +err_cmap: + fb_deferred_io_cleanup(info); + kfree(info->fbdefio); +err_defio: + vfree((void *)info->screen_base); +err_irq: + auok190x_power(par, 0); +err_gpio3: + gpio_free(board->gpio_nrst); +err_gpio2: + gpio_free(board->gpio_nsleep); +err_gpio1: + board->cleanup(par); +err_board: + regulator_put(par->regulator); +err_reg: + framebuffer_release(info); + + return ret; +} +EXPORT_SYMBOL_GPL(auok190x_common_probe); + +int __devexit auok190x_common_remove(struct platform_device *pdev) +{ + struct fb_info *info = platform_get_drvdata(pdev); + struct auok190xfb_par *par = info->par; + struct auok190x_board *board = par->board; + + pm_runtime_disable(info->device); + + sysfs_remove_group(&info->device->kobj, &auok190x_attr_group); + + unregister_framebuffer(info); + + fb_dealloc_cmap(&info->cmap); + + fb_deferred_io_cleanup(info); + kfree(info->fbdefio); + + vfree((void *)info->screen_base); + + auok190x_power(par, 0); + + gpio_free(board->gpio_nrst); + gpio_free(board->gpio_nsleep); + + board->cleanup(par); + + regulator_put(par->regulator); + + framebuffer_release(info); + + return 0; +} +EXPORT_SYMBOL_GPL(auok190x_common_remove); + +MODULE_DESCRIPTION("Common code for AUO-K190X controllers"); +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/auo_k190x.h b/drivers/video/auo_k190x.h new file mode 100644 index 000000000000..e35af1f51b28 --- /dev/null +++ b/drivers/video/auo_k190x.h @@ -0,0 +1,129 @@ +/* + * Private common definitions for AUO-K190X framebuffer drivers + * + * Copyright (C) 2012 Heiko Stuebner <heiko@sntech.de> + * + * 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. + */ + +/* + * I80 interface specific defines + */ + +#define AUOK190X_I80_CS 0x01 +#define AUOK190X_I80_DC 0x02 +#define AUOK190X_I80_WR 0x03 +#define AUOK190X_I80_OE 0x04 + +/* + * AUOK190x commands, common to both controllers + */ + +#define AUOK190X_CMD_INIT 0x0000 +#define AUOK190X_CMD_STANDBY 0x0001 +#define AUOK190X_CMD_WAKEUP 0x0002 +#define AUOK190X_CMD_TCON_RESET 0x0003 +#define AUOK190X_CMD_DATA_STOP 0x1002 +#define AUOK190X_CMD_LUT_START 0x1003 +#define AUOK190X_CMD_DISP_REFRESH 0x1004 +#define AUOK190X_CMD_DISP_RESET 0x1005 +#define AUOK190X_CMD_PRE_DISPLAY_START 0x100D +#define AUOK190X_CMD_PRE_DISPLAY_STOP 0x100F +#define AUOK190X_CMD_FLASH_W 0x2000 +#define AUOK190X_CMD_FLASH_E 0x2001 +#define AUOK190X_CMD_FLASH_STS 0x2002 +#define AUOK190X_CMD_FRAMERATE 0x3000 +#define AUOK190X_CMD_READ_VERSION 0x4000 +#define AUOK190X_CMD_READ_STATUS 0x4001 +#define AUOK190X_CMD_READ_LUT 0x4003 +#define AUOK190X_CMD_DRIVERTIMING 0x5000 +#define AUOK190X_CMD_LBALANCE 0x5001 +#define AUOK190X_CMD_AGINGMODE 0x6000 +#define AUOK190X_CMD_AGINGEXIT 0x6001 + +/* + * Common settings for AUOK190X_CMD_INIT + */ + +#define AUOK190X_INIT_DATA_FILTER (0 << 12) +#define AUOK190X_INIT_DATA_BYPASS (1 << 12) +#define AUOK190X_INIT_INVERSE_WHITE (0 << 9) +#define AUOK190X_INIT_INVERSE_BLACK (1 << 9) +#define AUOK190X_INIT_SCAN_DOWN (0 << 1) +#define AUOK190X_INIT_SCAN_UP (1 << 1) +#define AUOK190X_INIT_SHIFT_LEFT (0 << 0) +#define AUOK190X_INIT_SHIFT_RIGHT (1 << 0) + +/* Common bits to pixels + * Mode 15-12 11-8 7-4 3-0 + * format0 4 3 2 1 + * format1 3 4 1 2 + */ + +#define AUOK190X_INIT_FORMAT0 0 +#define AUOK190X_INIT_FORMAT1 (1 << 6) + +/* + * settings for AUOK190X_CMD_RESET + */ + +#define AUOK190X_RESET_TCON (0 << 0) +#define AUOK190X_RESET_NORMAL (1 << 0) +#define AUOK190X_RESET_PON (1 << 1) + +/* + * AUOK190X_CMD_VERSION + */ + +#define AUOK190X_VERSION_TEMP_MASK (0x1ff) +#define AUOK190X_VERSION_EPD_MASK (0xff) +#define AUOK190X_VERSION_SIZE_INT(_val) ((_val & 0xfc00) >> 10) +#define AUOK190X_VERSION_SIZE_FLOAT(_val) ((_val & 0x3c0) >> 6) +#define AUOK190X_VERSION_MODEL(_val) (_val & 0x3f) +#define AUOK190X_VERSION_LUT(_val) (_val & 0xff) +#define AUOK190X_VERSION_TCON(_val) ((_val & 0xff00) >> 8) + +/* + * update modes for CMD_PARTIALDISP on K1900 and CMD_DDMA on K1901 + */ + +#define AUOK190X_UPDATE_MODE(_res) ((_res & 0x7) << 12) +#define AUOK190X_UPDATE_NONFLASH (1 << 15) + +/* + * track panel specific parameters for common init + */ + +struct auok190x_init_data { + char *id; + struct auok190x_board *board; + + void (*update_partial)(struct auok190xfb_par *par, u16 y1, u16 y2); + void (*update_all)(struct auok190xfb_par *par); + bool (*need_refresh)(struct auok190xfb_par *par); + void (*init)(struct auok190xfb_par *par); +}; + + +extern void auok190x_send_command_nowait(struct auok190xfb_par *par, u16 data); +extern int auok190x_send_command(struct auok190xfb_par *par, u16 data); +extern void auok190x_send_cmdargs_nowait(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv); +extern int auok190x_send_cmdargs(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv); +extern void auok190x_send_cmdargs_pixels_nowait(struct auok190xfb_par *par, + u16 cmd, int argc, u16 *argv, + int size, u16 *data); +extern int auok190x_send_cmdargs_pixels(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv, int size, + u16 *data); +extern int auok190x_read_cmdargs(struct auok190xfb_par *par, u16 cmd, + int argc, u16 *argv); + +extern int auok190x_common_probe(struct platform_device *pdev, + struct auok190x_init_data *init); +extern int auok190x_common_remove(struct platform_device *pdev); + +extern const struct dev_pm_ops auok190x_pm; diff --git a/drivers/video/bfin_adv7393fb.c b/drivers/video/bfin_adv7393fb.c index 1a268a294478..33ea874c87d2 100644 --- a/drivers/video/bfin_adv7393fb.c +++ b/drivers/video/bfin_adv7393fb.c @@ -414,14 +414,14 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, if (ret) { dev_err(&client->dev, "PPI0_FS3 GPIO request failed\n"); ret = -EBUSY; - goto out_8; + goto free_fbdev; } } if (peripheral_request_list(ppi_pins, DRIVER_NAME)) { dev_err(&client->dev, "requesting PPI peripheral failed\n"); ret = -EFAULT; - goto out_8; + goto free_gpio; } fbdev->fb_mem = @@ -432,7 +432,7 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, dev_err(&client->dev, "couldn't allocate dma buffer (%d bytes)\n", (u32) fbdev->fb_len); ret = -ENOMEM; - goto out_7; + goto free_ppi_pins; } fbdev->info.screen_base = (void *)fbdev->fb_mem; @@ -464,27 +464,27 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, if (!fbdev->info.pseudo_palette) { dev_err(&client->dev, "failed to allocate pseudo_palette\n"); ret = -ENOMEM; - goto out_6; + goto free_fb_mem; } if (fb_alloc_cmap(&fbdev->info.cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { dev_err(&client->dev, "failed to allocate colormap (%d entries)\n", BFIN_LCD_NBR_PALETTE_ENTRIES); ret = -EFAULT; - goto out_5; + goto free_palette; } if (request_dma(CH_PPI, "BF5xx_PPI_DMA") < 0) { dev_err(&client->dev, "unable to request PPI DMA\n"); ret = -EFAULT; - goto out_4; + goto free_cmap; } if (request_irq(IRQ_PPI_ERROR, ppi_irq_error, 0, "PPI ERROR", fbdev) < 0) { dev_err(&client->dev, "unable to request PPI ERROR IRQ\n"); ret = -EFAULT; - goto out_3; + goto free_ch_ppi; } fbdev->open = 0; @@ -494,14 +494,14 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, if (ret) { dev_err(&client->dev, "i2c attach: init error\n"); - goto out_1; + goto free_irq_ppi; } if (register_framebuffer(&fbdev->info) < 0) { dev_err(&client->dev, "unable to register framebuffer\n"); ret = -EFAULT; - goto out_1; + goto free_irq_ppi; } dev_info(&client->dev, "fb%d: %s frame buffer device\n", @@ -512,7 +512,7 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, if (!entry) { dev_err(&client->dev, "unable to create /proc entry\n"); ret = -EFAULT; - goto out_0; + goto free_fb; } entry->read_proc = adv7393_read_proc; @@ -521,22 +521,25 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, return 0; - out_0: +free_fb: unregister_framebuffer(&fbdev->info); - out_1: +free_irq_ppi: free_irq(IRQ_PPI_ERROR, fbdev); - out_3: +free_ch_ppi: free_dma(CH_PPI); - out_4: - dma_free_coherent(NULL, fbdev->fb_len, fbdev->fb_mem, - fbdev->dma_handle); - out_5: +free_cmap: fb_dealloc_cmap(&fbdev->info.cmap); - out_6: +free_palette: kfree(fbdev->info.pseudo_palette); - out_7: +free_fb_mem: + dma_free_coherent(NULL, fbdev->fb_len, fbdev->fb_mem, + fbdev->dma_handle); +free_ppi_pins: peripheral_free_list(ppi_pins); - out_8: +free_gpio: + if (ANOMALY_05000400) + gpio_free(P_IDENT(P_PPI0_FS3)); +free_fbdev: kfree(fbdev); return ret; diff --git a/drivers/video/cobalt_lcdfb.c b/drivers/video/cobalt_lcdfb.c index f56699d8122a..eae46f6457e2 100644 --- a/drivers/video/cobalt_lcdfb.c +++ b/drivers/video/cobalt_lcdfb.c @@ -1,7 +1,8 @@ /* - * Cobalt server LCD frame buffer driver. + * Cobalt/SEAD3 LCD frame buffer driver. * * Copyright (C) 2008 Yoichi Yuasa <yuasa@linux-mips.org> + * Copyright (C) 2012 MIPS Technologies, Inc. * * 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 @@ -62,6 +63,7 @@ #define LCD_CUR_POS(x) ((x) & LCD_CUR_POS_MASK) #define LCD_TEXT_POS(x) ((x) | LCD_TEXT_MODE) +#ifdef CONFIG_MIPS_COBALT static inline void lcd_write_control(struct fb_info *info, u8 control) { writel((u32)control << 24, info->screen_base); @@ -81,6 +83,47 @@ static inline u8 lcd_read_data(struct fb_info *info) { return readl(info->screen_base + LCD_DATA_REG_OFFSET) >> 24; } +#else + +#define LCD_CTL 0x00 +#define LCD_DATA 0x08 +#define CPLD_STATUS 0x10 +#define CPLD_DATA 0x18 + +static inline void cpld_wait(struct fb_info *info) +{ + do { + } while (readl(info->screen_base + CPLD_STATUS) & 1); +} + +static inline void lcd_write_control(struct fb_info *info, u8 control) +{ + cpld_wait(info); + writel(control, info->screen_base + LCD_CTL); +} + +static inline u8 lcd_read_control(struct fb_info *info) +{ + cpld_wait(info); + readl(info->screen_base + LCD_CTL); + cpld_wait(info); + return readl(info->screen_base + CPLD_DATA) & 0xff; +} + +static inline void lcd_write_data(struct fb_info *info, u8 data) +{ + cpld_wait(info); + writel(data, info->screen_base + LCD_DATA); +} + +static inline u8 lcd_read_data(struct fb_info *info) +{ + cpld_wait(info); + readl(info->screen_base + LCD_DATA); + cpld_wait(info); + return readl(info->screen_base + CPLD_DATA) & 0xff; +} +#endif static int lcd_busy_wait(struct fb_info *info) { diff --git a/drivers/video/ep93xx-fb.c b/drivers/video/ep93xx-fb.c index f8babbeee275..345d96230978 100644 --- a/drivers/video/ep93xx-fb.c +++ b/drivers/video/ep93xx-fb.c @@ -507,16 +507,16 @@ static int __devinit ep93xxfb_probe(struct platform_device *pdev) err = fb_alloc_cmap(&info->cmap, 256, 0); if (err) - goto failed; + goto failed_cmap; err = ep93xxfb_alloc_videomem(info); if (err) - goto failed; + goto failed_videomem; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { err = -ENXIO; - goto failed; + goto failed_resource; } /* @@ -532,7 +532,7 @@ static int __devinit ep93xxfb_probe(struct platform_device *pdev) fbi->mmio_base = ioremap(res->start, resource_size(res)); if (!fbi->mmio_base) { err = -ENXIO; - goto failed; + goto failed_resource; } strcpy(info->fix.id, pdev->name); @@ -553,24 +553,24 @@ static int __devinit ep93xxfb_probe(struct platform_device *pdev) if (err == 0) { dev_err(info->dev, "No suitable video mode found\n"); err = -EINVAL; - goto failed; + goto failed_mode; } if (mach_info->setup) { err = mach_info->setup(pdev); if (err) - return err; + goto failed_mode; } err = ep93xxfb_check_var(&info->var, info); if (err) - goto failed; + goto failed_check; fbi->clk = clk_get(info->dev, NULL); if (IS_ERR(fbi->clk)) { err = PTR_ERR(fbi->clk); fbi->clk = NULL; - goto failed; + goto failed_check; } ep93xxfb_set_par(info); @@ -585,15 +585,17 @@ static int __devinit ep93xxfb_probe(struct platform_device *pdev) return 0; failed: - if (fbi->clk) - clk_put(fbi->clk); - if (fbi->mmio_base) - iounmap(fbi->mmio_base); - ep93xxfb_dealloc_videomem(info); - if (&info->cmap) - fb_dealloc_cmap(&info->cmap); + clk_put(fbi->clk); +failed_check: if (fbi->mach_info->teardown) fbi->mach_info->teardown(pdev); +failed_mode: + iounmap(fbi->mmio_base); +failed_resource: + ep93xxfb_dealloc_videomem(info); +failed_videomem: + fb_dealloc_cmap(&info->cmap); +failed_cmap: kfree(info); platform_set_drvdata(pdev, NULL); diff --git a/drivers/video/exynos/exynos_dp_core.c b/drivers/video/exynos/exynos_dp_core.c index 2a4481cf260c..a36b2d28280e 100644 --- a/drivers/video/exynos/exynos_dp_core.c +++ b/drivers/video/exynos/exynos_dp_core.c @@ -21,14 +21,14 @@ #include <video/exynos_dp.h> -#include <plat/cpu.h> - #include "exynos_dp_core.h" static int exynos_dp_init_dp(struct exynos_dp_device *dp) { exynos_dp_reset(dp); + exynos_dp_swreset(dp); + /* SW defined function Normal operation */ exynos_dp_enable_sw_function(dp); @@ -478,7 +478,7 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) int lane_count; u8 buf[5]; - u8 *adjust_request; + u8 adjust_request[2]; u8 voltage_swing; u8 pre_emphasis; u8 training_lane; @@ -493,8 +493,8 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) /* set training pattern 2 for EQ */ exynos_dp_set_training_pattern(dp, TRAINING_PTN2); - adjust_request = link_status + (DPCD_ADDR_ADJUST_REQUEST_LANE0_1 - - DPCD_ADDR_LANE0_1_STATUS); + adjust_request[0] = link_status[4]; + adjust_request[1] = link_status[5]; exynos_dp_get_adjust_train(dp, adjust_request); @@ -566,7 +566,7 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) u8 buf[5]; u32 reg; - u8 *adjust_request; + u8 adjust_request[2]; udelay(400); @@ -575,8 +575,8 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) lane_count = dp->link_train.lane_count; if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) { - adjust_request = link_status + (DPCD_ADDR_ADJUST_REQUEST_LANE0_1 - - DPCD_ADDR_LANE0_1_STATUS); + adjust_request[0] = link_status[4]; + adjust_request[1] = link_status[5]; if (exynos_dp_channel_eq_ok(link_status, lane_count) == 0) { /* traing pattern Set to Normal */ @@ -770,7 +770,7 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp, return -ETIMEDOUT; } - mdelay(100); + udelay(1); } /* Set to use the register calculated M/N video */ @@ -804,7 +804,7 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp, return -ETIMEDOUT; } - mdelay(100); + mdelay(1); } if (retval != 0) @@ -860,7 +860,8 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) return -EINVAL; } - dp = kzalloc(sizeof(struct exynos_dp_device), GFP_KERNEL); + dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device), + GFP_KERNEL); if (!dp) { dev_err(&pdev->dev, "no memory for device data\n"); return -ENOMEM; @@ -871,8 +872,7 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) dp->clock = clk_get(&pdev->dev, "dp"); if (IS_ERR(dp->clock)) { dev_err(&pdev->dev, "failed to get clock\n"); - ret = PTR_ERR(dp->clock); - goto err_dp; + return PTR_ERR(dp->clock); } clk_enable(dp->clock); @@ -884,35 +884,25 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) goto err_clock; } - res = request_mem_region(res->start, resource_size(res), - dev_name(&pdev->dev)); - if (!res) { - dev_err(&pdev->dev, "failed to request registers region\n"); - ret = -EINVAL; - goto err_clock; - } - - dp->res = res; - - dp->reg_base = ioremap(res->start, resource_size(res)); + dp->reg_base = devm_request_and_ioremap(&pdev->dev, res); if (!dp->reg_base) { dev_err(&pdev->dev, "failed to ioremap\n"); ret = -ENOMEM; - goto err_req_region; + goto err_clock; } dp->irq = platform_get_irq(pdev, 0); if (!dp->irq) { dev_err(&pdev->dev, "failed to get irq\n"); ret = -ENODEV; - goto err_ioremap; + goto err_clock; } - ret = request_irq(dp->irq, exynos_dp_irq_handler, 0, - "exynos-dp", dp); + ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, 0, + "exynos-dp", dp); if (ret) { dev_err(&pdev->dev, "failed to request irq\n"); - goto err_ioremap; + goto err_clock; } dp->video_info = pdata->video_info; @@ -924,7 +914,7 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) ret = exynos_dp_detect_hpd(dp); if (ret) { dev_err(&pdev->dev, "unable to detect hpd\n"); - goto err_irq; + goto err_clock; } exynos_dp_handle_edid(dp); @@ -933,7 +923,7 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) dp->video_info->link_rate); if (ret) { dev_err(&pdev->dev, "unable to do link train\n"); - goto err_irq; + goto err_clock; } exynos_dp_enable_scramble(dp, 1); @@ -947,23 +937,15 @@ static int __devinit exynos_dp_probe(struct platform_device *pdev) ret = exynos_dp_config_video(dp, dp->video_info); if (ret) { dev_err(&pdev->dev, "unable to config video\n"); - goto err_irq; + goto err_clock; } platform_set_drvdata(pdev, dp); return 0; -err_irq: - free_irq(dp->irq, dp); -err_ioremap: - iounmap(dp->reg_base); -err_req_region: - release_mem_region(res->start, resource_size(res)); err_clock: clk_put(dp->clock); -err_dp: - kfree(dp); return ret; } @@ -976,16 +958,9 @@ static int __devexit exynos_dp_remove(struct platform_device *pdev) if (pdata && pdata->phy_exit) pdata->phy_exit(); - free_irq(dp->irq, dp); - iounmap(dp->reg_base); - clk_disable(dp->clock); clk_put(dp->clock); - release_mem_region(dp->res->start, resource_size(dp->res)); - - kfree(dp); - return 0; } diff --git a/drivers/video/exynos/exynos_dp_core.h b/drivers/video/exynos/exynos_dp_core.h index 90ceaca0fa24..1e0f998e0c9f 100644 --- a/drivers/video/exynos/exynos_dp_core.h +++ b/drivers/video/exynos/exynos_dp_core.h @@ -26,7 +26,6 @@ struct link_train { struct exynos_dp_device { struct device *dev; - struct resource *res; struct clk *clock; unsigned int irq; void __iomem *reg_base; @@ -39,8 +38,10 @@ struct exynos_dp_device { void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable); void exynos_dp_stop_video(struct exynos_dp_device *dp); void exynos_dp_lane_swap(struct exynos_dp_device *dp, bool enable); +void exynos_dp_init_analog_param(struct exynos_dp_device *dp); void exynos_dp_init_interrupt(struct exynos_dp_device *dp); void exynos_dp_reset(struct exynos_dp_device *dp); +void exynos_dp_swreset(struct exynos_dp_device *dp); void exynos_dp_config_interrupt(struct exynos_dp_device *dp); u32 exynos_dp_get_pll_lock_status(struct exynos_dp_device *dp); void exynos_dp_set_pll_power_down(struct exynos_dp_device *dp, bool enable); diff --git a/drivers/video/exynos/exynos_dp_reg.c b/drivers/video/exynos/exynos_dp_reg.c index 6548afa0e3d2..6ce76d56c3a1 100644 --- a/drivers/video/exynos/exynos_dp_reg.c +++ b/drivers/video/exynos/exynos_dp_reg.c @@ -16,8 +16,6 @@ #include <video/exynos_dp.h> -#include <plat/cpu.h> - #include "exynos_dp_core.h" #include "exynos_dp_reg.h" @@ -65,6 +63,28 @@ void exynos_dp_lane_swap(struct exynos_dp_device *dp, bool enable) writel(reg, dp->reg_base + EXYNOS_DP_LANE_MAP); } +void exynos_dp_init_analog_param(struct exynos_dp_device *dp) +{ + u32 reg; + + reg = TX_TERMINAL_CTRL_50_OHM; + writel(reg, dp->reg_base + EXYNOS_DP_ANALOG_CTL_1); + + reg = SEL_24M | TX_DVDD_BIT_1_0625V; + writel(reg, dp->reg_base + EXYNOS_DP_ANALOG_CTL_2); + + reg = DRIVE_DVDD_BIT_1_0625V | VCO_BIT_600_MICRO; + writel(reg, dp->reg_base + EXYNOS_DP_ANALOG_CTL_3); + + reg = PD_RING_OSC | AUX_TERMINAL_CTRL_50_OHM | + TX_CUR1_2X | TX_CUR_8_MA; + writel(reg, dp->reg_base + EXYNOS_DP_PLL_FILTER_CTL_1); + + reg = CH3_AMP_400_MV | CH2_AMP_400_MV | + CH1_AMP_400_MV | CH0_AMP_400_MV; + writel(reg, dp->reg_base + EXYNOS_DP_TX_AMP_TUNING_CTL); +} + void exynos_dp_init_interrupt(struct exynos_dp_device *dp) { /* Set interrupt pin assertion polarity as high */ @@ -89,8 +109,6 @@ void exynos_dp_reset(struct exynos_dp_device *dp) { u32 reg; - writel(RESET_DP_TX, dp->reg_base + EXYNOS_DP_TX_SW_RESET); - exynos_dp_stop_video(dp); exynos_dp_enable_video_mute(dp, 0); @@ -131,9 +149,15 @@ void exynos_dp_reset(struct exynos_dp_device *dp) writel(0x00000101, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL); + exynos_dp_init_analog_param(dp); exynos_dp_init_interrupt(dp); } +void exynos_dp_swreset(struct exynos_dp_device *dp) +{ + writel(RESET_DP_TX, dp->reg_base + EXYNOS_DP_TX_SW_RESET); +} + void exynos_dp_config_interrupt(struct exynos_dp_device *dp) { u32 reg; @@ -271,6 +295,7 @@ void exynos_dp_set_analog_power_down(struct exynos_dp_device *dp, void exynos_dp_init_analog_func(struct exynos_dp_device *dp) { u32 reg; + int timeout_loop = 0; exynos_dp_set_analog_power_down(dp, POWER_ALL, 0); @@ -282,9 +307,19 @@ void exynos_dp_init_analog_func(struct exynos_dp_device *dp) writel(reg, dp->reg_base + EXYNOS_DP_DEBUG_CTL); /* Power up PLL */ - if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) + if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { exynos_dp_set_pll_power_down(dp, 0); + while (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) { + timeout_loop++; + if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) { + dev_err(dp->dev, "failed to get pll lock status\n"); + return; + } + usleep_range(10, 20); + } + } + /* Enable Serdes FIFO function and Link symbol clock domain module */ reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2); reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N diff --git a/drivers/video/exynos/exynos_dp_reg.h b/drivers/video/exynos/exynos_dp_reg.h index 42f608e2a43e..125b27cd57ae 100644 --- a/drivers/video/exynos/exynos_dp_reg.h +++ b/drivers/video/exynos/exynos_dp_reg.h @@ -24,6 +24,12 @@ #define EXYNOS_DP_LANE_MAP 0x35C +#define EXYNOS_DP_ANALOG_CTL_1 0x370 +#define EXYNOS_DP_ANALOG_CTL_2 0x374 +#define EXYNOS_DP_ANALOG_CTL_3 0x378 +#define EXYNOS_DP_PLL_FILTER_CTL_1 0x37C +#define EXYNOS_DP_TX_AMP_TUNING_CTL 0x380 + #define EXYNOS_DP_AUX_HW_RETRY_CTL 0x390 #define EXYNOS_DP_COMMON_INT_STA_1 0x3C4 @@ -166,6 +172,29 @@ #define LANE0_MAP_LOGIC_LANE_2 (0x2 << 0) #define LANE0_MAP_LOGIC_LANE_3 (0x3 << 0) +/* EXYNOS_DP_ANALOG_CTL_1 */ +#define TX_TERMINAL_CTRL_50_OHM (0x1 << 4) + +/* EXYNOS_DP_ANALOG_CTL_2 */ +#define SEL_24M (0x1 << 3) +#define TX_DVDD_BIT_1_0625V (0x4 << 0) + +/* EXYNOS_DP_ANALOG_CTL_3 */ +#define DRIVE_DVDD_BIT_1_0625V (0x4 << 5) +#define VCO_BIT_600_MICRO (0x5 << 0) + +/* EXYNOS_DP_PLL_FILTER_CTL_1 */ +#define PD_RING_OSC (0x1 << 6) +#define AUX_TERMINAL_CTRL_50_OHM (0x2 << 4) +#define TX_CUR1_2X (0x1 << 2) +#define TX_CUR_8_MA (0x2 << 0) + +/* EXYNOS_DP_TX_AMP_TUNING_CTL */ +#define CH3_AMP_400_MV (0x0 << 24) +#define CH2_AMP_400_MV (0x0 << 16) +#define CH1_AMP_400_MV (0x0 << 8) +#define CH0_AMP_400_MV (0x0 << 0) + /* EXYNOS_DP_AUX_HW_RETRY_CTL */ #define AUX_BIT_PERIOD_EXPECTED_DELAY(x) (((x) & 0x7) << 8) #define AUX_HW_RETRY_INTERVAL_MASK (0x3 << 3) diff --git a/drivers/video/exynos/exynos_mipi_dsi.c b/drivers/video/exynos/exynos_mipi_dsi.c index 557091dc0e97..6c1f5c314a42 100644 --- a/drivers/video/exynos/exynos_mipi_dsi.c +++ b/drivers/video/exynos/exynos_mipi_dsi.c @@ -58,7 +58,7 @@ static struct mipi_dsim_platform_data *to_dsim_plat(struct platform_device } static struct regulator_bulk_data supplies[] = { - { .supply = "vdd10", }, + { .supply = "vdd11", }, { .supply = "vdd18", }, }; @@ -102,6 +102,8 @@ static void exynos_mipi_update_cfg(struct mipi_dsim_device *dsim) /* set display timing. */ exynos_mipi_dsi_set_display_mode(dsim, dsim->dsim_config); + exynos_mipi_dsi_init_interrupt(dsim); + /* * data from Display controller(FIMD) is transferred in video mode * but in case of command mode, all settigs is updated to registers. @@ -413,27 +415,30 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) goto err_platform_get_irq; } + init_completion(&dsim_wr_comp); + init_completion(&dsim_rd_comp); + platform_set_drvdata(pdev, dsim); + ret = request_irq(dsim->irq, exynos_mipi_dsi_interrupt_handler, - IRQF_SHARED, pdev->name, dsim); + IRQF_SHARED, dev_name(&pdev->dev), dsim); if (ret != 0) { dev_err(&pdev->dev, "failed to request dsim irq\n"); ret = -EINVAL; goto err_bind; } - init_completion(&dsim_wr_comp); - init_completion(&dsim_rd_comp); - - /* enable interrupt */ + /* enable interrupts */ exynos_mipi_dsi_init_interrupt(dsim); /* initialize mipi-dsi client(lcd panel). */ if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->probe) dsim_ddi->dsim_lcd_drv->probe(dsim_ddi->dsim_lcd_dev); - /* in case that mipi got enabled at bootloader. */ - if (dsim_pd->enabled) - goto out; + /* in case mipi-dsi has been enabled by bootloader */ + if (dsim_pd->enabled) { + exynos_mipi_regulator_enable(dsim); + goto done; + } /* lcd panel power on. */ if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->power_on) @@ -453,12 +458,11 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev) dsim->suspended = false; -out: +done: platform_set_drvdata(pdev, dsim); - dev_dbg(&pdev->dev, "mipi-dsi driver(%s mode) has been probed.\n", - (dsim_config->e_interface == DSIM_COMMAND) ? - "CPU" : "RGB"); + dev_dbg(&pdev->dev, "%s() completed sucessfuly (%s mode)\n", __func__, + dsim_config->e_interface == DSIM_COMMAND ? "CPU" : "RGB"); return 0; @@ -515,10 +519,10 @@ static int __devexit exynos_mipi_dsi_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int exynos_mipi_dsi_suspend(struct platform_device *pdev, - pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int exynos_mipi_dsi_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; @@ -544,8 +548,9 @@ static int exynos_mipi_dsi_suspend(struct platform_device *pdev, return 0; } -static int exynos_mipi_dsi_resume(struct platform_device *pdev) +static int exynos_mipi_dsi_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; @@ -577,19 +582,19 @@ static int exynos_mipi_dsi_resume(struct platform_device *pdev) return 0; } -#else -#define exynos_mipi_dsi_suspend NULL -#define exynos_mipi_dsi_resume NULL #endif +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_driver exynos_mipi_dsi_driver = { .probe = exynos_mipi_dsi_probe, .remove = __devexit_p(exynos_mipi_dsi_remove), - .suspend = exynos_mipi_dsi_suspend, - .resume = exynos_mipi_dsi_resume, .driver = { .name = "exynos-mipi-dsim", .owner = THIS_MODULE, + .pm = &exynos_mipi_dsi_pm_ops, }, }; diff --git a/drivers/video/exynos/exynos_mipi_dsi_common.c b/drivers/video/exynos/exynos_mipi_dsi_common.c index 14909c1d3832..47b533a183be 100644 --- a/drivers/video/exynos/exynos_mipi_dsi_common.c +++ b/drivers/video/exynos/exynos_mipi_dsi_common.c @@ -76,33 +76,25 @@ static unsigned int dpll_table[15] = { irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id) { - unsigned int intsrc = 0; - unsigned int intmsk = 0; - struct mipi_dsim_device *dsim = NULL; - - dsim = dev_id; - if (!dsim) { - dev_dbg(dsim->dev, KERN_ERR "%s:error: wrong parameter\n", - __func__); - return IRQ_HANDLED; + struct mipi_dsim_device *dsim = dev_id; + unsigned int intsrc, intmsk; + + if (dsim == NULL) { + dev_err(dsim->dev, "%s: wrong parameter\n", __func__); + return IRQ_NONE; } intsrc = exynos_mipi_dsi_read_interrupt(dsim); intmsk = exynos_mipi_dsi_read_interrupt_mask(dsim); + intmsk = ~intmsk & intsrc; - intmsk = ~(intmsk) & intsrc; - - switch (intmsk) { - case INTMSK_RX_DONE: + if (intsrc & INTMSK_RX_DONE) { complete(&dsim_rd_comp); dev_dbg(dsim->dev, "MIPI INTMSK_RX_DONE\n"); - break; - case INTMSK_FIFO_EMPTY: + } + if (intsrc & INTMSK_FIFO_EMPTY) { complete(&dsim_wr_comp); dev_dbg(dsim->dev, "MIPI INTMSK_FIFO_EMPTY\n"); - break; - default: - break; } exynos_mipi_dsi_clear_interrupt(dsim, intmsk); @@ -738,11 +730,11 @@ int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, if (dsim_config->auto_vertical_cnt == 0) { exynos_mipi_dsi_set_main_disp_vporch(dsim, dsim_config->cmd_allow, - timing->upper_margin, - timing->lower_margin); + timing->lower_margin, + timing->upper_margin); exynos_mipi_dsi_set_main_disp_hporch(dsim, - timing->left_margin, - timing->right_margin); + timing->right_margin, + timing->left_margin); exynos_mipi_dsi_set_main_disp_sync_area(dsim, timing->vsync_len, timing->hsync_len); diff --git a/drivers/video/exynos/s6e8ax0.c b/drivers/video/exynos/s6e8ax0.c index 4aa9ac6218bf..05d080b63bc0 100644 --- a/drivers/video/exynos/s6e8ax0.c +++ b/drivers/video/exynos/s6e8ax0.c @@ -293,9 +293,20 @@ static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd) 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0, 0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8 }; + 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, + 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0, + 0xc1, 0x01, 0x41, 0xc1, 0x00, 0xc1, 0xf6, 0xf6, 0xc1 + }; - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); + if (lcd->dsim_dev->panel_reverse) + 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)); } static void s6e8ax0_display_cond(struct s6e8ax0 *lcd) diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index c27e153d8882..1ddeb11659d4 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -23,7 +23,7 @@ #include <linux/rmap.h> #include <linux/pagemap.h> -struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs) +static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs) { void *screen_base = (void __force *) info->screen_base; struct page *page; @@ -107,6 +107,10 @@ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma, /* protect against the workqueue changing the page list */ mutex_lock(&fbdefio->lock); + /* first write in this cycle, notify the driver */ + if (fbdefio->first_io && list_empty(&fbdefio->pagelist)) + fbdefio->first_io(info); + /* * We want the page to remain locked from ->page_mkwrite until * the PTE is marked dirty to avoid page_mkclean() being called diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index 67afa9c2289d..a55e3669d135 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -80,6 +80,8 @@ EXPORT_SYMBOL(framebuffer_alloc); */ void framebuffer_release(struct fb_info *info) { + if (!info) + return; kfree(info->apertures); kfree(info); } diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 6af3f16754f0..458c00664ade 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -834,7 +834,6 @@ static void update_lcdc(struct fb_info *info) diu_ops.set_pixel_clock(var->pixclock); out_be32(&hw->syn_pol, 0); /* SYNC SIGNALS POLARITY */ - out_be32(&hw->thresholds, 0x00037800); /* The Thresholds */ out_be32(&hw->int_status, 0); /* INTERRUPT STATUS */ out_be32(&hw->plut, 0x01F5F666); diff --git a/drivers/video/intelfb/intelfbdrv.c b/drivers/video/intelfb/intelfbdrv.c index 02fd2263610c..bdcbfbae2777 100644 --- a/drivers/video/intelfb/intelfbdrv.c +++ b/drivers/video/intelfb/intelfbdrv.c @@ -680,6 +680,7 @@ static int __devinit intelfb_pci_register(struct pci_dev *pdev, + dinfo->fb.size); if (!dinfo->aperture.virtual) { ERR_MSG("Cannot remap FB region.\n"); + agp_backend_release(bridge); cleanup(dinfo); return -ENODEV; } @@ -689,6 +690,7 @@ static int __devinit intelfb_pci_register(struct pci_dev *pdev, INTEL_REG_SIZE); if (!dinfo->mmio_base) { ERR_MSG("Cannot remap MMIO region.\n"); + agp_backend_release(bridge); cleanup(dinfo); return -ENODEV; } diff --git a/drivers/video/mb862xx/mb862xx-i2c.c b/drivers/video/mb862xx/mb862xx-i2c.c index 273769bb8deb..c87e17afb3e2 100644 --- a/drivers/video/mb862xx/mb862xx-i2c.c +++ b/drivers/video/mb862xx/mb862xx-i2c.c @@ -68,7 +68,7 @@ static int mb862xx_i2c_read_byte(struct i2c_adapter *adap, u8 *byte, int last) return 1; } -void mb862xx_i2c_stop(struct i2c_adapter *adap) +static void mb862xx_i2c_stop(struct i2c_adapter *adap) { struct mb862xxfb_par *par = adap->algo_data; diff --git a/drivers/video/mb862xx/mb862xxfbdrv.c b/drivers/video/mb862xx/mb862xxfbdrv.c index 11a7a333701d..00ce1f34b496 100644 --- a/drivers/video/mb862xx/mb862xxfbdrv.c +++ b/drivers/video/mb862xx/mb862xxfbdrv.c @@ -579,7 +579,7 @@ static ssize_t mb862xxfb_show_dispregs(struct device *dev, static DEVICE_ATTR(dispregs, 0444, mb862xxfb_show_dispregs, NULL); -irqreturn_t mb862xx_intr(int irq, void *dev_id) +static irqreturn_t mb862xx_intr(int irq, void *dev_id) { struct mb862xxfb_par *par = (struct mb862xxfb_par *) dev_id; unsigned long reg_ist, mask; diff --git a/drivers/video/mbx/mbxfb.c b/drivers/video/mbx/mbxfb.c index 55bf6196b7a0..ab0a8e527333 100644 --- a/drivers/video/mbx/mbxfb.c +++ b/drivers/video/mbx/mbxfb.c @@ -950,7 +950,7 @@ static int __devinit mbxfb_probe(struct platform_device *dev) mfbi->fb_virt_addr = ioremap_nocache(mfbi->fb_phys_addr, res_size(mfbi->fb_req)); - if (!mfbi->reg_virt_addr) { + if (!mfbi->fb_virt_addr) { dev_err(&dev->dev, "failed to ioremap frame buffer\n"); ret = -EINVAL; goto err4; diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index 6c6bc578d0fc..abbe691047bd 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -889,6 +889,18 @@ static int __devexit mxsfb_remove(struct platform_device *pdev) return 0; } +static void mxsfb_shutdown(struct platform_device *pdev) +{ + struct fb_info *fb_info = platform_get_drvdata(pdev); + struct mxsfb_info *host = to_imxfb_host(fb_info); + + /* + * Force stop the LCD controller as keeping it running during reboot + * might interfere with the BootROM's boot mode pads sampling. + */ + writel(CTRL_RUN, host->base + LCDC_CTRL + REG_CLR); +} + static struct platform_device_id mxsfb_devtype[] = { { .name = "imx23-fb", @@ -905,6 +917,7 @@ MODULE_DEVICE_TABLE(platform, mxsfb_devtype); static struct platform_driver mxsfb_driver = { .probe = mxsfb_probe, .remove = __devexit_p(mxsfb_remove), + .shutdown = mxsfb_shutdown, .id_table = mxsfb_devtype, .driver = { .name = DRIVER_NAME, diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index 1e7536d9a8fc..b48f95f0dfe2 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -39,14 +39,6 @@ config FB_OMAP_LCD_MIPID the Mobile Industry Processor Interface DBI-C/DCS specification. (Supported LCDs: Philips LPH8923, Sharp LS041Y3) -config FB_OMAP_BOOTLOADER_INIT - bool "Check bootloader initialization" - depends on FB_OMAP - help - Say Y here if you want to enable checking if the bootloader has - already initialized the display controller. In this case the - driver will skip the initialization. - config FB_OMAP_CONSISTENT_DMA_SIZE int "Consistent DMA memory size (MB)" depends on FB_OMAP diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index 74e7cf078505..ad741c3d1ae1 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -739,12 +739,6 @@ static void acx_panel_set_timings(struct omap_dss_device *dssdev, } } -static void acx_panel_get_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - *timings = dssdev->panel.timings; -} - static int acx_panel_check_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { @@ -762,7 +756,6 @@ static struct omap_dss_driver acx_panel_driver = { .resume = acx_panel_resume, .set_timings = acx_panel_set_timings, - .get_timings = acx_panel_get_timings, .check_timings = acx_panel_check_timings, .get_recommended_bpp = acx_get_recommended_bpp, diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index 30fe4dfeb227..e42f9dc22123 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c @@ -386,6 +386,106 @@ static struct panel_config generic_dpi_panels[] = { .name = "innolux_at080tn52", }, + + /* Mitsubishi AA084SB01 */ + { + { + .x_res = 800, + .y_res = 600, + .pixel_clock = 40000, + + .hsw = 1, + .hfp = 254, + .hbp = 1, + + .vsw = 1, + .vfp = 26, + .vbp = 1, + }, + .config = OMAP_DSS_LCD_TFT, + .name = "mitsubishi_aa084sb01", + }, + /* EDT ET0500G0DH6 */ + { + { + .x_res = 800, + .y_res = 480, + .pixel_clock = 33260, + + .hsw = 128, + .hfp = 216, + .hbp = 40, + + .vsw = 2, + .vfp = 35, + .vbp = 10, + }, + .config = OMAP_DSS_LCD_TFT, + .name = "edt_et0500g0dh6", + }, + + /* Prime-View PD050VL1 */ + { + { + .x_res = 640, + .y_res = 480, + + .pixel_clock = 25000, + + .hsw = 96, + .hfp = 18, + .hbp = 46, + + .vsw = 2, + .vfp = 10, + .vbp = 33, + }, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .name = "primeview_pd050vl1", + }, + + /* Prime-View PM070WL4 */ + { + { + .x_res = 800, + .y_res = 480, + + .pixel_clock = 32000, + + .hsw = 128, + .hfp = 42, + .hbp = 86, + + .vsw = 2, + .vfp = 10, + .vbp = 33, + }, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .name = "primeview_pm070wl4", + }, + + /* Prime-View PD104SLF */ + { + { + .x_res = 800, + .y_res = 600, + + .pixel_clock = 40000, + + .hsw = 128, + .hfp = 42, + .hbp = 86, + + .vsw = 4, + .vfp = 1, + .vbp = 23, + }, + .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | + OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC, + .name = "primeview_pd104slf", + }, }; struct panel_drv_data { @@ -549,12 +649,6 @@ static void generic_dpi_panel_set_timings(struct omap_dss_device *dssdev, dpi_set_timings(dssdev, timings); } -static void generic_dpi_panel_get_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - *timings = dssdev->panel.timings; -} - static int generic_dpi_panel_check_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { @@ -571,7 +665,6 @@ static struct omap_dss_driver dpi_driver = { .resume = generic_dpi_panel_resume, .set_timings = generic_dpi_panel_set_timings, - .get_timings = generic_dpi_panel_get_timings, .check_timings = generic_dpi_panel_check_timings, .driver = { diff --git a/drivers/video/omap2/displays/panel-n8x0.c b/drivers/video/omap2/displays/panel-n8x0.c index dc9408dc93d1..4a34cdc1371b 100644 --- a/drivers/video/omap2/displays/panel-n8x0.c +++ b/drivers/video/omap2/displays/panel-n8x0.c @@ -610,12 +610,6 @@ static int n8x0_panel_resume(struct omap_dss_device *dssdev) return 0; } -static void n8x0_panel_get_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - *timings = dssdev->panel.timings; -} - static void n8x0_panel_get_resolution(struct omap_dss_device *dssdev, u16 *xres, u16 *yres) { @@ -678,8 +672,6 @@ static struct omap_dss_driver n8x0_panel_driver = { .get_resolution = n8x0_panel_get_resolution, .get_recommended_bpp = omapdss_default_get_recommended_bpp, - .get_timings = n8x0_panel_get_timings, - .driver = { .name = "n8x0_panel", .owner = THIS_MODULE, diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index b2dd88b48420..2ce9992f403b 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c @@ -30,7 +30,6 @@ #include <linux/gpio.h> #include <linux/workqueue.h> #include <linux/slab.h> -#include <linux/regulator/consumer.h> #include <linux/mutex.h> #include <video/omapdss.h> @@ -55,73 +54,6 @@ static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable); static int taal_panel_reset(struct omap_dss_device *dssdev); -struct panel_regulator { - struct regulator *regulator; - const char *name; - int min_uV; - int max_uV; -}; - -static void free_regulators(struct panel_regulator *regulators, int n) -{ - int i; - - for (i = 0; i < n; i++) { - /* disable/put in reverse order */ - regulator_disable(regulators[n - i - 1].regulator); - regulator_put(regulators[n - i - 1].regulator); - } -} - -static int init_regulators(struct omap_dss_device *dssdev, - struct panel_regulator *regulators, int n) -{ - int r, i, v; - - for (i = 0; i < n; i++) { - struct regulator *reg; - - reg = regulator_get(&dssdev->dev, regulators[i].name); - if (IS_ERR(reg)) { - dev_err(&dssdev->dev, "failed to get regulator %s\n", - regulators[i].name); - r = PTR_ERR(reg); - goto err; - } - - /* FIXME: better handling of fixed vs. variable regulators */ - v = regulator_get_voltage(reg); - if (v < regulators[i].min_uV || v > regulators[i].max_uV) { - r = regulator_set_voltage(reg, regulators[i].min_uV, - regulators[i].max_uV); - if (r) { - dev_err(&dssdev->dev, - "failed to set regulator %s voltage\n", - regulators[i].name); - regulator_put(reg); - goto err; - } - } - - r = regulator_enable(reg); - if (r) { - dev_err(&dssdev->dev, "failed to enable regulator %s\n", - regulators[i].name); - regulator_put(reg); - goto err; - } - - regulators[i].regulator = reg; - } - - return 0; - -err: - free_regulators(regulators, i); - - return r; -} - /** * struct panel_config - panel configuration * @name: panel name @@ -150,8 +82,6 @@ struct panel_config { unsigned int low; } reset_sequence; - struct panel_regulator *regulators; - int num_regulators; }; enum { @@ -577,12 +507,6 @@ static const struct backlight_ops taal_bl_ops = { .update_status = taal_bl_update_status, }; -static void taal_get_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - *timings = dssdev->panel.timings; -} - static void taal_get_resolution(struct omap_dss_device *dssdev, u16 *xres, u16 *yres) { @@ -977,11 +901,6 @@ static int taal_probe(struct omap_dss_device *dssdev) atomic_set(&td->do_update, 0); - r = init_regulators(dssdev, panel_config->regulators, - panel_config->num_regulators); - if (r) - goto err_reg; - td->workqueue = create_singlethread_workqueue("taal_esd"); if (td->workqueue == NULL) { dev_err(&dssdev->dev, "can't create ESD workqueue\n"); @@ -1087,8 +1006,6 @@ err_bl: err_rst_gpio: destroy_workqueue(td->workqueue); err_wq: - free_regulators(panel_config->regulators, panel_config->num_regulators); -err_reg: kfree(td); err: return r; @@ -1125,9 +1042,6 @@ static void __exit taal_remove(struct omap_dss_device *dssdev) /* reset, to be sure that the panel is in a valid state */ taal_hw_reset(dssdev); - free_regulators(td->panel_config->regulators, - td->panel_config->num_regulators); - if (gpio_is_valid(panel_data->reset_gpio)) gpio_free(panel_data->reset_gpio); @@ -1909,8 +1823,6 @@ static struct omap_dss_driver taal_driver = { .run_test = taal_run_test, .memory_read = taal_memory_read, - .get_timings = taal_get_timings, - .driver = { .name = "taal", .owner = THIS_MODULE, diff --git a/drivers/video/omap2/displays/panel-tfp410.c b/drivers/video/omap2/displays/panel-tfp410.c index 52637fa8fda8..bff306e041ca 100644 --- a/drivers/video/omap2/displays/panel-tfp410.c +++ b/drivers/video/omap2/displays/panel-tfp410.c @@ -47,13 +47,9 @@ struct panel_drv_data { struct mutex lock; int pd_gpio; -}; -static inline struct tfp410_platform_data -*get_pdata(const struct omap_dss_device *dssdev) -{ - return dssdev->data; -} + struct i2c_adapter *i2c_adapter; +}; static int tfp410_power_on(struct omap_dss_device *dssdev) { @@ -68,7 +64,7 @@ static int tfp410_power_on(struct omap_dss_device *dssdev) goto err0; if (gpio_is_valid(ddata->pd_gpio)) - gpio_set_value(ddata->pd_gpio, 1); + gpio_set_value_cansleep(ddata->pd_gpio, 1); return 0; err0: @@ -83,18 +79,18 @@ static void tfp410_power_off(struct omap_dss_device *dssdev) return; if (gpio_is_valid(ddata->pd_gpio)) - gpio_set_value(ddata->pd_gpio, 0); + gpio_set_value_cansleep(ddata->pd_gpio, 0); omapdss_dpi_display_disable(dssdev); } static int tfp410_probe(struct omap_dss_device *dssdev) { - struct tfp410_platform_data *pdata = get_pdata(dssdev); struct panel_drv_data *ddata; int r; + int i2c_bus_num; - ddata = kzalloc(sizeof(*ddata), GFP_KERNEL); + ddata = devm_kzalloc(&dssdev->dev, sizeof(*ddata), GFP_KERNEL); if (!ddata) return -ENOMEM; @@ -104,10 +100,15 @@ static int tfp410_probe(struct omap_dss_device *dssdev) ddata->dssdev = dssdev; mutex_init(&ddata->lock); - if (pdata) + if (dssdev->data) { + struct tfp410_platform_data *pdata = dssdev->data; + ddata->pd_gpio = pdata->power_down_gpio; - else + i2c_bus_num = pdata->i2c_bus_num; + } else { ddata->pd_gpio = -1; + i2c_bus_num = -1; + } if (gpio_is_valid(ddata->pd_gpio)) { r = gpio_request_one(ddata->pd_gpio, GPIOF_OUT_INIT_LOW, @@ -115,13 +116,31 @@ static int tfp410_probe(struct omap_dss_device *dssdev) if (r) { dev_err(&dssdev->dev, "Failed to request PD GPIO %d\n", ddata->pd_gpio); - ddata->pd_gpio = -1; + return r; } } + if (i2c_bus_num != -1) { + struct i2c_adapter *adapter; + + adapter = i2c_get_adapter(i2c_bus_num); + if (!adapter) { + dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n", + i2c_bus_num); + r = -EINVAL; + goto err_i2c; + } + + ddata->i2c_adapter = adapter; + } + dev_set_drvdata(&dssdev->dev, ddata); return 0; +err_i2c: + if (gpio_is_valid(ddata->pd_gpio)) + gpio_free(ddata->pd_gpio); + return r; } static void __exit tfp410_remove(struct omap_dss_device *dssdev) @@ -130,14 +149,15 @@ static void __exit tfp410_remove(struct omap_dss_device *dssdev) mutex_lock(&ddata->lock); + if (ddata->i2c_adapter) + i2c_put_adapter(ddata->i2c_adapter); + if (gpio_is_valid(ddata->pd_gpio)) gpio_free(ddata->pd_gpio); dev_set_drvdata(&dssdev->dev, NULL); mutex_unlock(&ddata->lock); - - kfree(ddata); } static int tfp410_enable(struct omap_dss_device *dssdev) @@ -269,27 +289,17 @@ static int tfp410_read_edid(struct omap_dss_device *dssdev, u8 *edid, int len) { struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); - struct tfp410_platform_data *pdata = get_pdata(dssdev); - struct i2c_adapter *adapter; int r, l, bytes_read; mutex_lock(&ddata->lock); - if (pdata->i2c_bus_num == 0) { + if (!ddata->i2c_adapter) { r = -ENODEV; goto err; } - adapter = i2c_get_adapter(pdata->i2c_bus_num); - if (!adapter) { - dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n", - pdata->i2c_bus_num); - r = -EINVAL; - goto err; - } - l = min(EDID_LENGTH, len); - r = tfp410_ddc_read(adapter, edid, l, 0); + r = tfp410_ddc_read(ddata->i2c_adapter, edid, l, 0); if (r) goto err; @@ -299,7 +309,7 @@ static int tfp410_read_edid(struct omap_dss_device *dssdev, if (len > EDID_LENGTH && edid[0x7e] > 0) { l = min(EDID_LENGTH, len - EDID_LENGTH); - r = tfp410_ddc_read(adapter, edid + EDID_LENGTH, + r = tfp410_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH, l, EDID_LENGTH); if (r) goto err; @@ -319,21 +329,15 @@ err: static bool tfp410_detect(struct omap_dss_device *dssdev) { struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); - struct tfp410_platform_data *pdata = get_pdata(dssdev); - struct i2c_adapter *adapter; unsigned char out; int r; mutex_lock(&ddata->lock); - if (pdata->i2c_bus_num == 0) - goto out; - - adapter = i2c_get_adapter(pdata->i2c_bus_num); - if (!adapter) + if (!ddata->i2c_adapter) goto out; - r = tfp410_ddc_read(adapter, &out, 1, 0); + r = tfp410_ddc_read(ddata->i2c_adapter, &out, 1, 0); mutex_unlock(&ddata->lock); diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c index 32f3fcd7f0f0..4b6448b3c31f 100644 --- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c +++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c @@ -272,13 +272,16 @@ static const struct omap_video_timings tpo_td043_timings = { static int tpo_td043_power_on(struct tpo_td043_device *tpo_td043) { int nreset_gpio = tpo_td043->nreset_gpio; + int r; if (tpo_td043->powered_on) return 0; - regulator_enable(tpo_td043->vcc_reg); + r = regulator_enable(tpo_td043->vcc_reg); + if (r != 0) + return r; - /* wait for regulator to stabilize */ + /* wait for panel to stabilize */ msleep(160); if (gpio_is_valid(nreset_gpio)) @@ -470,6 +473,18 @@ static void tpo_td043_remove(struct omap_dss_device *dssdev) gpio_free(nreset_gpio); } +static void tpo_td043_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + dpi_set_timings(dssdev, timings); +} + +static int tpo_td043_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + return dpi_check_timings(dssdev, timings); +} + static struct omap_dss_driver tpo_td043_driver = { .probe = tpo_td043_probe, .remove = tpo_td043_remove, @@ -481,6 +496,9 @@ static struct omap_dss_driver tpo_td043_driver = { .set_mirror = tpo_td043_set_hmirror, .get_mirror = tpo_td043_get_hmirror, + .set_timings = tpo_td043_set_timings, + .check_timings = tpo_td043_check_timings, + .driver = { .name = "tpo_td043mtea1_panel", .owner = THIS_MODULE, diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig index 7be7c06a249e..43324e5ed25f 100644 --- a/drivers/video/omap2/dss/Kconfig +++ b/drivers/video/omap2/dss/Kconfig @@ -68,6 +68,10 @@ config OMAP4_DSS_HDMI HDMI Interface. This adds the High Definition Multimedia Interface. See http://www.hdmi.org/ for HDMI specification. +config OMAP4_DSS_HDMI_AUDIO + bool + depends on OMAP4_DSS_HDMI + config OMAP2_DSS_SDI bool "SDI support" depends on ARCH_OMAP3 @@ -90,15 +94,6 @@ config OMAP2_DSS_DSI See http://www.mipi.org/ for DSI spesifications. -config OMAP2_DSS_FAKE_VSYNC - bool "Fake VSYNC irq from manual update displays" - default n - help - If this is selected, DSI will generate a fake DISPC VSYNC interrupt - when DSI has sent a frame. This is only needed with DSI or RFBI - displays using manual mode, and you want VSYNC to, for example, - time animation. - config OMAP2_DSS_MIN_FCK_PER_PCK int "Minimum FCK/PCK ratio (for scaling)" range 0 32 diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c index b10b3bc1931e..ab22cc224f3e 100644 --- a/drivers/video/omap2/dss/apply.c +++ b/drivers/video/omap2/dss/apply.c @@ -99,6 +99,11 @@ struct mgr_priv_data { /* If true, a display is enabled using this manager */ bool enabled; + + bool extra_info_dirty; + bool shadow_extra_info_dirty; + + struct omap_video_timings timings; }; static struct { @@ -176,7 +181,7 @@ static bool mgr_manual_update(struct omap_overlay_manager *mgr) } static int dss_check_settings_low(struct omap_overlay_manager *mgr, - struct omap_dss_device *dssdev, bool applying) + bool applying) { struct omap_overlay_info *oi; struct omap_overlay_manager_info *mi; @@ -187,6 +192,9 @@ static int dss_check_settings_low(struct omap_overlay_manager *mgr, mp = get_mgr_priv(mgr); + if (!mp->enabled) + return 0; + if (applying && mp->user_info_dirty) mi = &mp->user_info; else @@ -206,26 +214,24 @@ static int dss_check_settings_low(struct omap_overlay_manager *mgr, ois[ovl->id] = oi; } - return dss_mgr_check(mgr, dssdev, mi, ois); + return dss_mgr_check(mgr, mi, &mp->timings, ois); } /* * check manager and overlay settings using overlay_info from data->info */ -static int dss_check_settings(struct omap_overlay_manager *mgr, - struct omap_dss_device *dssdev) +static int dss_check_settings(struct omap_overlay_manager *mgr) { - return dss_check_settings_low(mgr, dssdev, false); + return dss_check_settings_low(mgr, false); } /* * check manager and overlay settings using overlay_info from ovl->info if * dirty and from data->info otherwise */ -static int dss_check_settings_apply(struct omap_overlay_manager *mgr, - struct omap_dss_device *dssdev) +static int dss_check_settings_apply(struct omap_overlay_manager *mgr) { - return dss_check_settings_low(mgr, dssdev, true); + return dss_check_settings_low(mgr, true); } static bool need_isr(void) @@ -261,6 +267,20 @@ static bool need_isr(void) if (mp->shadow_info_dirty) return true; + /* + * NOTE: we don't check extra_info flags for disabled + * managers, once the manager is enabled, the extra_info + * related manager changes will be taken in by HW. + */ + + /* to write new values to registers */ + if (mp->extra_info_dirty) + return true; + + /* to set GO bit */ + if (mp->shadow_extra_info_dirty) + return true; + list_for_each_entry(ovl, &mgr->overlays, list) { struct ovl_priv_data *op; @@ -305,7 +325,7 @@ static bool need_go(struct omap_overlay_manager *mgr) mp = get_mgr_priv(mgr); - if (mp->shadow_info_dirty) + if (mp->shadow_info_dirty || mp->shadow_extra_info_dirty) return true; list_for_each_entry(ovl, &mgr->overlays, list) { @@ -320,20 +340,16 @@ static bool need_go(struct omap_overlay_manager *mgr) /* returns true if an extra_info field is currently being updated */ static bool extra_info_update_ongoing(void) { - const int num_ovls = omap_dss_get_num_overlays(); - struct ovl_priv_data *op; - struct omap_overlay *ovl; - struct mgr_priv_data *mp; + const int num_mgrs = dss_feat_get_num_mgrs(); int i; - for (i = 0; i < num_ovls; ++i) { - ovl = omap_dss_get_overlay(i); - op = get_ovl_priv(ovl); - - if (!ovl->manager) - continue; + for (i = 0; i < num_mgrs; ++i) { + struct omap_overlay_manager *mgr; + struct omap_overlay *ovl; + struct mgr_priv_data *mp; - mp = get_mgr_priv(ovl->manager); + mgr = omap_dss_get_overlay_manager(i); + mp = get_mgr_priv(mgr); if (!mp->enabled) continue; @@ -341,8 +357,15 @@ static bool extra_info_update_ongoing(void) if (!mp->updating) continue; - if (op->extra_info_dirty || op->shadow_extra_info_dirty) + if (mp->extra_info_dirty || mp->shadow_extra_info_dirty) return true; + + list_for_each_entry(ovl, &mgr->overlays, list) { + struct ovl_priv_data *op = get_ovl_priv(ovl); + + if (op->extra_info_dirty || op->shadow_extra_info_dirty) + return true; + } } return false; @@ -525,11 +548,13 @@ static void dss_ovl_write_regs(struct omap_overlay *ovl) oi = &op->info; + mp = get_mgr_priv(ovl->manager); + replication = dss_use_replication(ovl->manager->device, oi->color_mode); ilace = ovl->manager->device->type == OMAP_DISPLAY_TYPE_VENC; - r = dispc_ovl_setup(ovl->id, oi, ilace, replication); + r = dispc_ovl_setup(ovl->id, oi, ilace, replication, &mp->timings); if (r) { /* * We can't do much here, as this function can be called from @@ -543,8 +568,6 @@ static void dss_ovl_write_regs(struct omap_overlay *ovl) return; } - mp = get_mgr_priv(ovl->manager); - op->info_dirty = false; if (mp->updating) op->shadow_info_dirty = true; @@ -601,6 +624,22 @@ static void dss_mgr_write_regs(struct omap_overlay_manager *mgr) } } +static void dss_mgr_write_regs_extra(struct omap_overlay_manager *mgr) +{ + struct mgr_priv_data *mp = get_mgr_priv(mgr); + + DSSDBGF("%d", mgr->id); + + if (!mp->extra_info_dirty) + return; + + dispc_mgr_set_timings(mgr->id, &mp->timings); + + mp->extra_info_dirty = false; + if (mp->updating) + mp->shadow_extra_info_dirty = true; +} + static void dss_write_regs_common(void) { const int num_mgrs = omap_dss_get_num_overlay_managers(); @@ -646,7 +685,7 @@ static void dss_write_regs(void) if (!mp->enabled || mgr_manual_update(mgr) || mp->busy) continue; - r = dss_check_settings(mgr, mgr->device); + r = dss_check_settings(mgr); if (r) { DSSERR("cannot write registers for manager %s: " "illegal configuration\n", mgr->name); @@ -654,6 +693,7 @@ static void dss_write_regs(void) } dss_mgr_write_regs(mgr); + dss_mgr_write_regs_extra(mgr); } } @@ -693,6 +733,7 @@ static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr) mp = get_mgr_priv(mgr); mp->shadow_info_dirty = false; + mp->shadow_extra_info_dirty = false; list_for_each_entry(ovl, &mgr->overlays, list) { op = get_ovl_priv(ovl); @@ -711,7 +752,7 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr) WARN_ON(mp->updating); - r = dss_check_settings(mgr, mgr->device); + r = dss_check_settings(mgr); if (r) { DSSERR("cannot start manual update: illegal configuration\n"); spin_unlock_irqrestore(&data_lock, flags); @@ -719,6 +760,7 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr) } dss_mgr_write_regs(mgr); + dss_mgr_write_regs_extra(mgr); dss_write_regs_common(); @@ -857,7 +899,7 @@ int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) spin_lock_irqsave(&data_lock, flags); - r = dss_check_settings_apply(mgr, mgr->device); + r = dss_check_settings_apply(mgr); if (r) { spin_unlock_irqrestore(&data_lock, flags); DSSERR("failed to apply settings: illegal configuration.\n"); @@ -918,16 +960,13 @@ static void dss_ovl_setup_fifo(struct omap_overlay *ovl, bool use_fifo_merge) { struct ovl_priv_data *op = get_ovl_priv(ovl); - struct omap_dss_device *dssdev; u32 fifo_low, fifo_high; if (!op->enabled && !op->enabling) return; - dssdev = ovl->manager->device; - dispc_ovl_compute_fifo_thresholds(ovl->id, &fifo_low, &fifo_high, - use_fifo_merge); + use_fifo_merge, ovl_manual_update(ovl)); dss_apply_ovl_fifo_thresholds(ovl, fifo_low, fifo_high); } @@ -1050,7 +1089,7 @@ int dss_mgr_enable(struct omap_overlay_manager *mgr) mp->enabled = true; - r = dss_check_settings(mgr, mgr->device); + r = dss_check_settings(mgr); if (r) { DSSERR("failed to enable manager %d: check_settings failed\n", mgr->id); @@ -1225,6 +1264,35 @@ err: return r; } +static void dss_apply_mgr_timings(struct omap_overlay_manager *mgr, + struct omap_video_timings *timings) +{ + struct mgr_priv_data *mp = get_mgr_priv(mgr); + + mp->timings = *timings; + mp->extra_info_dirty = true; +} + +void dss_mgr_set_timings(struct omap_overlay_manager *mgr, + struct omap_video_timings *timings) +{ + unsigned long flags; + + mutex_lock(&apply_lock); + + spin_lock_irqsave(&data_lock, flags); + + dss_apply_mgr_timings(mgr, timings); + + dss_write_regs(); + dss_set_go_bits(); + + spin_unlock_irqrestore(&data_lock, flags); + + wait_pending_extra_info_updates(); + + mutex_unlock(&apply_lock); +} int dss_ovl_set_info(struct omap_overlay *ovl, struct omap_overlay_info *info) @@ -1393,7 +1461,7 @@ int dss_ovl_enable(struct omap_overlay *ovl) op->enabling = true; - r = dss_check_settings(ovl->manager, ovl->manager->device); + r = dss_check_settings(ovl->manager); if (r) { DSSERR("failed to enable overlay %d: check_settings failed\n", ovl->id); diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c index e8a120771ac6..72ded9cd2cb0 100644 --- a/drivers/video/omap2/dss/core.c +++ b/drivers/video/omap2/dss/core.c @@ -43,6 +43,8 @@ static struct { struct regulator *vdds_dsi_reg; struct regulator *vdds_sdi_reg; + + const char *default_display_name; } core; static char *def_disp_name; @@ -54,9 +56,6 @@ bool dss_debug; module_param_named(debug, dss_debug, bool, 0644); #endif -static int omap_dss_register_device(struct omap_dss_device *); -static void omap_dss_unregister_device(struct omap_dss_device *); - /* REGULATORS */ struct regulator *dss_get_vdds_dsi(void) @@ -87,6 +86,51 @@ struct regulator *dss_get_vdds_sdi(void) return reg; } +int dss_get_ctx_loss_count(struct device *dev) +{ + struct omap_dss_board_info *board_data = core.pdev->dev.platform_data; + int cnt; + + if (!board_data->get_context_loss_count) + return -ENOENT; + + cnt = board_data->get_context_loss_count(dev); + + WARN_ONCE(cnt < 0, "get_context_loss_count failed: %d\n", cnt); + + return cnt; +} + +int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask) +{ + struct omap_dss_board_info *board_data = core.pdev->dev.platform_data; + + if (!board_data->dsi_enable_pads) + return -ENOENT; + + return board_data->dsi_enable_pads(dsi_id, lane_mask); +} + +void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask) +{ + struct omap_dss_board_info *board_data = core.pdev->dev.platform_data; + + if (!board_data->dsi_enable_pads) + return; + + return board_data->dsi_disable_pads(dsi_id, lane_mask); +} + +int dss_set_min_bus_tput(struct device *dev, unsigned long tput) +{ + struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; + + if (pdata->set_min_bus_tput) + return pdata->set_min_bus_tput(dev, tput); + else + return 0; +} + #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) static int dss_debug_show(struct seq_file *s, void *unused) { @@ -121,34 +165,6 @@ static int dss_initialize_debugfs(void) debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir, &dss_debug_dump_clocks, &dss_debug_fops); -#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - debugfs_create_file("dispc_irq", S_IRUGO, dss_debugfs_dir, - &dispc_dump_irqs, &dss_debug_fops); -#endif - -#if defined(CONFIG_OMAP2_DSS_DSI) && defined(CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS) - dsi_create_debugfs_files_irq(dss_debugfs_dir, &dss_debug_fops); -#endif - - debugfs_create_file("dss", S_IRUGO, dss_debugfs_dir, - &dss_dump_regs, &dss_debug_fops); - debugfs_create_file("dispc", S_IRUGO, dss_debugfs_dir, - &dispc_dump_regs, &dss_debug_fops); -#ifdef CONFIG_OMAP2_DSS_RFBI - debugfs_create_file("rfbi", S_IRUGO, dss_debugfs_dir, - &rfbi_dump_regs, &dss_debug_fops); -#endif -#ifdef CONFIG_OMAP2_DSS_DSI - dsi_create_debugfs_files_reg(dss_debugfs_dir, &dss_debug_fops); -#endif -#ifdef CONFIG_OMAP2_DSS_VENC - debugfs_create_file("venc", S_IRUGO, dss_debugfs_dir, - &venc_dump_regs, &dss_debug_fops); -#endif -#ifdef CONFIG_OMAP4_DSS_HDMI - debugfs_create_file("hdmi", S_IRUGO, dss_debugfs_dir, - &hdmi_dump_regs, &dss_debug_fops); -#endif return 0; } @@ -157,6 +173,19 @@ static void dss_uninitialize_debugfs(void) if (dss_debugfs_dir) debugfs_remove_recursive(dss_debugfs_dir); } + +int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) +{ + struct dentry *d; + + d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir, + write, &dss_debug_fops); + + if (IS_ERR(d)) + return PTR_ERR(d); + + return 0; +} #else /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */ static inline int dss_initialize_debugfs(void) { @@ -165,14 +194,18 @@ static inline int dss_initialize_debugfs(void) static inline void dss_uninitialize_debugfs(void) { } +static inline int dss_debugfs_create_file(const char *name, + void (*write)(struct seq_file *)) +{ + return 0; +} #endif /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */ /* PLATFORM DEVICE */ -static int omap_dss_probe(struct platform_device *pdev) +static int __init omap_dss_probe(struct platform_device *pdev) { struct omap_dss_board_info *pdata = pdev->dev.platform_data; int r; - int i; core.pdev = pdev; @@ -187,28 +220,13 @@ static int omap_dss_probe(struct platform_device *pdev) if (r) goto err_debugfs; - for (i = 0; i < pdata->num_devices; ++i) { - struct omap_dss_device *dssdev = pdata->devices[i]; - - r = omap_dss_register_device(dssdev); - if (r) { - DSSERR("device %d %s register failed %d\n", i, - dssdev->name ?: "unnamed", r); - - while (--i >= 0) - omap_dss_unregister_device(pdata->devices[i]); - - goto err_register; - } - - if (def_disp_name && strcmp(def_disp_name, dssdev->name) == 0) - pdata->default_device = dssdev; - } + if (def_disp_name) + core.default_display_name = def_disp_name; + else if (pdata->default_device) + core.default_display_name = pdata->default_device->name; return 0; -err_register: - dss_uninitialize_debugfs(); err_debugfs: return r; @@ -216,17 +234,11 @@ err_debugfs: static int omap_dss_remove(struct platform_device *pdev) { - struct omap_dss_board_info *pdata = pdev->dev.platform_data; - int i; - dss_uninitialize_debugfs(); dss_uninit_overlays(pdev); dss_uninit_overlay_managers(pdev); - for (i = 0; i < pdata->num_devices; ++i) - omap_dss_unregister_device(pdata->devices[i]); - return 0; } @@ -251,7 +263,6 @@ static int omap_dss_resume(struct platform_device *pdev) } static struct platform_driver omap_dss_driver = { - .probe = omap_dss_probe, .remove = omap_dss_remove, .shutdown = omap_dss_shutdown, .suspend = omap_dss_suspend, @@ -326,7 +337,6 @@ static int dss_driver_probe(struct device *dev) int r; struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver); struct omap_dss_device *dssdev = to_dss_device(dev); - struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; bool force; DSSDBG("driver_probe: dev %s/%s, drv %s\n", @@ -335,7 +345,8 @@ static int dss_driver_probe(struct device *dev) dss_init_device(core.pdev, dssdev); - force = pdata->default_device == dssdev; + force = core.default_display_name && + strcmp(core.default_display_name, dssdev->name) == 0; dss_recheck_connections(dssdev, force); r = dssdrv->probe(dssdev); @@ -381,6 +392,8 @@ int omap_dss_register_driver(struct omap_dss_driver *dssdriver) if (dssdriver->get_recommended_bpp == NULL) dssdriver->get_recommended_bpp = omapdss_default_get_recommended_bpp; + if (dssdriver->get_timings == NULL) + dssdriver->get_timings = omapdss_default_get_timings; return driver_register(&dssdriver->driver); } @@ -427,27 +440,38 @@ static void omap_dss_dev_release(struct device *dev) reset_device(dev, 0); } -static int omap_dss_register_device(struct omap_dss_device *dssdev) +int omap_dss_register_device(struct omap_dss_device *dssdev, + struct device *parent, int disp_num) { - static int dev_num; - WARN_ON(!dssdev->driver_name); reset_device(&dssdev->dev, 1); dssdev->dev.bus = &dss_bus_type; - dssdev->dev.parent = &dss_bus; + dssdev->dev.parent = parent; dssdev->dev.release = omap_dss_dev_release; - dev_set_name(&dssdev->dev, "display%d", dev_num++); + dev_set_name(&dssdev->dev, "display%d", disp_num); return device_register(&dssdev->dev); } -static void omap_dss_unregister_device(struct omap_dss_device *dssdev) +void omap_dss_unregister_device(struct omap_dss_device *dssdev) { device_unregister(&dssdev->dev); } +static int dss_unregister_dss_dev(struct device *dev, void *data) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + omap_dss_unregister_device(dssdev); + return 0; +} + +void omap_dss_unregister_child_devices(struct device *parent) +{ + device_for_each_child(parent, NULL, dss_unregister_dss_dev); +} + /* BUS */ -static int omap_dss_bus_register(void) +static int __init omap_dss_bus_register(void) { int r; @@ -469,12 +493,56 @@ static int omap_dss_bus_register(void) } /* INIT */ +static int (*dss_output_drv_reg_funcs[])(void) __initdata = { +#ifdef CONFIG_OMAP2_DSS_DPI + dpi_init_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_SDI + sdi_init_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_RFBI + rfbi_init_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + venc_init_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_init_platform_driver, +#endif +#ifdef CONFIG_OMAP4_DSS_HDMI + hdmi_init_platform_driver, +#endif +}; + +static void (*dss_output_drv_unreg_funcs[])(void) __exitdata = { +#ifdef CONFIG_OMAP2_DSS_DPI + dpi_uninit_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_SDI + sdi_uninit_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_RFBI + rfbi_uninit_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_VENC + venc_uninit_platform_driver, +#endif +#ifdef CONFIG_OMAP2_DSS_DSI + dsi_uninit_platform_driver, +#endif +#ifdef CONFIG_OMAP4_DSS_HDMI + hdmi_uninit_platform_driver, +#endif +}; + +static bool dss_output_drv_loaded[ARRAY_SIZE(dss_output_drv_reg_funcs)]; static int __init omap_dss_register_drivers(void) { int r; + int i; - r = platform_driver_register(&omap_dss_driver); + r = platform_driver_probe(&omap_dss_driver, omap_dss_probe); if (r) return r; @@ -490,40 +558,18 @@ static int __init omap_dss_register_drivers(void) goto err_dispc; } - r = rfbi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize rfbi platform driver\n"); - goto err_rfbi; - } - - r = venc_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize venc platform driver\n"); - goto err_venc; - } - - r = dsi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize DSI platform driver\n"); - goto err_dsi; - } - - r = hdmi_init_platform_driver(); - if (r) { - DSSERR("Failed to initialize hdmi\n"); - goto err_hdmi; + /* + * It's ok if the output-driver register fails. It happens, for example, + * when there is no output-device (e.g. SDI for OMAP4). + */ + for (i = 0; i < ARRAY_SIZE(dss_output_drv_reg_funcs); ++i) { + r = dss_output_drv_reg_funcs[i](); + if (r == 0) + dss_output_drv_loaded[i] = true; } return 0; -err_hdmi: - dsi_uninit_platform_driver(); -err_dsi: - venc_uninit_platform_driver(); -err_venc: - rfbi_uninit_platform_driver(); -err_rfbi: - dispc_uninit_platform_driver(); err_dispc: dss_uninit_platform_driver(); err_dss: @@ -534,10 +580,13 @@ err_dss: static void __exit omap_dss_unregister_drivers(void) { - hdmi_uninit_platform_driver(); - dsi_uninit_platform_driver(); - venc_uninit_platform_driver(); - rfbi_uninit_platform_driver(); + int i; + + for (i = 0; i < ARRAY_SIZE(dss_output_drv_unreg_funcs); ++i) { + if (dss_output_drv_loaded[i]) + dss_output_drv_unreg_funcs[i](); + } + dispc_uninit_platform_driver(); dss_uninit_platform_driver(); diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index ee30937482e1..4749ac356469 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -131,23 +131,6 @@ static inline u32 dispc_read_reg(const u16 idx) return __raw_readl(dispc.base + idx); } -static int dispc_get_ctx_loss_count(void) -{ - struct device *dev = &dispc.pdev->dev; - struct omap_display_platform_data *pdata = dev->platform_data; - struct omap_dss_board_info *board_data = pdata->board_data; - int cnt; - - if (!board_data->get_context_loss_count) - return -ENOENT; - - cnt = board_data->get_context_loss_count(dev); - - WARN_ONCE(cnt < 0, "get_context_loss_count failed: %d\n", cnt); - - return cnt; -} - #define SR(reg) \ dispc.ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(DISPC_##reg) #define RR(reg) \ @@ -251,7 +234,7 @@ static void dispc_save_context(void) if (dss_has_feature(FEAT_CORE_CLK_DIV)) SR(DIVISOR); - dispc.ctx_loss_cnt = dispc_get_ctx_loss_count(); + dispc.ctx_loss_cnt = dss_get_ctx_loss_count(&dispc.pdev->dev); dispc.ctx_valid = true; DSSDBG("context saved, ctx_loss_count %d\n", dispc.ctx_loss_cnt); @@ -266,7 +249,7 @@ static void dispc_restore_context(void) if (!dispc.ctx_valid) return; - ctx = dispc_get_ctx_loss_count(); + ctx = dss_get_ctx_loss_count(&dispc.pdev->dev); if (ctx >= 0 && ctx == dispc.ctx_loss_cnt) return; @@ -413,14 +396,6 @@ static inline bool dispc_mgr_is_lcd(enum omap_channel channel) return false; } -static struct omap_dss_device *dispc_mgr_get_device(enum omap_channel channel) -{ - struct omap_overlay_manager *mgr = - omap_dss_get_overlay_manager(channel); - - return mgr ? mgr->device : NULL; -} - u32 dispc_mgr_get_vsync_irq(enum omap_channel channel) { switch (channel) { @@ -432,6 +407,7 @@ u32 dispc_mgr_get_vsync_irq(enum omap_channel channel) return DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; default: BUG(); + return 0; } } @@ -446,6 +422,7 @@ u32 dispc_mgr_get_framedone_irq(enum omap_channel channel) return 0; default: BUG(); + return 0; } } @@ -764,7 +741,7 @@ static void dispc_ovl_set_color_mode(enum omap_plane plane, case OMAP_DSS_COLOR_XRGB16_1555: m = 0xf; break; default: - BUG(); break; + BUG(); return; } } else { switch (color_mode) { @@ -801,13 +778,25 @@ static void dispc_ovl_set_color_mode(enum omap_plane plane, case OMAP_DSS_COLOR_XRGB16_1555: m = 0xf; break; default: - BUG(); break; + BUG(); return; } } REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1); } +static void dispc_ovl_configure_burst_type(enum omap_plane plane, + enum omap_dss_rotation_type rotation_type) +{ + if (dss_has_feature(FEAT_BURST_2D) == 0) + return; + + if (rotation_type == OMAP_DSS_ROT_TILER) + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), 1, 29, 29); + else + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), 0, 29, 29); +} + void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel) { int shift; @@ -845,6 +834,7 @@ void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel) break; default: BUG(); + return; } val = FLD_MOD(val, chan, shift, shift); @@ -872,6 +862,7 @@ static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane plane) break; default: BUG(); + return 0; } val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); @@ -983,20 +974,13 @@ static void dispc_ovl_enable_replication(enum omap_plane plane, bool enable) REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, shift, shift); } -void dispc_mgr_set_lcd_size(enum omap_channel channel, u16 width, u16 height) +static void dispc_mgr_set_size(enum omap_channel channel, u16 width, + u16 height) { u32 val; - BUG_ON((width > (1 << 11)) || (height > (1 << 11))); - val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); - dispc_write_reg(DISPC_SIZE_MGR(channel), val); -} -void dispc_set_digit_size(u16 width, u16 height) -{ - u32 val; - BUG_ON((width > (1 << 11)) || (height > (1 << 11))); val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); - dispc_write_reg(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT), val); + dispc_write_reg(DISPC_SIZE_MGR(channel), val); } static void dispc_read_plane_fifo_sizes(void) @@ -1063,7 +1047,8 @@ void dispc_enable_fifomerge(bool enable) } void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, - u32 *fifo_low, u32 *fifo_high, bool use_fifomerge) + u32 *fifo_low, u32 *fifo_high, bool use_fifomerge, + bool manual_update) { /* * All sizes are in bytes. Both the buffer and burst are made of @@ -1091,7 +1076,7 @@ void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, * combined fifo size */ - if (dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) { + if (manual_update && dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) { *fifo_low = ovl_fifo_size - burst_size * 2; *fifo_high = total_fifo_size - burst_size; } else { @@ -1185,6 +1170,94 @@ static void dispc_ovl_set_scale_param(enum omap_plane plane, dispc_ovl_set_fir(plane, fir_hinc, fir_vinc, color_comp); } +static void dispc_ovl_set_accu_uv(enum omap_plane plane, + u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, + bool ilace, enum omap_color_mode color_mode, u8 rotation) +{ + int h_accu2_0, h_accu2_1; + int v_accu2_0, v_accu2_1; + int chroma_hinc, chroma_vinc; + int idx; + + struct accu { + s8 h0_m, h0_n; + s8 h1_m, h1_n; + s8 v0_m, v0_n; + s8 v1_m, v1_n; + }; + + const struct accu *accu_table; + const struct accu *accu_val; + + static const struct accu accu_nv12[4] = { + { 0, 1, 0, 1 , -1, 2, 0, 1 }, + { 1, 2, -3, 4 , 0, 1, 0, 1 }, + { -1, 1, 0, 1 , -1, 2, 0, 1 }, + { -1, 2, -1, 2 , -1, 1, 0, 1 }, + }; + + static const struct accu accu_nv12_ilace[4] = { + { 0, 1, 0, 1 , -3, 4, -1, 4 }, + { -1, 4, -3, 4 , 0, 1, 0, 1 }, + { -1, 1, 0, 1 , -1, 4, -3, 4 }, + { -3, 4, -3, 4 , -1, 1, 0, 1 }, + }; + + static const struct accu accu_yuv[4] = { + { 0, 1, 0, 1, 0, 1, 0, 1 }, + { 0, 1, 0, 1, 0, 1, 0, 1 }, + { -1, 1, 0, 1, 0, 1, 0, 1 }, + { 0, 1, 0, 1, -1, 1, 0, 1 }, + }; + + switch (rotation) { + case OMAP_DSS_ROT_0: + idx = 0; + break; + case OMAP_DSS_ROT_90: + idx = 1; + break; + case OMAP_DSS_ROT_180: + idx = 2; + break; + case OMAP_DSS_ROT_270: + idx = 3; + break; + default: + BUG(); + return; + } + + switch (color_mode) { + case OMAP_DSS_COLOR_NV12: + if (ilace) + accu_table = accu_nv12_ilace; + else + accu_table = accu_nv12; + break; + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + accu_table = accu_yuv; + break; + default: + BUG(); + return; + } + + accu_val = &accu_table[idx]; + + chroma_hinc = 1024 * orig_width / out_width; + chroma_vinc = 1024 * orig_height / out_height; + + h_accu2_0 = (accu_val->h0_m * chroma_hinc / accu_val->h0_n) % 1024; + h_accu2_1 = (accu_val->h1_m * chroma_hinc / accu_val->h1_n) % 1024; + v_accu2_0 = (accu_val->v0_m * chroma_vinc / accu_val->v0_n) % 1024; + v_accu2_1 = (accu_val->v1_m * chroma_vinc / accu_val->v1_n) % 1024; + + dispc_ovl_set_vid_accu2_0(plane, h_accu2_0, v_accu2_0); + dispc_ovl_set_vid_accu2_1(plane, h_accu2_1, v_accu2_1); +} + static void dispc_ovl_set_scaling_common(enum omap_plane plane, u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, @@ -1258,6 +1331,10 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane, REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 8, 8); return; } + + dispc_ovl_set_accu_uv(plane, orig_width, orig_height, out_width, + out_height, ilace, color_mode, rotation); + switch (color_mode) { case OMAP_DSS_COLOR_NV12: /* UV is subsampled by 2 vertically*/ @@ -1280,6 +1357,7 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane, break; default: BUG(); + return; } if (out_width != orig_width) @@ -1297,9 +1375,6 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane plane, REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_x ? 1 : 0, 5, 5); /* set V scaling */ REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_y ? 1 : 0, 6, 6); - - dispc_ovl_set_vid_accu2_0(plane, 0x80, 0); - dispc_ovl_set_vid_accu2_1(plane, 0x80, 0); } static void dispc_ovl_set_scaling(enum omap_plane plane, @@ -1410,6 +1485,7 @@ static int color_mode_to_bpp(enum omap_color_mode color_mode) return 32; default: BUG(); + return 0; } } @@ -1423,6 +1499,7 @@ static s32 pixinc(int pixels, u8 ps) return 1 - (-pixels + 1) * ps; else BUG(); + return 0; } static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, @@ -1431,7 +1508,7 @@ static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, enum omap_color_mode color_mode, bool fieldmode, unsigned int field_offset, unsigned *offset0, unsigned *offset1, - s32 *row_inc, s32 *pix_inc) + s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim) { u8 ps; @@ -1477,10 +1554,10 @@ static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, else *offset0 = 0; - *row_inc = pixinc(1 + (screen_width - width) + - (fieldmode ? screen_width : 0), - ps); - *pix_inc = pixinc(1, ps); + *row_inc = pixinc(1 + + (y_predecim * screen_width - x_predecim * width) + + (fieldmode ? screen_width : 0), ps); + *pix_inc = pixinc(x_predecim, ps); break; case OMAP_DSS_ROT_0 + 4: @@ -1498,14 +1575,15 @@ static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, *offset0 = field_offset * screen_width * ps; else *offset0 = 0; - *row_inc = pixinc(1 - (screen_width + width) - - (fieldmode ? screen_width : 0), - ps); - *pix_inc = pixinc(1, ps); + *row_inc = pixinc(1 - + (y_predecim * screen_width + x_predecim * width) - + (fieldmode ? screen_width : 0), ps); + *pix_inc = pixinc(x_predecim, ps); break; default: BUG(); + return; } } @@ -1515,7 +1593,7 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, enum omap_color_mode color_mode, bool fieldmode, unsigned int field_offset, unsigned *offset0, unsigned *offset1, - s32 *row_inc, s32 *pix_inc) + s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim) { u8 ps; u16 fbw, fbh; @@ -1557,10 +1635,14 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 + field_offset * screen_width * ps; else *offset0 = *offset1; - *row_inc = pixinc(1 + (screen_width - fbw) + - (fieldmode ? screen_width : 0), - ps); - *pix_inc = pixinc(1, ps); + *row_inc = pixinc(1 + + (y_predecim * screen_width - fbw * x_predecim) + + (fieldmode ? screen_width : 0), ps); + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + *pix_inc = pixinc(x_predecim, 2 * ps); + else + *pix_inc = pixinc(x_predecim, ps); break; case OMAP_DSS_ROT_90: *offset1 = screen_width * (fbh - 1) * ps; @@ -1568,9 +1650,9 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 + field_offset * ps; else *offset0 = *offset1; - *row_inc = pixinc(screen_width * (fbh - 1) + 1 + - (fieldmode ? 1 : 0), ps); - *pix_inc = pixinc(-screen_width, ps); + *row_inc = pixinc(screen_width * (fbh * x_predecim - 1) + + y_predecim + (fieldmode ? 1 : 0), ps); + *pix_inc = pixinc(-x_predecim * screen_width, ps); break; case OMAP_DSS_ROT_180: *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps; @@ -1579,10 +1661,13 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, else *offset0 = *offset1; *row_inc = pixinc(-1 - - (screen_width - fbw) - - (fieldmode ? screen_width : 0), - ps); - *pix_inc = pixinc(-1, ps); + (y_predecim * screen_width - fbw * x_predecim) - + (fieldmode ? screen_width : 0), ps); + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + *pix_inc = pixinc(-x_predecim, 2 * ps); + else + *pix_inc = pixinc(-x_predecim, ps); break; case OMAP_DSS_ROT_270: *offset1 = (fbw - 1) * ps; @@ -1590,9 +1675,9 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 - field_offset * ps; else *offset0 = *offset1; - *row_inc = pixinc(-screen_width * (fbh - 1) - 1 - - (fieldmode ? 1 : 0), ps); - *pix_inc = pixinc(screen_width, ps); + *row_inc = pixinc(-screen_width * (fbh * x_predecim - 1) - + y_predecim - (fieldmode ? 1 : 0), ps); + *pix_inc = pixinc(x_predecim * screen_width, ps); break; /* mirroring */ @@ -1602,10 +1687,14 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 + field_offset * screen_width * ps; else *offset0 = *offset1; - *row_inc = pixinc(screen_width * 2 - 1 + + *row_inc = pixinc(y_predecim * screen_width * 2 - 1 + (fieldmode ? screen_width : 0), ps); - *pix_inc = pixinc(-1, ps); + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + *pix_inc = pixinc(-x_predecim, 2 * ps); + else + *pix_inc = pixinc(-x_predecim, ps); break; case OMAP_DSS_ROT_90 + 4: @@ -1614,10 +1703,10 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 + field_offset * ps; else *offset0 = *offset1; - *row_inc = pixinc(-screen_width * (fbh - 1) + 1 + - (fieldmode ? 1 : 0), + *row_inc = pixinc(-screen_width * (fbh * x_predecim - 1) + + y_predecim + (fieldmode ? 1 : 0), ps); - *pix_inc = pixinc(screen_width, ps); + *pix_inc = pixinc(x_predecim * screen_width, ps); break; case OMAP_DSS_ROT_180 + 4: @@ -1626,10 +1715,14 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 - field_offset * screen_width * ps; else *offset0 = *offset1; - *row_inc = pixinc(1 - screen_width * 2 - + *row_inc = pixinc(1 - y_predecim * screen_width * 2 - (fieldmode ? screen_width : 0), ps); - *pix_inc = pixinc(1, ps); + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + *pix_inc = pixinc(x_predecim, 2 * ps); + else + *pix_inc = pixinc(x_predecim, ps); break; case OMAP_DSS_ROT_270 + 4: @@ -1638,34 +1731,130 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror, *offset0 = *offset1 - field_offset * ps; else *offset0 = *offset1; - *row_inc = pixinc(screen_width * (fbh - 1) - 1 - - (fieldmode ? 1 : 0), + *row_inc = pixinc(screen_width * (fbh * x_predecim - 1) - + y_predecim - (fieldmode ? 1 : 0), ps); - *pix_inc = pixinc(-screen_width, ps); + *pix_inc = pixinc(-x_predecim * screen_width, ps); break; default: BUG(); + return; + } +} + +static void calc_tiler_rotation_offset(u16 screen_width, u16 width, + enum omap_color_mode color_mode, bool fieldmode, + unsigned int field_offset, unsigned *offset0, unsigned *offset1, + s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim) +{ + u8 ps; + + switch (color_mode) { + case OMAP_DSS_COLOR_CLUT1: + case OMAP_DSS_COLOR_CLUT2: + case OMAP_DSS_COLOR_CLUT4: + case OMAP_DSS_COLOR_CLUT8: + BUG(); + return; + default: + ps = color_mode_to_bpp(color_mode) / 8; + break; } + + DSSDBG("scrw %d, width %d\n", screen_width, width); + + /* + * field 0 = even field = bottom field + * field 1 = odd field = top field + */ + *offset1 = 0; + if (field_offset) + *offset0 = *offset1 + field_offset * screen_width * ps; + else + *offset0 = *offset1; + *row_inc = pixinc(1 + (y_predecim * screen_width - width * x_predecim) + + (fieldmode ? screen_width : 0), ps); + if (color_mode == OMAP_DSS_COLOR_YUV2 || + color_mode == OMAP_DSS_COLOR_UYVY) + *pix_inc = pixinc(x_predecim, 2 * ps); + else + *pix_inc = pixinc(x_predecim, ps); } -static unsigned long calc_fclk_five_taps(enum omap_channel channel, u16 width, +/* + * This function is used to avoid synclosts in OMAP3, because of some + * undocumented horizontal position and timing related limitations. + */ +static int check_horiz_timing_omap3(enum omap_channel channel, + const struct omap_video_timings *t, u16 pos_x, + u16 width, u16 height, u16 out_width, u16 out_height) +{ + int DS = DIV_ROUND_UP(height, out_height); + unsigned long nonactive, lclk, pclk; + static const u8 limits[3] = { 8, 10, 20 }; + u64 val, blank; + int i; + + nonactive = t->x_res + t->hfp + t->hsw + t->hbp - out_width; + pclk = dispc_mgr_pclk_rate(channel); + if (dispc_mgr_is_lcd(channel)) + lclk = dispc_mgr_lclk_rate(channel); + else + lclk = dispc_fclk_rate(); + + i = 0; + if (out_height < height) + i++; + if (out_width < width) + i++; + blank = div_u64((u64)(t->hbp + t->hsw + t->hfp) * lclk, pclk); + DSSDBG("blanking period + ppl = %llu (limit = %u)\n", blank, limits[i]); + if (blank <= limits[i]) + return -EINVAL; + + /* + * Pixel data should be prepared before visible display point starts. + * So, atleast DS-2 lines must have already been fetched by DISPC + * during nonactive - pos_x period. + */ + val = div_u64((u64)(nonactive - pos_x) * lclk, pclk); + DSSDBG("(nonactive - pos_x) * pcd = %llu max(0, DS - 2) * width = %d\n", + val, max(0, DS - 2) * width); + if (val < max(0, DS - 2) * width) + return -EINVAL; + + /* + * All lines need to be refilled during the nonactive period of which + * only one line can be loaded during the active period. So, atleast + * DS - 1 lines should be loaded during nonactive period. + */ + val = div_u64((u64)nonactive * lclk, pclk); + DSSDBG("nonactive * pcd = %llu, max(0, DS - 1) * width = %d\n", + val, max(0, DS - 1) * width); + if (val < max(0, DS - 1) * width) + return -EINVAL; + + return 0; +} + +static unsigned long calc_core_clk_five_taps(enum omap_channel channel, + const struct omap_video_timings *mgr_timings, u16 width, u16 height, u16 out_width, u16 out_height, enum omap_color_mode color_mode) { - u32 fclk = 0; + u32 core_clk = 0; u64 tmp, pclk = dispc_mgr_pclk_rate(channel); if (height <= out_height && width <= out_width) return (unsigned long) pclk; if (height > out_height) { - struct omap_dss_device *dssdev = dispc_mgr_get_device(channel); - unsigned int ppl = dssdev->panel.timings.x_res; + unsigned int ppl = mgr_timings->x_res; tmp = pclk * height * out_width; do_div(tmp, 2 * out_height * ppl); - fclk = tmp; + core_clk = tmp; if (height > 2 * out_height) { if (ppl == out_width) @@ -1673,23 +1862,23 @@ static unsigned long calc_fclk_five_taps(enum omap_channel channel, u16 width, tmp = pclk * (height - 2 * out_height) * out_width; do_div(tmp, 2 * out_height * (ppl - out_width)); - fclk = max(fclk, (u32) tmp); + core_clk = max_t(u32, core_clk, tmp); } } if (width > out_width) { tmp = pclk * width; do_div(tmp, out_width); - fclk = max(fclk, (u32) tmp); + core_clk = max_t(u32, core_clk, tmp); if (color_mode == OMAP_DSS_COLOR_RGB24U) - fclk <<= 1; + core_clk <<= 1; } - return fclk; + return core_clk; } -static unsigned long calc_fclk(enum omap_channel channel, u16 width, +static unsigned long calc_core_clk(enum omap_channel channel, u16 width, u16 height, u16 out_width, u16 out_height) { unsigned int hf, vf; @@ -1730,15 +1919,20 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width, } static int dispc_ovl_calc_scaling(enum omap_plane plane, - enum omap_channel channel, u16 width, u16 height, - u16 out_width, u16 out_height, - enum omap_color_mode color_mode, bool *five_taps) + enum omap_channel channel, + const struct omap_video_timings *mgr_timings, + u16 width, u16 height, u16 out_width, u16 out_height, + enum omap_color_mode color_mode, bool *five_taps, + int *x_predecim, int *y_predecim, u16 pos_x) { struct omap_overlay *ovl = omap_dss_get_overlay(plane); const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE); const int maxsinglelinewidth = dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH); - unsigned long fclk = 0; + const int max_decim_limit = 16; + unsigned long core_clk = 0; + int decim_x, decim_y, error, min_factor; + u16 in_width, in_height, in_width_max = 0; if (width == out_width && height == out_height) return 0; @@ -1746,64 +1940,154 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane, if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) return -EINVAL; - if (out_width < width / maxdownscale || - out_width > width * 8) + *x_predecim = max_decim_limit; + *y_predecim = max_decim_limit; + + if (color_mode == OMAP_DSS_COLOR_CLUT1 || + color_mode == OMAP_DSS_COLOR_CLUT2 || + color_mode == OMAP_DSS_COLOR_CLUT4 || + color_mode == OMAP_DSS_COLOR_CLUT8) { + *x_predecim = 1; + *y_predecim = 1; + *five_taps = false; + return 0; + } + + decim_x = DIV_ROUND_UP(DIV_ROUND_UP(width, out_width), maxdownscale); + decim_y = DIV_ROUND_UP(DIV_ROUND_UP(height, out_height), maxdownscale); + + min_factor = min(decim_x, decim_y); + + if (decim_x > *x_predecim || out_width > width * 8) return -EINVAL; - if (out_height < height / maxdownscale || - out_height > height * 8) + if (decim_y > *y_predecim || out_height > height * 8) return -EINVAL; if (cpu_is_omap24xx()) { - if (width > maxsinglelinewidth) - DSSERR("Cannot scale max input width exceeded"); *five_taps = false; - fclk = calc_fclk(channel, width, height, out_width, - out_height); + + do { + in_height = DIV_ROUND_UP(height, decim_y); + in_width = DIV_ROUND_UP(width, decim_x); + core_clk = calc_core_clk(channel, in_width, in_height, + out_width, out_height); + error = (in_width > maxsinglelinewidth || !core_clk || + core_clk > dispc_core_clk_rate()); + if (error) { + if (decim_x == decim_y) { + decim_x = min_factor; + decim_y++; + } else { + swap(decim_x, decim_y); + if (decim_x < decim_y) + decim_x++; + } + } + } while (decim_x <= *x_predecim && decim_y <= *y_predecim && + error); + + if (in_width > maxsinglelinewidth) { + DSSERR("Cannot scale max input width exceeded"); + return -EINVAL; + } } else if (cpu_is_omap34xx()) { - if (width > (maxsinglelinewidth * 2)) { + + do { + in_height = DIV_ROUND_UP(height, decim_y); + in_width = DIV_ROUND_UP(width, decim_x); + core_clk = calc_core_clk_five_taps(channel, mgr_timings, + in_width, in_height, out_width, out_height, + color_mode); + + error = check_horiz_timing_omap3(channel, mgr_timings, + pos_x, in_width, in_height, out_width, + out_height); + + if (in_width > maxsinglelinewidth) + if (in_height > out_height && + in_height < out_height * 2) + *five_taps = false; + if (!*five_taps) + core_clk = calc_core_clk(channel, in_width, + in_height, out_width, out_height); + error = (error || in_width > maxsinglelinewidth * 2 || + (in_width > maxsinglelinewidth && *five_taps) || + !core_clk || core_clk > dispc_core_clk_rate()); + if (error) { + if (decim_x == decim_y) { + decim_x = min_factor; + decim_y++; + } else { + swap(decim_x, decim_y); + if (decim_x < decim_y) + decim_x++; + } + } + } while (decim_x <= *x_predecim && decim_y <= *y_predecim + && error); + + if (check_horiz_timing_omap3(channel, mgr_timings, pos_x, width, + height, out_width, out_height)){ + DSSERR("horizontal timing too tight\n"); + return -EINVAL; + } + + if (in_width > (maxsinglelinewidth * 2)) { DSSERR("Cannot setup scaling"); DSSERR("width exceeds maximum width possible"); return -EINVAL; } - fclk = calc_fclk_five_taps(channel, width, height, out_width, - out_height, color_mode); - if (width > maxsinglelinewidth) { - if (height > out_height && height < out_height * 2) - *five_taps = false; - else { - DSSERR("cannot setup scaling with five taps"); - return -EINVAL; - } + + if (in_width > maxsinglelinewidth && *five_taps) { + DSSERR("cannot setup scaling with five taps"); + return -EINVAL; } - if (!*five_taps) - fclk = calc_fclk(channel, width, height, out_width, - out_height); } else { - if (width > maxsinglelinewidth) { + int decim_x_min = decim_x; + in_height = DIV_ROUND_UP(height, decim_y); + in_width_max = dispc_core_clk_rate() / + DIV_ROUND_UP(dispc_mgr_pclk_rate(channel), + out_width); + decim_x = DIV_ROUND_UP(width, in_width_max); + + decim_x = decim_x > decim_x_min ? decim_x : decim_x_min; + if (decim_x > *x_predecim) + return -EINVAL; + + do { + in_width = DIV_ROUND_UP(width, decim_x); + } while (decim_x <= *x_predecim && + in_width > maxsinglelinewidth && decim_x++); + + if (in_width > maxsinglelinewidth) { DSSERR("Cannot scale width exceeds max line width"); return -EINVAL; } - fclk = calc_fclk(channel, width, height, out_width, - out_height); + + core_clk = calc_core_clk(channel, in_width, in_height, + out_width, out_height); } - DSSDBG("required fclk rate = %lu Hz\n", fclk); - DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate()); + DSSDBG("required core clk rate = %lu Hz\n", core_clk); + DSSDBG("current core clk rate = %lu Hz\n", dispc_core_clk_rate()); - if (!fclk || fclk > dispc_fclk_rate()) { + if (!core_clk || core_clk > dispc_core_clk_rate()) { DSSERR("failed to set up scaling, " - "required fclk rate = %lu Hz, " - "current fclk rate = %lu Hz\n", - fclk, dispc_fclk_rate()); + "required core clk rate = %lu Hz, " + "current core clk rate = %lu Hz\n", + core_clk, dispc_core_clk_rate()); return -EINVAL; } + *x_predecim = decim_x; + *y_predecim = decim_y; return 0; } int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, - bool ilace, bool replication) + bool ilace, bool replication, + const struct omap_video_timings *mgr_timings) { struct omap_overlay *ovl = omap_dss_get_overlay(plane); bool five_taps = true; @@ -1814,8 +2098,11 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, s32 pix_inc; u16 frame_height = oi->height; unsigned int field_offset = 0; - u16 outw, outh; + u16 in_height = oi->height; + u16 in_width = oi->width; + u16 out_width, out_height; enum omap_channel channel; + int x_predecim = 1, y_predecim = 1; channel = dispc_ovl_get_channel_out(plane); @@ -1829,32 +2116,35 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, if (oi->paddr == 0) return -EINVAL; - outw = oi->out_width == 0 ? oi->width : oi->out_width; - outh = oi->out_height == 0 ? oi->height : oi->out_height; + out_width = oi->out_width == 0 ? oi->width : oi->out_width; + out_height = oi->out_height == 0 ? oi->height : oi->out_height; - if (ilace && oi->height == outh) + if (ilace && oi->height == out_height) fieldmode = 1; if (ilace) { if (fieldmode) - oi->height /= 2; + in_height /= 2; oi->pos_y /= 2; - outh /= 2; + out_height /= 2; DSSDBG("adjusting for ilace: height %d, pos_y %d, " "out_height %d\n", - oi->height, oi->pos_y, outh); + in_height, oi->pos_y, out_height); } if (!dss_feat_color_mode_supported(plane, oi->color_mode)) return -EINVAL; - r = dispc_ovl_calc_scaling(plane, channel, oi->width, oi->height, - outw, outh, oi->color_mode, - &five_taps); + r = dispc_ovl_calc_scaling(plane, channel, mgr_timings, in_width, + in_height, out_width, out_height, oi->color_mode, + &five_taps, &x_predecim, &y_predecim, oi->pos_x); if (r) return r; + in_width = DIV_ROUND_UP(in_width, x_predecim); + in_height = DIV_ROUND_UP(in_height, y_predecim); + if (oi->color_mode == OMAP_DSS_COLOR_YUV2 || oi->color_mode == OMAP_DSS_COLOR_UYVY || oi->color_mode == OMAP_DSS_COLOR_NV12) @@ -1868,32 +2158,46 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, * so the integer part must be added to the base address of the * bottom field. */ - if (!oi->height || oi->height == outh) + if (!in_height || in_height == out_height) field_offset = 0; else - field_offset = oi->height / outh / 2; + field_offset = in_height / out_height / 2; } /* Fields are independent but interleaved in memory. */ if (fieldmode) field_offset = 1; - if (oi->rotation_type == OMAP_DSS_ROT_DMA) + offset0 = 0; + offset1 = 0; + row_inc = 0; + pix_inc = 0; + + if (oi->rotation_type == OMAP_DSS_ROT_TILER) + calc_tiler_rotation_offset(oi->screen_width, in_width, + oi->color_mode, fieldmode, field_offset, + &offset0, &offset1, &row_inc, &pix_inc, + x_predecim, y_predecim); + else if (oi->rotation_type == OMAP_DSS_ROT_DMA) calc_dma_rotation_offset(oi->rotation, oi->mirror, - oi->screen_width, oi->width, frame_height, + oi->screen_width, in_width, frame_height, oi->color_mode, fieldmode, field_offset, - &offset0, &offset1, &row_inc, &pix_inc); + &offset0, &offset1, &row_inc, &pix_inc, + x_predecim, y_predecim); else calc_vrfb_rotation_offset(oi->rotation, oi->mirror, - oi->screen_width, oi->width, frame_height, + oi->screen_width, in_width, frame_height, oi->color_mode, fieldmode, field_offset, - &offset0, &offset1, &row_inc, &pix_inc); + &offset0, &offset1, &row_inc, &pix_inc, + x_predecim, y_predecim); DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n", offset0, offset1, row_inc, pix_inc); dispc_ovl_set_color_mode(plane, oi->color_mode); + dispc_ovl_configure_burst_type(plane, oi->rotation_type); + dispc_ovl_set_ba0(plane, oi->paddr + offset0); dispc_ovl_set_ba1(plane, oi->paddr + offset1); @@ -1906,19 +2210,18 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, dispc_ovl_set_row_inc(plane, row_inc); dispc_ovl_set_pix_inc(plane, pix_inc); - DSSDBG("%d,%d %dx%d -> %dx%d\n", oi->pos_x, oi->pos_y, oi->width, - oi->height, outw, outh); + DSSDBG("%d,%d %dx%d -> %dx%d\n", oi->pos_x, oi->pos_y, in_width, + in_height, out_width, out_height); dispc_ovl_set_pos(plane, oi->pos_x, oi->pos_y); - dispc_ovl_set_pic_size(plane, oi->width, oi->height); + dispc_ovl_set_pic_size(plane, in_width, in_height); if (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) { - dispc_ovl_set_scaling(plane, oi->width, oi->height, - outw, outh, - ilace, five_taps, fieldmode, + dispc_ovl_set_scaling(plane, in_width, in_height, out_width, + out_height, ilace, five_taps, fieldmode, oi->color_mode, oi->rotation); - dispc_ovl_set_vid_size(plane, outw, outh); + dispc_ovl_set_vid_size(plane, out_width, out_height); dispc_ovl_set_vid_color_conv(plane, cconv); } @@ -2087,8 +2390,10 @@ bool dispc_mgr_is_enabled(enum omap_channel channel) return !!REG_GET(DISPC_CONTROL, 1, 1); else if (channel == OMAP_DSS_CHANNEL_LCD2) return !!REG_GET(DISPC_CONTROL2, 0, 0); - else + else { BUG(); + return false; + } } void dispc_mgr_enable(enum omap_channel channel, bool enable) @@ -2285,6 +2590,12 @@ void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable) REG_FLD_MOD(DISPC_CONTROL, enable, 11, 11); } +static bool _dispc_mgr_size_ok(u16 width, u16 height) +{ + return width <= dss_feat_get_param_max(FEAT_PARAM_MGR_WIDTH) && + height <= dss_feat_get_param_max(FEAT_PARAM_MGR_HEIGHT); +} + static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp, int vsw, int vfp, int vbp) { @@ -2309,11 +2620,20 @@ static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp, return true; } -bool dispc_lcd_timings_ok(struct omap_video_timings *timings) +bool dispc_mgr_timings_ok(enum omap_channel channel, + const struct omap_video_timings *timings) { - return _dispc_lcd_timings_ok(timings->hsw, timings->hfp, - timings->hbp, timings->vsw, - timings->vfp, timings->vbp); + bool timings_ok; + + timings_ok = _dispc_mgr_size_ok(timings->x_res, timings->y_res); + + if (dispc_mgr_is_lcd(channel)) + timings_ok = timings_ok && _dispc_lcd_timings_ok(timings->hsw, + timings->hfp, timings->hbp, + timings->vsw, timings->vfp, + timings->vbp); + + return timings_ok; } static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw, @@ -2340,37 +2660,45 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw, } /* change name to mode? */ -void dispc_mgr_set_lcd_timings(enum omap_channel channel, +void dispc_mgr_set_timings(enum omap_channel channel, struct omap_video_timings *timings) { unsigned xtot, ytot; unsigned long ht, vt; + struct omap_video_timings t = *timings; + + DSSDBG("channel %d xres %u yres %u\n", channel, t.x_res, t.y_res); - if (!_dispc_lcd_timings_ok(timings->hsw, timings->hfp, - timings->hbp, timings->vsw, - timings->vfp, timings->vbp)) + if (!dispc_mgr_timings_ok(channel, &t)) { BUG(); + return; + } + + if (dispc_mgr_is_lcd(channel)) { + _dispc_mgr_set_lcd_timings(channel, t.hsw, t.hfp, t.hbp, t.vsw, + t.vfp, t.vbp); + + xtot = t.x_res + t.hfp + t.hsw + t.hbp; + ytot = t.y_res + t.vfp + t.vsw + t.vbp; - _dispc_mgr_set_lcd_timings(channel, timings->hsw, timings->hfp, - timings->hbp, timings->vsw, timings->vfp, - timings->vbp); + ht = (timings->pixel_clock * 1000) / xtot; + vt = (timings->pixel_clock * 1000) / xtot / ytot; - dispc_mgr_set_lcd_size(channel, timings->x_res, timings->y_res); + DSSDBG("pck %u\n", timings->pixel_clock); + DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n", + t.hsw, t.hfp, t.hbp, t.vsw, t.vfp, t.vbp); - xtot = timings->x_res + timings->hfp + timings->hsw + timings->hbp; - ytot = timings->y_res + timings->vfp + timings->vsw + timings->vbp; + DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); + } else { + enum dss_hdmi_venc_clk_source_select source; - ht = (timings->pixel_clock * 1000) / xtot; - vt = (timings->pixel_clock * 1000) / xtot / ytot; + source = dss_get_hdmi_venc_clk_source(); - DSSDBG("channel %d xres %u yres %u\n", channel, timings->x_res, - timings->y_res); - DSSDBG("pck %u\n", timings->pixel_clock); - DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n", - timings->hsw, timings->hfp, timings->hbp, - timings->vsw, timings->vfp, timings->vbp); + if (source == DSS_VENC_TV_CLK) + t.y_res /= 2; + } - DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); + dispc_mgr_set_size(channel, t.x_res, t.y_res); } static void dispc_mgr_set_lcd_divisor(enum omap_channel channel, u16 lck_div, @@ -2411,6 +2739,7 @@ unsigned long dispc_fclk_rate(void) break; default: BUG(); + return 0; } return r; @@ -2441,6 +2770,7 @@ unsigned long dispc_mgr_lclk_rate(enum omap_channel channel) break; default: BUG(); + return 0; } return r / lcd; @@ -2462,20 +2792,35 @@ unsigned long dispc_mgr_pclk_rate(enum omap_channel channel) return r / pcd; } else { - struct omap_dss_device *dssdev = - dispc_mgr_get_device(channel); + enum dss_hdmi_venc_clk_source_select source; - switch (dssdev->type) { - case OMAP_DISPLAY_TYPE_VENC: + source = dss_get_hdmi_venc_clk_source(); + + switch (source) { + case DSS_VENC_TV_CLK: return venc_get_pixel_clock(); - case OMAP_DISPLAY_TYPE_HDMI: + case DSS_HDMI_M_PCLK: return hdmi_get_pixel_clock(); default: BUG(); + return 0; } } } +unsigned long dispc_core_clk_rate(void) +{ + int lcd; + unsigned long fclk = dispc_fclk_rate(); + + if (dss_has_feature(FEAT_CORE_CLK_DIV)) + lcd = REG_GET(DISPC_DIVISOR, 23, 16); + else + lcd = REG_GET(DISPC_DIVISORo(OMAP_DSS_CHANNEL_LCD), 23, 16); + + return fclk / lcd; +} + void dispc_dump_clocks(struct seq_file *s) { int lcd, pcd; @@ -2588,7 +2933,7 @@ void dispc_dump_irqs(struct seq_file *s) } #endif -void dispc_dump_regs(struct seq_file *s) +static void dispc_dump_regs(struct seq_file *s) { int i, j; const char *mgr_names[] = { @@ -3247,27 +3592,6 @@ int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, return 0; } -#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC -void dispc_fake_vsync_irq(void) -{ - u32 irqstatus = DISPC_IRQ_VSYNC; - int i; - - WARN_ON(!in_interrupt()); - - for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { - struct omap_dispc_isr_data *isr_data; - isr_data = &dispc.registered_isr[i]; - - if (!isr_data->isr) - continue; - - if (isr_data->mask & irqstatus) - isr_data->isr(isr_data->arg, irqstatus); - } -} -#endif - static void _omap_dispc_initialize_irq(void) { unsigned long flags; @@ -3330,7 +3654,7 @@ static void _omap_dispc_initial_config(void) } /* DISPC HW IP initialisation */ -static int omap_dispchw_probe(struct platform_device *pdev) +static int __init omap_dispchw_probe(struct platform_device *pdev) { u32 rev; int r = 0; @@ -3399,6 +3723,11 @@ static int omap_dispchw_probe(struct platform_device *pdev) dispc_runtime_put(); + dss_debugfs_create_file("dispc", dispc_dump_regs); + +#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS + dss_debugfs_create_file("dispc_irq", dispc_dump_irqs); +#endif return 0; err_runtime_get: @@ -3407,7 +3736,7 @@ err_runtime_get: return r; } -static int omap_dispchw_remove(struct platform_device *pdev) +static int __exit omap_dispchw_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); @@ -3419,19 +3748,12 @@ static int omap_dispchw_remove(struct platform_device *pdev) static int dispc_runtime_suspend(struct device *dev) { dispc_save_context(); - dss_runtime_put(); return 0; } static int dispc_runtime_resume(struct device *dev) { - int r; - - r = dss_runtime_get(); - if (r < 0) - return r; - dispc_restore_context(); return 0; @@ -3443,8 +3765,7 @@ static const struct dev_pm_ops dispc_pm_ops = { }; static struct platform_driver omap_dispchw_driver = { - .probe = omap_dispchw_probe, - .remove = omap_dispchw_remove, + .remove = __exit_p(omap_dispchw_remove), .driver = { .name = "omapdss_dispc", .owner = THIS_MODULE, @@ -3452,12 +3773,12 @@ static struct platform_driver omap_dispchw_driver = { }, }; -int dispc_init_platform_driver(void) +int __init dispc_init_platform_driver(void) { - return platform_driver_register(&omap_dispchw_driver); + return platform_driver_probe(&omap_dispchw_driver, omap_dispchw_probe); } -void dispc_uninit_platform_driver(void) +void __exit dispc_uninit_platform_driver(void) { - return platform_driver_unregister(&omap_dispchw_driver); + platform_driver_unregister(&omap_dispchw_driver); } diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h index 5836bd1650f9..f278080e1063 100644 --- a/drivers/video/omap2/dss/dispc.h +++ b/drivers/video/omap2/dss/dispc.h @@ -120,6 +120,7 @@ static inline u16 DISPC_DEFAULT_COLOR(enum omap_channel channel) return 0x03AC; default: BUG(); + return 0; } } @@ -134,6 +135,7 @@ static inline u16 DISPC_TRANS_COLOR(enum omap_channel channel) return 0x03B0; default: BUG(); + return 0; } } @@ -144,10 +146,12 @@ static inline u16 DISPC_TIMING_H(enum omap_channel channel) return 0x0064; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x0400; default: BUG(); + return 0; } } @@ -158,10 +162,12 @@ static inline u16 DISPC_TIMING_V(enum omap_channel channel) return 0x0068; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x0404; default: BUG(); + return 0; } } @@ -172,10 +178,12 @@ static inline u16 DISPC_POL_FREQ(enum omap_channel channel) return 0x006C; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x0408; default: BUG(); + return 0; } } @@ -186,10 +194,12 @@ static inline u16 DISPC_DIVISORo(enum omap_channel channel) return 0x0070; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x040C; default: BUG(); + return 0; } } @@ -205,6 +215,7 @@ static inline u16 DISPC_SIZE_MGR(enum omap_channel channel) return 0x03CC; default: BUG(); + return 0; } } @@ -215,10 +226,12 @@ static inline u16 DISPC_DATA_CYCLE1(enum omap_channel channel) return 0x01D4; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03C0; default: BUG(); + return 0; } } @@ -229,10 +242,12 @@ static inline u16 DISPC_DATA_CYCLE2(enum omap_channel channel) return 0x01D8; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03C4; default: BUG(); + return 0; } } @@ -243,10 +258,12 @@ static inline u16 DISPC_DATA_CYCLE3(enum omap_channel channel) return 0x01DC; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03C8; default: BUG(); + return 0; } } @@ -257,10 +274,12 @@ static inline u16 DISPC_CPR_COEF_R(enum omap_channel channel) return 0x0220; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03BC; default: BUG(); + return 0; } } @@ -271,10 +290,12 @@ static inline u16 DISPC_CPR_COEF_G(enum omap_channel channel) return 0x0224; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03B8; default: BUG(); + return 0; } } @@ -285,10 +306,12 @@ static inline u16 DISPC_CPR_COEF_B(enum omap_channel channel) return 0x0228; case OMAP_DSS_CHANNEL_DIGIT: BUG(); + return 0; case OMAP_DSS_CHANNEL_LCD2: return 0x03B4; default: BUG(); + return 0; } } @@ -306,6 +329,7 @@ static inline u16 DISPC_OVL_BASE(enum omap_plane plane) return 0x0300; default: BUG(); + return 0; } } @@ -321,6 +345,7 @@ static inline u16 DISPC_BA0_OFFSET(enum omap_plane plane) return 0x0008; default: BUG(); + return 0; } } @@ -335,6 +360,7 @@ static inline u16 DISPC_BA1_OFFSET(enum omap_plane plane) return 0x000C; default: BUG(); + return 0; } } @@ -343,6 +369,7 @@ static inline u16 DISPC_BA0_UV_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0544; case OMAP_DSS_VIDEO2: @@ -351,6 +378,7 @@ static inline u16 DISPC_BA0_UV_OFFSET(enum omap_plane plane) return 0x0310; default: BUG(); + return 0; } } @@ -359,6 +387,7 @@ static inline u16 DISPC_BA1_UV_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0548; case OMAP_DSS_VIDEO2: @@ -367,6 +396,7 @@ static inline u16 DISPC_BA1_UV_OFFSET(enum omap_plane plane) return 0x0314; default: BUG(); + return 0; } } @@ -381,6 +411,7 @@ static inline u16 DISPC_POS_OFFSET(enum omap_plane plane) return 0x009C; default: BUG(); + return 0; } } @@ -395,6 +426,7 @@ static inline u16 DISPC_SIZE_OFFSET(enum omap_plane plane) return 0x00A8; default: BUG(); + return 0; } } @@ -410,6 +442,7 @@ static inline u16 DISPC_ATTR_OFFSET(enum omap_plane plane) return 0x0070; default: BUG(); + return 0; } } @@ -418,6 +451,7 @@ static inline u16 DISPC_ATTR2_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0568; case OMAP_DSS_VIDEO2: @@ -426,6 +460,7 @@ static inline u16 DISPC_ATTR2_OFFSET(enum omap_plane plane) return 0x032C; default: BUG(); + return 0; } } @@ -441,6 +476,7 @@ static inline u16 DISPC_FIFO_THRESH_OFFSET(enum omap_plane plane) return 0x008C; default: BUG(); + return 0; } } @@ -456,6 +492,7 @@ static inline u16 DISPC_FIFO_SIZE_STATUS_OFFSET(enum omap_plane plane) return 0x0088; default: BUG(); + return 0; } } @@ -471,6 +508,7 @@ static inline u16 DISPC_ROW_INC_OFFSET(enum omap_plane plane) return 0x00A4; default: BUG(); + return 0; } } @@ -486,6 +524,7 @@ static inline u16 DISPC_PIX_INC_OFFSET(enum omap_plane plane) return 0x0098; default: BUG(); + return 0; } } @@ -498,8 +537,10 @@ static inline u16 DISPC_WINDOW_SKIP_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO2: case OMAP_DSS_VIDEO3: BUG(); + return 0; default: BUG(); + return 0; } } @@ -512,8 +553,10 @@ static inline u16 DISPC_TABLE_BA_OFFSET(enum omap_plane plane) case OMAP_DSS_VIDEO2: case OMAP_DSS_VIDEO3: BUG(); + return 0; default: BUG(); + return 0; } } @@ -522,6 +565,7 @@ static inline u16 DISPC_FIR_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0024; @@ -529,6 +573,7 @@ static inline u16 DISPC_FIR_OFFSET(enum omap_plane plane) return 0x0090; default: BUG(); + return 0; } } @@ -537,6 +582,7 @@ static inline u16 DISPC_FIR2_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0580; case OMAP_DSS_VIDEO2: @@ -545,6 +591,7 @@ static inline u16 DISPC_FIR2_OFFSET(enum omap_plane plane) return 0x0424; default: BUG(); + return 0; } } @@ -553,6 +600,7 @@ static inline u16 DISPC_PIC_SIZE_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0028; @@ -560,6 +608,7 @@ static inline u16 DISPC_PIC_SIZE_OFFSET(enum omap_plane plane) return 0x0094; default: BUG(); + return 0; } } @@ -569,6 +618,7 @@ static inline u16 DISPC_ACCU0_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x002C; @@ -576,6 +626,7 @@ static inline u16 DISPC_ACCU0_OFFSET(enum omap_plane plane) return 0x0000; default: BUG(); + return 0; } } @@ -584,6 +635,7 @@ static inline u16 DISPC_ACCU2_0_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0584; case OMAP_DSS_VIDEO2: @@ -592,6 +644,7 @@ static inline u16 DISPC_ACCU2_0_OFFSET(enum omap_plane plane) return 0x0428; default: BUG(); + return 0; } } @@ -600,6 +653,7 @@ static inline u16 DISPC_ACCU1_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0030; @@ -607,6 +661,7 @@ static inline u16 DISPC_ACCU1_OFFSET(enum omap_plane plane) return 0x0004; default: BUG(); + return 0; } } @@ -615,6 +670,7 @@ static inline u16 DISPC_ACCU2_1_OFFSET(enum omap_plane plane) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0588; case OMAP_DSS_VIDEO2: @@ -623,6 +679,7 @@ static inline u16 DISPC_ACCU2_1_OFFSET(enum omap_plane plane) return 0x042C; default: BUG(); + return 0; } } @@ -632,6 +689,7 @@ static inline u16 DISPC_FIR_COEF_H_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0034 + i * 0x8; @@ -639,6 +697,7 @@ static inline u16 DISPC_FIR_COEF_H_OFFSET(enum omap_plane plane, u16 i) return 0x0010 + i * 0x8; default: BUG(); + return 0; } } @@ -648,6 +707,7 @@ static inline u16 DISPC_FIR_COEF_H2_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x058C + i * 0x8; case OMAP_DSS_VIDEO2: @@ -656,6 +716,7 @@ static inline u16 DISPC_FIR_COEF_H2_OFFSET(enum omap_plane plane, u16 i) return 0x0430 + i * 0x8; default: BUG(); + return 0; } } @@ -665,6 +726,7 @@ static inline u16 DISPC_FIR_COEF_HV_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: return 0x0038 + i * 0x8; @@ -672,6 +734,7 @@ static inline u16 DISPC_FIR_COEF_HV_OFFSET(enum omap_plane plane, u16 i) return 0x0014 + i * 0x8; default: BUG(); + return 0; } } @@ -681,6 +744,7 @@ static inline u16 DISPC_FIR_COEF_HV2_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0590 + i * 8; case OMAP_DSS_VIDEO2: @@ -689,6 +753,7 @@ static inline u16 DISPC_FIR_COEF_HV2_OFFSET(enum omap_plane plane, u16 i) return 0x0434 + i * 0x8; default: BUG(); + return 0; } } @@ -698,12 +763,14 @@ static inline u16 DISPC_CONV_COEF_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: case OMAP_DSS_VIDEO2: case OMAP_DSS_VIDEO3: return 0x0074 + i * 0x4; default: BUG(); + return 0; } } @@ -713,6 +780,7 @@ static inline u16 DISPC_FIR_COEF_V_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x0124 + i * 0x4; case OMAP_DSS_VIDEO2: @@ -721,6 +789,7 @@ static inline u16 DISPC_FIR_COEF_V_OFFSET(enum omap_plane plane, u16 i) return 0x0050 + i * 0x4; default: BUG(); + return 0; } } @@ -730,6 +799,7 @@ static inline u16 DISPC_FIR_COEF_V2_OFFSET(enum omap_plane plane, u16 i) switch (plane) { case OMAP_DSS_GFX: BUG(); + return 0; case OMAP_DSS_VIDEO1: return 0x05CC + i * 0x4; case OMAP_DSS_VIDEO2: @@ -738,6 +808,7 @@ static inline u16 DISPC_FIR_COEF_V2_OFFSET(enum omap_plane plane, u16 i) return 0x0470 + i * 0x4; default: BUG(); + return 0; } } @@ -754,6 +825,7 @@ static inline u16 DISPC_PRELOAD_OFFSET(enum omap_plane plane) return 0x00A0; default: BUG(); + return 0; } } #endif diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index 4424c198dbcd..249010630370 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -304,10 +304,18 @@ int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) return 24; default: BUG(); + return 0; } } EXPORT_SYMBOL(omapdss_default_get_recommended_bpp); +void omapdss_default_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + *timings = dssdev->panel.timings; +} +EXPORT_SYMBOL(omapdss_default_get_timings); + /* Checks if replication logic should be used. Only use for active matrix, * when overlay is in RGB12U or RGB16 mode, and LCD interface is * 18bpp or 24bpp */ @@ -340,6 +348,7 @@ bool dss_use_replication(struct omap_dss_device *dssdev, break; default: BUG(); + return false; } return bpp > 16; @@ -352,46 +361,6 @@ void dss_init_device(struct platform_device *pdev, int i; int r; - switch (dssdev->type) { -#ifdef CONFIG_OMAP2_DSS_DPI - case OMAP_DISPLAY_TYPE_DPI: - r = dpi_init_display(dssdev); - break; -#endif -#ifdef CONFIG_OMAP2_DSS_RFBI - case OMAP_DISPLAY_TYPE_DBI: - r = rfbi_init_display(dssdev); - break; -#endif -#ifdef CONFIG_OMAP2_DSS_VENC - case OMAP_DISPLAY_TYPE_VENC: - r = venc_init_display(dssdev); - break; -#endif -#ifdef CONFIG_OMAP2_DSS_SDI - case OMAP_DISPLAY_TYPE_SDI: - r = sdi_init_display(dssdev); - break; -#endif -#ifdef CONFIG_OMAP2_DSS_DSI - case OMAP_DISPLAY_TYPE_DSI: - r = dsi_init_display(dssdev); - break; -#endif - case OMAP_DISPLAY_TYPE_HDMI: - r = hdmi_init_display(dssdev); - break; - default: - DSSERR("Support for display '%s' not compiled in.\n", - dssdev->name); - return; - } - - if (r) { - DSSERR("failed to init display %s\n", dssdev->name); - return; - } - /* create device sysfs files */ i = 0; while ((attr = display_sysfs_attrs[i++]) != NULL) { diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index faaf305fda27..8c2056c9537b 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -156,7 +156,7 @@ static int dpi_set_mode(struct omap_dss_device *dssdev) t->pixel_clock = pck; } - dispc_mgr_set_lcd_timings(dssdev->manager->id, t); + dss_mgr_set_timings(dssdev->manager, t); return 0; } @@ -202,10 +202,6 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) goto err_reg_enable; } - r = dss_runtime_get(); - if (r) - goto err_get_dss; - r = dispc_runtime_get(); if (r) goto err_get_dispc; @@ -244,8 +240,6 @@ err_dsi_pll_init: err_get_dsi: dispc_runtime_put(); err_get_dispc: - dss_runtime_put(); -err_get_dss: if (cpu_is_omap34xx()) regulator_disable(dpi.vdds_dsi_reg); err_reg_enable: @@ -266,7 +260,6 @@ void omapdss_dpi_display_disable(struct omap_dss_device *dssdev) } dispc_runtime_put(); - dss_runtime_put(); if (cpu_is_omap34xx()) regulator_disable(dpi.vdds_dsi_reg); @@ -283,21 +276,15 @@ void dpi_set_timings(struct omap_dss_device *dssdev, DSSDBG("dpi_set_timings\n"); dssdev->panel.timings = *timings; if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { - r = dss_runtime_get(); - if (r) - return; - r = dispc_runtime_get(); - if (r) { - dss_runtime_put(); + if (r) return; - } dpi_set_mode(dssdev); - dispc_mgr_go(dssdev->manager->id); dispc_runtime_put(); - dss_runtime_put(); + } else { + dss_mgr_set_timings(dssdev->manager, timings); } } EXPORT_SYMBOL(dpi_set_timings); @@ -312,7 +299,7 @@ int dpi_check_timings(struct omap_dss_device *dssdev, unsigned long pck; struct dispc_clock_info dispc_cinfo; - if (!dispc_lcd_timings_ok(timings)) + if (dss_mgr_check_timings(dssdev->manager, timings)) return -EINVAL; if (timings->pixel_clock == 0) @@ -352,7 +339,7 @@ int dpi_check_timings(struct omap_dss_device *dssdev, } EXPORT_SYMBOL(dpi_check_timings); -int dpi_init_display(struct omap_dss_device *dssdev) +static int __init dpi_init_display(struct omap_dss_device *dssdev) { DSSDBG("init_display\n"); @@ -378,12 +365,58 @@ int dpi_init_display(struct omap_dss_device *dssdev) return 0; } -int dpi_init(void) +static void __init dpi_probe_pdata(struct platform_device *pdev) { + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int i, r; + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + if (dssdev->type != OMAP_DISPLAY_TYPE_DPI) + continue; + + r = dpi_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } + + r = omap_dss_register_device(dssdev, &pdev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } +} + +static int __init omap_dpi_probe(struct platform_device *pdev) +{ + dpi_probe_pdata(pdev); + + return 0; +} + +static int __exit omap_dpi_remove(struct platform_device *pdev) +{ + omap_dss_unregister_child_devices(&pdev->dev); + return 0; } -void dpi_exit(void) +static struct platform_driver omap_dpi_driver = { + .remove = __exit_p(omap_dpi_remove), + .driver = { + .name = "omapdss_dpi", + .owner = THIS_MODULE, + }, +}; + +int __init dpi_init_platform_driver(void) { + return platform_driver_probe(&omap_dpi_driver, omap_dpi_probe); } +void __exit dpi_uninit_platform_driver(void) +{ + platform_driver_unregister(&omap_dpi_driver); +} diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index 210a3c4f6150..ec363d8390ed 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -256,14 +256,13 @@ struct dsi_data { struct platform_device *pdev; void __iomem *base; + int module_id; + int irq; struct clk *dss_clk; struct clk *sys_clk; - int (*enable_pads)(int dsi_id, unsigned lane_mask); - void (*disable_pads)(int dsi_id, unsigned lane_mask); - struct dsi_clock_info current_cinfo; bool vdds_dsi_enabled; @@ -361,11 +360,6 @@ struct platform_device *dsi_get_dsidev_from_id(int module) return dsi_pdev_map[module]; } -static inline int dsi_get_dsidev_id(struct platform_device *dsidev) -{ - return dsidev->id; -} - static inline void dsi_write_reg(struct platform_device *dsidev, const struct dsi_reg idx, u32 val) { @@ -452,6 +446,7 @@ u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt) return 16; default: BUG(); + return 0; } } @@ -1184,10 +1179,9 @@ static unsigned long dsi_get_txbyteclkhs(struct platform_device *dsidev) static unsigned long dsi_fclk_rate(struct platform_device *dsidev) { unsigned long r; - int dsi_module = dsi_get_dsidev_id(dsidev); struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - if (dss_get_dsi_clk_source(dsi_module) == OMAP_DSS_CLK_SRC_FCK) { + if (dss_get_dsi_clk_source(dsi->module_id) == OMAP_DSS_CLK_SRC_FCK) { /* DSI FCLK source is DSS_CLK_FCK */ r = clk_get_rate(dsi->dss_clk); } else { @@ -1279,10 +1273,9 @@ static int dsi_pll_power(struct platform_device *dsidev, } /* calculate clock rates using dividers in cinfo */ -static int dsi_calc_clock_rates(struct omap_dss_device *dssdev, +static int dsi_calc_clock_rates(struct platform_device *dsidev, struct dsi_clock_info *cinfo) { - struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); if (cinfo->regn == 0 || cinfo->regn > dsi->regn_max) @@ -1297,21 +1290,8 @@ static int dsi_calc_clock_rates(struct omap_dss_device *dssdev, if (cinfo->regm_dsi > dsi->regm_dsi_max) return -EINVAL; - if (cinfo->use_sys_clk) { - cinfo->clkin = clk_get_rate(dsi->sys_clk); - /* XXX it is unclear if highfreq should be used - * with DSS_SYS_CLK source also */ - cinfo->highfreq = 0; - } else { - cinfo->clkin = dispc_mgr_pclk_rate(dssdev->manager->id); - - if (cinfo->clkin < 32000000) - cinfo->highfreq = 0; - else - cinfo->highfreq = 1; - } - - cinfo->fint = cinfo->clkin / (cinfo->regn * (cinfo->highfreq ? 2 : 1)); + cinfo->clkin = clk_get_rate(dsi->sys_clk); + cinfo->fint = cinfo->clkin / cinfo->regn; if (cinfo->fint > dsi->fint_max || cinfo->fint < dsi->fint_min) return -EINVAL; @@ -1378,27 +1358,21 @@ retry: memset(&cur, 0, sizeof(cur)); cur.clkin = dss_sys_clk; - cur.use_sys_clk = 1; - cur.highfreq = 0; - /* no highfreq: 0.75MHz < Fint = clkin / regn < 2.1MHz */ - /* highfreq: 0.75MHz < Fint = clkin / (2*regn) < 2.1MHz */ + /* 0.75MHz < Fint = clkin / regn < 2.1MHz */ /* To reduce PLL lock time, keep Fint high (around 2 MHz) */ for (cur.regn = 1; cur.regn < dsi->regn_max; ++cur.regn) { - if (cur.highfreq == 0) - cur.fint = cur.clkin / cur.regn; - else - cur.fint = cur.clkin / (2 * cur.regn); + cur.fint = cur.clkin / cur.regn; if (cur.fint > dsi->fint_max || cur.fint < dsi->fint_min) continue; - /* DSIPHY(MHz) = (2 * regm / regn) * (clkin / (highfreq + 1)) */ + /* DSIPHY(MHz) = (2 * regm / regn) * clkin */ for (cur.regm = 1; cur.regm < dsi->regm_max; ++cur.regm) { unsigned long a, b; a = 2 * cur.regm * (cur.clkin/1000); - b = cur.regn * (cur.highfreq + 1); + b = cur.regn; cur.clkin4ddr = a / b * 1000; if (cur.clkin4ddr > 1800 * 1000 * 1000) @@ -1486,9 +1460,7 @@ int dsi_pll_set_clock_div(struct platform_device *dsidev, DSSDBGF(); - dsi->current_cinfo.use_sys_clk = cinfo->use_sys_clk; - dsi->current_cinfo.highfreq = cinfo->highfreq; - + dsi->current_cinfo.clkin = cinfo->clkin; dsi->current_cinfo.fint = cinfo->fint; dsi->current_cinfo.clkin4ddr = cinfo->clkin4ddr; dsi->current_cinfo.dsi_pll_hsdiv_dispc_clk = @@ -1503,17 +1475,13 @@ int dsi_pll_set_clock_div(struct platform_device *dsidev, DSSDBG("DSI Fint %ld\n", cinfo->fint); - DSSDBG("clkin (%s) rate %ld, highfreq %d\n", - cinfo->use_sys_clk ? "dss_sys_clk" : "pclkfree", - cinfo->clkin, - cinfo->highfreq); + DSSDBG("clkin rate %ld\n", cinfo->clkin); /* DSIPHY == CLKIN4DDR */ - DSSDBG("CLKIN4DDR = 2 * %d / %d * %lu / %d = %lu\n", + DSSDBG("CLKIN4DDR = 2 * %d / %d * %lu = %lu\n", cinfo->regm, cinfo->regn, cinfo->clkin, - cinfo->highfreq + 1, cinfo->clkin4ddr); DSSDBG("Data rate on 1 DSI lane %ld Mbps\n", @@ -1568,10 +1536,6 @@ int dsi_pll_set_clock_div(struct platform_device *dsidev, if (dss_has_feature(FEAT_DSI_PLL_FREQSEL)) l = FLD_MOD(l, f, 4, 1); /* DSI_PLL_FREQSEL */ - l = FLD_MOD(l, cinfo->use_sys_clk ? 0 : 1, - 11, 11); /* DSI_PLL_CLKSEL */ - l = FLD_MOD(l, cinfo->highfreq, - 12, 12); /* DSI_PLL_HIGHFREQ */ l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ l = FLD_MOD(l, 0, 14, 14); /* DSIPHY_CLKINEN */ l = FLD_MOD(l, 1, 20, 20); /* DSI_HSDIVBYPASS */ @@ -1716,7 +1680,7 @@ static void dsi_dump_dsidev_clocks(struct platform_device *dsidev, struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); struct dsi_clock_info *cinfo = &dsi->current_cinfo; enum omap_dss_clk_source dispc_clk_src, dsi_clk_src; - int dsi_module = dsi_get_dsidev_id(dsidev); + int dsi_module = dsi->module_id; dispc_clk_src = dss_get_dispc_clk_source(); dsi_clk_src = dss_get_dsi_clk_source(dsi_module); @@ -1726,8 +1690,7 @@ static void dsi_dump_dsidev_clocks(struct platform_device *dsidev, seq_printf(s, "- DSI%d PLL -\n", dsi_module + 1); - seq_printf(s, "dsi pll source = %s\n", - cinfo->use_sys_clk ? "dss_sys_clk" : "pclkfree"); + seq_printf(s, "dsi pll clkin\t%lu\n", cinfo->clkin); seq_printf(s, "Fint\t\t%-16luregn %u\n", cinfo->fint, cinfo->regn); @@ -1789,7 +1752,6 @@ static void dsi_dump_dsidev_irqs(struct platform_device *dsidev, struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; struct dsi_irq_stats stats; - int dsi_module = dsi_get_dsidev_id(dsidev); spin_lock_irqsave(&dsi->irq_stats_lock, flags); @@ -1806,7 +1768,7 @@ static void dsi_dump_dsidev_irqs(struct platform_device *dsidev, #define PIS(x) \ seq_printf(s, "%-20s %10d\n", #x, stats.dsi_irqs[ffs(DSI_IRQ_##x)-1]); - seq_printf(s, "-- DSI%d interrupts --\n", dsi_module + 1); + seq_printf(s, "-- DSI%d interrupts --\n", dsi->module_id + 1); PIS(VC0); PIS(VC1); PIS(VC2); @@ -1886,22 +1848,6 @@ static void dsi2_dump_irqs(struct seq_file *s) dsi_dump_dsidev_irqs(dsidev, s); } - -void dsi_create_debugfs_files_irq(struct dentry *debugfs_dir, - const struct file_operations *debug_fops) -{ - struct platform_device *dsidev; - - dsidev = dsi_get_dsidev_from_id(0); - if (dsidev) - debugfs_create_file("dsi1_irqs", S_IRUGO, debugfs_dir, - &dsi1_dump_irqs, debug_fops); - - dsidev = dsi_get_dsidev_from_id(1); - if (dsidev) - debugfs_create_file("dsi2_irqs", S_IRUGO, debugfs_dir, - &dsi2_dump_irqs, debug_fops); -} #endif static void dsi_dump_dsidev_regs(struct platform_device *dsidev, @@ -2002,21 +1948,6 @@ static void dsi2_dump_regs(struct seq_file *s) dsi_dump_dsidev_regs(dsidev, s); } -void dsi_create_debugfs_files_reg(struct dentry *debugfs_dir, - const struct file_operations *debug_fops) -{ - struct platform_device *dsidev; - - dsidev = dsi_get_dsidev_from_id(0); - if (dsidev) - debugfs_create_file("dsi1_regs", S_IRUGO, debugfs_dir, - &dsi1_dump_regs, debug_fops); - - dsidev = dsi_get_dsidev_from_id(1); - if (dsidev) - debugfs_create_file("dsi2_regs", S_IRUGO, debugfs_dir, - &dsi2_dump_regs, debug_fops); -} enum dsi_cio_power_state { DSI_COMPLEXIO_POWER_OFF = 0x0, DSI_COMPLEXIO_POWER_ON = 0x1, @@ -2073,6 +2004,7 @@ static unsigned dsi_get_line_buf_size(struct platform_device *dsidev) return 1365 * 3; /* 1365x24 bits */ default: BUG(); + return 0; } } @@ -2337,7 +2269,7 @@ static int dsi_cio_init(struct omap_dss_device *dssdev) DSSDBGF(); - r = dsi->enable_pads(dsidev->id, dsi_get_lane_mask(dssdev)); + r = dss_dsi_enable_pads(dsi->module_id, dsi_get_lane_mask(dssdev)); if (r) return r; @@ -2447,7 +2379,7 @@ err_cio_pwr: dsi_cio_disable_lane_override(dsidev); err_scp_clk_dom: dsi_disable_scp_clk(dsidev); - dsi->disable_pads(dsidev->id, dsi_get_lane_mask(dssdev)); + dss_dsi_disable_pads(dsi->module_id, dsi_get_lane_mask(dssdev)); return r; } @@ -2461,7 +2393,7 @@ static void dsi_cio_uninit(struct omap_dss_device *dssdev) dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF); dsi_disable_scp_clk(dsidev); - dsi->disable_pads(dsidev->id, dsi_get_lane_mask(dssdev)); + dss_dsi_disable_pads(dsi->module_id, dsi_get_lane_mask(dssdev)); } static void dsi_config_tx_fifo(struct platform_device *dsidev, @@ -2485,6 +2417,7 @@ static void dsi_config_tx_fifo(struct platform_device *dsidev, if (add + size > 4) { DSSERR("Illegal FIFO configuration\n"); BUG(); + return; } v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4); @@ -2517,6 +2450,7 @@ static void dsi_config_rx_fifo(struct platform_device *dsidev, if (add + size > 4) { DSSERR("Illegal FIFO configuration\n"); BUG(); + return; } v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4); @@ -2658,6 +2592,7 @@ static int dsi_sync_vc(struct platform_device *dsidev, int channel) return dsi_sync_vc_l4(dsidev, channel); default: BUG(); + return -EINVAL; } } @@ -3226,6 +3161,7 @@ static int dsi_vc_generic_send_read_request(struct omap_dss_device *dssdev, data = reqdata[0] | (reqdata[1] << 8); } else { BUG(); + return -EINVAL; } r = dsi_vc_send_short(dsidev, channel, data_type, data, 0); @@ -3340,7 +3276,6 @@ static int dsi_vc_read_rx_fifo(struct platform_device *dsidev, int channel, goto err; } - BUG(); err: DSSERR("dsi_vc_read_rx_fifo(ch %d type %s) failed\n", channel, type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS"); @@ -3735,6 +3670,186 @@ static void dsi_config_blanking_modes(struct omap_dss_device *dssdev) dsi_write_reg(dsidev, DSI_CTRL, r); } +/* + * According to section 'HS Command Mode Interleaving' in OMAP TRM, Scenario 3 + * results in maximum transition time for data and clock lanes to enter and + * exit HS mode. Hence, this is the scenario where the least amount of command + * mode data can be interleaved. We program the minimum amount of TXBYTECLKHS + * clock cycles that can be used to interleave command mode data in HS so that + * all scenarios are satisfied. + */ +static int dsi_compute_interleave_hs(int blank, bool ddr_alwon, int enter_hs, + int exit_hs, int exiths_clk, int ddr_pre, int ddr_post) +{ + int transition; + + /* + * If DDR_CLK_ALWAYS_ON is set, we need to consider HS mode transition + * time of data lanes only, if it isn't set, we need to consider HS + * transition time of both data and clock lanes. HS transition time + * of Scenario 3 is considered. + */ + if (ddr_alwon) { + transition = enter_hs + exit_hs + max(enter_hs, 2) + 1; + } else { + int trans1, trans2; + trans1 = ddr_pre + enter_hs + exit_hs + max(enter_hs, 2) + 1; + trans2 = ddr_pre + enter_hs + exiths_clk + ddr_post + ddr_pre + + enter_hs + 1; + transition = max(trans1, trans2); + } + + return blank > transition ? blank - transition : 0; +} + +/* + * According to section 'LP Command Mode Interleaving' in OMAP TRM, Scenario 1 + * results in maximum transition time for data lanes to enter and exit LP mode. + * Hence, this is the scenario where the least amount of command mode data can + * be interleaved. We program the minimum amount of bytes that can be + * interleaved in LP so that all scenarios are satisfied. + */ +static int dsi_compute_interleave_lp(int blank, int enter_hs, int exit_hs, + int lp_clk_div, int tdsi_fclk) +{ + int trans_lp; /* time required for a LP transition, in TXBYTECLKHS */ + int tlp_avail; /* time left for interleaving commands, in CLKIN4DDR */ + int ttxclkesc; /* period of LP transmit escape clock, in CLKIN4DDR */ + int thsbyte_clk = 16; /* Period of TXBYTECLKHS clock, in CLKIN4DDR */ + int lp_inter; /* cmd mode data that can be interleaved, in bytes */ + + /* maximum LP transition time according to Scenario 1 */ + trans_lp = exit_hs + max(enter_hs, 2) + 1; + + /* CLKIN4DDR = 16 * TXBYTECLKHS */ + tlp_avail = thsbyte_clk * (blank - trans_lp); + + ttxclkesc = tdsi_fclk / lp_clk_div; + + lp_inter = ((tlp_avail - 8 * thsbyte_clk - 5 * tdsi_fclk) / ttxclkesc - + 26) / 16; + + return max(lp_inter, 0); +} + +static void dsi_config_cmd_mode_interleaving(struct omap_dss_device *dssdev) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + int blanking_mode; + int hfp_blanking_mode, hbp_blanking_mode, hsa_blanking_mode; + int hsa, hfp, hbp, width_bytes, bllp, lp_clk_div; + int ddr_clk_pre, ddr_clk_post, enter_hs_mode_lat, exit_hs_mode_lat; + int tclk_trail, ths_exit, exiths_clk; + bool ddr_alwon; + struct omap_video_timings *timings = &dssdev->panel.timings; + int bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt); + int ndl = dsi->num_lanes_used - 1; + int dsi_fclk_hsdiv = dssdev->clocks.dsi.regm_dsi + 1; + int hsa_interleave_hs = 0, hsa_interleave_lp = 0; + int hfp_interleave_hs = 0, hfp_interleave_lp = 0; + int hbp_interleave_hs = 0, hbp_interleave_lp = 0; + int bl_interleave_hs = 0, bl_interleave_lp = 0; + u32 r; + + r = dsi_read_reg(dsidev, DSI_CTRL); + blanking_mode = FLD_GET(r, 20, 20); + hfp_blanking_mode = FLD_GET(r, 21, 21); + hbp_blanking_mode = FLD_GET(r, 22, 22); + hsa_blanking_mode = FLD_GET(r, 23, 23); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING1); + hbp = FLD_GET(r, 11, 0); + hfp = FLD_GET(r, 23, 12); + hsa = FLD_GET(r, 31, 24); + + r = dsi_read_reg(dsidev, DSI_CLK_TIMING); + ddr_clk_post = FLD_GET(r, 7, 0); + ddr_clk_pre = FLD_GET(r, 15, 8); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING7); + exit_hs_mode_lat = FLD_GET(r, 15, 0); + enter_hs_mode_lat = FLD_GET(r, 31, 16); + + r = dsi_read_reg(dsidev, DSI_CLK_CTRL); + lp_clk_div = FLD_GET(r, 12, 0); + ddr_alwon = FLD_GET(r, 13, 13); + + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0); + ths_exit = FLD_GET(r, 7, 0); + + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1); + tclk_trail = FLD_GET(r, 15, 8); + + exiths_clk = ths_exit + tclk_trail; + + width_bytes = DIV_ROUND_UP(timings->x_res * bpp, 8); + bllp = hbp + hfp + hsa + DIV_ROUND_UP(width_bytes + 6, ndl); + + if (!hsa_blanking_mode) { + hsa_interleave_hs = dsi_compute_interleave_hs(hsa, ddr_alwon, + enter_hs_mode_lat, exit_hs_mode_lat, + exiths_clk, ddr_clk_pre, ddr_clk_post); + hsa_interleave_lp = dsi_compute_interleave_lp(hsa, + enter_hs_mode_lat, exit_hs_mode_lat, + lp_clk_div, dsi_fclk_hsdiv); + } + + if (!hfp_blanking_mode) { + hfp_interleave_hs = dsi_compute_interleave_hs(hfp, ddr_alwon, + enter_hs_mode_lat, exit_hs_mode_lat, + exiths_clk, ddr_clk_pre, ddr_clk_post); + hfp_interleave_lp = dsi_compute_interleave_lp(hfp, + enter_hs_mode_lat, exit_hs_mode_lat, + lp_clk_div, dsi_fclk_hsdiv); + } + + if (!hbp_blanking_mode) { + hbp_interleave_hs = dsi_compute_interleave_hs(hbp, ddr_alwon, + enter_hs_mode_lat, exit_hs_mode_lat, + exiths_clk, ddr_clk_pre, ddr_clk_post); + + hbp_interleave_lp = dsi_compute_interleave_lp(hbp, + enter_hs_mode_lat, exit_hs_mode_lat, + lp_clk_div, dsi_fclk_hsdiv); + } + + if (!blanking_mode) { + bl_interleave_hs = dsi_compute_interleave_hs(bllp, ddr_alwon, + enter_hs_mode_lat, exit_hs_mode_lat, + exiths_clk, ddr_clk_pre, ddr_clk_post); + + bl_interleave_lp = dsi_compute_interleave_lp(bllp, + enter_hs_mode_lat, exit_hs_mode_lat, + lp_clk_div, dsi_fclk_hsdiv); + } + + DSSDBG("DSI HS interleaving(TXBYTECLKHS) HSA %d, HFP %d, HBP %d, BLLP %d\n", + hsa_interleave_hs, hfp_interleave_hs, hbp_interleave_hs, + bl_interleave_hs); + + DSSDBG("DSI LP interleaving(bytes) HSA %d, HFP %d, HBP %d, BLLP %d\n", + hsa_interleave_lp, hfp_interleave_lp, hbp_interleave_lp, + bl_interleave_lp); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING4); + r = FLD_MOD(r, hsa_interleave_hs, 23, 16); + r = FLD_MOD(r, hfp_interleave_hs, 15, 8); + r = FLD_MOD(r, hbp_interleave_hs, 7, 0); + dsi_write_reg(dsidev, DSI_VM_TIMING4, r); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING5); + r = FLD_MOD(r, hsa_interleave_lp, 23, 16); + r = FLD_MOD(r, hfp_interleave_lp, 15, 8); + r = FLD_MOD(r, hbp_interleave_lp, 7, 0); + dsi_write_reg(dsidev, DSI_VM_TIMING5, r); + + r = dsi_read_reg(dsidev, DSI_VM_TIMING6); + r = FLD_MOD(r, bl_interleave_hs, 31, 15); + r = FLD_MOD(r, bl_interleave_lp, 16, 0); + dsi_write_reg(dsidev, DSI_VM_TIMING6, r); +} + static int dsi_proto_config(struct omap_dss_device *dssdev) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); @@ -3769,6 +3884,7 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) break; default: BUG(); + return -EINVAL; } r = dsi_read_reg(dsidev, DSI_CTRL); @@ -3793,6 +3909,7 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_VIDEO_MODE) { dsi_config_vp_sync_events(dssdev); dsi_config_blanking_modes(dssdev); + dsi_config_cmd_mode_interleaving(dssdev); } dsi_vc_initial_config(dsidev, 0); @@ -4008,6 +4125,7 @@ int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel) break; default: BUG(); + return -EINVAL; }; dsi_if_enable(dsidev, false); @@ -4192,10 +4310,6 @@ static void dsi_framedone_irq_callback(void *data, u32 mask) __cancel_delayed_work(&dsi->framedone_timeout_work); dsi_handle_framedone(dsidev, 0); - -#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC - dispc_fake_vsync_irq(); -#endif } int omap_dsi_update(struct omap_dss_device *dssdev, int channel, @@ -4259,13 +4373,12 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev) dispc_mgr_enable_stallmode(dssdev->manager->id, true); dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 1); - dispc_mgr_set_lcd_timings(dssdev->manager->id, &timings); + dss_mgr_set_timings(dssdev->manager, &timings); } else { dispc_mgr_enable_stallmode(dssdev->manager->id, false); dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 0); - dispc_mgr_set_lcd_timings(dssdev->manager->id, - &dssdev->panel.timings); + dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); } dispc_mgr_set_lcd_display_type(dssdev->manager->id, @@ -4294,13 +4407,11 @@ static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev) struct dsi_clock_info cinfo; int r; - /* we always use DSS_CLK_SYSCK as input clock */ - cinfo.use_sys_clk = true; cinfo.regn = dssdev->clocks.dsi.regn; cinfo.regm = dssdev->clocks.dsi.regm; cinfo.regm_dispc = dssdev->clocks.dsi.regm_dispc; cinfo.regm_dsi = dssdev->clocks.dsi.regm_dsi; - r = dsi_calc_clock_rates(dssdev, &cinfo); + r = dsi_calc_clock_rates(dsidev, &cinfo); if (r) { DSSERR("Failed to calc dsi clocks\n"); return r; @@ -4345,7 +4456,7 @@ static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev) static int dsi_display_init_dsi(struct omap_dss_device *dssdev) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - int dsi_module = dsi_get_dsidev_id(dsidev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int r; r = dsi_pll_init(dsidev, true, true); @@ -4357,7 +4468,7 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev) goto err1; dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src); - dss_select_dsi_clk_source(dsi_module, dssdev->clocks.dsi.dsi_fclk_src); + dss_select_dsi_clk_source(dsi->module_id, dssdev->clocks.dsi.dsi_fclk_src); dss_select_lcd_clk_source(dssdev->manager->id, dssdev->clocks.dispc.channel.lcd_clk_src); @@ -4396,7 +4507,7 @@ err3: dsi_cio_uninit(dssdev); err2: dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK); - dss_select_dsi_clk_source(dsi_module, OMAP_DSS_CLK_SRC_FCK); + dss_select_dsi_clk_source(dsi->module_id, OMAP_DSS_CLK_SRC_FCK); dss_select_lcd_clk_source(dssdev->manager->id, OMAP_DSS_CLK_SRC_FCK); err1: @@ -4410,7 +4521,6 @@ static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev, { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - int dsi_module = dsi_get_dsidev_id(dsidev); if (enter_ulps && !dsi->ulps_enabled) dsi_enter_ulps(dsidev); @@ -4423,7 +4533,7 @@ static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev, dsi_vc_enable(dsidev, 3, 0); dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK); - dss_select_dsi_clk_source(dsi_module, OMAP_DSS_CLK_SRC_FCK); + dss_select_dsi_clk_source(dsi->module_id, OMAP_DSS_CLK_SRC_FCK); dss_select_lcd_clk_source(dssdev->manager->id, OMAP_DSS_CLK_SRC_FCK); dsi_cio_uninit(dssdev); dsi_pll_uninit(dsidev, disconnect_lanes); @@ -4527,7 +4637,7 @@ int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable) } EXPORT_SYMBOL(omapdss_dsi_enable_te); -int dsi_init_display(struct omap_dss_device *dssdev) +static int __init dsi_init_display(struct omap_dss_device *dssdev) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); @@ -4680,13 +4790,39 @@ static void dsi_put_clocks(struct platform_device *dsidev) clk_put(dsi->sys_clk); } +static void __init dsi_probe_pdata(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct omap_dss_board_info *pdata = dsidev->dev.platform_data; + int i, r; + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) + continue; + + if (dssdev->phy.dsi.module != dsi->module_id) + continue; + + r = dsi_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } + + r = omap_dss_register_device(dssdev, &dsidev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } +} + /* DSI1 HW IP initialisation */ -static int omap_dsihw_probe(struct platform_device *dsidev) +static int __init omap_dsihw_probe(struct platform_device *dsidev) { - struct omap_display_platform_data *dss_plat_data; - struct omap_dss_board_info *board_info; u32 rev; - int r, i, dsi_module = dsi_get_dsidev_id(dsidev); + int r, i; struct resource *dsi_mem; struct dsi_data *dsi; @@ -4694,15 +4830,11 @@ static int omap_dsihw_probe(struct platform_device *dsidev) if (!dsi) return -ENOMEM; + dsi->module_id = dsidev->id; dsi->pdev = dsidev; - dsi_pdev_map[dsi_module] = dsidev; + dsi_pdev_map[dsi->module_id] = dsidev; dev_set_drvdata(&dsidev->dev, dsi); - dss_plat_data = dsidev->dev.platform_data; - board_info = dss_plat_data->board_data; - dsi->enable_pads = board_info->dsi_enable_pads; - dsi->disable_pads = board_info->dsi_disable_pads; - spin_lock_init(&dsi->irq_lock); spin_lock_init(&dsi->errors_lock); dsi->errors = 0; @@ -4780,8 +4912,21 @@ static int omap_dsihw_probe(struct platform_device *dsidev) else dsi->num_lanes_supported = 3; + dsi_probe_pdata(dsidev); + dsi_runtime_put(dsidev); + if (dsi->module_id == 0) + dss_debugfs_create_file("dsi1_regs", dsi1_dump_regs); + else if (dsi->module_id == 1) + dss_debugfs_create_file("dsi2_regs", dsi2_dump_regs); + +#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS + if (dsi->module_id == 0) + dss_debugfs_create_file("dsi1_irqs", dsi1_dump_irqs); + else if (dsi->module_id == 1) + dss_debugfs_create_file("dsi2_irqs", dsi2_dump_irqs); +#endif return 0; err_runtime_get: @@ -4790,12 +4935,14 @@ err_runtime_get: return r; } -static int omap_dsihw_remove(struct platform_device *dsidev) +static int __exit omap_dsihw_remove(struct platform_device *dsidev) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); WARN_ON(dsi->scp_clk_refcount > 0); + omap_dss_unregister_child_devices(&dsidev->dev); + pm_runtime_disable(&dsidev->dev); dsi_put_clocks(dsidev); @@ -4816,7 +4963,6 @@ static int omap_dsihw_remove(struct platform_device *dsidev) static int dsi_runtime_suspend(struct device *dev) { dispc_runtime_put(); - dss_runtime_put(); return 0; } @@ -4825,20 +4971,11 @@ static int dsi_runtime_resume(struct device *dev) { int r; - r = dss_runtime_get(); - if (r) - goto err_get_dss; - r = dispc_runtime_get(); if (r) - goto err_get_dispc; + return r; return 0; - -err_get_dispc: - dss_runtime_put(); -err_get_dss: - return r; } static const struct dev_pm_ops dsi_pm_ops = { @@ -4847,8 +4984,7 @@ static const struct dev_pm_ops dsi_pm_ops = { }; static struct platform_driver omap_dsihw_driver = { - .probe = omap_dsihw_probe, - .remove = omap_dsihw_remove, + .remove = __exit_p(omap_dsihw_remove), .driver = { .name = "omapdss_dsi", .owner = THIS_MODULE, @@ -4856,12 +4992,12 @@ static struct platform_driver omap_dsihw_driver = { }, }; -int dsi_init_platform_driver(void) +int __init dsi_init_platform_driver(void) { - return platform_driver_register(&omap_dsihw_driver); + return platform_driver_probe(&omap_dsihw_driver, omap_dsihw_probe); } -void dsi_uninit_platform_driver(void) +void __exit dsi_uninit_platform_driver(void) { - return platform_driver_unregister(&omap_dsihw_driver); + platform_driver_unregister(&omap_dsihw_driver); } diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index bd2d5e159463..6ea1ff149f6f 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -62,6 +62,9 @@ struct dss_reg { #define REG_FLD_MOD(idx, val, start, end) \ dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end)) +static int dss_runtime_get(void); +static void dss_runtime_put(void); + static struct { struct platform_device *pdev; void __iomem *base; @@ -277,7 +280,7 @@ void dss_dump_clocks(struct seq_file *s) dss_runtime_put(); } -void dss_dump_regs(struct seq_file *s) +static void dss_dump_regs(struct seq_file *s) { #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r)) @@ -322,6 +325,7 @@ void dss_select_dispc_clk_source(enum omap_dss_clk_source clk_src) break; default: BUG(); + return; } dss_feat_get_reg_field(FEAT_REG_DISPC_CLK_SWITCH, &start, &end); @@ -335,7 +339,7 @@ void dss_select_dsi_clk_source(int dsi_module, enum omap_dss_clk_source clk_src) { struct platform_device *dsidev; - int b; + int b, pos; switch (clk_src) { case OMAP_DSS_CLK_SRC_FCK: @@ -355,9 +359,11 @@ void dss_select_dsi_clk_source(int dsi_module, break; default: BUG(); + return; } - REG_FLD_MOD(DSS_CONTROL, b, 1, 1); /* DSI_CLK_SWITCH */ + pos = dsi_module == 0 ? 1 : 10; + REG_FLD_MOD(DSS_CONTROL, b, pos, pos); /* DSIx_CLK_SWITCH */ dss.dsi_clk_source[dsi_module] = clk_src; } @@ -389,6 +395,7 @@ void dss_select_lcd_clk_source(enum omap_channel channel, break; default: BUG(); + return; } pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 12; @@ -706,7 +713,7 @@ static void dss_put_clocks(void) clk_put(dss.dss_clk); } -int dss_runtime_get(void) +static int dss_runtime_get(void) { int r; @@ -717,7 +724,7 @@ int dss_runtime_get(void) return r < 0 ? r : 0; } -void dss_runtime_put(void) +static void dss_runtime_put(void) { int r; @@ -740,7 +747,7 @@ void dss_debug_dump_clocks(struct seq_file *s) #endif /* DSS HW IP initialisation */ -static int omap_dsshw_probe(struct platform_device *pdev) +static int __init omap_dsshw_probe(struct platform_device *pdev) { struct resource *dss_mem; u32 rev; @@ -785,40 +792,24 @@ static int omap_dsshw_probe(struct platform_device *pdev) dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK; - r = dpi_init(); - if (r) { - DSSERR("Failed to initialize DPI\n"); - goto err_dpi; - } - - r = sdi_init(); - if (r) { - DSSERR("Failed to initialize SDI\n"); - goto err_sdi; - } - rev = dss_read_reg(DSS_REVISION); printk(KERN_INFO "OMAP DSS rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); dss_runtime_put(); + dss_debugfs_create_file("dss", dss_dump_regs); + return 0; -err_sdi: - dpi_exit(); -err_dpi: - dss_runtime_put(); + err_runtime_get: pm_runtime_disable(&pdev->dev); dss_put_clocks(); return r; } -static int omap_dsshw_remove(struct platform_device *pdev) +static int __exit omap_dsshw_remove(struct platform_device *pdev) { - dpi_exit(); - sdi_exit(); - pm_runtime_disable(&pdev->dev); dss_put_clocks(); @@ -829,11 +820,24 @@ static int omap_dsshw_remove(struct platform_device *pdev) static int dss_runtime_suspend(struct device *dev) { dss_save_context(); + dss_set_min_bus_tput(dev, 0); return 0; } static int dss_runtime_resume(struct device *dev) { + int r; + /* + * Set an arbitrarily high tput request to ensure OPP100. + * What we should really do is to make a request to stay in OPP100, + * without any tput requirements, but that is not currently possible + * via the PM layer. + */ + + r = dss_set_min_bus_tput(dev, 1000000000); + if (r) + return r; + dss_restore_context(); return 0; } @@ -844,8 +848,7 @@ static const struct dev_pm_ops dss_pm_ops = { }; static struct platform_driver omap_dsshw_driver = { - .probe = omap_dsshw_probe, - .remove = omap_dsshw_remove, + .remove = __exit_p(omap_dsshw_remove), .driver = { .name = "omapdss_dss", .owner = THIS_MODULE, @@ -853,12 +856,12 @@ static struct platform_driver omap_dsshw_driver = { }, }; -int dss_init_platform_driver(void) +int __init dss_init_platform_driver(void) { - return platform_driver_register(&omap_dsshw_driver); + return platform_driver_probe(&omap_dsshw_driver, omap_dsshw_probe); } void dss_uninit_platform_driver(void) { - return platform_driver_unregister(&omap_dsshw_driver); + platform_driver_unregister(&omap_dsshw_driver); } diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index d4b3dff2ead3..dd1092ceaeef 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -150,9 +150,6 @@ struct dsi_clock_info { u16 regm_dsi; /* OMAP3: REGM4 * OMAP4: REGM5 */ u16 lp_clk_div; - - u8 highfreq; - bool use_sys_clk; }; struct seq_file; @@ -162,6 +159,16 @@ struct platform_device; struct bus_type *dss_get_bus(void); struct regulator *dss_get_vdds_dsi(void); struct regulator *dss_get_vdds_sdi(void); +int dss_get_ctx_loss_count(struct device *dev); +int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask); +void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask); +int dss_set_min_bus_tput(struct device *dev, unsigned long tput); +int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)); + +int omap_dss_register_device(struct omap_dss_device *dssdev, + struct device *parent, int disp_num); +void omap_dss_unregister_device(struct omap_dss_device *dssdev); +void omap_dss_unregister_child_devices(struct device *parent); /* apply */ void dss_apply_init(void); @@ -179,6 +186,9 @@ void dss_mgr_get_info(struct omap_overlay_manager *mgr, int dss_mgr_set_device(struct omap_overlay_manager *mgr, struct omap_dss_device *dssdev); int dss_mgr_unset_device(struct omap_overlay_manager *mgr); +void dss_mgr_set_timings(struct omap_overlay_manager *mgr, + struct omap_video_timings *timings); +const struct omap_video_timings *dss_mgr_get_timings(struct omap_overlay_manager *mgr); bool dss_ovl_is_enabled(struct omap_overlay *ovl); int dss_ovl_enable(struct omap_overlay *ovl); @@ -208,9 +218,11 @@ int dss_init_overlay_managers(struct platform_device *pdev); void dss_uninit_overlay_managers(struct platform_device *pdev); int dss_mgr_simple_check(struct omap_overlay_manager *mgr, const struct omap_overlay_manager_info *info); +int dss_mgr_check_timings(struct omap_overlay_manager *mgr, + const struct omap_video_timings *timings); int dss_mgr_check(struct omap_overlay_manager *mgr, - struct omap_dss_device *dssdev, struct omap_overlay_manager_info *info, + const struct omap_video_timings *mgr_timings, struct omap_overlay_info **overlay_infos); /* overlay */ @@ -220,22 +232,18 @@ void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr); void dss_recheck_connections(struct omap_dss_device *dssdev, bool force); int dss_ovl_simple_check(struct omap_overlay *ovl, const struct omap_overlay_info *info); -int dss_ovl_check(struct omap_overlay *ovl, - struct omap_overlay_info *info, struct omap_dss_device *dssdev); +int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info, + const struct omap_video_timings *mgr_timings); /* DSS */ -int dss_init_platform_driver(void); +int dss_init_platform_driver(void) __init; void dss_uninit_platform_driver(void); -int dss_runtime_get(void); -void dss_runtime_put(void); - void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select); enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void); const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src); void dss_dump_clocks(struct seq_file *s); -void dss_dump_regs(struct seq_file *s); #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) void dss_debug_dump_clocks(struct seq_file *s); #endif @@ -265,19 +273,8 @@ int dss_calc_clock_div(bool is_tft, unsigned long req_pck, struct dispc_clock_info *dispc_cinfo); /* SDI */ -#ifdef CONFIG_OMAP2_DSS_SDI -int sdi_init(void); -void sdi_exit(void); -int sdi_init_display(struct omap_dss_device *display); -#else -static inline int sdi_init(void) -{ - return 0; -} -static inline void sdi_exit(void) -{ -} -#endif +int sdi_init_platform_driver(void) __init; +void sdi_uninit_platform_driver(void) __exit; /* DSI */ #ifdef CONFIG_OMAP2_DSS_DSI @@ -285,19 +282,14 @@ static inline void sdi_exit(void) struct dentry; struct file_operations; -int dsi_init_platform_driver(void); -void dsi_uninit_platform_driver(void); +int dsi_init_platform_driver(void) __init; +void dsi_uninit_platform_driver(void) __exit; int dsi_runtime_get(struct platform_device *dsidev); void dsi_runtime_put(struct platform_device *dsidev); void dsi_dump_clocks(struct seq_file *s); -void dsi_create_debugfs_files_irq(struct dentry *debugfs_dir, - const struct file_operations *debug_fops); -void dsi_create_debugfs_files_reg(struct dentry *debugfs_dir, - const struct file_operations *debug_fops); -int dsi_init_display(struct omap_dss_device *display); void dsi_irq_handler(void); u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt); @@ -314,13 +306,6 @@ void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev); void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev); struct platform_device *dsi_get_dsidev_from_id(int module); #else -static inline int dsi_init_platform_driver(void) -{ - return 0; -} -static inline void dsi_uninit_platform_driver(void) -{ -} static inline int dsi_runtime_get(struct platform_device *dsidev) { return 0; @@ -377,28 +362,14 @@ static inline struct platform_device *dsi_get_dsidev_from_id(int module) #endif /* DPI */ -#ifdef CONFIG_OMAP2_DSS_DPI -int dpi_init(void); -void dpi_exit(void); -int dpi_init_display(struct omap_dss_device *dssdev); -#else -static inline int dpi_init(void) -{ - return 0; -} -static inline void dpi_exit(void) -{ -} -#endif +int dpi_init_platform_driver(void) __init; +void dpi_uninit_platform_driver(void) __exit; /* DISPC */ -int dispc_init_platform_driver(void); -void dispc_uninit_platform_driver(void); +int dispc_init_platform_driver(void) __init; +void dispc_uninit_platform_driver(void) __exit; void dispc_dump_clocks(struct seq_file *s); -void dispc_dump_irqs(struct seq_file *s); -void dispc_dump_regs(struct seq_file *s); void dispc_irq_handler(void); -void dispc_fake_vsync_irq(void); int dispc_runtime_get(void); void dispc_runtime_put(void); @@ -409,12 +380,12 @@ void dispc_disable_sidle(void); void dispc_lcd_enable_signal_polarity(bool act_high); void dispc_lcd_enable_signal(bool enable); void dispc_pck_free_enable(bool enable); -void dispc_set_digit_size(u16 width, u16 height); void dispc_enable_fifomerge(bool enable); void dispc_enable_gamma_table(bool enable); void dispc_set_loadmode(enum omap_dss_load_mode mode); -bool dispc_lcd_timings_ok(struct omap_video_timings *timings); +bool dispc_mgr_timings_ok(enum omap_channel channel, + const struct omap_video_timings *timings); unsigned long dispc_fclk_rate(void); void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck, struct dispc_clock_info *cinfo); @@ -424,15 +395,16 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate, void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high); void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane, - u32 *fifo_low, u32 *fifo_high, bool use_fifomerge); + u32 *fifo_low, u32 *fifo_high, bool use_fifomerge, + bool manual_update); int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi, - bool ilace, bool replication); + bool ilace, bool replication, + const struct omap_video_timings *mgr_timings); int dispc_ovl_enable(enum omap_plane plane, bool enable); void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel); void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable); -void dispc_mgr_set_lcd_size(enum omap_channel channel, u16 width, u16 height); u32 dispc_mgr_get_vsync_irq(enum omap_channel channel); u32 dispc_mgr_get_framedone_irq(enum omap_channel channel); bool dispc_mgr_go_busy(enum omap_channel channel); @@ -445,12 +417,13 @@ void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable); void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines); void dispc_mgr_set_lcd_display_type(enum omap_channel channel, enum omap_lcd_display_type type); -void dispc_mgr_set_lcd_timings(enum omap_channel channel, +void dispc_mgr_set_timings(enum omap_channel channel, struct omap_video_timings *timings); void dispc_mgr_set_pol_freq(enum omap_channel channel, enum omap_panel_config config, u8 acbi, u8 acb); unsigned long dispc_mgr_lclk_rate(enum omap_channel channel); unsigned long dispc_mgr_pclk_rate(enum omap_channel channel); +unsigned long dispc_core_clk_rate(void); int dispc_mgr_set_clock_div(enum omap_channel channel, struct dispc_clock_info *cinfo); int dispc_mgr_get_clock_div(enum omap_channel channel, @@ -460,19 +433,10 @@ void dispc_mgr_setup(enum omap_channel channel, /* VENC */ #ifdef CONFIG_OMAP2_DSS_VENC -int venc_init_platform_driver(void); -void venc_uninit_platform_driver(void); -void venc_dump_regs(struct seq_file *s); -int venc_init_display(struct omap_dss_device *display); +int venc_init_platform_driver(void) __init; +void venc_uninit_platform_driver(void) __exit; unsigned long venc_get_pixel_clock(void); #else -static inline int venc_init_platform_driver(void) -{ - return 0; -} -static inline void venc_uninit_platform_driver(void) -{ -} static inline unsigned long venc_get_pixel_clock(void) { WARN("%s: VENC not compiled in, returning pclk as 0\n", __func__); @@ -482,23 +446,10 @@ static inline unsigned long venc_get_pixel_clock(void) /* HDMI */ #ifdef CONFIG_OMAP4_DSS_HDMI -int hdmi_init_platform_driver(void); -void hdmi_uninit_platform_driver(void); -int hdmi_init_display(struct omap_dss_device *dssdev); +int hdmi_init_platform_driver(void) __init; +void hdmi_uninit_platform_driver(void) __exit; unsigned long hdmi_get_pixel_clock(void); -void hdmi_dump_regs(struct seq_file *s); #else -static inline int hdmi_init_display(struct omap_dss_device *dssdev) -{ - return 0; -} -static inline int hdmi_init_platform_driver(void) -{ - return 0; -} -static inline void hdmi_uninit_platform_driver(void) -{ -} static inline unsigned long hdmi_get_pixel_clock(void) { WARN("%s: HDMI not compiled in, returning pclk as 0\n", __func__); @@ -514,22 +465,18 @@ int omapdss_hdmi_read_edid(u8 *buf, int len); bool omapdss_hdmi_detect(void); int hdmi_panel_init(void); void hdmi_panel_exit(void); +#ifdef CONFIG_OMAP4_DSS_HDMI_AUDIO +int hdmi_audio_enable(void); +void hdmi_audio_disable(void); +int hdmi_audio_start(void); +void hdmi_audio_stop(void); +bool hdmi_mode_has_audio(void); +int hdmi_audio_config(struct omap_dss_audio *audio); +#endif /* RFBI */ -#ifdef CONFIG_OMAP2_DSS_RFBI -int rfbi_init_platform_driver(void); -void rfbi_uninit_platform_driver(void); -void rfbi_dump_regs(struct seq_file *s); -int rfbi_init_display(struct omap_dss_device *display); -#else -static inline int rfbi_init_platform_driver(void) -{ - return 0; -} -static inline void rfbi_uninit_platform_driver(void) -{ -} -#endif +int rfbi_init_platform_driver(void) __init; +void rfbi_uninit_platform_driver(void) __exit; #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index ce14aa6dd672..938709724f0c 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -52,6 +52,8 @@ struct omap_dss_features { const char * const *clksrc_names; const struct dss_param_range *dss_params; + const enum omap_dss_rotation_type supported_rotation_types; + const u32 buffer_size_unit; const u32 burst_size_unit; }; @@ -311,6 +313,8 @@ static const struct dss_param_range omap2_dss_param_range[] = { * scaler cannot scale a image with width more than 768. */ [FEAT_PARAM_LINEWIDTH] = { 1, 768 }, + [FEAT_PARAM_MGR_WIDTH] = { 1, 2048 }, + [FEAT_PARAM_MGR_HEIGHT] = { 1, 2048 }, }; static const struct dss_param_range omap3_dss_param_range[] = { @@ -324,6 +328,8 @@ static const struct dss_param_range omap3_dss_param_range[] = { [FEAT_PARAM_DSIPLL_LPDIV] = { 1, (1 << 13) - 1}, [FEAT_PARAM_DOWNSCALE] = { 1, 4 }, [FEAT_PARAM_LINEWIDTH] = { 1, 1024 }, + [FEAT_PARAM_MGR_WIDTH] = { 1, 2048 }, + [FEAT_PARAM_MGR_HEIGHT] = { 1, 2048 }, }; static const struct dss_param_range omap4_dss_param_range[] = { @@ -337,6 +343,8 @@ static const struct dss_param_range omap4_dss_param_range[] = { [FEAT_PARAM_DSIPLL_LPDIV] = { 0, (1 << 13) - 1 }, [FEAT_PARAM_DOWNSCALE] = { 1, 4 }, [FEAT_PARAM_LINEWIDTH] = { 1, 2048 }, + [FEAT_PARAM_MGR_WIDTH] = { 1, 2048 }, + [FEAT_PARAM_MGR_HEIGHT] = { 1, 2048 }, }; static const enum dss_feat_id omap2_dss_feat_list[] = { @@ -399,6 +407,7 @@ static const enum dss_feat_id omap4430_es1_0_dss_feat_list[] = { FEAT_FIR_COEF_V, FEAT_ALPHA_FREE_ZORDER, FEAT_FIFO_MERGE, + FEAT_BURST_2D, }; static const enum dss_feat_id omap4430_es2_0_1_2_dss_feat_list[] = { @@ -416,6 +425,7 @@ static const enum dss_feat_id omap4430_es2_0_1_2_dss_feat_list[] = { FEAT_FIR_COEF_V, FEAT_ALPHA_FREE_ZORDER, FEAT_FIFO_MERGE, + FEAT_BURST_2D, }; static const enum dss_feat_id omap4_dss_feat_list[] = { @@ -434,6 +444,7 @@ static const enum dss_feat_id omap4_dss_feat_list[] = { FEAT_FIR_COEF_V, FEAT_ALPHA_FREE_ZORDER, FEAT_FIFO_MERGE, + FEAT_BURST_2D, }; /* OMAP2 DSS Features */ @@ -451,6 +462,7 @@ static const struct omap_dss_features omap2_dss_features = { .overlay_caps = omap2_dss_overlay_caps, .clksrc_names = omap2_dss_clk_source_names, .dss_params = omap2_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB, .buffer_size_unit = 1, .burst_size_unit = 8, }; @@ -470,6 +482,7 @@ static const struct omap_dss_features omap3430_dss_features = { .overlay_caps = omap3430_dss_overlay_caps, .clksrc_names = omap3_dss_clk_source_names, .dss_params = omap3_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB, .buffer_size_unit = 1, .burst_size_unit = 8, }; @@ -488,6 +501,7 @@ static const struct omap_dss_features omap3630_dss_features = { .overlay_caps = omap3630_dss_overlay_caps, .clksrc_names = omap3_dss_clk_source_names, .dss_params = omap3_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB, .buffer_size_unit = 1, .burst_size_unit = 8, }; @@ -508,6 +522,7 @@ static const struct omap_dss_features omap4430_es1_0_dss_features = { .overlay_caps = omap4_dss_overlay_caps, .clksrc_names = omap4_dss_clk_source_names, .dss_params = omap4_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER, .buffer_size_unit = 16, .burst_size_unit = 16, }; @@ -527,6 +542,7 @@ static const struct omap_dss_features omap4430_es2_0_1_2_dss_features = { .overlay_caps = omap4_dss_overlay_caps, .clksrc_names = omap4_dss_clk_source_names, .dss_params = omap4_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER, .buffer_size_unit = 16, .burst_size_unit = 16, }; @@ -546,6 +562,7 @@ static const struct omap_dss_features omap4_dss_features = { .overlay_caps = omap4_dss_overlay_caps, .clksrc_names = omap4_dss_clk_source_names, .dss_params = omap4_dss_param_range, + .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER, .buffer_size_unit = 16, .burst_size_unit = 16, }; @@ -562,13 +579,17 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = { .pll_enable = ti_hdmi_4xxx_pll_enable, .pll_disable = ti_hdmi_4xxx_pll_disable, .video_enable = ti_hdmi_4xxx_wp_video_start, + .video_disable = ti_hdmi_4xxx_wp_video_stop, .dump_wrapper = ti_hdmi_4xxx_wp_dump, .dump_core = ti_hdmi_4xxx_core_dump, .dump_pll = ti_hdmi_4xxx_pll_dump, .dump_phy = ti_hdmi_4xxx_phy_dump, -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) .audio_enable = ti_hdmi_4xxx_wp_audio_enable, + .audio_disable = ti_hdmi_4xxx_wp_audio_disable, + .audio_start = ti_hdmi_4xxx_audio_start, + .audio_stop = ti_hdmi_4xxx_audio_stop, + .audio_config = ti_hdmi_4xxx_audio_config, #endif }; @@ -662,6 +683,11 @@ void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end) *end = omap_current_dss_features->reg_fields[id].end; } +bool dss_feat_rotation_type_supported(enum omap_dss_rotation_type rot_type) +{ + return omap_current_dss_features->supported_rotation_types & rot_type; +} + void dss_features_init(void) { if (cpu_is_omap24xx()) diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h index c332e7ddfce1..bdf469f080e7 100644 --- a/drivers/video/omap2/dss/dss_features.h +++ b/drivers/video/omap2/dss/dss_features.h @@ -62,6 +62,7 @@ enum dss_feat_id { FEAT_FIFO_MERGE, /* An unknown HW bug causing the normal FIFO thresholds not to work */ FEAT_OMAP3_DSI_FIFO_BUG, + FEAT_BURST_2D, }; /* DSS register field id */ @@ -91,6 +92,8 @@ enum dss_range_param { FEAT_PARAM_DSIPLL_LPDIV, FEAT_PARAM_DOWNSCALE, FEAT_PARAM_LINEWIDTH, + FEAT_PARAM_MGR_WIDTH, + FEAT_PARAM_MGR_HEIGHT, }; /* DSS Feature Functions */ @@ -108,6 +111,8 @@ const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id); u32 dss_feat_get_buffer_size_unit(void); /* in bytes */ u32 dss_feat_get_burst_size_unit(void); /* in bytes */ +bool dss_feat_rotation_type_supported(enum omap_dss_rotation_type rot_type); + bool dss_has_feature(enum dss_feat_id id); void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end); void dss_features_init(void); diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index c4b4f6950a92..8195c7166d20 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -33,12 +33,6 @@ #include <linux/pm_runtime.h> #include <linux/clk.h> #include <video/omapdss.h> -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -#include <sound/soc.h> -#include <sound/pcm_params.h> -#include "ti_hdmi_4xxx_ip.h" -#endif #include "ti_hdmi.h" #include "dss.h" @@ -63,7 +57,6 @@ static struct { struct mutex lock; - struct omap_display_platform_data *pdata; struct platform_device *pdev; struct hdmi_ip_data ip_data; @@ -130,25 +123,12 @@ static int hdmi_runtime_get(void) DSSDBG("hdmi_runtime_get\n"); - /* - * HACK: Add dss_runtime_get() to ensure DSS clock domain is enabled. - * This should be removed later. - */ - r = dss_runtime_get(); - if (r < 0) - goto err_get_dss; - r = pm_runtime_get_sync(&hdmi.pdev->dev); WARN_ON(r < 0); if (r < 0) - goto err_get_hdmi; + return r; return 0; - -err_get_hdmi: - dss_runtime_put(); -err_get_dss: - return r; } static void hdmi_runtime_put(void) @@ -159,15 +139,9 @@ static void hdmi_runtime_put(void) r = pm_runtime_put_sync(&hdmi.pdev->dev); WARN_ON(r < 0); - - /* - * HACK: This is added to complement the dss_runtime_get() call in - * hdmi_runtime_get(). This should be removed later. - */ - dss_runtime_put(); } -int hdmi_init_display(struct omap_dss_device *dssdev) +static int __init hdmi_init_display(struct omap_dss_device *dssdev) { DSSDBG("init_display\n"); @@ -344,7 +318,7 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data); - hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0); + hdmi.ip_data.ops->video_disable(&hdmi.ip_data); /* config the PLL and PHY hdmi_set_pll_pwrfirst */ r = hdmi.ip_data.ops->pll_enable(&hdmi.ip_data); @@ -376,10 +350,11 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) dispc_enable_gamma_table(0); /* tv size */ - dispc_set_digit_size(dssdev->panel.timings.x_res, - dssdev->panel.timings.y_res); + dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); - hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 1); + r = hdmi.ip_data.ops->video_enable(&hdmi.ip_data); + if (r) + goto err_vid_enable; r = dss_mgr_enable(dssdev->manager); if (r) @@ -388,7 +363,8 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) return 0; err_mgr_enable: - hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0); + hdmi.ip_data.ops->video_disable(&hdmi.ip_data); +err_vid_enable: hdmi.ip_data.ops->phy_disable(&hdmi.ip_data); hdmi.ip_data.ops->pll_disable(&hdmi.ip_data); err: @@ -400,7 +376,7 @@ static void hdmi_power_off(struct omap_dss_device *dssdev) { dss_mgr_disable(dssdev->manager); - hdmi.ip_data.ops->video_enable(&hdmi.ip_data, 0); + hdmi.ip_data.ops->video_disable(&hdmi.ip_data); hdmi.ip_data.ops->phy_disable(&hdmi.ip_data); hdmi.ip_data.ops->pll_disable(&hdmi.ip_data); hdmi_runtime_put(); @@ -436,10 +412,12 @@ void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev) r = hdmi_power_on(dssdev); if (r) DSSERR("failed to power on device\n"); + } else { + dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); } } -void hdmi_dump_regs(struct seq_file *s) +static void hdmi_dump_regs(struct seq_file *s) { mutex_lock(&hdmi.lock); @@ -555,248 +533,201 @@ void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev) mutex_unlock(&hdmi.lock); } -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) - -static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) +static int hdmi_get_clocks(struct platform_device *pdev) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - struct platform_device *pdev = to_platform_device(codec->dev); - struct hdmi_ip_data *ip_data = snd_soc_codec_get_drvdata(codec); - int err = 0; + struct clk *clk; - if (!(ip_data->ops) && !(ip_data->ops->audio_enable)) { - dev_err(&pdev->dev, "Cannot enable/disable audio\n"); - return -ENODEV; + clk = clk_get(&pdev->dev, "sys_clk"); + if (IS_ERR(clk)) { + DSSERR("can't get sys_clk\n"); + return PTR_ERR(clk); } - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ip_data->ops->audio_enable(ip_data, true); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ip_data->ops->audio_enable(ip_data, false); - break; - default: - err = -EINVAL; - } - return err; -} - -static int hdmi_audio_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - struct hdmi_ip_data *ip_data = snd_soc_codec_get_drvdata(codec); - struct hdmi_audio_format audio_format; - struct hdmi_audio_dma audio_dma; - struct hdmi_core_audio_config core_cfg; - struct hdmi_core_infoframe_audio aud_if_cfg; - int err, n, cts; - enum hdmi_core_audio_sample_freq sample_freq; - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - core_cfg.i2s_cfg.word_max_length = - HDMI_AUDIO_I2S_MAX_WORD_20BITS; - core_cfg.i2s_cfg.word_length = HDMI_AUDIO_I2S_CHST_WORD_16_BITS; - core_cfg.i2s_cfg.in_length_bits = - HDMI_AUDIO_I2S_INPUT_LENGTH_16; - core_cfg.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_LEFT; - audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES; - audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS; - audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT; - audio_dma.transfer_size = 0x10; - break; - case SNDRV_PCM_FORMAT_S24_LE: - core_cfg.i2s_cfg.word_max_length = - HDMI_AUDIO_I2S_MAX_WORD_24BITS; - core_cfg.i2s_cfg.word_length = HDMI_AUDIO_I2S_CHST_WORD_24_BITS; - core_cfg.i2s_cfg.in_length_bits = - HDMI_AUDIO_I2S_INPUT_LENGTH_24; - audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_ONESAMPLE; - audio_format.sample_size = HDMI_AUDIO_SAMPLE_24BITS; - audio_format.justification = HDMI_AUDIO_JUSTIFY_RIGHT; - core_cfg.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT; - audio_dma.transfer_size = 0x20; - break; - default: + hdmi.sys_clk = clk; + + return 0; +} + +static void hdmi_put_clocks(void) +{ + if (hdmi.sys_clk) + clk_put(hdmi.sys_clk); +} + +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts) +{ + u32 deep_color; + bool deep_color_correct = false; + u32 pclk = hdmi.ip_data.cfg.timings.pixel_clock; + + if (n == NULL || cts == NULL) return -EINVAL; - } - switch (params_rate(params)) { + /* TODO: When implemented, query deep color mode here. */ + deep_color = 100; + + /* + * When using deep color, the default N value (as in the HDMI + * specification) yields to an non-integer CTS. Hence, we + * modify it while keeping the restrictions described in + * section 7.2.1 of the HDMI 1.4a specification. + */ + switch (sample_freq) { case 32000: - sample_freq = HDMI_AUDIO_FS_32000; + case 48000: + case 96000: + case 192000: + if (deep_color == 125) + if (pclk == 27027 || pclk == 74250) + deep_color_correct = true; + if (deep_color == 150) + if (pclk == 27027) + deep_color_correct = true; break; case 44100: - sample_freq = HDMI_AUDIO_FS_44100; - break; - case 48000: - sample_freq = HDMI_AUDIO_FS_48000; + case 88200: + case 176400: + if (deep_color == 125) + if (pclk == 27027) + deep_color_correct = true; break; default: return -EINVAL; } - err = hdmi_config_audio_acr(ip_data, params_rate(params), &n, &cts); - if (err < 0) - return err; - - /* Audio wrapper config */ - audio_format.stereo_channels = HDMI_AUDIO_STEREO_ONECHANNEL; - audio_format.active_chnnls_msk = 0x03; - audio_format.type = HDMI_AUDIO_TYPE_LPCM; - audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST; - /* Disable start/stop signals of IEC 60958 blocks */ - audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_OFF; + if (deep_color_correct) { + switch (sample_freq) { + case 32000: + *n = 8192; + break; + case 44100: + *n = 12544; + break; + case 48000: + *n = 8192; + break; + case 88200: + *n = 25088; + break; + case 96000: + *n = 16384; + break; + case 176400: + *n = 50176; + break; + case 192000: + *n = 32768; + break; + default: + return -EINVAL; + } + } else { + switch (sample_freq) { + case 32000: + *n = 4096; + break; + case 44100: + *n = 6272; + break; + case 48000: + *n = 6144; + break; + case 88200: + *n = 12544; + break; + case 96000: + *n = 12288; + break; + case 176400: + *n = 25088; + break; + case 192000: + *n = 24576; + break; + default: + return -EINVAL; + } + } + /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */ + *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10); - audio_dma.block_size = 0xC0; - audio_dma.mode = HDMI_AUDIO_TRANSF_DMA; - audio_dma.fifo_threshold = 0x20; /* in number of samples */ + return 0; +} - hdmi_wp_audio_config_dma(ip_data, &audio_dma); - hdmi_wp_audio_config_format(ip_data, &audio_format); +int hdmi_audio_enable(void) +{ + DSSDBG("audio_enable\n"); - /* - * I2S config - */ - core_cfg.i2s_cfg.en_high_bitrate_aud = false; - /* Only used with high bitrate audio */ - core_cfg.i2s_cfg.cbit_order = false; - /* Serial data and word select should change on sck rising edge */ - core_cfg.i2s_cfg.sck_edge_mode = HDMI_AUDIO_I2S_SCK_EDGE_RISING; - core_cfg.i2s_cfg.vbit = HDMI_AUDIO_I2S_VBIT_FOR_PCM; - /* Set I2S word select polarity */ - core_cfg.i2s_cfg.ws_polarity = HDMI_AUDIO_I2S_WS_POLARITY_LOW_IS_LEFT; - core_cfg.i2s_cfg.direction = HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST; - /* Set serial data to word select shift. See Phillips spec. */ - core_cfg.i2s_cfg.shift = HDMI_AUDIO_I2S_FIRST_BIT_SHIFT; - /* Enable one of the four available serial data channels */ - core_cfg.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN; - - /* Core audio config */ - core_cfg.freq_sample = sample_freq; - core_cfg.n = n; - core_cfg.cts = cts; - if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) { - core_cfg.aud_par_busclk = 0; - core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_SW; - core_cfg.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK); - } else { - core_cfg.aud_par_busclk = (((128 * 31) - 1) << 8); - core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_HW; - core_cfg.use_mclk = true; - } + return hdmi.ip_data.ops->audio_enable(&hdmi.ip_data); +} - if (core_cfg.use_mclk) - core_cfg.mclk_mode = HDMI_AUDIO_MCLK_128FS; - core_cfg.layout = HDMI_AUDIO_LAYOUT_2CH; - core_cfg.en_spdif = false; - /* Use sample frequency from channel status word */ - core_cfg.fs_override = true; - /* Enable ACR packets */ - core_cfg.en_acr_pkt = true; - /* Disable direct streaming digital audio */ - core_cfg.en_dsd_audio = false; - /* Use parallel audio interface */ - core_cfg.en_parallel_aud_input = true; - - hdmi_core_audio_config(ip_data, &core_cfg); +void hdmi_audio_disable(void) +{ + DSSDBG("audio_disable\n"); - /* - * Configure packet - * info frame audio see doc CEA861-D page 74 - */ - aud_if_cfg.db1_coding_type = HDMI_INFOFRAME_AUDIO_DB1CT_FROM_STREAM; - aud_if_cfg.db1_channel_count = 2; - aud_if_cfg.db2_sample_freq = HDMI_INFOFRAME_AUDIO_DB2SF_FROM_STREAM; - aud_if_cfg.db2_sample_size = HDMI_INFOFRAME_AUDIO_DB2SS_FROM_STREAM; - aud_if_cfg.db4_channel_alloc = 0x00; - aud_if_cfg.db5_downmix_inh = false; - aud_if_cfg.db5_lsv = 0; - - hdmi_core_audio_infoframe_config(ip_data, &aud_if_cfg); - return 0; + hdmi.ip_data.ops->audio_disable(&hdmi.ip_data); } -static int hdmi_audio_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +int hdmi_audio_start(void) { - if (!hdmi.ip_data.cfg.cm.mode) { - pr_err("Current video settings do not support audio.\n"); - return -EIO; - } - return 0; + DSSDBG("audio_start\n"); + + return hdmi.ip_data.ops->audio_start(&hdmi.ip_data); } -static int hdmi_audio_codec_probe(struct snd_soc_codec *codec) +void hdmi_audio_stop(void) { - struct hdmi_ip_data *priv = &hdmi.ip_data; + DSSDBG("audio_stop\n"); - snd_soc_codec_set_drvdata(codec, priv); - return 0; + hdmi.ip_data.ops->audio_stop(&hdmi.ip_data); } -static struct snd_soc_codec_driver hdmi_audio_codec_drv = { - .probe = hdmi_audio_codec_probe, -}; +bool hdmi_mode_has_audio(void) +{ + if (hdmi.ip_data.cfg.cm.mode == HDMI_HDMI) + return true; + else + return false; +} -static struct snd_soc_dai_ops hdmi_audio_codec_ops = { - .hw_params = hdmi_audio_hw_params, - .trigger = hdmi_audio_trigger, - .startup = hdmi_audio_startup, -}; +int hdmi_audio_config(struct omap_dss_audio *audio) +{ + return hdmi.ip_data.ops->audio_config(&hdmi.ip_data, audio); +} -static struct snd_soc_dai_driver hdmi_codec_dai_drv = { - .name = "hdmi-audio-codec", - .playback = { - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - }, - .ops = &hdmi_audio_codec_ops, -}; #endif -static int hdmi_get_clocks(struct platform_device *pdev) +static void __init hdmi_probe_pdata(struct platform_device *pdev) { - struct clk *clk; + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int r, i; - clk = clk_get(&pdev->dev, "sys_clk"); - if (IS_ERR(clk)) { - DSSERR("can't get sys_clk\n"); - return PTR_ERR(clk); - } + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; - hdmi.sys_clk = clk; + if (dssdev->type != OMAP_DISPLAY_TYPE_HDMI) + continue; - return 0; -} + r = hdmi_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } -static void hdmi_put_clocks(void) -{ - if (hdmi.sys_clk) - clk_put(hdmi.sys_clk); + r = omap_dss_register_device(dssdev, &pdev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } } /* HDMI HW IP initialisation */ -static int omapdss_hdmihw_probe(struct platform_device *pdev) +static int __init omapdss_hdmihw_probe(struct platform_device *pdev) { struct resource *hdmi_mem; int r; - hdmi.pdata = pdev->dev.platform_data; hdmi.pdev = pdev; mutex_init(&hdmi.lock); @@ -830,28 +761,18 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) hdmi_panel_init(); -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) + dss_debugfs_create_file("hdmi", hdmi_dump_regs); + + hdmi_probe_pdata(pdev); - /* Register ASoC codec DAI */ - r = snd_soc_register_codec(&pdev->dev, &hdmi_audio_codec_drv, - &hdmi_codec_dai_drv, 1); - if (r) { - DSSERR("can't register ASoC HDMI audio codec\n"); - return r; - } -#endif return 0; } -static int omapdss_hdmihw_remove(struct platform_device *pdev) +static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) { - hdmi_panel_exit(); + omap_dss_unregister_child_devices(&pdev->dev); -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) - snd_soc_unregister_codec(&pdev->dev); -#endif + hdmi_panel_exit(); pm_runtime_disable(&pdev->dev); @@ -867,7 +788,6 @@ static int hdmi_runtime_suspend(struct device *dev) clk_disable(hdmi.sys_clk); dispc_runtime_put(); - dss_runtime_put(); return 0; } @@ -876,23 +796,13 @@ static int hdmi_runtime_resume(struct device *dev) { int r; - r = dss_runtime_get(); - if (r < 0) - goto err_get_dss; - r = dispc_runtime_get(); if (r < 0) - goto err_get_dispc; - + return r; clk_enable(hdmi.sys_clk); return 0; - -err_get_dispc: - dss_runtime_put(); -err_get_dss: - return r; } static const struct dev_pm_ops hdmi_pm_ops = { @@ -901,8 +811,7 @@ static const struct dev_pm_ops hdmi_pm_ops = { }; static struct platform_driver omapdss_hdmihw_driver = { - .probe = omapdss_hdmihw_probe, - .remove = omapdss_hdmihw_remove, + .remove = __exit_p(omapdss_hdmihw_remove), .driver = { .name = "omapdss_hdmi", .owner = THIS_MODULE, @@ -910,12 +819,12 @@ static struct platform_driver omapdss_hdmihw_driver = { }, }; -int hdmi_init_platform_driver(void) +int __init hdmi_init_platform_driver(void) { - return platform_driver_register(&omapdss_hdmihw_driver); + return platform_driver_probe(&omapdss_hdmihw_driver, omapdss_hdmihw_probe); } -void hdmi_uninit_platform_driver(void) +void __exit hdmi_uninit_platform_driver(void) { - return platform_driver_unregister(&omapdss_hdmihw_driver); + platform_driver_unregister(&omapdss_hdmihw_driver); } diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c index 533d5dc634d2..1179e3c4b1c7 100644 --- a/drivers/video/omap2/dss/hdmi_panel.c +++ b/drivers/video/omap2/dss/hdmi_panel.c @@ -30,7 +30,12 @@ #include "dss.h" static struct { - struct mutex hdmi_lock; + /* This protects the panel ops, mainly when accessing the HDMI IP. */ + struct mutex lock; +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) + /* This protects the audio ops, specifically. */ + spinlock_t audio_lock; +#endif } hdmi; @@ -54,12 +59,168 @@ static void hdmi_panel_remove(struct omap_dss_device *dssdev) } +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev) +{ + unsigned long flags; + int r; + + mutex_lock(&hdmi.lock); + spin_lock_irqsave(&hdmi.audio_lock, flags); + + /* enable audio only if the display is active and supports audio */ + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || + !hdmi_mode_has_audio()) { + DSSERR("audio not supported or display is off\n"); + r = -EPERM; + goto err; + } + + r = hdmi_audio_enable(); + + if (!r) + dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED; + +err: + spin_unlock_irqrestore(&hdmi.audio_lock, flags); + mutex_unlock(&hdmi.lock); + return r; +} + +static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev) +{ + unsigned long flags; + + spin_lock_irqsave(&hdmi.audio_lock, flags); + + hdmi_audio_disable(); + + dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED; + + spin_unlock_irqrestore(&hdmi.audio_lock, flags); +} + +static int hdmi_panel_audio_start(struct omap_dss_device *dssdev) +{ + unsigned long flags; + int r; + + spin_lock_irqsave(&hdmi.audio_lock, flags); + /* + * No need to check the panel state. It was checked when trasitioning + * to AUDIO_ENABLED. + */ + if (dssdev->audio_state != OMAP_DSS_AUDIO_ENABLED) { + DSSERR("audio start from invalid state\n"); + r = -EPERM; + goto err; + } + + r = hdmi_audio_start(); + + if (!r) + dssdev->audio_state = OMAP_DSS_AUDIO_PLAYING; + +err: + spin_unlock_irqrestore(&hdmi.audio_lock, flags); + return r; +} + +static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev) +{ + unsigned long flags; + + spin_lock_irqsave(&hdmi.audio_lock, flags); + + hdmi_audio_stop(); + dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED; + + spin_unlock_irqrestore(&hdmi.audio_lock, flags); +} + +static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev) +{ + bool r = false; + + mutex_lock(&hdmi.lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) + goto err; + + if (!hdmi_mode_has_audio()) + goto err; + + r = true; +err: + mutex_unlock(&hdmi.lock); + return r; +} + +static int hdmi_panel_audio_config(struct omap_dss_device *dssdev, + struct omap_dss_audio *audio) +{ + unsigned long flags; + int r; + + mutex_lock(&hdmi.lock); + spin_lock_irqsave(&hdmi.audio_lock, flags); + + /* config audio only if the display is active and supports audio */ + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || + !hdmi_mode_has_audio()) { + DSSERR("audio not supported or display is off\n"); + r = -EPERM; + goto err; + } + + r = hdmi_audio_config(audio); + + if (!r) + dssdev->audio_state = OMAP_DSS_AUDIO_CONFIGURED; + +err: + spin_unlock_irqrestore(&hdmi.audio_lock, flags); + mutex_unlock(&hdmi.lock); + return r; +} + +#else +static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev) +{ + return -EPERM; +} + +static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev) +{ +} + +static int hdmi_panel_audio_start(struct omap_dss_device *dssdev) +{ + return -EPERM; +} + +static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev) +{ +} + +static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev) +{ + return false; +} + +static int hdmi_panel_audio_config(struct omap_dss_device *dssdev, + struct omap_dss_audio *audio) +{ + return -EPERM; +} +#endif + static int hdmi_panel_enable(struct omap_dss_device *dssdev) { int r = 0; DSSDBG("ENTER hdmi_panel_enable\n"); - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { r = -EINVAL; @@ -75,40 +236,52 @@ static int hdmi_panel_enable(struct omap_dss_device *dssdev) dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } static void hdmi_panel_disable(struct omap_dss_device *dssdev) { - mutex_lock(&hdmi.hdmi_lock); - - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + mutex_lock(&hdmi.lock); + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + /* + * TODO: notify audio users that the display was disabled. For + * now, disable audio locally to not break our audio state + * machine. + */ + hdmi_panel_audio_disable(dssdev); omapdss_hdmi_display_disable(dssdev); + } dssdev->state = OMAP_DSS_DISPLAY_DISABLED; - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); } static int hdmi_panel_suspend(struct omap_dss_device *dssdev) { int r = 0; - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { r = -EINVAL; goto err; } - dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + /* + * TODO: notify audio users that the display was suspended. For now, + * disable audio locally to not break our audio state machine. + */ + hdmi_panel_audio_disable(dssdev); + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; omapdss_hdmi_display_disable(dssdev); err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } @@ -117,7 +290,7 @@ static int hdmi_panel_resume(struct omap_dss_device *dssdev) { int r = 0; - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { r = -EINVAL; @@ -129,11 +302,12 @@ static int hdmi_panel_resume(struct omap_dss_device *dssdev) DSSERR("failed to power on\n"); goto err; } + /* TODO: notify audio users that the panel resumed. */ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } @@ -141,11 +315,11 @@ err: static void hdmi_get_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); *timings = dssdev->panel.timings; - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); } static void hdmi_set_timings(struct omap_dss_device *dssdev, @@ -153,12 +327,18 @@ static void hdmi_set_timings(struct omap_dss_device *dssdev, { DSSDBG("hdmi_set_timings\n"); - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); + + /* + * TODO: notify audio users that there was a timings change. For + * now, disable audio locally to not break our audio state machine. + */ + hdmi_panel_audio_disable(dssdev); dssdev->panel.timings = *timings; omapdss_hdmi_display_set_timing(dssdev); - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); } static int hdmi_check_timings(struct omap_dss_device *dssdev, @@ -168,11 +348,11 @@ static int hdmi_check_timings(struct omap_dss_device *dssdev, DSSDBG("hdmi_check_timings\n"); - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); r = omapdss_hdmi_display_check_timing(dssdev, timings); - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } @@ -180,7 +360,7 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len) { int r; - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { r = omapdss_hdmi_display_enable(dssdev); @@ -194,7 +374,7 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len) dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) omapdss_hdmi_display_disable(dssdev); err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } @@ -203,7 +383,7 @@ static bool hdmi_detect(struct omap_dss_device *dssdev) { int r; - mutex_lock(&hdmi.hdmi_lock); + mutex_lock(&hdmi.lock); if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { r = omapdss_hdmi_display_enable(dssdev); @@ -217,7 +397,7 @@ static bool hdmi_detect(struct omap_dss_device *dssdev) dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) omapdss_hdmi_display_disable(dssdev); err: - mutex_unlock(&hdmi.hdmi_lock); + mutex_unlock(&hdmi.lock); return r; } @@ -234,6 +414,12 @@ static struct omap_dss_driver hdmi_driver = { .check_timings = hdmi_check_timings, .read_edid = hdmi_read_edid, .detect = hdmi_detect, + .audio_enable = hdmi_panel_audio_enable, + .audio_disable = hdmi_panel_audio_disable, + .audio_start = hdmi_panel_audio_start, + .audio_stop = hdmi_panel_audio_stop, + .audio_supported = hdmi_panel_audio_supported, + .audio_config = hdmi_panel_audio_config, .driver = { .name = "hdmi_panel", .owner = THIS_MODULE, @@ -242,7 +428,11 @@ static struct omap_dss_driver hdmi_driver = { int hdmi_panel_init(void) { - mutex_init(&hdmi.hdmi_lock); + mutex_init(&hdmi.lock); + +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) + spin_lock_init(&hdmi.audio_lock); +#endif omap_dss_register_driver(&hdmi_driver); diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index e7364603f6a1..0cbcde4c688a 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -654,9 +654,20 @@ static int dss_mgr_check_zorder(struct omap_overlay_manager *mgr, return 0; } +int dss_mgr_check_timings(struct omap_overlay_manager *mgr, + const struct omap_video_timings *timings) +{ + if (!dispc_mgr_timings_ok(mgr->id, timings)) { + DSSERR("check_manager: invalid timings\n"); + return -EINVAL; + } + + return 0; +} + int dss_mgr_check(struct omap_overlay_manager *mgr, - struct omap_dss_device *dssdev, struct omap_overlay_manager_info *info, + const struct omap_video_timings *mgr_timings, struct omap_overlay_info **overlay_infos) { struct omap_overlay *ovl; @@ -668,6 +679,10 @@ int dss_mgr_check(struct omap_overlay_manager *mgr, return r; } + r = dss_mgr_check_timings(mgr, mgr_timings); + if (r) + return r; + list_for_each_entry(ovl, &mgr->overlays, list) { struct omap_overlay_info *oi; int r; @@ -677,7 +692,7 @@ int dss_mgr_check(struct omap_overlay_manager *mgr, if (oi == NULL) continue; - r = dss_ovl_check(ovl, oi, dssdev); + r = dss_ovl_check(ovl, oi, mgr_timings); if (r) return r; } diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c index 6e821810deec..b0ba60f88dd2 100644 --- a/drivers/video/omap2/dss/overlay.c +++ b/drivers/video/omap2/dss/overlay.c @@ -628,19 +628,23 @@ int dss_ovl_simple_check(struct omap_overlay *ovl, return -EINVAL; } + if (dss_feat_rotation_type_supported(info->rotation_type) == 0) { + DSSERR("check_overlay: rotation type %d not supported\n", + info->rotation_type); + return -EINVAL; + } + return 0; } -int dss_ovl_check(struct omap_overlay *ovl, - struct omap_overlay_info *info, struct omap_dss_device *dssdev) +int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info, + const struct omap_video_timings *mgr_timings) { u16 outw, outh; u16 dw, dh; - if (dssdev == NULL) - return 0; - - dssdev->driver->get_resolution(dssdev, &dw, &dh); + dw = mgr_timings->x_res; + dh = mgr_timings->y_res; if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { outw = info->width; diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index 788a0ef6323a..3d8c206e90e5 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -304,13 +304,23 @@ static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width, u16 height, void (*callback)(void *data), void *data) { u32 l; + struct omap_video_timings timings = { + .hsw = 1, + .hfp = 1, + .hbp = 1, + .vsw = 1, + .vfp = 0, + .vbp = 0, + .x_res = width, + .y_res = height, + }; /*BUG_ON(callback == 0);*/ BUG_ON(rfbi.framedone_callback != NULL); DSSDBG("rfbi_transfer_area %dx%d\n", width, height); - dispc_mgr_set_lcd_size(dssdev->manager->id, width, height); + dss_mgr_set_timings(dssdev->manager, &timings); dispc_mgr_enable(dssdev->manager->id, true); @@ -766,6 +776,16 @@ int omap_rfbi_prepare_update(struct omap_dss_device *dssdev, u16 *x, u16 *y, u16 *w, u16 *h) { u16 dw, dh; + struct omap_video_timings timings = { + .hsw = 1, + .hfp = 1, + .hbp = 1, + .vsw = 1, + .vfp = 0, + .vbp = 0, + .x_res = *w, + .y_res = *h, + }; dssdev->driver->get_resolution(dssdev, &dw, &dh); @@ -784,7 +804,7 @@ int omap_rfbi_prepare_update(struct omap_dss_device *dssdev, if (*w == 0 || *h == 0) return -EINVAL; - dispc_mgr_set_lcd_size(dssdev->manager->id, *w, *h); + dss_mgr_set_timings(dssdev->manager, &timings); return 0; } @@ -799,7 +819,7 @@ int omap_rfbi_update(struct omap_dss_device *dssdev, } EXPORT_SYMBOL(omap_rfbi_update); -void rfbi_dump_regs(struct seq_file *s) +static void rfbi_dump_regs(struct seq_file *s) { #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, rfbi_read_reg(r)) @@ -900,15 +920,39 @@ void omapdss_rfbi_display_disable(struct omap_dss_device *dssdev) } EXPORT_SYMBOL(omapdss_rfbi_display_disable); -int rfbi_init_display(struct omap_dss_device *dssdev) +static int __init rfbi_init_display(struct omap_dss_device *dssdev) { rfbi.dssdev[dssdev->phy.rfbi.channel] = dssdev; dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; return 0; } +static void __init rfbi_probe_pdata(struct platform_device *pdev) +{ + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int i, r; + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + if (dssdev->type != OMAP_DISPLAY_TYPE_DBI) + continue; + + r = rfbi_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } + + r = omap_dss_register_device(dssdev, &pdev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } +} + /* RFBI HW IP initialisation */ -static int omap_rfbihw_probe(struct platform_device *pdev) +static int __init omap_rfbihw_probe(struct platform_device *pdev) { u32 rev; struct resource *rfbi_mem; @@ -956,6 +1000,10 @@ static int omap_rfbihw_probe(struct platform_device *pdev) rfbi_runtime_put(); + dss_debugfs_create_file("rfbi", rfbi_dump_regs); + + rfbi_probe_pdata(pdev); + return 0; err_runtime_get: @@ -963,8 +1011,9 @@ err_runtime_get: return r; } -static int omap_rfbihw_remove(struct platform_device *pdev) +static int __exit omap_rfbihw_remove(struct platform_device *pdev) { + omap_dss_unregister_child_devices(&pdev->dev); pm_runtime_disable(&pdev->dev); return 0; } @@ -972,7 +1021,6 @@ static int omap_rfbihw_remove(struct platform_device *pdev) static int rfbi_runtime_suspend(struct device *dev) { dispc_runtime_put(); - dss_runtime_put(); return 0; } @@ -981,20 +1029,11 @@ static int rfbi_runtime_resume(struct device *dev) { int r; - r = dss_runtime_get(); - if (r < 0) - goto err_get_dss; - r = dispc_runtime_get(); if (r < 0) - goto err_get_dispc; + return r; return 0; - -err_get_dispc: - dss_runtime_put(); -err_get_dss: - return r; } static const struct dev_pm_ops rfbi_pm_ops = { @@ -1003,8 +1042,7 @@ static const struct dev_pm_ops rfbi_pm_ops = { }; static struct platform_driver omap_rfbihw_driver = { - .probe = omap_rfbihw_probe, - .remove = omap_rfbihw_remove, + .remove = __exit_p(omap_rfbihw_remove), .driver = { .name = "omapdss_rfbi", .owner = THIS_MODULE, @@ -1012,12 +1050,12 @@ static struct platform_driver omap_rfbihw_driver = { }, }; -int rfbi_init_platform_driver(void) +int __init rfbi_init_platform_driver(void) { - return platform_driver_register(&omap_rfbihw_driver); + return platform_driver_probe(&omap_rfbihw_driver, omap_rfbihw_probe); } -void rfbi_uninit_platform_driver(void) +void __exit rfbi_uninit_platform_driver(void) { - return platform_driver_unregister(&omap_rfbihw_driver); + platform_driver_unregister(&omap_rfbihw_driver); } diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index 8266ca0d666b..3a43dc2a9b46 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -24,6 +24,7 @@ #include <linux/err.h> #include <linux/regulator/consumer.h> #include <linux/export.h> +#include <linux/platform_device.h> #include <video/omapdss.h> #include "dss.h" @@ -71,10 +72,6 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) if (r) goto err_reg_enable; - r = dss_runtime_get(); - if (r) - goto err_get_dss; - r = dispc_runtime_get(); if (r) goto err_get_dispc; @@ -107,7 +104,7 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) } - dispc_mgr_set_lcd_timings(dssdev->manager->id, t); + dss_mgr_set_timings(dssdev->manager, t); r = dss_set_clock_div(&dss_cinfo); if (r) @@ -137,8 +134,6 @@ err_set_dss_clock_div: err_calc_clock_div: dispc_runtime_put(); err_get_dispc: - dss_runtime_put(); -err_get_dss: regulator_disable(sdi.vdds_sdi_reg); err_reg_enable: omap_dss_stop_device(dssdev); @@ -154,7 +149,6 @@ void omapdss_sdi_display_disable(struct omap_dss_device *dssdev) dss_sdi_disable(); dispc_runtime_put(); - dss_runtime_put(); regulator_disable(sdi.vdds_sdi_reg); @@ -162,7 +156,7 @@ void omapdss_sdi_display_disable(struct omap_dss_device *dssdev) } EXPORT_SYMBOL(omapdss_sdi_display_disable); -int sdi_init_display(struct omap_dss_device *dssdev) +static int __init sdi_init_display(struct omap_dss_device *dssdev) { DSSDBG("SDI init\n"); @@ -182,11 +176,58 @@ int sdi_init_display(struct omap_dss_device *dssdev) return 0; } -int sdi_init(void) +static void __init sdi_probe_pdata(struct platform_device *pdev) +{ + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int i, r; + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + if (dssdev->type != OMAP_DISPLAY_TYPE_SDI) + continue; + + r = sdi_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } + + r = omap_dss_register_device(dssdev, &pdev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } +} + +static int __init omap_sdi_probe(struct platform_device *pdev) { + sdi_probe_pdata(pdev); + + return 0; +} + +static int __exit omap_sdi_remove(struct platform_device *pdev) +{ + omap_dss_unregister_child_devices(&pdev->dev); + return 0; } -void sdi_exit(void) +static struct platform_driver omap_sdi_driver = { + .remove = __exit_p(omap_sdi_remove), + .driver = { + .name = "omapdss_sdi", + .owner = THIS_MODULE, + }, +}; + +int __init sdi_init_platform_driver(void) +{ + return platform_driver_probe(&omap_sdi_driver, omap_sdi_probe); +} + +void __exit sdi_uninit_platform_driver(void) { + platform_driver_unregister(&omap_sdi_driver); } diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h index 1f58b84d6901..e734cb444bc7 100644 --- a/drivers/video/omap2/dss/ti_hdmi.h +++ b/drivers/video/omap2/dss/ti_hdmi.h @@ -96,7 +96,9 @@ struct ti_hdmi_ip_ops { void (*pll_disable)(struct hdmi_ip_data *ip_data); - void (*video_enable)(struct hdmi_ip_data *ip_data, bool start); + int (*video_enable)(struct hdmi_ip_data *ip_data); + + void (*video_disable)(struct hdmi_ip_data *ip_data); void (*dump_wrapper)(struct hdmi_ip_data *ip_data, struct seq_file *s); @@ -106,9 +108,17 @@ struct ti_hdmi_ip_ops { void (*dump_phy)(struct hdmi_ip_data *ip_data, struct seq_file *s); -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) - void (*audio_enable)(struct hdmi_ip_data *ip_data, bool start); +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) + int (*audio_enable)(struct hdmi_ip_data *ip_data); + + void (*audio_disable)(struct hdmi_ip_data *ip_data); + + int (*audio_start)(struct hdmi_ip_data *ip_data); + + void (*audio_stop)(struct hdmi_ip_data *ip_data); + + int (*audio_config)(struct hdmi_ip_data *ip_data, + struct omap_dss_audio *audio); #endif }; @@ -173,7 +183,8 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data); int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, u8 *edid, int len); bool ti_hdmi_4xxx_detect(struct hdmi_ip_data *ip_data); -void ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start); +int ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_wp_video_stop(struct hdmi_ip_data *ip_data); int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data); void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data); @@ -181,8 +192,13 @@ void ti_hdmi_4xxx_wp_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s); -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -void ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data, bool enable); +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts); +int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data); +int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data); +void ti_hdmi_4xxx_audio_stop(struct hdmi_ip_data *ip_data); +int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data, + struct omap_dss_audio *audio); #endif #endif diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c index bfe6fe65c8be..4dae1b291079 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c @@ -29,9 +29,14 @@ #include <linux/string.h> #include <linux/seq_file.h> #include <linux/gpio.h> +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +#include <sound/asound.h> +#include <sound/asoundef.h> +#endif #include "ti_hdmi_4xxx_ip.h" #include "dss.h" +#include "dss_features.h" static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx, u32 val) @@ -298,9 +303,9 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data) REG_FLD_MOD(phy_base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27); r = request_threaded_irq(gpio_to_irq(ip_data->hpd_gpio), - NULL, hpd_irq_handler, - IRQF_DISABLED | IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING, "hpd", ip_data); + NULL, hpd_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, "hpd", ip_data); if (r) { DSSERR("HPD IRQ request failed\n"); hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF); @@ -699,9 +704,15 @@ static void hdmi_wp_init(struct omap_video_timings *timings, } -void ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start) +int ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data) +{ + REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, true, 31, 31); + return 0; +} + +void ti_hdmi_4xxx_wp_video_stop(struct hdmi_ip_data *ip_data) { - REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, start, 31, 31); + REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, false, 31, 31); } static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt, @@ -886,10 +897,12 @@ void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) #define CORE_REG(i, name) name(i) #define DUMPCORE(r) seq_printf(s, "%-35s %08x\n", #r,\ - hdmi_read_reg(hdmi_pll_base(ip_data), r)) -#define DUMPCOREAV(i, r) seq_printf(s, "%s[%d]%*s %08x\n", #r, i, \ + hdmi_read_reg(hdmi_core_sys_base(ip_data), r)) +#define DUMPCOREAV(r) seq_printf(s, "%-35s %08x\n", #r,\ + hdmi_read_reg(hdmi_av_base(ip_data), r)) +#define DUMPCOREAV2(i, r) seq_printf(s, "%s[%d]%*s %08x\n", #r, i, \ (i < 10) ? 32 - strlen(#r) : 31 - strlen(#r), " ", \ - hdmi_read_reg(hdmi_pll_base(ip_data), CORE_REG(i, r))) + hdmi_read_reg(hdmi_av_base(ip_data), CORE_REG(i, r))) DUMPCORE(HDMI_CORE_SYS_VND_IDL); DUMPCORE(HDMI_CORE_SYS_DEV_IDL); @@ -898,6 +911,13 @@ void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) DUMPCORE(HDMI_CORE_SYS_SRST); DUMPCORE(HDMI_CORE_CTRL1); DUMPCORE(HDMI_CORE_SYS_SYS_STAT); + DUMPCORE(HDMI_CORE_SYS_DE_DLY); + DUMPCORE(HDMI_CORE_SYS_DE_CTRL); + DUMPCORE(HDMI_CORE_SYS_DE_TOP); + DUMPCORE(HDMI_CORE_SYS_DE_CNTL); + DUMPCORE(HDMI_CORE_SYS_DE_CNTH); + DUMPCORE(HDMI_CORE_SYS_DE_LINL); + DUMPCORE(HDMI_CORE_SYS_DE_LINH_1); DUMPCORE(HDMI_CORE_SYS_VID_ACEN); DUMPCORE(HDMI_CORE_SYS_VID_MODE); DUMPCORE(HDMI_CORE_SYS_INTR_STATE); @@ -907,102 +927,91 @@ void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) DUMPCORE(HDMI_CORE_SYS_INTR4); DUMPCORE(HDMI_CORE_SYS_UMASK1); DUMPCORE(HDMI_CORE_SYS_TMDS_CTRL); - DUMPCORE(HDMI_CORE_SYS_DE_DLY); - DUMPCORE(HDMI_CORE_SYS_DE_CTRL); - DUMPCORE(HDMI_CORE_SYS_DE_TOP); - DUMPCORE(HDMI_CORE_SYS_DE_CNTL); - DUMPCORE(HDMI_CORE_SYS_DE_CNTH); - DUMPCORE(HDMI_CORE_SYS_DE_LINL); - DUMPCORE(HDMI_CORE_SYS_DE_LINH_1); - DUMPCORE(HDMI_CORE_DDC_CMD); - DUMPCORE(HDMI_CORE_DDC_STATUS); DUMPCORE(HDMI_CORE_DDC_ADDR); + DUMPCORE(HDMI_CORE_DDC_SEGM); DUMPCORE(HDMI_CORE_DDC_OFFSET); DUMPCORE(HDMI_CORE_DDC_COUNT1); DUMPCORE(HDMI_CORE_DDC_COUNT2); + DUMPCORE(HDMI_CORE_DDC_STATUS); + DUMPCORE(HDMI_CORE_DDC_CMD); DUMPCORE(HDMI_CORE_DDC_DATA); - DUMPCORE(HDMI_CORE_DDC_SEGM); - DUMPCORE(HDMI_CORE_AV_HDMI_CTRL); - DUMPCORE(HDMI_CORE_AV_DPD); - DUMPCORE(HDMI_CORE_AV_PB_CTRL1); - DUMPCORE(HDMI_CORE_AV_PB_CTRL2); - DUMPCORE(HDMI_CORE_AV_AVI_TYPE); - DUMPCORE(HDMI_CORE_AV_AVI_VERS); - DUMPCORE(HDMI_CORE_AV_AVI_LEN); - DUMPCORE(HDMI_CORE_AV_AVI_CHSUM); + DUMPCOREAV(HDMI_CORE_AV_ACR_CTRL); + DUMPCOREAV(HDMI_CORE_AV_FREQ_SVAL); + DUMPCOREAV(HDMI_CORE_AV_N_SVAL1); + DUMPCOREAV(HDMI_CORE_AV_N_SVAL2); + DUMPCOREAV(HDMI_CORE_AV_N_SVAL3); + DUMPCOREAV(HDMI_CORE_AV_CTS_SVAL1); + DUMPCOREAV(HDMI_CORE_AV_CTS_SVAL2); + DUMPCOREAV(HDMI_CORE_AV_CTS_SVAL3); + DUMPCOREAV(HDMI_CORE_AV_CTS_HVAL1); + DUMPCOREAV(HDMI_CORE_AV_CTS_HVAL2); + DUMPCOREAV(HDMI_CORE_AV_CTS_HVAL3); + DUMPCOREAV(HDMI_CORE_AV_AUD_MODE); + DUMPCOREAV(HDMI_CORE_AV_SPDIF_CTRL); + DUMPCOREAV(HDMI_CORE_AV_HW_SPDIF_FS); + DUMPCOREAV(HDMI_CORE_AV_SWAP_I2S); + DUMPCOREAV(HDMI_CORE_AV_SPDIF_ERTH); + DUMPCOREAV(HDMI_CORE_AV_I2S_IN_MAP); + DUMPCOREAV(HDMI_CORE_AV_I2S_IN_CTRL); + DUMPCOREAV(HDMI_CORE_AV_I2S_CHST0); + DUMPCOREAV(HDMI_CORE_AV_I2S_CHST1); + DUMPCOREAV(HDMI_CORE_AV_I2S_CHST2); + DUMPCOREAV(HDMI_CORE_AV_I2S_CHST4); + DUMPCOREAV(HDMI_CORE_AV_I2S_CHST5); + DUMPCOREAV(HDMI_CORE_AV_ASRC); + DUMPCOREAV(HDMI_CORE_AV_I2S_IN_LEN); + DUMPCOREAV(HDMI_CORE_AV_HDMI_CTRL); + DUMPCOREAV(HDMI_CORE_AV_AUDO_TXSTAT); + DUMPCOREAV(HDMI_CORE_AV_AUD_PAR_BUSCLK_1); + DUMPCOREAV(HDMI_CORE_AV_AUD_PAR_BUSCLK_2); + DUMPCOREAV(HDMI_CORE_AV_AUD_PAR_BUSCLK_3); + DUMPCOREAV(HDMI_CORE_AV_TEST_TXCTRL); + DUMPCOREAV(HDMI_CORE_AV_DPD); + DUMPCOREAV(HDMI_CORE_AV_PB_CTRL1); + DUMPCOREAV(HDMI_CORE_AV_PB_CTRL2); + DUMPCOREAV(HDMI_CORE_AV_AVI_TYPE); + DUMPCOREAV(HDMI_CORE_AV_AVI_VERS); + DUMPCOREAV(HDMI_CORE_AV_AVI_LEN); + DUMPCOREAV(HDMI_CORE_AV_AVI_CHSUM); for (i = 0; i < HDMI_CORE_AV_AVI_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_AVI_DBYTE); + DUMPCOREAV2(i, HDMI_CORE_AV_AVI_DBYTE); + + DUMPCOREAV(HDMI_CORE_AV_SPD_TYPE); + DUMPCOREAV(HDMI_CORE_AV_SPD_VERS); + DUMPCOREAV(HDMI_CORE_AV_SPD_LEN); + DUMPCOREAV(HDMI_CORE_AV_SPD_CHSUM); for (i = 0; i < HDMI_CORE_AV_SPD_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_SPD_DBYTE); + DUMPCOREAV2(i, HDMI_CORE_AV_SPD_DBYTE); + + DUMPCOREAV(HDMI_CORE_AV_AUDIO_TYPE); + DUMPCOREAV(HDMI_CORE_AV_AUDIO_VERS); + DUMPCOREAV(HDMI_CORE_AV_AUDIO_LEN); + DUMPCOREAV(HDMI_CORE_AV_AUDIO_CHSUM); for (i = 0; i < HDMI_CORE_AV_AUD_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_AUD_DBYTE); + DUMPCOREAV2(i, HDMI_CORE_AV_AUD_DBYTE); + + DUMPCOREAV(HDMI_CORE_AV_MPEG_TYPE); + DUMPCOREAV(HDMI_CORE_AV_MPEG_VERS); + DUMPCOREAV(HDMI_CORE_AV_MPEG_LEN); + DUMPCOREAV(HDMI_CORE_AV_MPEG_CHSUM); for (i = 0; i < HDMI_CORE_AV_MPEG_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_MPEG_DBYTE); + DUMPCOREAV2(i, HDMI_CORE_AV_MPEG_DBYTE); for (i = 0; i < HDMI_CORE_AV_GEN_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_GEN_DBYTE); + DUMPCOREAV2(i, HDMI_CORE_AV_GEN_DBYTE); + + DUMPCOREAV(HDMI_CORE_AV_CP_BYTE1); for (i = 0; i < HDMI_CORE_AV_GEN2_DBYTE_NELEMS; i++) - DUMPCOREAV(i, HDMI_CORE_AV_GEN2_DBYTE); - - DUMPCORE(HDMI_CORE_AV_ACR_CTRL); - DUMPCORE(HDMI_CORE_AV_FREQ_SVAL); - DUMPCORE(HDMI_CORE_AV_N_SVAL1); - DUMPCORE(HDMI_CORE_AV_N_SVAL2); - DUMPCORE(HDMI_CORE_AV_N_SVAL3); - DUMPCORE(HDMI_CORE_AV_CTS_SVAL1); - DUMPCORE(HDMI_CORE_AV_CTS_SVAL2); - DUMPCORE(HDMI_CORE_AV_CTS_SVAL3); - DUMPCORE(HDMI_CORE_AV_CTS_HVAL1); - DUMPCORE(HDMI_CORE_AV_CTS_HVAL2); - DUMPCORE(HDMI_CORE_AV_CTS_HVAL3); - DUMPCORE(HDMI_CORE_AV_AUD_MODE); - DUMPCORE(HDMI_CORE_AV_SPDIF_CTRL); - DUMPCORE(HDMI_CORE_AV_HW_SPDIF_FS); - DUMPCORE(HDMI_CORE_AV_SWAP_I2S); - DUMPCORE(HDMI_CORE_AV_SPDIF_ERTH); - DUMPCORE(HDMI_CORE_AV_I2S_IN_MAP); - DUMPCORE(HDMI_CORE_AV_I2S_IN_CTRL); - DUMPCORE(HDMI_CORE_AV_I2S_CHST0); - DUMPCORE(HDMI_CORE_AV_I2S_CHST1); - DUMPCORE(HDMI_CORE_AV_I2S_CHST2); - DUMPCORE(HDMI_CORE_AV_I2S_CHST4); - DUMPCORE(HDMI_CORE_AV_I2S_CHST5); - DUMPCORE(HDMI_CORE_AV_ASRC); - DUMPCORE(HDMI_CORE_AV_I2S_IN_LEN); - DUMPCORE(HDMI_CORE_AV_HDMI_CTRL); - DUMPCORE(HDMI_CORE_AV_AUDO_TXSTAT); - DUMPCORE(HDMI_CORE_AV_AUD_PAR_BUSCLK_1); - DUMPCORE(HDMI_CORE_AV_AUD_PAR_BUSCLK_2); - DUMPCORE(HDMI_CORE_AV_AUD_PAR_BUSCLK_3); - DUMPCORE(HDMI_CORE_AV_TEST_TXCTRL); - DUMPCORE(HDMI_CORE_AV_DPD); - DUMPCORE(HDMI_CORE_AV_PB_CTRL1); - DUMPCORE(HDMI_CORE_AV_PB_CTRL2); - DUMPCORE(HDMI_CORE_AV_AVI_TYPE); - DUMPCORE(HDMI_CORE_AV_AVI_VERS); - DUMPCORE(HDMI_CORE_AV_AVI_LEN); - DUMPCORE(HDMI_CORE_AV_AVI_CHSUM); - DUMPCORE(HDMI_CORE_AV_SPD_TYPE); - DUMPCORE(HDMI_CORE_AV_SPD_VERS); - DUMPCORE(HDMI_CORE_AV_SPD_LEN); - DUMPCORE(HDMI_CORE_AV_SPD_CHSUM); - DUMPCORE(HDMI_CORE_AV_AUDIO_TYPE); - DUMPCORE(HDMI_CORE_AV_AUDIO_VERS); - DUMPCORE(HDMI_CORE_AV_AUDIO_LEN); - DUMPCORE(HDMI_CORE_AV_AUDIO_CHSUM); - DUMPCORE(HDMI_CORE_AV_MPEG_TYPE); - DUMPCORE(HDMI_CORE_AV_MPEG_VERS); - DUMPCORE(HDMI_CORE_AV_MPEG_LEN); - DUMPCORE(HDMI_CORE_AV_MPEG_CHSUM); - DUMPCORE(HDMI_CORE_AV_CP_BYTE1); - DUMPCORE(HDMI_CORE_AV_CEC_ADDR_ID); + DUMPCOREAV2(i, HDMI_CORE_AV_GEN2_DBYTE); + + DUMPCOREAV(HDMI_CORE_AV_CEC_ADDR_ID); } void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) @@ -1016,9 +1025,8 @@ void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s) DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL); } -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data, +#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) +static void ti_hdmi_4xxx_wp_audio_config_format(struct hdmi_ip_data *ip_data, struct hdmi_audio_format *aud_fmt) { u32 r; @@ -1037,7 +1045,7 @@ void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data, hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG, r); } -void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data, +static void ti_hdmi_4xxx_wp_audio_config_dma(struct hdmi_ip_data *ip_data, struct hdmi_audio_dma *aud_dma) { u32 r; @@ -1055,7 +1063,7 @@ void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data, hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL, r); } -void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, +static void ti_hdmi_4xxx_core_audio_config(struct hdmi_ip_data *ip_data, struct hdmi_core_audio_config *cfg) { u32 r; @@ -1106,27 +1114,33 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, REG_FLD_MOD(av_base, HDMI_CORE_AV_SPDIF_CTRL, cfg->fs_override, 1, 1); - /* I2S parameters */ - REG_FLD_MOD(av_base, HDMI_CORE_AV_I2S_CHST4, - cfg->freq_sample, 3, 0); - + /* + * Set IEC-60958-3 channel status word. It is passed to the IP + * just as it is received. The user of the driver is responsible + * for its contents. + */ + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST0, + cfg->iec60958_cfg->status[0]); + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST1, + cfg->iec60958_cfg->status[1]); + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST2, + cfg->iec60958_cfg->status[2]); + /* yes, this is correct: status[3] goes to CHST4 register */ + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST4, + cfg->iec60958_cfg->status[3]); + /* yes, this is correct: status[4] goes to CHST5 register */ + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST5, + cfg->iec60958_cfg->status[4]); + + /* set I2S parameters */ r = hdmi_read_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL); - r = FLD_MOD(r, cfg->i2s_cfg.en_high_bitrate_aud, 7, 7); r = FLD_MOD(r, cfg->i2s_cfg.sck_edge_mode, 6, 6); - r = FLD_MOD(r, cfg->i2s_cfg.cbit_order, 5, 5); r = FLD_MOD(r, cfg->i2s_cfg.vbit, 4, 4); - r = FLD_MOD(r, cfg->i2s_cfg.ws_polarity, 3, 3); r = FLD_MOD(r, cfg->i2s_cfg.justification, 2, 2); r = FLD_MOD(r, cfg->i2s_cfg.direction, 1, 1); r = FLD_MOD(r, cfg->i2s_cfg.shift, 0, 0); hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_IN_CTRL, r); - r = hdmi_read_reg(av_base, HDMI_CORE_AV_I2S_CHST5); - r = FLD_MOD(r, cfg->freq_sample, 7, 4); - r = FLD_MOD(r, cfg->i2s_cfg.word_length, 3, 1); - r = FLD_MOD(r, cfg->i2s_cfg.word_max_length, 0, 0); - hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_CHST5, r); - REG_FLD_MOD(av_base, HDMI_CORE_AV_I2S_IN_LEN, cfg->i2s_cfg.in_length_bits, 3, 0); @@ -1138,12 +1152,19 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, r = FLD_MOD(r, cfg->en_parallel_aud_input, 2, 2); r = FLD_MOD(r, cfg->en_spdif, 1, 1); hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_MODE, r); + + /* Audio channel mappings */ + /* TODO: Make channel mapping dynamic. For now, map channels + * in the ALSA order: FL/FR/RL/RR/C/LFE/SL/SR. Remapping is needed as + * HDMI speaker order is different. See CEA-861 Section 6.6.2. + */ + hdmi_write_reg(av_base, HDMI_CORE_AV_I2S_IN_MAP, 0x78); + REG_FLD_MOD(av_base, HDMI_CORE_AV_SWAP_I2S, 1, 5, 5); } -void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, - struct hdmi_core_infoframe_audio *info_aud) +static void ti_hdmi_4xxx_core_audio_infoframe_cfg(struct hdmi_ip_data *ip_data, + struct snd_cea_861_aud_if *info_aud) { - u8 val; u8 sum = 0, checksum = 0; void __iomem *av_base = hdmi_av_base(ip_data); @@ -1157,24 +1178,23 @@ void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, hdmi_write_reg(av_base, HDMI_CORE_AV_AUDIO_LEN, 0x0a); sum += 0x84 + 0x001 + 0x00a; - val = (info_aud->db1_coding_type << 4) - | (info_aud->db1_channel_count - 1); - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(0), val); - sum += val; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(0), + info_aud->db1_ct_cc); + sum += info_aud->db1_ct_cc; - val = (info_aud->db2_sample_freq << 2) | info_aud->db2_sample_size; - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(1), val); - sum += val; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(1), + info_aud->db2_sf_ss); + sum += info_aud->db2_sf_ss; - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(2), 0x00); + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(2), info_aud->db3); + sum += info_aud->db3; - val = info_aud->db4_channel_alloc; - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(3), val); - sum += val; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(3), info_aud->db4_ca); + sum += info_aud->db4_ca; - val = (info_aud->db5_downmix_inh << 7) | (info_aud->db5_lsv << 3); - hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(4), val); - sum += val; + hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(4), + info_aud->db5_dminh_lsv); + sum += info_aud->db5_dminh_lsv; hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(5), 0x00); hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(6), 0x00); @@ -1192,70 +1212,212 @@ void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, */ } -int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data, - u32 sample_freq, u32 *n, u32 *cts) +int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data, + struct omap_dss_audio *audio) { - u32 r; - u32 deep_color = 0; - u32 pclk = ip_data->cfg.timings.pixel_clock; - - if (n == NULL || cts == NULL) + struct hdmi_audio_format audio_format; + struct hdmi_audio_dma audio_dma; + struct hdmi_core_audio_config core; + int err, n, cts, channel_count; + unsigned int fs_nr; + bool word_length_16b = false; + + if (!audio || !audio->iec || !audio->cea || !ip_data) return -EINVAL; + + core.iec60958_cfg = audio->iec; /* - * Obtain current deep color configuration. This needed - * to calculate the TMDS clock based on the pixel clock. + * In the IEC-60958 status word, check if the audio sample word length + * is 16-bit as several optimizations can be performed in such case. */ - r = REG_GET(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, 1, 0); - switch (r) { - case 1: /* No deep color selected */ - deep_color = 100; + if (!(audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24)) + if (audio->iec->status[4] & IEC958_AES4_CON_WORDLEN_20_16) + word_length_16b = true; + + /* I2S configuration. See Phillips' specification */ + if (word_length_16b) + core.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_LEFT; + else + core.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT; + /* + * The I2S input word length is twice the lenght given in the IEC-60958 + * status word. If the word size is greater than + * 20 bits, increment by one. + */ + core.i2s_cfg.in_length_bits = audio->iec->status[4] + & IEC958_AES4_CON_WORDLEN; + if (audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24) + core.i2s_cfg.in_length_bits++; + core.i2s_cfg.sck_edge_mode = HDMI_AUDIO_I2S_SCK_EDGE_RISING; + core.i2s_cfg.vbit = HDMI_AUDIO_I2S_VBIT_FOR_PCM; + core.i2s_cfg.direction = HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST; + core.i2s_cfg.shift = HDMI_AUDIO_I2S_FIRST_BIT_SHIFT; + + /* convert sample frequency to a number */ + switch (audio->iec->status[3] & IEC958_AES3_CON_FS) { + case IEC958_AES3_CON_FS_32000: + fs_nr = 32000; + break; + case IEC958_AES3_CON_FS_44100: + fs_nr = 44100; + break; + case IEC958_AES3_CON_FS_48000: + fs_nr = 48000; break; - case 2: /* 10-bit deep color selected */ - deep_color = 125; + case IEC958_AES3_CON_FS_88200: + fs_nr = 88200; break; - case 3: /* 12-bit deep color selected */ - deep_color = 150; + case IEC958_AES3_CON_FS_96000: + fs_nr = 96000; + break; + case IEC958_AES3_CON_FS_176400: + fs_nr = 176400; + break; + case IEC958_AES3_CON_FS_192000: + fs_nr = 192000; break; default: return -EINVAL; } - switch (sample_freq) { - case 32000: - if ((deep_color == 125) && ((pclk == 54054) - || (pclk == 74250))) - *n = 8192; - else - *n = 4096; + err = hdmi_compute_acr(fs_nr, &n, &cts); + + /* Audio clock regeneration settings */ + core.n = n; + core.cts = cts; + if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) { + core.aud_par_busclk = 0; + core.cts_mode = HDMI_AUDIO_CTS_MODE_SW; + core.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK); + } else { + core.aud_par_busclk = (((128 * 31) - 1) << 8); + core.cts_mode = HDMI_AUDIO_CTS_MODE_HW; + core.use_mclk = true; + } + + if (core.use_mclk) + core.mclk_mode = HDMI_AUDIO_MCLK_128FS; + + /* Audio channels settings */ + channel_count = (audio->cea->db1_ct_cc & + CEA861_AUDIO_INFOFRAME_DB1CC) + 1; + + switch (channel_count) { + case 2: + audio_format.active_chnnls_msk = 0x03; + break; + case 3: + audio_format.active_chnnls_msk = 0x07; + break; + case 4: + audio_format.active_chnnls_msk = 0x0f; + break; + case 5: + audio_format.active_chnnls_msk = 0x1f; break; - case 44100: - *n = 6272; + case 6: + audio_format.active_chnnls_msk = 0x3f; break; - case 48000: - if ((deep_color == 125) && ((pclk == 54054) - || (pclk == 74250))) - *n = 8192; - else - *n = 6144; + case 7: + audio_format.active_chnnls_msk = 0x7f; + break; + case 8: + audio_format.active_chnnls_msk = 0xff; break; default: - *n = 0; return -EINVAL; } - /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */ - *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10); + /* + * the HDMI IP needs to enable four stereo channels when transmitting + * more than 2 audio channels + */ + if (channel_count == 2) { + audio_format.stereo_channels = HDMI_AUDIO_STEREO_ONECHANNEL; + core.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN; + core.layout = HDMI_AUDIO_LAYOUT_2CH; + } else { + audio_format.stereo_channels = HDMI_AUDIO_STEREO_FOURCHANNELS; + core.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN | + HDMI_AUDIO_I2S_SD1_EN | HDMI_AUDIO_I2S_SD2_EN | + HDMI_AUDIO_I2S_SD3_EN; + core.layout = HDMI_AUDIO_LAYOUT_8CH; + } + + core.en_spdif = false; + /* use sample frequency from channel status word */ + core.fs_override = true; + /* enable ACR packets */ + core.en_acr_pkt = true; + /* disable direct streaming digital audio */ + core.en_dsd_audio = false; + /* use parallel audio interface */ + core.en_parallel_aud_input = true; + + /* DMA settings */ + if (word_length_16b) + audio_dma.transfer_size = 0x10; + else + audio_dma.transfer_size = 0x20; + audio_dma.block_size = 0xC0; + audio_dma.mode = HDMI_AUDIO_TRANSF_DMA; + audio_dma.fifo_threshold = 0x20; /* in number of samples */ + + /* audio FIFO format settings */ + if (word_length_16b) { + audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES; + audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS; + audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT; + } else { + audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_ONESAMPLE; + audio_format.sample_size = HDMI_AUDIO_SAMPLE_24BITS; + audio_format.justification = HDMI_AUDIO_JUSTIFY_RIGHT; + } + audio_format.type = HDMI_AUDIO_TYPE_LPCM; + audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST; + /* disable start/stop signals of IEC 60958 blocks */ + audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_ON; + + /* configure DMA and audio FIFO format*/ + ti_hdmi_4xxx_wp_audio_config_dma(ip_data, &audio_dma); + ti_hdmi_4xxx_wp_audio_config_format(ip_data, &audio_format); + + /* configure the core*/ + ti_hdmi_4xxx_core_audio_config(ip_data, &core); + + /* configure CEA 861 audio infoframe*/ + ti_hdmi_4xxx_core_audio_infoframe_cfg(ip_data, audio->cea); return 0; } -void ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data, bool enable) +int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data) +{ + REG_FLD_MOD(hdmi_wp_base(ip_data), + HDMI_WP_AUDIO_CTRL, true, 31, 31); + return 0; +} + +void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data) +{ + REG_FLD_MOD(hdmi_wp_base(ip_data), + HDMI_WP_AUDIO_CTRL, false, 31, 31); +} + +int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data) { REG_FLD_MOD(hdmi_av_base(ip_data), - HDMI_CORE_AV_AUD_MODE, enable, 0, 0); + HDMI_CORE_AV_AUD_MODE, true, 0, 0); REG_FLD_MOD(hdmi_wp_base(ip_data), - HDMI_WP_AUDIO_CTRL, enable, 31, 31); + HDMI_WP_AUDIO_CTRL, true, 30, 30); + return 0; +} + +void ti_hdmi_4xxx_audio_stop(struct hdmi_ip_data *ip_data) +{ + REG_FLD_MOD(hdmi_av_base(ip_data), + HDMI_CORE_AV_AUD_MODE, false, 0, 0); REG_FLD_MOD(hdmi_wp_base(ip_data), - HDMI_WP_AUDIO_CTRL, enable, 30, 30); + HDMI_WP_AUDIO_CTRL, false, 30, 30); } #endif diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h index a14d1a0e6e41..8366ae19e82e 100644 --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h @@ -24,11 +24,6 @@ #include <linux/string.h> #include <video/omapdss.h> #include "ti_hdmi.h" -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -#include <sound/soc.h> -#include <sound/pcm_params.h> -#endif /* HDMI Wrapper */ @@ -57,6 +52,13 @@ #define HDMI_CORE_SYS_SRST 0x14 #define HDMI_CORE_CTRL1 0x20 #define HDMI_CORE_SYS_SYS_STAT 0x24 +#define HDMI_CORE_SYS_DE_DLY 0xC8 +#define HDMI_CORE_SYS_DE_CTRL 0xCC +#define HDMI_CORE_SYS_DE_TOP 0xD0 +#define HDMI_CORE_SYS_DE_CNTL 0xD8 +#define HDMI_CORE_SYS_DE_CNTH 0xDC +#define HDMI_CORE_SYS_DE_LINL 0xE0 +#define HDMI_CORE_SYS_DE_LINH_1 0xE4 #define HDMI_CORE_SYS_VID_ACEN 0x124 #define HDMI_CORE_SYS_VID_MODE 0x128 #define HDMI_CORE_SYS_INTR_STATE 0x1C0 @@ -66,50 +68,24 @@ #define HDMI_CORE_SYS_INTR4 0x1D0 #define HDMI_CORE_SYS_UMASK1 0x1D4 #define HDMI_CORE_SYS_TMDS_CTRL 0x208 -#define HDMI_CORE_SYS_DE_DLY 0xC8 -#define HDMI_CORE_SYS_DE_CTRL 0xCC -#define HDMI_CORE_SYS_DE_TOP 0xD0 -#define HDMI_CORE_SYS_DE_CNTL 0xD8 -#define HDMI_CORE_SYS_DE_CNTH 0xDC -#define HDMI_CORE_SYS_DE_LINL 0xE0 -#define HDMI_CORE_SYS_DE_LINH_1 0xE4 + #define HDMI_CORE_CTRL1_VEN_FOLLOWVSYNC 0x1 #define HDMI_CORE_CTRL1_HEN_FOLLOWHSYNC 0x1 -#define HDMI_CORE_CTRL1_BSEL_24BITBUS 0x1 +#define HDMI_CORE_CTRL1_BSEL_24BITBUS 0x1 #define HDMI_CORE_CTRL1_EDGE_RISINGEDGE 0x1 /* HDMI DDC E-DID */ -#define HDMI_CORE_DDC_CMD 0x3CC -#define HDMI_CORE_DDC_STATUS 0x3C8 #define HDMI_CORE_DDC_ADDR 0x3B4 +#define HDMI_CORE_DDC_SEGM 0x3B8 #define HDMI_CORE_DDC_OFFSET 0x3BC #define HDMI_CORE_DDC_COUNT1 0x3C0 #define HDMI_CORE_DDC_COUNT2 0x3C4 +#define HDMI_CORE_DDC_STATUS 0x3C8 +#define HDMI_CORE_DDC_CMD 0x3CC #define HDMI_CORE_DDC_DATA 0x3D0 -#define HDMI_CORE_DDC_SEGM 0x3B8 /* HDMI IP Core Audio Video */ -#define HDMI_CORE_AV_HDMI_CTRL 0xBC -#define HDMI_CORE_AV_DPD 0xF4 -#define HDMI_CORE_AV_PB_CTRL1 0xF8 -#define HDMI_CORE_AV_PB_CTRL2 0xFC -#define HDMI_CORE_AV_AVI_TYPE 0x100 -#define HDMI_CORE_AV_AVI_VERS 0x104 -#define HDMI_CORE_AV_AVI_LEN 0x108 -#define HDMI_CORE_AV_AVI_CHSUM 0x10C -#define HDMI_CORE_AV_AVI_DBYTE(n) (n * 4 + 0x110) -#define HDMI_CORE_AV_AVI_DBYTE_NELEMS 15 -#define HDMI_CORE_AV_SPD_DBYTE(n) (n * 4 + 0x190) -#define HDMI_CORE_AV_SPD_DBYTE_NELEMS 27 -#define HDMI_CORE_AV_AUD_DBYTE(n) (n * 4 + 0x210) -#define HDMI_CORE_AV_AUD_DBYTE_NELEMS 10 -#define HDMI_CORE_AV_MPEG_DBYTE(n) (n * 4 + 0x290) -#define HDMI_CORE_AV_MPEG_DBYTE_NELEMS 27 -#define HDMI_CORE_AV_GEN_DBYTE(n) (n * 4 + 0x300) -#define HDMI_CORE_AV_GEN_DBYTE_NELEMS 31 -#define HDMI_CORE_AV_GEN2_DBYTE(n) (n * 4 + 0x380) -#define HDMI_CORE_AV_GEN2_DBYTE_NELEMS 31 #define HDMI_CORE_AV_ACR_CTRL 0x4 #define HDMI_CORE_AV_FREQ_SVAL 0x8 #define HDMI_CORE_AV_N_SVAL1 0xC @@ -148,25 +124,39 @@ #define HDMI_CORE_AV_AVI_VERS 0x104 #define HDMI_CORE_AV_AVI_LEN 0x108 #define HDMI_CORE_AV_AVI_CHSUM 0x10C +#define HDMI_CORE_AV_AVI_DBYTE(n) (n * 4 + 0x110) #define HDMI_CORE_AV_SPD_TYPE 0x180 #define HDMI_CORE_AV_SPD_VERS 0x184 #define HDMI_CORE_AV_SPD_LEN 0x188 #define HDMI_CORE_AV_SPD_CHSUM 0x18C +#define HDMI_CORE_AV_SPD_DBYTE(n) (n * 4 + 0x190) #define HDMI_CORE_AV_AUDIO_TYPE 0x200 #define HDMI_CORE_AV_AUDIO_VERS 0x204 #define HDMI_CORE_AV_AUDIO_LEN 0x208 #define HDMI_CORE_AV_AUDIO_CHSUM 0x20C +#define HDMI_CORE_AV_AUD_DBYTE(n) (n * 4 + 0x210) #define HDMI_CORE_AV_MPEG_TYPE 0x280 #define HDMI_CORE_AV_MPEG_VERS 0x284 #define HDMI_CORE_AV_MPEG_LEN 0x288 #define HDMI_CORE_AV_MPEG_CHSUM 0x28C +#define HDMI_CORE_AV_MPEG_DBYTE(n) (n * 4 + 0x290) +#define HDMI_CORE_AV_GEN_DBYTE(n) (n * 4 + 0x300) #define HDMI_CORE_AV_CP_BYTE1 0x37C +#define HDMI_CORE_AV_GEN2_DBYTE(n) (n * 4 + 0x380) #define HDMI_CORE_AV_CEC_ADDR_ID 0x3FC + #define HDMI_CORE_AV_SPD_DBYTE_ELSIZE 0x4 #define HDMI_CORE_AV_GEN2_DBYTE_ELSIZE 0x4 #define HDMI_CORE_AV_MPEG_DBYTE_ELSIZE 0x4 #define HDMI_CORE_AV_GEN_DBYTE_ELSIZE 0x4 +#define HDMI_CORE_AV_AVI_DBYTE_NELEMS 15 +#define HDMI_CORE_AV_SPD_DBYTE_NELEMS 27 +#define HDMI_CORE_AV_AUD_DBYTE_NELEMS 10 +#define HDMI_CORE_AV_MPEG_DBYTE_NELEMS 27 +#define HDMI_CORE_AV_GEN_DBYTE_NELEMS 31 +#define HDMI_CORE_AV_GEN2_DBYTE_NELEMS 31 + /* PLL */ #define PLLCTRL_PLL_CONTROL 0x0 @@ -284,35 +274,6 @@ enum hdmi_core_infoframe { HDMI_INFOFRAME_AVI_DB5PR_8 = 7, HDMI_INFOFRAME_AVI_DB5PR_9 = 8, HDMI_INFOFRAME_AVI_DB5PR_10 = 9, - HDMI_INFOFRAME_AUDIO_DB1CT_FROM_STREAM = 0, - HDMI_INFOFRAME_AUDIO_DB1CT_IEC60958 = 1, - HDMI_INFOFRAME_AUDIO_DB1CT_AC3 = 2, - HDMI_INFOFRAME_AUDIO_DB1CT_MPEG1 = 3, - HDMI_INFOFRAME_AUDIO_DB1CT_MP3 = 4, - HDMI_INFOFRAME_AUDIO_DB1CT_MPEG2_MULTICH = 5, - HDMI_INFOFRAME_AUDIO_DB1CT_AAC = 6, - HDMI_INFOFRAME_AUDIO_DB1CT_DTS = 7, - HDMI_INFOFRAME_AUDIO_DB1CT_ATRAC = 8, - HDMI_INFOFRAME_AUDIO_DB1CT_ONEBIT = 9, - HDMI_INFOFRAME_AUDIO_DB1CT_DOLBY_DIGITAL_PLUS = 10, - HDMI_INFOFRAME_AUDIO_DB1CT_DTS_HD = 11, - HDMI_INFOFRAME_AUDIO_DB1CT_MAT = 12, - HDMI_INFOFRAME_AUDIO_DB1CT_DST = 13, - HDMI_INFOFRAME_AUDIO_DB1CT_WMA_PRO = 14, - HDMI_INFOFRAME_AUDIO_DB2SF_FROM_STREAM = 0, - HDMI_INFOFRAME_AUDIO_DB2SF_32000 = 1, - HDMI_INFOFRAME_AUDIO_DB2SF_44100 = 2, - HDMI_INFOFRAME_AUDIO_DB2SF_48000 = 3, - HDMI_INFOFRAME_AUDIO_DB2SF_88200 = 4, - HDMI_INFOFRAME_AUDIO_DB2SF_96000 = 5, - HDMI_INFOFRAME_AUDIO_DB2SF_176400 = 6, - HDMI_INFOFRAME_AUDIO_DB2SF_192000 = 7, - HDMI_INFOFRAME_AUDIO_DB2SS_FROM_STREAM = 0, - HDMI_INFOFRAME_AUDIO_DB2SS_16BIT = 1, - HDMI_INFOFRAME_AUDIO_DB2SS_20BIT = 2, - HDMI_INFOFRAME_AUDIO_DB2SS_24BIT = 3, - HDMI_INFOFRAME_AUDIO_DB5_DM_INH_PERMITTED = 0, - HDMI_INFOFRAME_AUDIO_DB5_DM_INH_PROHIBITED = 1 }; enum hdmi_packing_mode { @@ -322,17 +283,6 @@ enum hdmi_packing_mode { HDMI_PACK_ALREADYPACKED = 7 }; -enum hdmi_core_audio_sample_freq { - HDMI_AUDIO_FS_32000 = 0x3, - HDMI_AUDIO_FS_44100 = 0x0, - HDMI_AUDIO_FS_48000 = 0x2, - HDMI_AUDIO_FS_88200 = 0x8, - HDMI_AUDIO_FS_96000 = 0xA, - HDMI_AUDIO_FS_176400 = 0xC, - HDMI_AUDIO_FS_192000 = 0xE, - HDMI_AUDIO_FS_NOT_INDICATED = 0x1 -}; - enum hdmi_core_audio_layout { HDMI_AUDIO_LAYOUT_2CH = 0, HDMI_AUDIO_LAYOUT_8CH = 1 @@ -387,37 +337,12 @@ enum hdmi_audio_blk_strt_end_sig { }; enum hdmi_audio_i2s_config { - HDMI_AUDIO_I2S_WS_POLARITY_LOW_IS_LEFT = 0, - HDMI_AUDIO_I2S_WS_POLARIT_YLOW_IS_RIGHT = 1, HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST = 0, HDMI_AUDIO_I2S_LSB_SHIFTED_FIRST = 1, - HDMI_AUDIO_I2S_MAX_WORD_20BITS = 0, - HDMI_AUDIO_I2S_MAX_WORD_24BITS = 1, - HDMI_AUDIO_I2S_CHST_WORD_NOT_SPECIFIED = 0, - HDMI_AUDIO_I2S_CHST_WORD_16_BITS = 1, - HDMI_AUDIO_I2S_CHST_WORD_17_BITS = 6, - HDMI_AUDIO_I2S_CHST_WORD_18_BITS = 2, - HDMI_AUDIO_I2S_CHST_WORD_19_BITS = 4, - HDMI_AUDIO_I2S_CHST_WORD_20_BITS_20MAX = 5, - HDMI_AUDIO_I2S_CHST_WORD_20_BITS_24MAX = 1, - HDMI_AUDIO_I2S_CHST_WORD_21_BITS = 6, - HDMI_AUDIO_I2S_CHST_WORD_22_BITS = 2, - HDMI_AUDIO_I2S_CHST_WORD_23_BITS = 4, - HDMI_AUDIO_I2S_CHST_WORD_24_BITS = 5, HDMI_AUDIO_I2S_SCK_EDGE_FALLING = 0, HDMI_AUDIO_I2S_SCK_EDGE_RISING = 1, HDMI_AUDIO_I2S_VBIT_FOR_PCM = 0, HDMI_AUDIO_I2S_VBIT_FOR_COMPRESSED = 1, - HDMI_AUDIO_I2S_INPUT_LENGTH_NA = 0, - HDMI_AUDIO_I2S_INPUT_LENGTH_16 = 2, - HDMI_AUDIO_I2S_INPUT_LENGTH_17 = 12, - HDMI_AUDIO_I2S_INPUT_LENGTH_18 = 4, - HDMI_AUDIO_I2S_INPUT_LENGTH_19 = 8, - HDMI_AUDIO_I2S_INPUT_LENGTH_20 = 10, - HDMI_AUDIO_I2S_INPUT_LENGTH_21 = 13, - HDMI_AUDIO_I2S_INPUT_LENGTH_22 = 5, - HDMI_AUDIO_I2S_INPUT_LENGTH_23 = 9, - HDMI_AUDIO_I2S_INPUT_LENGTH_24 = 11, HDMI_AUDIO_I2S_FIRST_BIT_SHIFT = 0, HDMI_AUDIO_I2S_FIRST_BIT_NO_SHIFT = 1, HDMI_AUDIO_I2S_SD0_EN = 1, @@ -446,20 +371,6 @@ struct hdmi_core_video_config { enum hdmi_core_tclkselclkmult tclk_sel_clkmult; }; -/* - * Refer to section 8.2 in HDMI 1.3 specification for - * details about infoframe databytes - */ -struct hdmi_core_infoframe_audio { - u8 db1_coding_type; - u8 db1_channel_count; - u8 db2_sample_freq; - u8 db2_sample_size; - u8 db4_channel_alloc; - bool db5_downmix_inh; - u8 db5_lsv; /* Level shift values for downmix */ -}; - struct hdmi_core_packet_enable_repeat { u32 audio_pkt; u32 audio_pkt_repeat; @@ -496,15 +407,10 @@ struct hdmi_audio_dma { }; struct hdmi_core_audio_i2s_config { - u8 word_max_length; - u8 word_length; u8 in_length_bits; u8 justification; - u8 en_high_bitrate_aud; u8 sck_edge_mode; - u8 cbit_order; u8 vbit; - u8 ws_polarity; u8 direction; u8 shift; u8 active_sds; @@ -512,7 +418,7 @@ struct hdmi_core_audio_i2s_config { struct hdmi_core_audio_config { struct hdmi_core_audio_i2s_config i2s_cfg; - enum hdmi_core_audio_sample_freq freq_sample; + struct snd_aes_iec958 *iec60958_cfg; bool fs_override; u32 n; u32 cts; @@ -527,17 +433,4 @@ struct hdmi_core_audio_config { bool en_spdif; }; -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) -int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data, - u32 sample_freq, u32 *n, u32 *cts); -void hdmi_core_audio_infoframe_config(struct hdmi_ip_data *ip_data, - struct hdmi_core_infoframe_audio *info_aud); -void hdmi_core_audio_config(struct hdmi_ip_data *ip_data, - struct hdmi_core_audio_config *cfg); -void hdmi_wp_audio_config_dma(struct hdmi_ip_data *ip_data, - struct hdmi_audio_dma *aud_dma); -void hdmi_wp_audio_config_format(struct hdmi_ip_data *ip_data, - struct hdmi_audio_format *aud_fmt); -#endif #endif diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index 9c3daf71750c..2b8973931ff4 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -415,6 +415,7 @@ static const struct venc_config *venc_timings_to_config( return &venc_config_ntsc_trm; BUG(); + return NULL; } static int venc_power_on(struct omap_dss_device *dssdev) @@ -440,10 +441,11 @@ static int venc_power_on(struct omap_dss_device *dssdev) venc_write_reg(VENC_OUTPUT_CONTROL, l); - dispc_set_digit_size(dssdev->panel.timings.x_res, - dssdev->panel.timings.y_res/2); + dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings); - regulator_enable(venc.vdda_dac_reg); + r = regulator_enable(venc.vdda_dac_reg); + if (r) + goto err; if (dssdev->platform_enable) dssdev->platform_enable(dssdev); @@ -485,16 +487,68 @@ unsigned long venc_get_pixel_clock(void) return 13500000; } +static ssize_t display_output_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + const char *ret; + + switch (dssdev->phy.venc.type) { + case OMAP_DSS_VENC_TYPE_COMPOSITE: + ret = "composite"; + break; + case OMAP_DSS_VENC_TYPE_SVIDEO: + ret = "svideo"; + break; + default: + return -EINVAL; + } + + return snprintf(buf, PAGE_SIZE, "%s\n", ret); +} + +static ssize_t display_output_type_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct omap_dss_device *dssdev = to_dss_device(dev); + enum omap_dss_venc_type new_type; + + if (sysfs_streq("composite", buf)) + new_type = OMAP_DSS_VENC_TYPE_COMPOSITE; + else if (sysfs_streq("svideo", buf)) + new_type = OMAP_DSS_VENC_TYPE_SVIDEO; + else + return -EINVAL; + + mutex_lock(&venc.venc_lock); + + if (dssdev->phy.venc.type != new_type) { + dssdev->phy.venc.type = new_type; + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + venc_power_off(dssdev); + venc_power_on(dssdev); + } + } + + mutex_unlock(&venc.venc_lock); + + return size; +} + +static DEVICE_ATTR(output_type, S_IRUGO | S_IWUSR, + display_output_type_show, display_output_type_store); + /* driver */ static int venc_panel_probe(struct omap_dss_device *dssdev) { dssdev->panel.timings = omap_dss_pal_timings; - return 0; + return device_create_file(&dssdev->dev, &dev_attr_output_type); } static void venc_panel_remove(struct omap_dss_device *dssdev) { + device_remove_file(&dssdev->dev, &dev_attr_output_type); } static int venc_panel_enable(struct omap_dss_device *dssdev) @@ -577,12 +631,6 @@ static int venc_panel_resume(struct omap_dss_device *dssdev) return venc_panel_enable(dssdev); } -static void venc_get_timings(struct omap_dss_device *dssdev, - struct omap_video_timings *timings) -{ - *timings = dssdev->panel.timings; -} - static void venc_set_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { @@ -597,6 +645,8 @@ static void venc_set_timings(struct omap_dss_device *dssdev, /* turn the venc off and on to get new timings to use */ venc_panel_disable(dssdev); venc_panel_enable(dssdev); + } else { + dss_mgr_set_timings(dssdev->manager, timings); } } @@ -661,7 +711,6 @@ static struct omap_dss_driver venc_driver = { .get_resolution = omapdss_default_get_resolution, .get_recommended_bpp = omapdss_default_get_recommended_bpp, - .get_timings = venc_get_timings, .set_timings = venc_set_timings, .check_timings = venc_check_timings, @@ -675,7 +724,7 @@ static struct omap_dss_driver venc_driver = { }; /* driver end */ -int venc_init_display(struct omap_dss_device *dssdev) +static int __init venc_init_display(struct omap_dss_device *dssdev) { DSSDBG("init_display\n"); @@ -695,7 +744,7 @@ int venc_init_display(struct omap_dss_device *dssdev) return 0; } -void venc_dump_regs(struct seq_file *s) +static void venc_dump_regs(struct seq_file *s) { #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r)) @@ -779,8 +828,32 @@ static void venc_put_clocks(void) clk_put(venc.tv_dac_clk); } +static void __init venc_probe_pdata(struct platform_device *pdev) +{ + struct omap_dss_board_info *pdata = pdev->dev.platform_data; + int r, i; + + for (i = 0; i < pdata->num_devices; ++i) { + struct omap_dss_device *dssdev = pdata->devices[i]; + + if (dssdev->type != OMAP_DISPLAY_TYPE_VENC) + continue; + + r = venc_init_display(dssdev); + if (r) { + DSSERR("device %s init failed: %d\n", dssdev->name, r); + continue; + } + + r = omap_dss_register_device(dssdev, &pdev->dev, i); + if (r) + DSSERR("device %s register failed: %d\n", + dssdev->name, r); + } +} + /* VENC HW IP initialisation */ -static int omap_venchw_probe(struct platform_device *pdev) +static int __init omap_venchw_probe(struct platform_device *pdev) { u8 rev_id; struct resource *venc_mem; @@ -824,6 +897,10 @@ static int omap_venchw_probe(struct platform_device *pdev) if (r) goto err_reg_panel_driver; + dss_debugfs_create_file("venc", venc_dump_regs); + + venc_probe_pdata(pdev); + return 0; err_reg_panel_driver: @@ -833,12 +910,15 @@ err_runtime_get: return r; } -static int omap_venchw_remove(struct platform_device *pdev) +static int __exit omap_venchw_remove(struct platform_device *pdev) { + omap_dss_unregister_child_devices(&pdev->dev); + if (venc.vdda_dac_reg != NULL) { regulator_put(venc.vdda_dac_reg); venc.vdda_dac_reg = NULL; } + omap_dss_unregister_driver(&venc_driver); pm_runtime_disable(&pdev->dev); @@ -853,7 +933,6 @@ static int venc_runtime_suspend(struct device *dev) clk_disable(venc.tv_dac_clk); dispc_runtime_put(); - dss_runtime_put(); return 0; } @@ -862,23 +941,14 @@ static int venc_runtime_resume(struct device *dev) { int r; - r = dss_runtime_get(); - if (r < 0) - goto err_get_dss; - r = dispc_runtime_get(); if (r < 0) - goto err_get_dispc; + return r; if (venc.tv_dac_clk) clk_enable(venc.tv_dac_clk); return 0; - -err_get_dispc: - dss_runtime_put(); -err_get_dss: - return r; } static const struct dev_pm_ops venc_pm_ops = { @@ -887,8 +957,7 @@ static const struct dev_pm_ops venc_pm_ops = { }; static struct platform_driver omap_venchw_driver = { - .probe = omap_venchw_probe, - .remove = omap_venchw_remove, + .remove = __exit_p(omap_venchw_remove), .driver = { .name = "omapdss_venc", .owner = THIS_MODULE, @@ -896,18 +965,18 @@ static struct platform_driver omap_venchw_driver = { }, }; -int venc_init_platform_driver(void) +int __init venc_init_platform_driver(void) { if (cpu_is_omap44xx()) return 0; - return platform_driver_register(&omap_venchw_driver); + return platform_driver_probe(&omap_venchw_driver, omap_venchw_probe); } -void venc_uninit_platform_driver(void) +void __exit venc_uninit_platform_driver(void) { if (cpu_is_omap44xx()) return; - return platform_driver_unregister(&omap_venchw_driver); + platform_driver_unregister(&omap_venchw_driver); } diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c index 6a09ef87e14f..c6cf372d22c5 100644 --- a/drivers/video/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c @@ -70,7 +70,7 @@ static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) DBG("omapfb_setup_plane\n"); - if (ofbi->num_overlays != 1) { + if (ofbi->num_overlays == 0) { r = -EINVAL; goto out; } @@ -185,7 +185,7 @@ static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) { struct omapfb_info *ofbi = FB2OFB(fbi); - if (ofbi->num_overlays != 1) { + if (ofbi->num_overlays == 0) { memset(pi, 0, sizeof(*pi)); } else { struct omap_overlay *ovl; @@ -225,6 +225,9 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) down_write_nested(&rg->lock, rg->id); atomic_inc(&rg->lock_count); + if (rg->size == size && rg->type == mi->type) + goto out; + if (atomic_read(&rg->map_count)) { r = -EBUSY; goto out; @@ -247,12 +250,10 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) } } - if (rg->size != size || rg->type != mi->type) { - r = omapfb_realloc_fbmem(fbi, size, mi->type); - if (r) { - dev_err(fbdev->dev, "realloc fbmem failed\n"); - goto out; - } + r = omapfb_realloc_fbmem(fbi, size, mi->type); + if (r) { + dev_err(fbdev->dev, "realloc fbmem failed\n"); + goto out; } out: diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c index b00db4068d21..3450ea0966c9 100644 --- a/drivers/video/omap2/omapfb/omapfb-main.c +++ b/drivers/video/omap2/omapfb/omapfb-main.c @@ -179,6 +179,7 @@ static unsigned omapfb_get_vrfb_offset(const struct omapfb_info *ofbi, int rot) break; default: BUG(); + return 0; } offset *= vrfb->bytespp; @@ -1502,7 +1503,7 @@ static int omapfb_parse_vram_param(const char *param, int max_entries, fbnum = simple_strtoul(p, &p, 10); - if (p == param) + if (p == start) return -EINVAL; if (*p != ':') @@ -2307,7 +2308,7 @@ static int omapfb_init_display(struct omapfb2_device *fbdev, return 0; } -static int omapfb_probe(struct platform_device *pdev) +static int __init omapfb_probe(struct platform_device *pdev) { struct omapfb2_device *fbdev = NULL; int r = 0; @@ -2448,7 +2449,7 @@ err0: return r; } -static int omapfb_remove(struct platform_device *pdev) +static int __exit omapfb_remove(struct platform_device *pdev) { struct omapfb2_device *fbdev = platform_get_drvdata(pdev); @@ -2462,8 +2463,7 @@ static int omapfb_remove(struct platform_device *pdev) } static struct platform_driver omapfb_driver = { - .probe = omapfb_probe, - .remove = omapfb_remove, + .remove = __exit_p(omapfb_remove), .driver = { .name = "omapfb", .owner = THIS_MODULE, @@ -2474,7 +2474,7 @@ static int __init omapfb_init(void) { DBG("omapfb_init\n"); - if (platform_driver_register(&omapfb_driver)) { + if (platform_driver_probe(&omapfb_driver, omapfb_probe)) { printk(KERN_ERR "failed to register omapfb driver\n"); return -ENODEV; } diff --git a/drivers/video/omap2/omapfb/omapfb.h b/drivers/video/omap2/omapfb/omapfb.h index c0bdc9b54ecf..30361a09aecd 100644 --- a/drivers/video/omap2/omapfb/omapfb.h +++ b/drivers/video/omap2/omapfb/omapfb.h @@ -166,6 +166,7 @@ static inline struct omapfb_display_data *get_display_data( /* This should never happen */ BUG(); + return NULL; } static inline void omapfb_lock(struct omapfb2_device *fbdev) diff --git a/drivers/video/omap2/vrfb.c b/drivers/video/omap2/vrfb.c index 4e5b960c32c8..7e990220ad2a 100644 --- a/drivers/video/omap2/vrfb.c +++ b/drivers/video/omap2/vrfb.c @@ -179,8 +179,10 @@ void omap_vrfb_setup(struct vrfb *vrfb, unsigned long paddr, pixel_size_exp = 2; else if (bytespp == 2) pixel_size_exp = 1; - else + else { BUG(); + return; + } vrfb_width = ALIGN(width * bytespp, VRFB_PAGE_WIDTH) / bytespp; vrfb_height = ALIGN(height, VRFB_PAGE_HEIGHT); diff --git a/drivers/video/pxa3xx-gcu.c b/drivers/video/pxa3xx-gcu.c index 1d71c08a818f..0b4ae0cebeda 100644 --- a/drivers/video/pxa3xx-gcu.c +++ b/drivers/video/pxa3xx-gcu.c @@ -316,12 +316,9 @@ pxa3xx_gcu_wait_idle(struct pxa3xx_gcu_priv *priv) ret = wait_event_interruptible_timeout(priv->wait_idle, !priv->shared->hw_running, HZ*4); - if (ret < 0) + if (ret != 0) break; - if (ret > 0) - continue; - if (gc_readl(priv, REG_GCRBEXHR) == rbexhr && priv->shared->num_interrupts == num) { QERROR("TIMEOUT"); diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index f3105160bf98..5f9d8e69029e 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -47,7 +47,7 @@ #ifdef CONFIG_FB_S3C_DEBUG_REGWRITE #undef writel #define writel(v, r) do { \ - printk(KERN_DEBUG "%s: %08x => %p\n", __func__, (unsigned int)v, r); \ + pr_debug("%s: %08x => %p\n", __func__, (unsigned int)v, r); \ __raw_writel(v, r); \ } while (0) #endif /* FB_S3C_DEBUG_REGWRITE */ @@ -495,7 +495,6 @@ static int s3c_fb_set_par(struct fb_info *info) u32 alpha = 0; u32 data; u32 pagewidth; - int clkdiv; dev_dbg(sfb->dev, "setting framebuffer parameters\n"); @@ -532,48 +531,9 @@ static int s3c_fb_set_par(struct fb_info *info) /* disable the window whilst we update it */ writel(0, regs + WINCON(win_no)); - /* use platform specified window as the basis for the lcd timings */ - - if (win_no == sfb->pdata->default_win) { - clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock); - - data = sfb->pdata->vidcon0; - data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); - - if (clkdiv > 1) - data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR; - else - data &= ~VIDCON0_CLKDIR; /* 1:1 clock */ - - /* write the timing data to the panel */ - - if (sfb->variant.is_2443) - data |= (1 << 5); - - writel(data, regs + VIDCON0); - + if (!sfb->output_on) s3c_fb_enable(sfb, 1); - data = VIDTCON0_VBPD(var->upper_margin - 1) | - VIDTCON0_VFPD(var->lower_margin - 1) | - VIDTCON0_VSPW(var->vsync_len - 1); - - writel(data, regs + sfb->variant.vidtcon); - - data = VIDTCON1_HBPD(var->left_margin - 1) | - VIDTCON1_HFPD(var->right_margin - 1) | - VIDTCON1_HSPW(var->hsync_len - 1); - - /* VIDTCON1 */ - writel(data, regs + sfb->variant.vidtcon + 4); - - data = VIDTCON2_LINEVAL(var->yres - 1) | - VIDTCON2_HOZVAL(var->xres - 1) | - VIDTCON2_LINEVAL_E(var->yres - 1) | - VIDTCON2_HOZVAL_E(var->xres - 1); - writel(data, regs + sfb->variant.vidtcon + 8); - } - /* write the buffer address */ /* start and end registers stride is 8 */ @@ -839,6 +799,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) struct s3c_fb *sfb = win->parent; unsigned int index = win->index; u32 wincon; + u32 output_on = sfb->output_on; dev_dbg(sfb->dev, "blank mode %d\n", blank_mode); @@ -877,34 +838,18 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) shadow_protect_win(win, 1); writel(wincon, sfb->regs + sfb->variant.wincon + (index * 4)); - shadow_protect_win(win, 0); /* Check the enabled state to see if we need to be running the * main LCD interface, as if there are no active windows then * it is highly likely that we also do not need to output * anything. */ - - /* We could do something like the following code, but the current - * system of using framebuffer events means that we cannot make - * the distinction between just window 0 being inactive and all - * the windows being down. - * - * s3c_fb_enable(sfb, sfb->enabled ? 1 : 0); - */ - - /* we're stuck with this until we can do something about overriding - * the power control using the blanking event for a single fb. - */ - if (index == sfb->pdata->default_win) { - shadow_protect_win(win, 1); - s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0); - shadow_protect_win(win, 0); - } + s3c_fb_enable(sfb, sfb->enabled ? 1 : 0); + shadow_protect_win(win, 0); pm_runtime_put_sync(sfb->dev); - return 0; + return output_on == sfb->output_on; } /** @@ -1111,7 +1056,7 @@ static struct fb_ops s3c_fb_ops = { * * Calculate the pixel clock when none has been given through platform data. */ -static void __devinit s3c_fb_missing_pixclock(struct fb_videomode *mode) +static void s3c_fb_missing_pixclock(struct fb_videomode *mode) { u64 pixclk = 1000000000000ULL; u32 div; @@ -1144,11 +1089,11 @@ static int __devinit s3c_fb_alloc_memory(struct s3c_fb *sfb, dev_dbg(sfb->dev, "allocating memory for display\n"); - real_size = windata->win_mode.xres * windata->win_mode.yres; + real_size = windata->xres * windata->yres; virt_size = windata->virtual_x * windata->virtual_y; dev_dbg(sfb->dev, "real_size=%u (%u.%u), virt_size=%u (%u.%u)\n", - real_size, windata->win_mode.xres, windata->win_mode.yres, + real_size, windata->xres, windata->yres, virt_size, windata->virtual_x, windata->virtual_y); size = (real_size > virt_size) ? real_size : virt_size; @@ -1230,7 +1175,7 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, struct s3c_fb_win **res) { struct fb_var_screeninfo *var; - struct fb_videomode *initmode; + struct fb_videomode initmode; struct s3c_fb_pd_win *windata; struct s3c_fb_win *win; struct fb_info *fbinfo; @@ -1251,11 +1196,11 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, } windata = sfb->pdata->win[win_no]; - initmode = &windata->win_mode; + initmode = *sfb->pdata->vtiming; WARN_ON(windata->max_bpp == 0); - WARN_ON(windata->win_mode.xres == 0); - WARN_ON(windata->win_mode.yres == 0); + WARN_ON(windata->xres == 0); + WARN_ON(windata->yres == 0); win = fbinfo->par; *res = win; @@ -1294,7 +1239,9 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, } /* setup the initial video mode from the window */ - fb_videomode_to_var(&fbinfo->var, initmode); + initmode.xres = windata->xres; + initmode.yres = windata->yres; + fb_videomode_to_var(&fbinfo->var, &initmode); fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; fbinfo->fix.accel = FB_ACCEL_NONE; @@ -1339,6 +1286,53 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, } /** + * s3c_fb_set_rgb_timing() - set video timing for rgb interface. + * @sfb: The base resources for the hardware. + * + * Set horizontal and vertical lcd rgb interface timing. + */ +static void s3c_fb_set_rgb_timing(struct s3c_fb *sfb) +{ + struct fb_videomode *vmode = sfb->pdata->vtiming; + void __iomem *regs = sfb->regs; + int clkdiv; + u32 data; + + if (!vmode->pixclock) + s3c_fb_missing_pixclock(vmode); + + clkdiv = s3c_fb_calc_pixclk(sfb, vmode->pixclock); + + data = sfb->pdata->vidcon0; + data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); + + if (clkdiv > 1) + data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR; + else + data &= ~VIDCON0_CLKDIR; /* 1:1 clock */ + + if (sfb->variant.is_2443) + data |= (1 << 5); + writel(data, regs + VIDCON0); + + data = VIDTCON0_VBPD(vmode->upper_margin - 1) | + VIDTCON0_VFPD(vmode->lower_margin - 1) | + VIDTCON0_VSPW(vmode->vsync_len - 1); + writel(data, regs + sfb->variant.vidtcon); + + data = VIDTCON1_HBPD(vmode->left_margin - 1) | + VIDTCON1_HFPD(vmode->right_margin - 1) | + VIDTCON1_HSPW(vmode->hsync_len - 1); + writel(data, regs + sfb->variant.vidtcon + 4); + + data = VIDTCON2_LINEVAL(vmode->yres - 1) | + VIDTCON2_HOZVAL(vmode->xres - 1) | + VIDTCON2_LINEVAL_E(vmode->yres - 1) | + VIDTCON2_HOZVAL_E(vmode->xres - 1); + writel(data, regs + sfb->variant.vidtcon + 8); +} + +/** * s3c_fb_clear_win() - clear hardware window registers. * @sfb: The base resources for the hardware. * @win: The window to process. @@ -1481,15 +1475,14 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) writel(0xffffff, regs + WKEYCON1); } + s3c_fb_set_rgb_timing(sfb); + /* we have the register setup, start allocating framebuffers */ for (win = 0; win < fbdrv->variant.nr_windows; win++) { if (!pd->win[win]) continue; - if (!pd->win[win]->win_mode.pixclock) - s3c_fb_missing_pixclock(&pd->win[win]->win_mode); - ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win], &sfb->windows[win]); if (ret < 0) { @@ -1564,6 +1557,8 @@ static int s3c_fb_suspend(struct device *dev) struct s3c_fb_win *win; int win_no; + pm_runtime_get_sync(sfb->dev); + for (win_no = S3C_FB_MAX_WIN - 1; win_no >= 0; win_no--) { win = sfb->windows[win_no]; if (!win) @@ -1577,6 +1572,9 @@ static int s3c_fb_suspend(struct device *dev) clk_disable(sfb->lcd_clk); clk_disable(sfb->bus_clk); + + pm_runtime_put_sync(sfb->dev); + return 0; } @@ -1589,6 +1587,8 @@ static int s3c_fb_resume(struct device *dev) int win_no; u32 reg; + pm_runtime_get_sync(sfb->dev); + clk_enable(sfb->bus_clk); if (!sfb->variant.has_clksel) @@ -1623,6 +1623,8 @@ static int s3c_fb_resume(struct device *dev) shadow_protect_win(win, 0); } + s3c_fb_set_rgb_timing(sfb); + /* restore framebuffers */ for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) { win = sfb->windows[win_no]; @@ -1633,6 +1635,8 @@ static int s3c_fb_resume(struct device *dev) s3c_fb_set_par(win->fbinfo); } + pm_runtime_put_sync(sfb->dev); + return 0; } #endif diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index eafb19da2c07..930e550e752a 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c @@ -31,6 +31,7 @@ #include "sh_mobile_lcdcfb.h" +/* HDMI Core Control Register (HTOP0) */ #define HDMI_SYSTEM_CTRL 0x00 /* System control */ #define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control, bits 19..16 of 20-bit N for Audio Clock Regeneration packet */ @@ -201,6 +202,68 @@ #define HDMI_REVISION_ID 0xF1 /* Revision ID */ #define HDMI_TEST_MODE 0xFE /* Test mode */ +/* HDMI Control Register (HTOP1) */ +#define HDMI_HTOP1_TEST_MODE 0x0000 /* Test mode */ +#define HDMI_HTOP1_VIDEO_INPUT 0x0008 /* VideoInput */ +#define HDMI_HTOP1_CORE_RSTN 0x000C /* CoreResetn */ +#define HDMI_HTOP1_PLLBW 0x0018 /* PLLBW */ +#define HDMI_HTOP1_CLK_TO_PHY 0x001C /* Clk to Phy */ +#define HDMI_HTOP1_VIDEO_INPUT2 0x0020 /* VideoInput2 */ +#define HDMI_HTOP1_TISEMP0_1 0x0024 /* tisemp0-1 */ +#define HDMI_HTOP1_TISEMP2_C 0x0028 /* tisemp2-c */ +#define HDMI_HTOP1_TISIDRV 0x002C /* tisidrv */ +#define HDMI_HTOP1_TISEN 0x0034 /* tisen */ +#define HDMI_HTOP1_TISDREN 0x0038 /* tisdren */ +#define HDMI_HTOP1_CISRANGE 0x003C /* cisrange */ +#define HDMI_HTOP1_ENABLE_SELECTOR 0x0040 /* Enable Selector */ +#define HDMI_HTOP1_MACRO_RESET 0x0044 /* Macro reset */ +#define HDMI_HTOP1_PLL_CALIBRATION 0x0048 /* PLL calibration */ +#define HDMI_HTOP1_RE_CALIBRATION 0x004C /* Re-calibration */ +#define HDMI_HTOP1_CURRENT 0x0050 /* Current */ +#define HDMI_HTOP1_PLL_LOCK_DETECT 0x0054 /* PLL lock detect */ +#define HDMI_HTOP1_PHY_TEST_MODE 0x0058 /* PHY Test Mode */ +#define HDMI_HTOP1_CLK_SET 0x0080 /* Clock Set */ +#define HDMI_HTOP1_DDC_FAIL_SAFE 0x0084 /* DDC fail safe */ +#define HDMI_HTOP1_PRBS 0x0088 /* PRBS */ +#define HDMI_HTOP1_EDID_AINC_CONTROL 0x008C /* EDID ainc Control */ +#define HDMI_HTOP1_HTOP_DCL_MODE 0x00FC /* Deep Coloer Mode */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF0 0x0100 /* Deep Color:FRC COEF0 */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF1 0x0104 /* Deep Color:FRC COEF1 */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF2 0x0108 /* Deep Color:FRC COEF2 */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF3 0x010C /* Deep Color:FRC COEF3 */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF0_C 0x0110 /* Deep Color:FRC COEF0C */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF1_C 0x0114 /* Deep Color:FRC COEF1C */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF2_C 0x0118 /* Deep Color:FRC COEF2C */ +#define HDMI_HTOP1_HTOP_DCL_FRC_COEF3_C 0x011C /* Deep Color:FRC COEF3C */ +#define HDMI_HTOP1_HTOP_DCL_FRC_MODE 0x0120 /* Deep Color:FRC Mode */ +#define HDMI_HTOP1_HTOP_DCL_RECT_START1 0x0124 /* Deep Color:Rect Start1 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_SIZE1 0x0128 /* Deep Color:Rect Size1 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_START2 0x012C /* Deep Color:Rect Start2 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_SIZE2 0x0130 /* Deep Color:Rect Size2 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_START3 0x0134 /* Deep Color:Rect Start3 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_SIZE3 0x0138 /* Deep Color:Rect Size3 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_START4 0x013C /* Deep Color:Rect Start4 */ +#define HDMI_HTOP1_HTOP_DCL_RECT_SIZE4 0x0140 /* Deep Color:Rect Size4 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y1_1 0x0144 /* Deep Color:Fil Para Y1_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y1_2 0x0148 /* Deep Color:Fil Para Y1_2 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB1_1 0x014C /* Deep Color:Fil Para CB1_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB1_2 0x0150 /* Deep Color:Fil Para CB1_2 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR1_1 0x0154 /* Deep Color:Fil Para CR1_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR1_2 0x0158 /* Deep Color:Fil Para CR1_2 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y2_1 0x015C /* Deep Color:Fil Para Y2_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y2_2 0x0160 /* Deep Color:Fil Para Y2_2 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB2_1 0x0164 /* Deep Color:Fil Para CB2_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB2_2 0x0168 /* Deep Color:Fil Para CB2_2 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR2_1 0x016C /* Deep Color:Fil Para CR2_1 */ +#define HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR2_2 0x0170 /* Deep Color:Fil Para CR2_2 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_Y1 0x0174 /* Deep Color:Cor Para Y1 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_CB1 0x0178 /* Deep Color:Cor Para CB1 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_CR1 0x017C /* Deep Color:Cor Para CR1 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_Y2 0x0180 /* Deep Color:Cor Para Y2 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_CB2 0x0184 /* Deep Color:Cor Para CB2 */ +#define HDMI_HTOP1_HTOP_DCL_COR_PARA_CR2 0x0188 /* Deep Color:Cor Para CR2 */ +#define HDMI_HTOP1_EDID_DATA_READ 0x0200 /* EDID Data Read 128Byte:0x03FC */ + enum hotplug_state { HDMI_HOTPLUG_DISCONNECTED, HDMI_HOTPLUG_CONNECTED, @@ -211,6 +274,7 @@ struct sh_hdmi { struct sh_mobile_lcdc_entity entity; void __iomem *base; + void __iomem *htop1; enum hotplug_state hp_state; /* hot-plug status */ u8 preprogrammed_vic; /* use a pre-programmed VIC or the external mode */ @@ -222,20 +286,66 @@ struct sh_hdmi { struct delayed_work edid_work; struct fb_videomode mode; struct fb_monspecs monspec; + + /* register access functions */ + void (*write)(struct sh_hdmi *hdmi, u8 data, u8 reg); + u8 (*read)(struct sh_hdmi *hdmi, u8 reg); }; #define entity_to_sh_hdmi(e) container_of(e, struct sh_hdmi, entity) -static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) +static void __hdmi_write8(struct sh_hdmi *hdmi, u8 data, u8 reg) { iowrite8(data, hdmi->base + reg); } -static u8 hdmi_read(struct sh_hdmi *hdmi, u8 reg) +static u8 __hdmi_read8(struct sh_hdmi *hdmi, u8 reg) { return ioread8(hdmi->base + reg); } +static void __hdmi_write32(struct sh_hdmi *hdmi, u8 data, u8 reg) +{ + iowrite32((u32)data, hdmi->base + (reg * 4)); + udelay(100); +} + +static u8 __hdmi_read32(struct sh_hdmi *hdmi, u8 reg) +{ + return (u8)ioread32(hdmi->base + (reg * 4)); +} + +static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) +{ + hdmi->write(hdmi, data, reg); +} + +static u8 hdmi_read(struct sh_hdmi *hdmi, u8 reg) +{ + return hdmi->read(hdmi, reg); +} + +static void hdmi_bit_set(struct sh_hdmi *hdmi, u8 mask, u8 data, u8 reg) +{ + u8 val = hdmi_read(hdmi, reg); + + val &= ~mask; + val |= (data & mask); + + hdmi_write(hdmi, val, reg); +} + +static void hdmi_htop1_write(struct sh_hdmi *hdmi, u32 data, u32 reg) +{ + iowrite32(data, hdmi->htop1 + reg); + udelay(100); +} + +static u32 hdmi_htop1_read(struct sh_hdmi *hdmi, u32 reg) +{ + return ioread32(hdmi->htop1 + reg); +} + /* * HDMI sound */ @@ -693,11 +803,11 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi) msleep(10); /* PS mode b->d, reset PLLA and PLLB */ - hdmi_write(hdmi, 0x4C, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x4C, HDMI_SYSTEM_CTRL); udelay(10); - hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x40, HDMI_SYSTEM_CTRL); } static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, @@ -746,7 +856,9 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, /* Read EDID */ dev_dbg(hdmi->dev, "Read back EDID code:"); for (i = 0; i < 128; i++) { - edid[i] = hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW); + edid[i] = (hdmi->htop1) ? + (u8)hdmi_htop1_read(hdmi, HDMI_HTOP1_EDID_DATA_READ + (i * 4)) : + hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW); #ifdef DEBUG if ((i % 16) == 0) { printk(KERN_CONT "\n"); @@ -917,13 +1029,13 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) u8 status1, status2, mask1, mask2; /* mode_b and PLLA and PLLB reset */ - hdmi_write(hdmi, 0x2C, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x2C, HDMI_SYSTEM_CTRL); /* How long shall reset be held? */ udelay(10); /* mode_b and PLLA and PLLB reset release */ - hdmi_write(hdmi, 0x20, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x20, HDMI_SYSTEM_CTRL); status1 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_1); status2 = hdmi_read(hdmi, HDMI_INTERRUPT_STATUS_2); @@ -1001,7 +1113,7 @@ static int sh_hdmi_display_on(struct sh_mobile_lcdc_entity *entity) */ if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { /* PS mode d->e. All functions are active */ - hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x80, HDMI_SYSTEM_CTRL); dev_dbg(hdmi->dev, "HDMI running\n"); } @@ -1016,7 +1128,7 @@ static void sh_hdmi_display_off(struct sh_mobile_lcdc_entity *entity) dev_dbg(hdmi->dev, "%s(%p)\n", __func__, hdmi); /* PS mode e->a */ - hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); + hdmi_bit_set(hdmi, 0xFC, 0x10, HDMI_SYSTEM_CTRL); } static const struct sh_mobile_lcdc_entity_ops sh_hdmi_ops = { @@ -1110,10 +1222,58 @@ out: dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, hdmi); } +static void sh_hdmi_htop1_init(struct sh_hdmi *hdmi) +{ + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_MODE); + hdmi_htop1_write(hdmi, 0x0000000b, 0x0010); + hdmi_htop1_write(hdmi, 0x00006710, HDMI_HTOP1_HTOP_DCL_FRC_MODE); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y1_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y1_2); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB1_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB1_2); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR1_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR1_2); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y2_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_Y2_2); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB2_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CB2_2); + hdmi_htop1_write(hdmi, 0x01020406, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR2_1); + hdmi_htop1_write(hdmi, 0x07080806, HDMI_HTOP1_HTOP_DCL_FIL_PARA_CR2_2); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_Y1); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_CB1); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_CR1); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_Y2); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_CB2); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_HTOP_DCL_COR_PARA_CR2); + hdmi_htop1_write(hdmi, 0x00000008, HDMI_HTOP1_CURRENT); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_TISEMP0_1); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_TISEMP2_C); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_PHY_TEST_MODE); + hdmi_htop1_write(hdmi, 0x00000081, HDMI_HTOP1_TISIDRV); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_PLLBW); + hdmi_htop1_write(hdmi, 0x0000000f, HDMI_HTOP1_TISEN); + hdmi_htop1_write(hdmi, 0x0000000f, HDMI_HTOP1_TISDREN); + hdmi_htop1_write(hdmi, 0x00000003, HDMI_HTOP1_ENABLE_SELECTOR); + hdmi_htop1_write(hdmi, 0x00000001, HDMI_HTOP1_MACRO_RESET); + hdmi_htop1_write(hdmi, 0x00000016, HDMI_HTOP1_CISRANGE); + msleep(100); + hdmi_htop1_write(hdmi, 0x00000001, HDMI_HTOP1_ENABLE_SELECTOR); + msleep(100); + hdmi_htop1_write(hdmi, 0x00000003, HDMI_HTOP1_ENABLE_SELECTOR); + hdmi_htop1_write(hdmi, 0x00000001, HDMI_HTOP1_MACRO_RESET); + hdmi_htop1_write(hdmi, 0x0000000f, HDMI_HTOP1_TISEN); + hdmi_htop1_write(hdmi, 0x0000000f, HDMI_HTOP1_TISDREN); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_VIDEO_INPUT); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_CLK_TO_PHY); + hdmi_htop1_write(hdmi, 0x00000000, HDMI_HTOP1_VIDEO_INPUT2); + hdmi_htop1_write(hdmi, 0x0000000a, HDMI_HTOP1_CLK_SET); +} + static int __init sh_hdmi_probe(struct platform_device *pdev) { struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *htop1_res; int irq = platform_get_irq(pdev, 0), ret; struct sh_hdmi *hdmi; long rate; @@ -1121,6 +1281,15 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) if (!res || !pdata || irq < 0) return -ENODEV; + htop1_res = NULL; + if (pdata->flags & HDMI_HAS_HTOP1) { + htop1_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!htop1_res) { + dev_err(&pdev->dev, "htop1 needs register base\n"); + return -EINVAL; + } + } + hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL); if (!hdmi) { dev_err(&pdev->dev, "Cannot allocate device data\n"); @@ -1138,6 +1307,15 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) goto egetclk; } + /* select register access functions */ + if (pdata->flags & HDMI_32BIT_REG) { + hdmi->write = __hdmi_write32; + hdmi->read = __hdmi_read32; + } else { + hdmi->write = __hdmi_write8; + hdmi->read = __hdmi_read8; + } + /* An arbitrary relaxed pixclock just to get things started: from standard 480p */ rate = clk_round_rate(hdmi->hdmi_clk, PICOS2KHZ(37037)); if (rate > 0) @@ -1176,6 +1354,24 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); + /* init interrupt polarity */ + if (pdata->flags & HDMI_OUTPUT_PUSH_PULL) + hdmi_bit_set(hdmi, 0x02, 0x02, HDMI_SYSTEM_CTRL); + + if (pdata->flags & HDMI_OUTPUT_POLARITY_HI) + hdmi_bit_set(hdmi, 0x01, 0x01, HDMI_SYSTEM_CTRL); + + /* enable htop1 register if needed */ + if (htop1_res) { + hdmi->htop1 = ioremap(htop1_res->start, resource_size(htop1_res)); + if (!hdmi->htop1) { + dev_err(&pdev->dev, "control register region already claimed\n"); + ret = -ENOMEM; + goto emap_htop1; + } + sh_hdmi_htop1_init(hdmi); + } + /* Product and revision IDs are 0 in sh-mobile version */ dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); @@ -1199,6 +1395,9 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) ecodec: free_irq(irq, hdmi); ereqirq: + if (hdmi->htop1) + iounmap(hdmi->htop1); +emap_htop1: pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); iounmap(hdmi->base); @@ -1230,6 +1429,8 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); clk_disable(hdmi->hdmi_clk); clk_put(hdmi->hdmi_clk); + if (hdmi->htop1) + iounmap(hdmi->htop1); iounmap(hdmi->base); release_mem_region(res->start, resource_size(res)); kfree(hdmi); diff --git a/drivers/video/sis/init.h b/drivers/video/sis/init.h index aff73842d877..85d6738b6c64 100644 --- a/drivers/video/sis/init.h +++ b/drivers/video/sis/init.h @@ -105,51 +105,6 @@ static const unsigned short ModeIndex_1920x1440[] = {0x68, 0x69, 0x00, 0x6b}; static const unsigned short ModeIndex_300_2048x1536[]= {0x6c, 0x6d, 0x00, 0x00}; static const unsigned short ModeIndex_310_2048x1536[]= {0x6c, 0x6d, 0x00, 0x6e}; -static const unsigned short SiS_DRAMType[17][5]={ - {0x0C,0x0A,0x02,0x40,0x39}, - {0x0D,0x0A,0x01,0x40,0x48}, - {0x0C,0x09,0x02,0x20,0x35}, - {0x0D,0x09,0x01,0x20,0x44}, - {0x0C,0x08,0x02,0x10,0x31}, - {0x0D,0x08,0x01,0x10,0x40}, - {0x0C,0x0A,0x01,0x20,0x34}, - {0x0C,0x09,0x01,0x08,0x32}, - {0x0B,0x08,0x02,0x08,0x21}, - {0x0C,0x08,0x01,0x08,0x30}, - {0x0A,0x08,0x02,0x04,0x11}, - {0x0B,0x0A,0x01,0x10,0x28}, - {0x09,0x08,0x02,0x02,0x01}, - {0x0B,0x09,0x01,0x08,0x24}, - {0x0B,0x08,0x01,0x04,0x20}, - {0x0A,0x08,0x01,0x02,0x10}, - {0x09,0x08,0x01,0x01,0x00} -}; - -static const unsigned short SiS_SDRDRAM_TYPE[13][5] = -{ - { 2,12, 9,64,0x35}, - { 1,13, 9,64,0x44}, - { 2,12, 8,32,0x31}, - { 2,11, 9,32,0x25}, - { 1,12, 9,32,0x34}, - { 1,13, 8,32,0x40}, - { 2,11, 8,16,0x21}, - { 1,12, 8,16,0x30}, - { 1,11, 9,16,0x24}, - { 1,11, 8, 8,0x20}, - { 2, 9, 8, 4,0x01}, - { 1,10, 8, 4,0x10}, - { 1, 9, 8, 2,0x00} -}; - -static const unsigned short SiS_DDRDRAM_TYPE[4][5] = -{ - { 2,12, 9,64,0x35}, - { 2,12, 8,32,0x31}, - { 2,11, 8,16,0x21}, - { 2, 9, 8, 4,0x01} -}; - static const unsigned char SiS_MDA_DAC[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c index 078ca2167d6f..a7a48db64ce2 100644 --- a/drivers/video/sis/sis_main.c +++ b/drivers/video/sis/sis_main.c @@ -4222,6 +4222,26 @@ sisfb_post_300_buswidth(struct sis_video_info *ivideo) return 1; /* 32bit */ } +static const unsigned short __devinitconst SiS_DRAMType[17][5] = { + {0x0C,0x0A,0x02,0x40,0x39}, + {0x0D,0x0A,0x01,0x40,0x48}, + {0x0C,0x09,0x02,0x20,0x35}, + {0x0D,0x09,0x01,0x20,0x44}, + {0x0C,0x08,0x02,0x10,0x31}, + {0x0D,0x08,0x01,0x10,0x40}, + {0x0C,0x0A,0x01,0x20,0x34}, + {0x0C,0x09,0x01,0x08,0x32}, + {0x0B,0x08,0x02,0x08,0x21}, + {0x0C,0x08,0x01,0x08,0x30}, + {0x0A,0x08,0x02,0x04,0x11}, + {0x0B,0x0A,0x01,0x10,0x28}, + {0x09,0x08,0x02,0x02,0x01}, + {0x0B,0x09,0x01,0x08,0x24}, + {0x0B,0x08,0x01,0x04,0x20}, + {0x0A,0x08,0x01,0x02,0x10}, + {0x09,0x08,0x01,0x01,0x00} +}; + static int __devinit sisfb_post_300_rwtest(struct sis_video_info *ivideo, int iteration, int buswidth, int PseudoRankCapacity, int PseudoAdrPinCount, @@ -4231,27 +4251,8 @@ sisfb_post_300_rwtest(struct sis_video_info *ivideo, int iteration, int buswidth unsigned short sr14; unsigned int k, RankCapacity, PageCapacity, BankNumHigh, BankNumMid; unsigned int PhysicalAdrOtherPage, PhysicalAdrHigh, PhysicalAdrHalfPage; - static const unsigned short SiS_DRAMType[17][5] = { - {0x0C,0x0A,0x02,0x40,0x39}, - {0x0D,0x0A,0x01,0x40,0x48}, - {0x0C,0x09,0x02,0x20,0x35}, - {0x0D,0x09,0x01,0x20,0x44}, - {0x0C,0x08,0x02,0x10,0x31}, - {0x0D,0x08,0x01,0x10,0x40}, - {0x0C,0x0A,0x01,0x20,0x34}, - {0x0C,0x09,0x01,0x08,0x32}, - {0x0B,0x08,0x02,0x08,0x21}, - {0x0C,0x08,0x01,0x08,0x30}, - {0x0A,0x08,0x02,0x04,0x11}, - {0x0B,0x0A,0x01,0x10,0x28}, - {0x09,0x08,0x02,0x02,0x01}, - {0x0B,0x09,0x01,0x08,0x24}, - {0x0B,0x08,0x01,0x04,0x20}, - {0x0A,0x08,0x01,0x02,0x10}, - {0x09,0x08,0x01,0x01,0x00} - }; - for(k = 0; k <= 16; k++) { + for(k = 0; k < ARRAY_SIZE(SiS_DRAMType); k++) { RankCapacity = buswidth * SiS_DRAMType[k][3]; diff --git a/drivers/video/skeletonfb.c b/drivers/video/skeletonfb.c index 30f7a815a62b..5b6abc6de84b 100644 --- a/drivers/video/skeletonfb.c +++ b/drivers/video/skeletonfb.c @@ -1036,6 +1036,6 @@ static void __exit xxxfb_exit(void) */ module_init(xxxfb_init); -module_exit(xxxfb_remove); +module_exit(xxxfb_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/video/smscufx.c b/drivers/video/smscufx.c index ccbfef5e828f..af3ef27ad36c 100644 --- a/drivers/video/smscufx.c +++ b/drivers/video/smscufx.c @@ -846,7 +846,7 @@ static void ufx_raw_rect(struct ufx_data *dev, u16 *cmd, int x, int y, } } -int ufx_handle_damage(struct ufx_data *dev, int x, int y, +static int ufx_handle_damage(struct ufx_data *dev, int x, int y, int width, int height) { size_t packed_line_len = ALIGN((width * 2), 4); @@ -1083,7 +1083,7 @@ static int ufx_ops_open(struct fb_info *info, int user) struct fb_deferred_io *fbdefio; - fbdefio = kmalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); + fbdefio = kzalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); if (fbdefio) { fbdefio->delay = UFX_DEFIO_WRITE_DELAY; diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c index 7af1e8166182..8af64148294b 100644 --- a/drivers/video/udlfb.c +++ b/drivers/video/udlfb.c @@ -893,7 +893,7 @@ static int dlfb_ops_open(struct fb_info *info, int user) struct fb_deferred_io *fbdefio; - fbdefio = kmalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); + fbdefio = kzalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); if (fbdefio) { fbdefio->delay = DL_DEFIO_WRITE_DELAY; diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index 0c8837565bc7..c80e770e1800 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -1276,17 +1276,12 @@ static int viafb_dfph_proc_open(struct inode *inode, struct file *file) static ssize_t viafb_dfph_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) { - char buf[20]; - u8 reg_val = 0; - unsigned long length; - if (count < 1) - return -EINVAL; - length = count > 20 ? 20 : count; - if (copy_from_user(&buf[0], buffer, length)) - return -EFAULT; - buf[length - 1] = '\0'; /*Ensure end string */ - if (kstrtou8(buf, 0, ®_val) < 0) - return -EINVAL; + int err; + u8 reg_val; + err = kstrtou8_from_user(buffer, count, 0, ®_val); + if (err) + return err; + viafb_write_reg_mask(CR97, VIACR, reg_val, 0x0f); return count; } @@ -1316,17 +1311,12 @@ static int viafb_dfpl_proc_open(struct inode *inode, struct file *file) static ssize_t viafb_dfpl_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) { - char buf[20]; - u8 reg_val = 0; - unsigned long length; - if (count < 1) - return -EINVAL; - length = count > 20 ? 20 : count; - if (copy_from_user(&buf[0], buffer, length)) - return -EFAULT; - buf[length - 1] = '\0'; /*Ensure end string */ - if (kstrtou8(buf, 0, ®_val) < 0) - return -EINVAL; + int err; + u8 reg_val; + err = kstrtou8_from_user(buffer, count, 0, ®_val); + if (err) + return err; + viafb_write_reg_mask(CR99, VIACR, reg_val, 0x0f); return count; } diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index 44dca1f041c5..413ef89c2d1b 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -32,6 +32,13 @@ struct jffs2_inodirty; struct jffs2_mount_opts { bool override_compr; unsigned int compr; + + /* The size of the reserved pool. The reserved pool is the JFFS2 flash + * space which may only be used by root cannot be used by the other + * users. This is implemented simply by means of not allowing the + * latter users to write to the file system if the amount if the + * available space is less then 'rp_size'. */ + unsigned int rp_size; }; /* A struct for the overall file system control. Pointers to diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 6784d1e7a7eb..0c96eb52c797 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -18,6 +18,37 @@ #include "nodelist.h" #include "debug.h" +/* + * Check whether the user is allowed to write. + */ +static int jffs2_rp_can_write(struct jffs2_sb_info *c) +{ + uint32_t avail; + struct jffs2_mount_opts *opts = &c->mount_opts; + + avail = c->dirty_size + c->free_size + c->unchecked_size + + c->erasing_size - c->resv_blocks_write * c->sector_size + - c->nospc_dirty_size; + + if (avail < 2 * opts->rp_size) + jffs2_dbg(1, "rpsize %u, dirty_size %u, free_size %u, " + "erasing_size %u, unchecked_size %u, " + "nr_erasing_blocks %u, avail %u, resrv %u\n", + opts->rp_size, c->dirty_size, c->free_size, + c->erasing_size, c->unchecked_size, + c->nr_erasing_blocks, avail, c->nospc_dirty_size); + + if (avail > opts->rp_size) + return 1; + + /* Always allow root */ + if (capable(CAP_SYS_RESOURCE)) + return 1; + + jffs2_dbg(1, "forbid writing\n"); + return 0; +} + /** * jffs2_reserve_space - request physical space to write nodes to flash * @c: superblock info @@ -55,6 +86,15 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, spin_lock(&c->erase_completion_lock); + /* + * Check if the free space is greater then size of the reserved pool. + * If not, only allow root to proceed with writing. + */ + if (prio != ALLOC_DELETION && !jffs2_rp_can_write(c)) { + ret = -ENOSPC; + goto out; + } + /* this needs a little more thought (true <tglx> :)) */ while(ret == -EAGAIN) { while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) { @@ -158,6 +198,8 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, jffs2_dbg(1, "%s(): ret is %d\n", __func__, ret); } } + +out: spin_unlock(&c->erase_completion_lock); if (!ret) ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1); diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index dc0437e84763..1ea349fff68b 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -1266,19 +1266,25 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, /* Symlink's inode data is the target path. Read it and * keep in RAM to facilitate quick follow symlink * operation. */ - f->target = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL); + uint32_t csize = je32_to_cpu(latest_node->csize); + if (csize > JFFS2_MAX_NAME_LEN) { + mutex_unlock(&f->sem); + jffs2_do_clear_inode(c, f); + return -ENAMETOOLONG; + } + f->target = kmalloc(csize + 1, GFP_KERNEL); if (!f->target) { - JFFS2_ERROR("can't allocate %d bytes of memory for the symlink target path cache\n", je32_to_cpu(latest_node->csize)); + JFFS2_ERROR("can't allocate %u bytes of memory for the symlink target path cache\n", csize); mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -ENOMEM; } ret = jffs2_flash_read(c, ref_offset(rii.latest_ref) + sizeof(*latest_node), - je32_to_cpu(latest_node->csize), &retlen, (char *)f->target); + csize, &retlen, (char *)f->target); - if (ret || retlen != je32_to_cpu(latest_node->csize)) { - if (retlen != je32_to_cpu(latest_node->csize)) + if (ret || retlen != csize) { + if (retlen != csize) ret = -EIO; kfree(f->target); f->target = NULL; @@ -1287,7 +1293,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, return ret; } - f->target[je32_to_cpu(latest_node->csize)] = '\0'; + f->target[csize] = '\0'; dbg_readinode("symlink's target '%s' cached\n", f->target); } @@ -1415,6 +1421,7 @@ int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *i mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); } + jffs2_xattr_do_crccheck_inode(c, ic); kfree (f); return ret; } diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index bc586f204228..61ea41389f90 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -90,6 +90,8 @@ static int jffs2_show_options(struct seq_file *s, struct dentry *root) if (opts->override_compr) seq_printf(s, ",compr=%s", jffs2_compr_name(opts->compr)); + if (opts->rp_size) + seq_printf(s, ",rp_size=%u", opts->rp_size / 1024); return 0; } @@ -154,15 +156,18 @@ static const struct export_operations jffs2_export_ops = { * JFFS2 mount options. * * Opt_override_compr: override default compressor + * Opt_rp_size: size of reserved pool in KiB * Opt_err: just end of array marker */ enum { Opt_override_compr, + Opt_rp_size, Opt_err, }; static const match_table_t tokens = { {Opt_override_compr, "compr=%s"}, + {Opt_rp_size, "rp_size=%u"}, {Opt_err, NULL}, }; @@ -170,6 +175,7 @@ static int jffs2_parse_options(struct jffs2_sb_info *c, char *data) { substring_t args[MAX_OPT_ARGS]; char *p, *name; + unsigned int opt; if (!data) return 0; @@ -207,6 +213,17 @@ static int jffs2_parse_options(struct jffs2_sb_info *c, char *data) kfree(name); c->mount_opts.override_compr = true; break; + case Opt_rp_size: + if (match_int(&args[0], &opt)) + return -EINVAL; + opt *= 1024; + if (opt > c->mtd->size) { + pr_warn("Too large reserve pool specified, max " + "is %llu KB\n", c->mtd->size / 1024); + return -EINVAL; + } + c->mount_opts.rp_size = opt; + break; default: pr_err("Error: unrecognized mount option '%s' or missing value\n", p); diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index b55b803eddcb..3034e970eb9a 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -11,6 +11,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define JFFS2_XATTR_IS_CORRUPTED 1 + #include <linux/kernel.h> #include <linux/slab.h> #include <linux/fs.h> @@ -153,7 +155,7 @@ static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_dat JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", offset, je32_to_cpu(rx.hdr_crc), crc); xd->flags |= JFFS2_XFLAGS_INVALID; - return -EIO; + return JFFS2_XATTR_IS_CORRUPTED; } totlen = PAD(sizeof(rx) + rx.name_len + 1 + je16_to_cpu(rx.value_len)); if (je16_to_cpu(rx.magic) != JFFS2_MAGIC_BITMASK @@ -169,7 +171,7 @@ static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_dat je32_to_cpu(rx.xid), xd->xid, je32_to_cpu(rx.version), xd->version); xd->flags |= JFFS2_XFLAGS_INVALID; - return -EIO; + return JFFS2_XATTR_IS_CORRUPTED; } xd->xprefix = rx.xprefix; xd->name_len = rx.name_len; @@ -227,12 +229,12 @@ static int do_load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum data[xd->name_len] = '\0'; crc = crc32(0, data, length); if (crc != xd->data_crc) { - JFFS2_WARNING("node CRC failed (JFFS2_NODETYPE_XREF)" + JFFS2_WARNING("node CRC failed (JFFS2_NODETYPE_XATTR)" " at %#08x, read: 0x%08x calculated: 0x%08x\n", ref_offset(xd->node), xd->data_crc, crc); kfree(data); xd->flags |= JFFS2_XFLAGS_INVALID; - return -EIO; + return JFFS2_XATTR_IS_CORRUPTED; } xd->flags |= JFFS2_XFLAGS_HOT; @@ -270,7 +272,7 @@ static int load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *x if (xd->xname) return 0; if (xd->flags & JFFS2_XFLAGS_INVALID) - return -EIO; + return JFFS2_XATTR_IS_CORRUPTED; if (unlikely(is_xattr_datum_unchecked(c, xd))) rc = do_verify_xattr_datum(c, xd); if (!rc) @@ -435,6 +437,8 @@ static void unrefer_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datu * is called to release xattr related objects when unmounting. * check_xattr_ref_inode(c, ic) * is used to confirm inode does not have duplicate xattr name/value pair. + * jffs2_xattr_do_crccheck_inode(c, ic) + * is used to force xattr data integrity check during the initial gc scan. * -------------------------------------------------- */ static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) { @@ -462,7 +466,7 @@ static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref if (crc != je32_to_cpu(rr.node_crc)) { JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", offset, je32_to_cpu(rr.node_crc), crc); - return -EIO; + return JFFS2_XATTR_IS_CORRUPTED; } if (je16_to_cpu(rr.magic) != JFFS2_MAGIC_BITMASK || je16_to_cpu(rr.nodetype) != JFFS2_NODETYPE_XREF @@ -472,7 +476,7 @@ static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref offset, je16_to_cpu(rr.magic), JFFS2_MAGIC_BITMASK, je16_to_cpu(rr.nodetype), JFFS2_NODETYPE_XREF, je32_to_cpu(rr.totlen), PAD(sizeof(rr))); - return -EIO; + return JFFS2_XATTR_IS_CORRUPTED; } ref->ino = je32_to_cpu(rr.ino); ref->xid = je32_to_cpu(rr.xid); @@ -682,6 +686,11 @@ static int check_xattr_ref_inode(struct jffs2_sb_info *c, struct jffs2_inode_cac return rc; } +void jffs2_xattr_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + check_xattr_ref_inode(c, ic); +} + /* -------- xattr subsystem functions --------------- * jffs2_init_xattr_subsystem(c) * is used to initialize semaphore and list_head, and some variables. diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h index 7be4beb306f3..467ff376ee26 100644 --- a/fs/jffs2/xattr.h +++ b/fs/jffs2/xattr.h @@ -77,6 +77,7 @@ extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c); extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c, uint32_t xid, uint32_t version); +extern void jffs2_xattr_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); @@ -108,6 +109,7 @@ extern ssize_t jffs2_listxattr(struct dentry *, char *, size_t); #define jffs2_build_xattr_subsystem(c) #define jffs2_clear_xattr_subsystem(c) +#define jffs2_xattr_do_crccheck_inode(c, ic) #define jffs2_xattr_delete_inode(c, ic) #define jffs2_xattr_free_inode(c, ic) #define jffs2_verify_xattr(c) (1) diff --git a/fs/nls/Kconfig b/fs/nls/Kconfig index b5eac98fd7bd..e2ce79ef48c4 100644 --- a/fs/nls/Kconfig +++ b/fs/nls/Kconfig @@ -452,7 +452,7 @@ config NLS_KOI8_U input/output character sets. Say Y here for the preferred Ukrainian (koi8-u) and Belarusian (koi8-ru) character sets. -config NLS_CODEPAGE_MACROMAN +config NLS_MAC_ROMAN tristate "Codepage macroman" ---help--- The Apple HFS file system family can deal with filenames in @@ -467,7 +467,7 @@ config NLS_CODEPAGE_MACROMAN If unsure, say Y. -config NLS_CODEPAGE_MACCELTIC +config NLS_MAC_CELTIC tristate "Codepage macceltic" ---help--- The Apple HFS file system family can deal with filenames in @@ -481,7 +481,7 @@ config NLS_CODEPAGE_MACCELTIC If unsure, say Y. -config NLS_CODEPAGE_MACCENTEURO +config NLS_MAC_CENTEURO tristate "Codepage maccenteuro" ---help--- The Apple HFS file system family can deal with filenames in @@ -495,7 +495,7 @@ config NLS_CODEPAGE_MACCENTEURO If unsure, say Y. -config NLS_CODEPAGE_MACCROATIAN +config NLS_MAC_CROATIAN tristate "Codepage maccroatian" ---help--- The Apple HFS file system family can deal with filenames in @@ -509,7 +509,7 @@ config NLS_CODEPAGE_MACCROATIAN If unsure, say Y. -config NLS_CODEPAGE_MACCYRILLIC +config NLS_MAC_CYRILLIC tristate "Codepage maccyrillic" ---help--- The Apple HFS file system family can deal with filenames in @@ -523,7 +523,7 @@ config NLS_CODEPAGE_MACCYRILLIC If unsure, say Y. -config NLS_CODEPAGE_MACGAELIC +config NLS_MAC_GAELIC tristate "Codepage macgaelic" ---help--- The Apple HFS file system family can deal with filenames in @@ -537,7 +537,7 @@ config NLS_CODEPAGE_MACGAELIC If unsure, say Y. -config NLS_CODEPAGE_MACGREEK +config NLS_MAC_GREEK tristate "Codepage macgreek" ---help--- The Apple HFS file system family can deal with filenames in @@ -551,7 +551,7 @@ config NLS_CODEPAGE_MACGREEK If unsure, say Y. -config NLS_CODEPAGE_MACICELAND +config NLS_MAC_ICELAND tristate "Codepage maciceland" ---help--- The Apple HFS file system family can deal with filenames in @@ -565,7 +565,7 @@ config NLS_CODEPAGE_MACICELAND If unsure, say Y. -config NLS_CODEPAGE_MACINUIT +config NLS_MAC_INUIT tristate "Codepage macinuit" ---help--- The Apple HFS file system family can deal with filenames in @@ -579,7 +579,7 @@ config NLS_CODEPAGE_MACINUIT If unsure, say Y. -config NLS_CODEPAGE_MACROMANIAN +config NLS_MAC_ROMANIAN tristate "Codepage macromanian" ---help--- The Apple HFS file system family can deal with filenames in @@ -593,7 +593,7 @@ config NLS_CODEPAGE_MACROMANIAN If unsure, say Y. -config NLS_CODEPAGE_MACTURKISH +config NLS_MAC_TURKISH tristate "Codepage macturkish" ---help--- The Apple HFS file system family can deal with filenames in diff --git a/fs/nls/Makefile b/fs/nls/Makefile index b6b0550a7c80..8ae37c1b5249 100644 --- a/fs/nls/Makefile +++ b/fs/nls/Makefile @@ -2,18 +2,6 @@ # Makefile for native language support # -CONFIG_NLS_MACCELTIC=m -CONFIG_NLS_MACCENTEURO=m -CONFIG_NLS_MACCROATIAN=m -CONFIG_NLS_MACCYRILLIC=m -CONFIG_NLS_MACGAELIC=m -CONFIG_NLS_MACGREEK=m -CONFIG_NLS_MACICELAND=m -CONFIG_NLS_MACINUIT=m -CONFIG_NLS_MACROMANIAN=m -CONFIG_NLS_MACROMAN=m -CONFIG_NLS_MACTURKISH=m - obj-$(CONFIG_NLS) += nls_base.o obj-$(CONFIG_NLS_CODEPAGE_437) += nls_cp437.o @@ -54,14 +42,14 @@ obj-$(CONFIG_NLS_ISO8859_15) += nls_iso8859-15.o obj-$(CONFIG_NLS_KOI8_R) += nls_koi8-r.o obj-$(CONFIG_NLS_KOI8_U) += nls_koi8-u.o nls_koi8-ru.o obj-$(CONFIG_NLS_UTF8) += nls_utf8.o -obj-$(CONFIG_NLS_MACCELTIC) += nls_macceltic.o -obj-$(CONFIG_NLS_MACCENTEURO) += nls_maccenteuro.o -obj-$(CONFIG_NLS_MACCROATIAN) += nls_maccroatian.o -obj-$(CONFIG_NLS_MACCYRILLIC) += nls_maccyrillic.o -obj-$(CONFIG_NLS_MACGAELIC) += nls_macgaelic.o -obj-$(CONFIG_NLS_MACGREEK) += nls_macgreek.o -obj-$(CONFIG_NLS_MACICELAND) += nls_maciceland.o -obj-$(CONFIG_NLS_MACINUIT) += nls_macinuit.o -obj-$(CONFIG_NLS_MACROMANIAN) += nls_macromanian.o -obj-$(CONFIG_NLS_MACROMAN) += nls_macroman.o -obj-$(CONFIG_NLS_MACTURKISH) += nls_macturkish.o +obj-$(CONFIG_NLS_MAC_CELTIC) += mac-celtic.o +obj-$(CONFIG_NLS_MAC_CENTEURO) += mac-centeuro.o +obj-$(CONFIG_NLS_MAC_CROATIAN) += mac-croatian.o +obj-$(CONFIG_NLS_MAC_CYRILLIC) += mac-cyrillic.o +obj-$(CONFIG_NLS_MAC_GAELIC) += mac-gaelic.o +obj-$(CONFIG_NLS_MAC_GREEK) += mac-greek.o +obj-$(CONFIG_NLS_MAC_ICELAND) += mac-iceland.o +obj-$(CONFIG_NLS_MAC_INUIT) += mac-inuit.o +obj-$(CONFIG_NLS_MAC_ROMANIAN) += mac-romanian.o +obj-$(CONFIG_NLS_MAC_ROMAN) += mac-roman.o +obj-$(CONFIG_NLS_MAC_TURKISH) += mac-turkish.o diff --git a/fs/nls/nls_macceltic.c b/fs/nls/mac-celtic.c index 95ac5b41ad1c..634a8b717b02 100644 --- a/fs/nls/nls_macceltic.c +++ b/fs/nls/mac-celtic.c @@ -1,5 +1,5 @@ /* - * linux/fs/nls/nls_macceltic.c + * linux/fs/nls/mac-celtic.c * * Charset macceltic translation tables. * Generated automatically from the Unicode and charset diff --git a/fs/nls/nls_maccenteuro.c b/fs/nls/mac-centeuro.c index ce0d57ef39f6..979e6265ac5e 100644 --- a/fs/nls/nls_maccenteuro.c +++ b/fs/nls/mac-centeuro.c @@ -1,5 +1,5 @@ /* - * linux/fs/nls/nls_maccenteuro.c + * linux/fs/nls/mac-centeuro.c * * Charset maccenteuro translation tables. * Generated automatically from the Unicode and charset diff --git a/fs/nls/nls_maccroatian.c b/fs/nls/mac-croatian.c index 10b01c3eed66..dd3f675911ee 100644 --- a/fs/nls/nls_maccroatian.c +++ b/fs/nls/mac-croatian.c @@ -1,5 +1,5 @@ /* - * linux/fs/nls/nls_maccroatian.c + * linux/fs/nls/mac-croatian.c * * Charset maccroatian translation tables. * Generated automatically from the Unicode and charset diff --git a/fs/nls/nls_maccyrillic.c b/fs/nls/mac-cyrillic.c index 318473fbb26c..1112c84dd8bb 100644 --- a/fs/nls/nls_maccyrillic.c +++ b/fs/nls/mac-cyrillic.c @@ -1,5 +1,5 @@ /* - * linux/fs/nls/nls_maccyrillic.c + * linux/fs/nls/mac-cyrillic.c * * Charset maccyrillic translation tables. * Generated automatically from the Unicode and charset diff --git a/fs/nls/nls_macgaelic.c b/fs/nls/mac-gaelic.c index 615d8e128f14..2de9158409c8 100644 --- a/fs/nls/nls_macgaelic.c +++ b/fs/nls/mac-gaelic.c @@ -1,5 +1,5 @@ /* - * linux/fs/nls/nls_macgaelic.c + * linux/fs/nls/mac-gaelic.c * * Charset macgaelic translation tables. * Generated automatically from the Unicode and charset diff --git a/fs/nls/nls_macgreek.c b/fs/nls/mac-greek.c index 79880f30494f..a86310082802 100644 --- a/fs/nls/nls_macgreek.c +++ b/fs/nls/mac-greek.c @@ -1,5 +1,5 @@ /* - * linux/fs/nls/nls_macgreek.c + * linux/fs/nls/mac-greek.c * * Charset macgreek translation tables. * Generated automatically from the Unicode and charset diff --git a/fs/nls/nls_maciceland.c b/fs/nls/mac-iceland.c index 1e688c59b252..babe2998d5ce 100644 --- a/fs/nls/nls_maciceland.c +++ b/fs/nls/mac-iceland.c @@ -1,5 +1,5 @@ /* - * linux/fs/nls/nls_maciceland.c + * linux/fs/nls/mac-iceland.c * * Charset maciceland translation tables. * Generated automatically from the Unicode and charset diff --git a/fs/nls/nls_macinuit.c b/fs/nls/mac-inuit.c index f333d98941d6..312364f010dc 100644 --- a/fs/nls/nls_macinuit.c +++ b/fs/nls/mac-inuit.c @@ -1,5 +1,5 @@ /* - * linux/fs/nls/nls_macinuit.c + * linux/fs/nls/mac-inuit.c * * Charset macinuit translation tables. * Generated automatically from the Unicode and charset diff --git a/fs/nls/nls_macroman.c b/fs/nls/mac-roman.c index 6315a857ab68..53ce0809cbd2 100644 --- a/fs/nls/nls_macroman.c +++ b/fs/nls/mac-roman.c @@ -1,5 +1,5 @@ /* - * linux/fs/nls/nls_macroman.c + * linux/fs/nls/mac-roman.c * * Charset macroman translation tables. * Generated automatically from the Unicode and charset diff --git a/fs/nls/nls_macromanian.c b/fs/nls/mac-romanian.c index b83c07a57d25..add6f7a0c666 100644 --- a/fs/nls/nls_macromanian.c +++ b/fs/nls/mac-romanian.c @@ -1,5 +1,5 @@ /* - * linux/fs/nls/nls_macromanian.c + * linux/fs/nls/mac-romanian.c * * Charset macromanian translation tables. * Generated automatically from the Unicode and charset diff --git a/fs/nls/nls_macturkish.c b/fs/nls/mac-turkish.c index 0cc2c6572826..dffa96d5de00 100644 --- a/fs/nls/nls_macturkish.c +++ b/fs/nls/mac-turkish.c @@ -1,5 +1,5 @@ /* - * linux/fs/nls/nls_macturkish.c + * linux/fs/nls/mac-turkish.c * * Charset macturkish translation tables. * Generated automatically from the Unicode and charset diff --git a/include/linux/fb.h b/include/linux/fb.h index a3229d7ab9f2..ac3f1c605843 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -611,6 +611,7 @@ struct fb_deferred_io { struct mutex lock; /* mutex that protects the page list */ struct list_head pagelist; /* list of touched pages */ /* callback */ + void (*first_io)(struct fb_info *info); void (*deferred_io)(struct fb_info *info, struct list_head *pagelist); }; #endif diff --git a/include/linux/mtd/gpmi-nand.h b/include/linux/mtd/gpmi-nand.h index 69b6dbf46b5e..ed3c4e09f3d1 100644 --- a/include/linux/mtd/gpmi-nand.h +++ b/include/linux/mtd/gpmi-nand.h @@ -23,12 +23,12 @@ #define GPMI_NAND_RES_SIZE 6 /* Resource names for the GPMI NAND driver. */ -#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "GPMI NAND GPMI Registers" +#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand" #define GPMI_NAND_GPMI_INTERRUPT_RES_NAME "GPMI NAND GPMI Interrupt" -#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "GPMI NAND BCH Registers" -#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "GPMI NAND BCH Interrupt" +#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "bch" +#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "bch" #define GPMI_NAND_DMA_CHANNELS_RES_NAME "GPMI NAND DMA Channels" -#define GPMI_NAND_DMA_INTERRUPT_RES_NAME "GPMI NAND DMA Interrupt" +#define GPMI_NAND_DMA_INTERRUPT_RES_NAME "gpmi-dma" /** * struct gpmi_nand_platform_data - GPMI NAND driver platform data. diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index cf5ea8cdcf8e..63dadc0dfb62 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -157,6 +157,15 @@ struct mtd_info { unsigned int erasesize_mask; unsigned int writesize_mask; + /* + * read ops return -EUCLEAN if max number of bitflips corrected on any + * one region comprising an ecc step equals or exceeds this value. + * Settable by driver, else defaults to ecc_strength. User can override + * in sysfs. N.B. The meaning of the -EUCLEAN return code has changed; + * see Documentation/ABI/testing/sysfs-class-mtd for more detail. + */ + unsigned int bitflip_threshold; + // Kernel-only stuff starts here. const char *name; int index; @@ -164,7 +173,7 @@ struct mtd_info { /* ECC layout structure pointer - read only! */ struct nand_ecclayout *ecclayout; - /* max number of correctible bit errors per writesize */ + /* max number of correctible bit errors per ecc step */ unsigned int ecc_strength; /* Data for variable erase regions. If numeraseregions is zero, diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 1482340d3d9f..57977c640529 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -161,8 +161,6 @@ typedef enum { * Option constants for bizarre disfunctionality and real * features. */ -/* Chip can not auto increment pages */ -#define NAND_NO_AUTOINCR 0x00000001 /* Buswidth is 16 bit */ #define NAND_BUSWIDTH_16 0x00000002 /* Device supports partial programming without padding */ @@ -207,7 +205,6 @@ typedef enum { (NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK) /* Macros to identify the above */ -#define NAND_CANAUTOINCR(chip) (!(chip->options & NAND_NO_AUTOINCR)) #define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING)) #define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG)) #define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK)) @@ -216,7 +213,7 @@ typedef enum { && (chip->page_shift > 9)) /* Mask to zero out the chip options, which come from the id table */ -#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR) +#define NAND_CHIPOPTIONS_MSK 0x0000ffff /* Non chip related options */ /* This option skips the bbt scan during initialization. */ @@ -363,21 +360,20 @@ struct nand_ecc_ctrl { int (*correct)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc); int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page); + uint8_t *buf, int oob_required, int page); void (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf); + const uint8_t *buf, int oob_required); int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page); + uint8_t *buf, int oob_required, int page); int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip, uint32_t offs, uint32_t len, uint8_t *buf); void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf); + const uint8_t *buf, int oob_required); int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip, int page); int (*read_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd); - int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page, - int sndcmd); + int page); + int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page); int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page); }; @@ -459,6 +455,8 @@ struct nand_buffers { * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 * @pagebuf: [INTERN] holds the pagenumber which is currently in * data_buf. + * @pagebuf_bitflips: [INTERN] holds the bitflip count for the page which is + * currently in data_buf. * @subpagesize: [INTERN] holds the subpagesize * @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded), * non 0 if ONFI supported. @@ -505,7 +503,8 @@ struct nand_chip { int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int page, int cached, int raw); + const uint8_t *buf, int oob_required, int page, + int cached, int raw); int chip_delay; unsigned int options; @@ -519,6 +518,7 @@ struct nand_chip { uint64_t chipsize; int pagemask; int pagebuf; + unsigned int pagebuf_bitflips; int subpagesize; uint8_t cellinfo; int badblockpos; @@ -654,6 +654,7 @@ struct platform_nand_ctrl { void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); + unsigned char (*read_byte)(struct mtd_info *mtd); void *priv; }; diff --git a/include/video/auo_k190xfb.h b/include/video/auo_k190xfb.h new file mode 100644 index 000000000000..609efe8c686e --- /dev/null +++ b/include/video/auo_k190xfb.h @@ -0,0 +1,106 @@ +/* + * Definitions for AUO-K190X framebuffer drivers + * + * Copyright (C) 2012 Heiko Stuebner <heiko@sntech.de> + * + * 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. + */ + +#ifndef _LINUX_VIDEO_AUO_K190XFB_H_ +#define _LINUX_VIDEO_AUO_K190XFB_H_ + +/* Controller standby command needs a param */ +#define AUOK190X_QUIRK_STANDBYPARAM (1 << 0) + +/* Controller standby is completely broken */ +#define AUOK190X_QUIRK_STANDBYBROKEN (1 << 1) + +/* + * Resolutions for the displays + */ +#define AUOK190X_RESOLUTION_800_600 0 +#define AUOK190X_RESOLUTION_1024_768 1 + +/* + * struct used by auok190x. board specific stuff comes from *board + */ +struct auok190xfb_par { + struct fb_info *info; + struct auok190x_board *board; + + struct regulator *regulator; + + struct mutex io_lock; + struct delayed_work work; + wait_queue_head_t waitq; + int resolution; + int rotation; + int consecutive_threshold; + int update_cnt; + + /* panel and controller informations */ + int epd_type; + int panel_size_int; + int panel_size_float; + int panel_model; + int tcon_version; + int lut_version; + + /* individual controller callbacks */ + void (*update_partial)(struct auok190xfb_par *par, u16 y1, u16 y2); + void (*update_all)(struct auok190xfb_par *par); + bool (*need_refresh)(struct auok190xfb_par *par); + void (*init)(struct auok190xfb_par *par); + void (*recover)(struct auok190xfb_par *par); + + int update_mode; /* mode to use for updates */ + int last_mode; /* update mode last used */ + int flash; + + /* power management */ + int autosuspend_delay; + bool standby; + bool manual_standby; +}; + +/** + * Board specific platform-data + * @init: initialize the controller interface + * @cleanup: cleanup the controller interface + * @wait_for_rdy: wait until the controller is not busy anymore + * @set_ctl: change an interface control + * @set_hdb: write a value to the data register + * @get_hdb: read a value from the data register + * @setup_irq: method to setup the irq handling on the busy gpio + * @gpio_nsleep: sleep gpio + * @gpio_nrst: reset gpio + * @gpio_nbusy: busy gpio + * @resolution: one of the AUOK190X_RESOLUTION constants + * @rotation: rotation of the framebuffer + * @quirks: controller quirks to honor + * @fps: frames per second for defio + */ +struct auok190x_board { + int (*init)(struct auok190xfb_par *); + void (*cleanup)(struct auok190xfb_par *); + int (*wait_for_rdy)(struct auok190xfb_par *); + + void (*set_ctl)(struct auok190xfb_par *, unsigned char, u8); + void (*set_hdb)(struct auok190xfb_par *, u16); + u16 (*get_hdb)(struct auok190xfb_par *); + + int (*setup_irq)(struct fb_info *); + + int gpio_nsleep; + int gpio_nrst; + int gpio_nbusy; + + int resolution; + int rotation; + int quirks; + int fps; +}; + +#endif diff --git a/include/video/exynos_dp.h b/include/video/exynos_dp.h index 8847a9d6dd42..bd8cabd344db 100644 --- a/include/video/exynos_dp.h +++ b/include/video/exynos_dp.h @@ -14,7 +14,7 @@ #define DP_TIMEOUT_LOOP_COUNT 100 #define MAX_CR_LOOP 5 -#define MAX_EQ_LOOP 4 +#define MAX_EQ_LOOP 5 enum link_rate_type { LINK_RATE_1_62GBPS = 0x06, diff --git a/include/video/exynos_mipi_dsim.h b/include/video/exynos_mipi_dsim.h index 772c770535f1..83ce5e667d47 100644 --- a/include/video/exynos_mipi_dsim.h +++ b/include/video/exynos_mipi_dsim.h @@ -315,6 +315,7 @@ struct mipi_dsim_lcd_device { int id; int bus_id; int irq; + int panel_reverse; struct mipi_dsim_device *master; void *platform_data; diff --git a/include/video/omapdss.h b/include/video/omapdss.h index 1c46a14341dd..c8e59b4a3364 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -51,6 +51,8 @@ struct omap_dss_device; struct omap_overlay_manager; +struct snd_aes_iec958; +struct snd_cea_861_aud_if; enum omap_display_type { OMAP_DISPLAY_TYPE_NONE = 0, @@ -158,6 +160,13 @@ enum omap_dss_display_state { OMAP_DSS_DISPLAY_SUSPENDED, }; +enum omap_dss_audio_state { + OMAP_DSS_AUDIO_DISABLED = 0, + OMAP_DSS_AUDIO_ENABLED, + OMAP_DSS_AUDIO_CONFIGURED, + OMAP_DSS_AUDIO_PLAYING, +}; + /* XXX perhaps this should be removed */ enum omap_dss_overlay_managers { OMAP_DSS_OVL_MGR_LCD, @@ -166,8 +175,9 @@ enum omap_dss_overlay_managers { }; enum omap_dss_rotation_type { - OMAP_DSS_ROT_DMA = 0, - OMAP_DSS_ROT_VRFB = 1, + OMAP_DSS_ROT_DMA = 1 << 0, + OMAP_DSS_ROT_VRFB = 1 << 1, + OMAP_DSS_ROT_TILER = 1 << 2, }; /* clockwise rotation angle */ @@ -309,6 +319,7 @@ struct omap_dss_board_info { struct omap_dss_device *default_device; int (*dsi_enable_pads)(int dsi_id, unsigned lane_mask); void (*dsi_disable_pads)(int dsi_id, unsigned lane_mask); + int (*set_min_bus_tput)(struct device *dev, unsigned long r); }; /* Init with the board info */ @@ -316,11 +327,6 @@ extern int omap_display_init(struct omap_dss_board_info *board_data); /* HDMI mux init*/ extern int omap_hdmi_init(enum omap_hdmi_flags flags); -struct omap_display_platform_data { - struct omap_dss_board_info *board_data; - /* TODO: Additional members to be added when PM is considered */ -}; - struct omap_video_timings { /* Unit: pixels */ u16 x_res; @@ -587,6 +593,8 @@ struct omap_dss_device { enum omap_dss_display_state state; + enum omap_dss_audio_state audio_state; + /* platform specific */ int (*platform_enable)(struct omap_dss_device *dssdev); void (*platform_disable)(struct omap_dss_device *dssdev); @@ -599,6 +607,11 @@ struct omap_dss_hdmi_data int hpd_gpio; }; +struct omap_dss_audio { + struct snd_aes_iec958 *iec; + struct snd_cea_861_aud_if *cea; +}; + struct omap_dss_driver { struct device_driver driver; @@ -646,6 +659,24 @@ struct omap_dss_driver { int (*read_edid)(struct omap_dss_device *dssdev, u8 *buf, int len); bool (*detect)(struct omap_dss_device *dssdev); + + /* + * For display drivers that support audio. This encompasses + * HDMI and DisplayPort at the moment. + */ + /* + * Note: These functions might sleep. Do not call while + * holding a spinlock/readlock. + */ + int (*audio_enable)(struct omap_dss_device *dssdev); + void (*audio_disable)(struct omap_dss_device *dssdev); + bool (*audio_supported)(struct omap_dss_device *dssdev); + int (*audio_config)(struct omap_dss_device *dssdev, + struct omap_dss_audio *audio); + /* Note: These functions may not sleep */ + int (*audio_start)(struct omap_dss_device *dssdev); + void (*audio_stop)(struct omap_dss_device *dssdev); + }; int omap_dss_register_driver(struct omap_dss_driver *); @@ -670,6 +701,8 @@ struct omap_overlay *omap_dss_get_overlay(int num); void omapdss_default_get_resolution(struct omap_dss_device *dssdev, u16 *xres, u16 *yres); int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev); +void omapdss_default_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings); typedef void (*omap_dispc_isr_t) (void *arg, u32 mask); int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask); diff --git a/include/video/sh_mobile_hdmi.h b/include/video/sh_mobile_hdmi.h index 728f9de9c258..63d20efa254a 100644 --- a/include/video/sh_mobile_hdmi.h +++ b/include/video/sh_mobile_hdmi.h @@ -18,9 +18,11 @@ struct clk; /* * flags format * - * 0x0000000A + * 0x00000CBA * * A: Audio source select + * B: Int output option + * C: Chip specific option */ /* Audio source select */ @@ -30,6 +32,14 @@ struct clk; #define HDMI_SND_SRC_DSD (2 << 0) #define HDMI_SND_SRC_HBR (3 << 0) +/* Int output option */ +#define HDMI_OUTPUT_PUSH_PULL (1 << 4) /* System control : output mode */ +#define HDMI_OUTPUT_POLARITY_HI (1 << 5) /* System control : output polarity */ + +/* Chip specific option */ +#define HDMI_32BIT_REG (1 << 8) +#define HDMI_HAS_HTOP1 (1 << 9) + struct sh_mobile_hdmi_info { unsigned int flags; long (*clk_optimize_parent)(unsigned long target, unsigned long *best_freq, diff --git a/mm/slub.c b/mm/slub.c index 80848cd3901c..8c691fa1cf3c 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1369,7 +1369,7 @@ static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node) inc_slabs_node(s, page_to_nid(page), page->objects); page->slab = s; - page->flags |= 1 << PG_slab; + __SetPageSlab(page); start = page_address(page); @@ -1514,15 +1514,19 @@ static inline void *acquire_slab(struct kmem_cache *s, freelist = page->freelist; counters = page->counters; new.counters = counters; - if (mode) + if (mode) { new.inuse = page->objects; + new.freelist = NULL; + } else { + new.freelist = freelist; + } VM_BUG_ON(new.frozen); new.frozen = 1; } while (!__cmpxchg_double_slab(s, page, freelist, counters, - NULL, new.counters, + new.freelist, new.counters, "lock and freeze")); remove_partial(n, page); @@ -1564,7 +1568,6 @@ static void *get_partial_node(struct kmem_cache *s, object = t; available = page->objects - page->inuse; } else { - page->freelist = t; available = put_cpu_partial(s, page, 0); stat(s, CPU_PARTIAL_NODE); } @@ -1579,7 +1582,7 @@ static void *get_partial_node(struct kmem_cache *s, /* * Get a page from somewhere. Search in increasing NUMA distances. */ -static struct page *get_any_partial(struct kmem_cache *s, gfp_t flags, +static void *get_any_partial(struct kmem_cache *s, gfp_t flags, struct kmem_cache_cpu *c) { #ifdef CONFIG_NUMA @@ -2766,7 +2769,7 @@ static unsigned long calculate_alignment(unsigned long flags, } static void -init_kmem_cache_node(struct kmem_cache_node *n, struct kmem_cache *s) +init_kmem_cache_node(struct kmem_cache_node *n) { n->nr_partial = 0; spin_lock_init(&n->list_lock); @@ -2836,7 +2839,7 @@ static void early_kmem_cache_node_alloc(int node) init_object(kmem_cache_node, n, SLUB_RED_ACTIVE); init_tracking(kmem_cache_node, n); #endif - init_kmem_cache_node(n, kmem_cache_node); + init_kmem_cache_node(n); inc_slabs_node(kmem_cache_node, node, page->objects); add_partial(n, page, DEACTIVATE_TO_HEAD); @@ -2876,7 +2879,7 @@ static int init_kmem_cache_nodes(struct kmem_cache *s) } s->node[node] = n; - init_kmem_cache_node(n, s); + init_kmem_cache_node(n); } return 1; } @@ -3625,7 +3628,7 @@ static int slab_mem_going_online_callback(void *arg) ret = -ENOMEM; goto out; } - init_kmem_cache_node(n, s); + init_kmem_cache_node(n); s->node[nid] = n; } out: @@ -3968,9 +3971,9 @@ struct kmem_cache *kmem_cache_create(const char *name, size_t size, } return s; } - kfree(n); kfree(s); } + kfree(n); err: up_write(&slub_lock); diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 0a5027b94714..b8ac8710f47f 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -1988,6 +1988,13 @@ static int hdspm_get_system_sample_rate(struct hdspm *hdspm) period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ); rate = hdspm_calc_dds_value(hdspm, period); + if (rate > 207000) { + /* Unreasonable high sample rate as seen on PCI MADI cards. + * Use the cached value instead. + */ + rate = hdspm->system_sample_rate; + } + return rate; } diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index cf3ed0362c9c..28dd76c7cb1c 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -543,7 +543,7 @@ static int imx_ssi_probe(struct platform_device *pdev) ret); goto failed_clk; } - clk_enable(ssi->clk); + clk_prepare_enable(ssi->clk); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -641,7 +641,7 @@ failed_ac97: failed_ioremap: release_mem_region(res->start, resource_size(res)); failed_get_resource: - clk_disable(ssi->clk); + clk_disable_unprepare(ssi->clk); clk_put(ssi->clk); failed_clk: kfree(ssi); @@ -664,7 +664,7 @@ static int __devexit imx_ssi_remove(struct platform_device *pdev) iounmap(ssi->base); release_mem_region(res->start, resource_size(res)); - clk_disable(ssi->clk); + clk_disable_unprepare(ssi->clk); clk_put(ssi->clk); kfree(ssi); diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 7cee22515d9d..2ef98536f1da 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1052,6 +1052,13 @@ static int fsi_dma_quit(struct fsi_priv *fsi, struct fsi_stream *io) return 0; } +static dma_addr_t fsi_dma_get_area(struct fsi_stream *io) +{ + struct snd_pcm_runtime *runtime = io->substream->runtime; + + return io->dma + samples_to_bytes(runtime, io->buff_sample_pos); +} + static void fsi_dma_complete(void *data) { struct fsi_stream *io = (struct fsi_stream *)data; @@ -1061,7 +1068,7 @@ static void fsi_dma_complete(void *data) enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; - dma_sync_single_for_cpu(dai->dev, io->dma, + dma_sync_single_for_cpu(dai->dev, fsi_dma_get_area(io), samples_to_bytes(runtime, io->period_samples), dir); io->buff_sample_pos += io->period_samples; @@ -1078,13 +1085,6 @@ static void fsi_dma_complete(void *data) snd_pcm_period_elapsed(io->substream); } -static dma_addr_t fsi_dma_get_area(struct fsi_stream *io) -{ - struct snd_pcm_runtime *runtime = io->substream->runtime; - - return io->dma + samples_to_bytes(runtime, io->buff_sample_pos); -} - static void fsi_dma_do_tasklet(unsigned long data) { struct fsi_stream *io = (struct fsi_stream *)data; @@ -1110,7 +1110,7 @@ static void fsi_dma_do_tasklet(unsigned long data) len = samples_to_bytes(runtime, io->period_samples); buf = fsi_dma_get_area(io); - dma_sync_single_for_device(dai->dev, io->dma, len, dir); + dma_sync_single_for_device(dai->dev, buf, len, dir); sg_init_table(&sg, 1); sg_set_page(&sg, pfn_to_page(PFN_DOWN(buf)), @@ -1172,9 +1172,16 @@ static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io) static void fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, int start) { + struct fsi_master *master = fsi_get_master(fsi); + u32 clk = fsi_is_port_a(fsi) ? CRA : CRB; u32 enable = start ? DMA_ON : 0; fsi_reg_mask_set(fsi, OUT_DMAC, DMA_ON, enable); + + dmaengine_terminate_all(io->chan); + + if (fsi_is_clk_master(fsi)) + fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0); } static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io) diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 24839d932648..cdf8b7601973 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -788,6 +788,9 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, int count = 0, needs_knot = 0; int err; + kfree(subs->rate_list.list); + subs->rate_list.list = NULL; + list_for_each_entry(fp, &subs->fmt_list, list) { if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) return 0; |