diff options
author | Andrey Konovalov <andrey.konovalov@linaro.org> | 2012-11-15 22:44:53 +0400 |
---|---|---|
committer | Andrey Konovalov <andrey.konovalov@linaro.org> | 2012-11-15 22:44:53 +0400 |
commit | 57fbab2997d30022f0c1104427ce6990d32ac847 (patch) | |
tree | 5e2e292a1c0e547adc2c80acad1aa9b0538e504b | |
parent | 68fc0f11830b38b24bcf6cfc9238578e86a6fcfd (diff) | |
parent | 0ae2593412cad1161f0e638ac930a0157dd08ba2 (diff) |
Merge branch 'tracking-samslt-all' into merge-linux-linaroll-20121115.0
256 files changed, 42176 insertions, 325 deletions
diff --git a/Documentation/devicetree/bindings/arm/exynos/tvmixer.txt b/Documentation/devicetree/bindings/arm/exynos/tvmixer.txt new file mode 100644 index 000000000000..5b4f02e5973f --- /dev/null +++ b/Documentation/devicetree/bindings/arm/exynos/tvmixer.txt @@ -0,0 +1,26 @@ +* Samsung video/graphics mixer and blender + +Mixer blends graphics data from multiple sources and sends resulting +data to TVOUT module. + + +Required properties: + + - compatible : "samsung,s5pv210-tvmixer" + - reg : shall contain memory addresses and sizes of mixer and video + processor devices + - reg-names : "mxr" for mixer's address/size and "vp" to for video + processor's. + - interrupt-names : shall contain "irq" + + +Example: + + tvmixer@12c10000 { + compatible = "samsung,s5pv210-tvmixer"; + reg = <0x12c10000 0x10000>, + <0x12c00000 0x10000>; + reg-names = "mxr", "vp"; + interrupts = <0 123 0>; + interrupt-names = "irq"; + }; diff --git a/Documentation/devicetree/bindings/arm/samsung/wakeup-eint.txt b/Documentation/devicetree/bindings/arm/samsung/wakeup-eint.txt new file mode 100644 index 000000000000..52aa0493914a --- /dev/null +++ b/Documentation/devicetree/bindings/arm/samsung/wakeup-eint.txt @@ -0,0 +1,152 @@ +* Samsung GPIO Wakeup Interrupt Controller + +This document is split into following sections. + +[1] Samsung Exynos4 GPIO Wakeup Interrupt Source Controller +[2] Samsung Exynos5 GPIO Wakeup Interrupt Source Controller + + +[1] Samsung Exynos4 GPIO Wakeup Interrupt Source Controller + +Samsung Exynos4 processor supports 32 external wakeup interrupt sources. First +16 of these interrupts are directly connected to GIC and the rest 16 of the +interrupts are grouped together to deliver a single interrupt to GIC. + +Required properties: + + - compatible: should be "samsung,exynos4210-wakeup-eint". + + - reg: physical base address of the controller and length of memory + mapped region. + + - interrupt-controller: Identifies the node as an interrupt controller. + + - interrupt-cells: Specifies the number of cells required to specify the + interrupt source number. The value of should be <2>. The first cell + represents the wakeup interrupt source number and the second cell + should be zero (currently unused). + + - interrupts: List of interrupts generated by the gpio wakeup interrupt + controller which are connected to a parent interrupt controller. The + format of the interrupt specifier depends on the interrupt parent + controller. + +Optional properties: + + - interrupt-parent: phandle of the parent interrupt controller, required + if not inheriting the interrupt parent from the parent node. + +Example: + + The following example is from the Exynos4210 dtsi file. + + wakeup_eint: interrupt-controller-wakeup-eint { + compatible = "samsung,exynos4210-wakeup-eint"; + reg = <0x11000000 0x1000>; + #interrupt-cells = <2>; + interrupt-controller; + interrupts = <0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>, + <0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>, + <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>, + <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>, + <0 32 0>; + }; + + +[2] Samsung Exynos5 GPIO Wakeup Interrupt Source Controller + +Samsung Exynos5 processor supports 32 external wakeup interrupt sources. First +16 of these interrupts are directly connected to GIC and the rest 16 of the +interrupts are grouped together to deliver a single interrupt to interrupt +combiner controller. + +Since the wakeup interrupts has two interrupt parents, a interrupt nexus +child node is used that includes a interrupt-map used to translate wakeup +interrupt specifiers into gic and combiner domain interrupts. + +Required properties: + + - compatible: should be "samsung,exynos5210-wakeup-eint". + + - reg: physical base address of the controller and length of memory + mapped region. + + - interrupt-controller: Identifies the node as an interrupt controller. + + - interrupt-cells: Specifies the number of cells required to specify the + interrupt source number. The value of should be <2>. The first cell + represents the wakeup interrupt source number and the second cell + should be zero (currently unused). + + - interrupts: List of interrupts generated by the gpio wakeup interrupt + controller. Since both gic and combiner controllers are interrupt + parents, a interrupt nexus child node is used to translate the interrupt + specifiers into respective gic and combiner interrupt domains (see below). + The interrupt specifier should be two cells - the first cell should be the + interrupt number originating from the wakeup controller and the second + cell should be zero (unused). + + - interrupt-parent: The phandle of the interrupt nexus child node. + + - interrupt-nexus child node: This node is used to translate the interrupt + specifiers of the wakeup interrupt controller node to the respecitive + gic or combiner interrupt domain. The interrupt nexus node should include + the following properties. + + - interrupt-cells: Specifies the number of cells required to specify the + interrupt source number. The value of should be <2>. The first cell + represents the wakeup interrupt source number and the second cell + should be zero (currently unused). + + - #address-cells: value of this property should be zero. + + - #size-cells: value of this property should be zero. + + - interrupt-map: The interrpt-map specifies how interrupt specifiers are + translated. There should be a interrupt-map entry for each interrupt + generated by the wakeup interrupt controller. The format of each entry + in the interrupt map should be + + <#intr 0 parent-phandle parent-interrupt-specifier> + + where #intr is one of the entries in the 'interrupts' property of the + parent node. + + +Example: + + wakeup_eint: interrupt-controller@11400000 { + compatible = "samsung,exynos5210-wakeup-eint"; + reg = <0x11400000 0x1000>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&eint_nexus>; + interrupts = <0x0 0>, <0x1 0>, <0x2 0>, <0x3 0>, + <0x4 0>, <0x5 0>, <0x6 0>, <0x7 0>, + <0x8 0>, <0x9 0>, <0xa 0>, <0xb 0>, + <0xc 0>, <0xd 0>, <0xe 0>, <0xf 0>, + <0x10 0>; + + wakeup_map: interrupt-map { + #interrupt-cells = <2>; + #address-cells = <0>; + #size-cells = <0>; + interrupt-map = <0x0 0 &combiner 23 0>, + <0x1 0 &combiner 24 0>, + <0x2 0 &combiner 25 0>, + <0x3 0 &combiner 25 1>, + <0x4 0 &combiner 26 0>, + <0x5 0 &combiner 26 1>, + <0x6 0 &combiner 27 0>, + <0x7 0 &combiner 27 1>, + <0x8 0 &combiner 28 0>, + <0x9 0 &combiner 28 1>, + <0xa 0 &combiner 29 0>, + <0xb 0 &combiner 29 1>, + <0xc 0 &combiner 30 0>, + <0xd 0 &combiner 30 1>, + <0xe 0 &combiner 31 0>, + <0xf 0 &combiner 31 1>, + <0x10 0 &gic 0 32 0>; + }; + }; diff --git a/Documentation/devicetree/bindings/fb/samsung-fb.txt b/Documentation/devicetree/bindings/fb/samsung-fb.txt new file mode 100644 index 000000000000..612bd9f83277 --- /dev/null +++ b/Documentation/devicetree/bindings/fb/samsung-fb.txt @@ -0,0 +1,148 @@ +* Samsung Display Controller Framebuffer Controller + +The display controller is used to transfer image data from memory to a +external display device such as an RGB interface LCD panel. It supports +various color formats such as rgb and yuv. It also supports multiple window +overlays. + +Required properties: + + - compatible: should be one of the following + - samsung,exynos4210-fimd: for fimd compatible with Exynos4210 fimd + - samsung,s5pv210-fimd: for fimd compatible with s5pv210 fimd + + - reg: physical base address of the controller and length of memory + mapped region. + + - interrupts: Three interrupts should be specified. The format of the + interrupt specifier depends on the interrupt controller. The interrupts + should be specified in the following order. + - VSYNC (Video Frame) interrupt + - Video FIFO level interrupt + - FIMD System Interrupt + + - gpios: The gpios used to interface with the external LCD panel. For a + panel with rgb interface, the gpio interface consists of video data + lines, HSYNC, VSYNC, Pixel Clock and Data Enable. The gpio's used for + these interface lines can be listed under this property in any order. + + - samsung,fimd-display: The fimd controller is interfaced with the a + display device such as a lcd panel. This property should specify the + phandle of the display device node. For a display device node that + represents a RGB type display interface, it is expected to specify the + video interface timing using the following properties. + + - lcd-htiming: Specifies the horizontal timing for the overlay. The + horizontal timing includes four parameters in the following order. + + - horizontal back porch (in number of lcd clocks) + - horizontal front porch (in number of lcd clocks) + - hsync pulse width (in number of lcd clocks) + - Display panels X resolution. + + - lcd-vtiming: Specifies the vertical timing for the overlay. The + vertical timing includes four parameters in the following order. + + - vertical back porch (in number of lcd lines) + - vertical front porch (in number of lcd lines) + - vsync pulse width (in number of lcd clocks) + - Y resolution. + + - Overlay/Windows: Multiple overlays/windows can be specified as child + nodes. Each window should have the following properties (optional + window properties are marked as 'optional'). + + - samsung,fimd-win-id: Specifies the window number of the fimd controller. + + - samsung,fimd-win-bpp: Specifies the bits per pixel. Two values should + be specified in the following order. + - default-bpp: bpp supported by the overlay. + - max-bpp: maximum required bpp for the overlay. + + - samsung,fimd-win-res: (OPTIONAL) Specifies the window resolution in + pixels. The resolution contains the X and Y pixel values with X being + specified first. If this property is not specified, the window + resolution is set to be equal to the display panel resolution. + + - samsung,fimd-win-virtres: (OPTIONAL) Specifies the resolution of the + virtual frame buffer for the window. The resolution contains the X + and Y resolution in pixels with value of X being the specified first. + +Optional properties: + + - samsung,fimd-vidout-rgb: Video output format is RGB. + - samsung,fimd-inv-hsync: invert hsync pulse polarity. + - samsung,fimd-inv-vsync: invert vsync pulse polarity. + - samsung,fimd-inv-vclk: invert video clock polarity. + - samsung,fimd-inv-vden: invert video enable signal polarity. + - samsung,fimd-frame-rate: Number of video frames per second. + +Example: + + The following is an example for the fimd framebuffer controller is split + into two portions. The SoC specific portion can be specified in the SoC + specific dts file. The board specific portion can be specified in the + board specific dts file. + + - SoC Specific portion + + fimd@11C00000 { + compatible = "samsung,exynos4210-fimd"; + interrupt-parent = <&combiner>; + reg = <0x11C00000 0x8000>; + interrupts = <11 1>, <11 0>, <11 2>; + }; + + - Board Specific portion + + fimd@11C00000 { + samsung,fimd-display = <&lcd_fimd0>; + samsung,fimd-vidout-rgb; + samsung,fimd-inv-hsync; + samsung,fimd-inv-vsync; + samsung,fimd-inv-vclk; + samsung,fimd-frame-rate = <60>; + + gpios = <&gpf0 0 2 0 0>, + <&gpf0 1 2 0 0>, + <&gpf0 2 2 0 0>, + <&gpf0 3 2 0 0>, + <&gpf0 4 2 0 0>, + <&gpf0 5 2 0 0>, + <&gpf0 6 2 0 0>, + <&gpf0 7 2 0 0>, + <&gpf1 0 2 0 0>, + <&gpf1 1 2 0 0>, + <&gpf1 2 2 0 0>, + <&gpf1 3 2 0 0>, + <&gpf1 4 2 0 0>, + <&gpf1 5 2 0 0>, + <&gpf1 6 2 0 0>, + <&gpf1 7 2 0 0>, + <&gpf2 0 2 0 0>, + <&gpf2 1 2 0 0>, + <&gpf2 2 2 0 0>, + <&gpf2 3 2 0 0>, + <&gpf2 4 2 0 0>, + <&gpf2 5 2 0 0>, + <&gpf2 6 2 0 0>, + <&gpf2 7 2 0 0>, + <&gpf3 0 2 0 0>, + <&gpf3 1 2 0 0>, + <&gpf3 2 2 0 0>, + <&gpf3 3 2 0 0>; + + window0 { + samsung,fimd-win-id = <0>; + samsung,fimd-win-bpp = <32 24>; + samsung,fimd-win-res = <512 300>; + samsung,fimd-win-vres = <1024 600>; + }; + + window1 { + samsung,fimd-win-id = <1>; + samsung,fimd-win-bpp = <32 24>; + samsung,fimd-win-res = <1024 200>; + samsung,fimd-win-vres = <1024 600>; + }; + }; diff --git a/Documentation/devicetree/bindings/i2c/sil-mhl9234.txt b/Documentation/devicetree/bindings/i2c/sil-mhl9234.txt new file mode 100644 index 000000000000..b5d92ea6bd74 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/sil-mhl9234.txt @@ -0,0 +1,14 @@ +* Silicon Image Mobile HD Link (MHL) 9234 + +Required properties : + - compatible : "sil,mhl-9234" + - reg : i2c device address + - gpio-reset : gpio line used to reset IC + +Example: + + mhl@39 { + compatible = "sil,mhl-9234"; + reg = <0x39>; + gpio-reset = <&gpf3 4 0 0 0>; + }; diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt index 2f5322b119eb..ebd3167f5e92 100644 --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt @@ -51,6 +51,7 @@ plx,pex8648 48-Lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch ramtron,24c64 i2c serial eeprom (24cxx) ricoh,rs5c372a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC samsung,24ad0xd1 S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power) +samsung,s5pv210-hdmiphy PLL-based clock generator for HDMI PHY found on s5pv210+ SoCs st-micro,24c256 i2c serial eeprom (24cxx) stm,m41t00 Serial Access TIMEKEEPER stm,m41t62 Serial real-time clock (RTC) with alarm diff --git a/Documentation/devicetree/bindings/lcd/lcd-pwrctrl.txt b/Documentation/devicetree/bindings/lcd/lcd-pwrctrl.txt new file mode 100644 index 000000000000..22604a208c60 --- /dev/null +++ b/Documentation/devicetree/bindings/lcd/lcd-pwrctrl.txt @@ -0,0 +1,36 @@ +* Power controller for simple lcd panels + +Some LCD panels provide a simple control interface for the host system. The +control mechanism would include a nRESET line connected to a gpio of the host +system and a Vcc supply line which the host can optionally be controlled using +a voltage regulator. Such simple panels do not support serial command +interface (such as i2c or spi) or memory-mapped-io interface. + +Required properties: +- compatible: should be 'lcd-powercontrol' + +- lcd-reset-gpio: The GPIO number of the host system used to control the + nRESET line. The format of the gpio specifier depends on the gpio controller + of the host system. + +Optional properties: +- lcd-reset-active-high: When the nRESET line is asserted low, the lcd panel + is reset and stays in reset mode as long as the nRESET line is asserted low. + This is the default behaviour of most lcd panels. If a lcd panel requires the + nRESET line to be asserted high for panel reset, then this property is used. + Note: Some platforms might allow inverting the polarity of the gpio output + in the 'lcd-reset-gpio' gpio specifier. On such platforms, if the polarity + is used to control the output of the gpio, then this property should not be + used. + +- vcc-lcd-supply: phandle of the regulator that controls the vcc supply to + the lcd panel. + +Example: + + lcd_pwrctrl { + compatible = "lcd-powercontrol"; + lcd-reset-gpio = <&gpe0 4 1 0 0>; + lcd-reset-active-high; + lcd-vcc-supply = <®ulator7>; + }; diff --git a/Documentation/devicetree/bindings/regulator/max8997-pmic.txt b/Documentation/devicetree/bindings/regulator/max8997-pmic.txt new file mode 100644 index 000000000000..5da0c70289ea --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/max8997-pmic.txt @@ -0,0 +1,147 @@ +* Maxim MAX8997 Voltage and Current Regulator + +The Maxim MAX8997 is a multi-function device which includes volatage and +current regulators, rtc, charger controller and other sub-blocks. It is +interfaced to the host controller using a i2c interface. Each sub-block is +addressed by the host system using different i2c slave address. This document +describes the bindings for 'pmic' sub-block of max8997. + +Required properties: +- compatible: Should be "maxim,max8997-pmic". +- reg: Specifies the i2c slave address of the pmic block. It should be 0x66. + +- max8997,pmic-buck1-dvs-voltage: A set of 8 voltage values in micro-volt (uV) + units for buck1 when changing voltage using gpio dvs. Refer to [1] below + for additional information. + +- max8997,pmic-buck2-dvs-voltage: A set of 8 voltage values in micro-volt (uV) + units for buck2 when changing voltage using gpio dvs. Refer to [1] below + for additional information. + +- max8997,pmic-buck5-dvs-voltage: A set of 8 voltage values in micro-volt (uV) + units for buck5 when changing voltage using gpio dvs. Refer to [1] below + for additional information. + +[1] If none of the 'max8997,pmic-buck[1/2/5]-uses-gpio-dvs' optional + property is specified, the 'max8997,pmic-buck[1/2/5]-dvs-voltage' + property should specify atleast one voltage level (which would be a + safe operating voltage). + + If either of the 'max8997,pmic-buck[1/2/5]-uses-gpio-dvs' optional + property is specified, then all the eigth voltage values for the + 'max8997,pmic-buck[1/2/5]-dvs-voltage' should be specified. + +Optional properties: +- interrupt-parent: Specifies the phandle of the interrupt controller to which + the interrupts from max8997 are delivered to. +- interrupts: Interrupt specifiers for two interrupt sources. + - First interrupt specifier is for 'irq1' interrupt. + - Second interrupt specifier is for 'alert' interrupt. +- max8997,pmic-buck1-uses-gpio-dvs: 'buck1' can be controlled by gpio dvs. +- max8997,pmic-buck2-uses-gpio-dvs: 'buck2' can be controlled by gpio dvs. +- max8997,pmic-buck5-uses-gpio-dvs: 'buck5' can be controlled by gpio dvs. + +Additional properties required if either of the optional properties are used: +- max8997,pmic-ignore-gpiodvs-side-effect: When GPIO-DVS mode is used for + multiple bucks, changing the voltage value of one of the bucks may affect + that of another buck, which is the side effect of the change (set_voltage). + Use this property to ignore such side effects and change the voltage. + +- max8997,pmic-buck125-default-dvs-idx: Default voltage setting selected from + the possible 8 options selectable by the dvs gpios. The value of this + property should be between 0 and 7. If not specified or if out of range, the + default value of this property is set to 0. + +- max8997,pmic-buck125-dvs-gpios: GPIO specifiers for three host gpio's used + for dvs. The format of the gpio specifier depends in the gpio controller. + + +Regulators: The regulators of max8997 that have to be instantiated should be +included in a sub-node named 'regulators'. Regulator nodes included in this +sub-node should be of the format as listed below. + + regulator_name { + standard regulator bindings here + }; + +The following are the names of the regulators that the max8997 pmic block +supports. Note: The 'n' in LDOn and BUCKn represents the LDO or BUCK number +as per the datasheet of max8997. + + - LDOn + - valid values for n are 1 to 18 and 21 + - Example: LDO0, LD01, LDO2, LDO21 + - BUCKn + - valid values for n are 1 to 7. + - Example: BUCK1, BUCK2, BUCK3, BUCK7 + + - ENVICHG: Battery Charging Current Monitor Output. This is a fixed + voltage type regulator + + - ESAFEOUT1: (ldo19) + - ESAFEOUT2: (ld020) + + - CHARGER_CV: main battery charger voltage control + - CHARGER: main battery charger current control + - CHARGER_TOPOFF: end of charge current threshold level + +The bindings inside the regulator nodes use the standard regulator bindings +which are documented elsewhere. + +Example: + + max8997_pmic@66 { + compatible = "maxim,max8997-pmic"; + interrupt-parent = <&wakeup_eint>; + reg = <0x66>; + interrupts = <4 0>, <3 0>; + + max8997,pmic-buck1-uses-gpio-dvs; + max8997,pmic-buck2-uses-gpio-dvs; + max8997,pmic-buck5-uses-gpio-dvs; + + max8997,pmic-ignore-gpiodvs-side-effect; + max8997,pmic-buck125-default-dvs-idx = <0>; + + max8997,pmic-buck125-dvs-gpios = <&gpx0 0 1 0 0>, /* SET1 */ + <&gpx0 1 1 0 0>, /* SET2 */ + <&gpx0 2 1 0 0>; /* SET3 */ + + max8997,pmic-buck1-dvs-voltage = <1350000>, <1300000>, + <1250000>, <1200000>, + <1150000>, <1100000>, + <1000000>, <950000>; + + max8997,pmic-buck2-dvs-voltage = <1100000>, <1100000>, + <1100000>, <1100000>, + <1000000>, <1000000>, + <1000000>, <1000000>; + + max8997,pmic-buck5-dvs-voltage = <1200000>, <1200000>, + <1200000>, <1200000>, + <1200000>, <1200000>, + <1200000>, <1200000>; + + regulators { + ldo1_reg: LDO1 { + regulator-name = "VDD_ABB_3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + ldo2_reg: LDO2 { + regulator-name = "VDD_ALIVE_1.1V"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + }; + + buck1_reg: BUCK1 { + regulator-name = "VDD_ARM_1.2V"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt new file mode 100644 index 000000000000..74739a4e3dcd --- /dev/null +++ b/Documentation/devicetree/bindings/sound/samsung-i2s.txt @@ -0,0 +1,62 @@ +* Samsung I2S controller + +Required SoC Specific properties: + +- compatible : "samsung,samsung-i2s" +- reg: physical base address of the controller and length of memory mapped + region. +- tx-dma-channel-secondary: The dma channel specifier for secondary tx + operations. The format of the dma specifier depends on the dma + controller. +- tx-dma-channel: The dma channel specifier for tx operations. The format of + the dma specifier depends on the dma controller. +- rx-dma-channel: The dma channel specifier for rx operations. The format of + the dma specifier depends on the dma controller. +- supports-6ch: If the Primary DAI has 5.1 Channel support, this flag is + enabled. +- supports-rstclr: This flag should be set if I2S software reset bit control is + required. When this flag is set I2S software reset bit will be enabled or + disabled based on need. +- supports-secdai: If the I2S block has a secondary sound source support, then + this flag is enabled. + +Required Board Specific Properties: + +- gpios: The gpio specifier for data out,data in, LRCLK, CDCLK and SCLK + interface lines. The format of the gpio specifier depends on the gpio + controller. +- idma-addr: Internal DMA register base address of the audio sub system(used in + secondary sound source). + +Aliases: + +- All the I2S controller nodes should be represented in the aliases node using + the following format 'i2s{n}' where n is a unique number for the alias. + +Example: + +- SoC Specific Portion: + +i2s@03830000 { + compatible = "samsung,samsung-i2s"; + reg = <0x03830000 0x100>; + tx-dma-channel-secondary = <&pdma0 8>; + tx-dma-channel = <&pdma0 10>; + rx-dma-channel = <&pdma0 9>; + supports-6ch; + supports-rstclr; + supports-secdai; +}; + +- Board Specific Portion: + +i2s_0: i2s@03830000 { + gpios = <&gpz 0 2 0 0>, + <&gpz 1 2 0 0>, + <&gpz 2 2 0 0>, + <&gpz 3 2 0 0>, + <&gpz 4 2 0 0>, + <&gpz 5 2 0 0>, + <&gpz 6 2 0 0>; + idma-addr = <0x03000000>; +}; diff --git a/Documentation/devicetree/bindings/usb/s3c-hsotg.txt b/Documentation/devicetree/bindings/usb/s3c-hsotg.txt new file mode 100644 index 000000000000..30b26c033873 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/s3c-hsotg.txt @@ -0,0 +1,16 @@ +Samsung HSOTG controller s3c-hsotg + +Required properties: + - compatible : should be "samsung,exynos-hsotg". + - reg : should contain at least address and length of the standard HSOTG + register set for the device. + - interrupts : one HSOTG interrupt should be described here. + +Example (Samsung Origen): + + usb@12480000 { + compatible = "samsung,exynos-hsotg"; + reg = <0x12480000 0x20000>; + interrupts = <0 71 0>; + }; + @@ -384,6 +384,7 @@ KBUILD_LDFLAGS_MODULE := -T $(srctree)/scripts/module-common.lds # Read KERNELRELEASE from include/config/kernel.release (if it exists) KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null) KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION) +KERNELVERSIONLOCAL= $(shell cat .scmversion 2> /dev/null) export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION export ARCH SRCARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC @@ -794,7 +795,8 @@ $(vmlinux-dirs): prepare scripts include/config/kernel.release: include/config/auto.conf FORCE $(Q)rm -f $@ $(Q)echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))" > $@ - + $(Q)rm -f .scmversion + $(Q)($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion --save-scmversion $(srctree)) # Things we need to do before we recursively start building the kernel # or the modules are listed in "prepare". @@ -845,7 +847,8 @@ define filechk_utsrelease.h echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2; \ exit 1; \ fi; \ - (echo \#define UTS_RELEASE \"$(KERNELRELEASE)\";) + (echo \#define UTS_RELEASE \"$(KERNELRELEASE)\"; \ + echo \#define KERNEL_VERSION_LOCAL \"$(KERNELVERSIONLOCAL)\";) endef define filechk_version.h diff --git a/arch/arm/boot/dts/exynos4210-origen.dts b/arch/arm/boot/dts/exynos4210-origen.dts index 3e68f52e8454..3b38d0cab334 100644 --- a/arch/arm/boot/dts/exynos4210-origen.dts +++ b/arch/arm/boot/dts/exynos4210-origen.dts @@ -29,11 +29,25 @@ bootargs ="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc"; }; + serial@13800000 { + status = "okay"; + }; + + serial@13810000 { + status = "okay"; + }; + + serial@13820000 { + status = "okay"; + }; + + serial@13830000 { + status = "okay"; + }; + sdhci@12530000 { - samsung,sdhci-bus-width = <4>; - linux,mmc_cap_4_bit_data; - samsung,sdhci-cd-internal; - gpio-cd = <&gpk2 2 2 3 3>; + bus-width = <4>; + samsung,cd-pinmux-gpio = <&gpk2 2 2 3 3>; gpios = <&gpk2 0 2 0 3>, <&gpk2 1 2 0 3>, <&gpk2 3 2 3 3>, @@ -44,10 +58,8 @@ }; sdhci@12510000 { - samsung,sdhci-bus-width = <4>; - linux,mmc_cap_4_bit_data; - samsung,sdhci-cd-internal; - gpio-cd = <&gpk0 2 2 3 3>; + bus-width = <4>; + samsung,cd-pinmux-gpio = <&gpk0 2 2 3 3>; gpios = <&gpk0 0 2 0 3>, <&gpk0 1 2 0 3>, <&gpk0 3 2 3 3>, @@ -57,6 +69,66 @@ status = "okay"; }; + lcd_fimd0: lcd_panel0 { + compatible = "lcd-powercontrol"; + vcc-lcd-supply = <&buck7_reg>; + lcd-reset-gpio = <&gpe3 4 1 0 0>; + lcd-htiming = <64 16 48 1024>; + lcd-vtiming = <64 16 3 600>; + }; + + fimd@11C00000 { + samsung,fimd-display = <&lcd_fimd0>; + samsung,fimd-vidout-rgb; + samsung,fimd-inv-hsync; + samsung,fimd-inv-vsync; + samsung,fimd-inv-vclk; + samsung,fimd-frame-rate = <60>; + + gpios = <&gpf0 0 2 0 0>, + <&gpf0 1 2 0 0>, + <&gpf0 2 2 0 0>, + <&gpf0 3 2 0 0>, + <&gpf0 4 2 0 0>, + <&gpf0 5 2 0 0>, + <&gpf0 6 2 0 0>, + <&gpf0 7 2 0 0>, + <&gpf1 0 2 0 0>, + <&gpf1 1 2 0 0>, + <&gpf1 2 2 0 0>, + <&gpf1 3 2 0 0>, + <&gpf1 4 2 0 0>, + <&gpf1 5 2 0 0>, + <&gpf1 6 2 0 0>, + <&gpf1 7 2 0 0>, + <&gpf2 0 2 0 0>, + <&gpf2 1 2 0 0>, + <&gpf2 2 2 0 0>, + <&gpf2 3 2 0 0>, + <&gpf2 4 2 0 0>, + <&gpf2 5 2 0 0>, + <&gpf2 6 2 0 0>, + <&gpf2 7 2 0 0>, + <&gpf3 0 2 0 0>, + <&gpf3 1 2 0 0>, + <&gpf3 2 2 0 0>, + <&gpf3 3 2 0 0>; + + window0 { + samsung,fimd-win-id = <0>; + samsung,fimd-win-bpp = <32 24>; + samsung,fimd-win-res = <1024 600>; + samsung,fimd-win-vres = <1024 600>; + }; + + window1 { + samsung,fimd-win-id = <1>; + samsung,fimd-win-bpp = <32 24>; + samsung,fimd-win-res = <1024 600>; + samsung,fimd-win-vres = <1024 600>; + }; + }; + gpio_keys { compatible = "gpio-keys"; #address-cells = <1>; @@ -105,4 +177,237 @@ linux,default-trigger = "heartbeat"; }; }; + + i2c@13860000 { + #address-cells = <1>; + #size-cells = <0>; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <20000>; + gpios = <&gpd1 0 2 3 0>, + <&gpd1 1 2 3 0>; + status = "okay"; + + max8997_pmic@66 { + compatible = "maxim,max8997-pmic"; + interrupt-parent = <&wakeup_eint>; + reg = <0x66>; + interrupts = <4 0>, <3 0>; + + max8997,pmic-ignore-gpiodvs-side-effect; + max8997,pmic-buck125-default-dvs-idx = <0>; + max8997,pmic-buck125-dvs-gpios = <&gpx0 0 1 0 0>, /* SET1 */ + <&gpx0 1 1 0 0>, /* SET2 */ + <&gpx0 2 1 0 0>; /* SET3 */ + max8997,pmic-buck1-dvs-voltage = <1350000>, <1300000>, + <1250000>, <1200000>, + <1150000>, <1100000>, + <1000000>, <950000>; + max8997,pmic-buck2-dvs-voltage = <1100000>, <1100000>, + <1100000>, <1100000>, + <1000000>, <1000000>, + <1000000>, <1000000>; + max8997,pmic-buck5-dvs-voltage = <1200000>, <1200000>, + <1200000>, <1200000>, + <1200000>, <1200000>, + <1200000>, <1200000>; + + regulators { + ldo1_reg: LDO1 { + regulator-name = "VDD_ABB_3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + ldo2_reg: LDO2 { + regulator-name = "VDD_ALIVE_1.1V"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + }; + + ldo3_reg: LDO3 { + regulator-name = "VMIPI_1.1V"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + }; + + ldo4_reg: LDO4 { + regulator-name = "VDD_RTC_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo6_reg: LDO6 { + regulator-name = "VMIPI_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo7_reg: LDO7 { + regulator-name = "VDD_AUD_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo8_reg: LDO8 { + regulator-name = "VADC_3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + ldo11_reg: LDO11 { + regulator-name = "VDD_AUD_3V"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + }; + + ldo17_reg: LDO17 { + regulator-name = "vmmc"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + buck1_reg: BUCK1 { + regulator-name = "vdd_arm"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + }; + + buck7_reg: BUCK7 { + regulator-name = "VDD_LCD_3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + }; + }; + }; + + unidisplay_ts@41 { + compatible = "pixcir,unidisplay-ts"; + interrupt-parent = <&wakeup_eint>; + reg = <0x41>; + interrupts = <25 0>; + }; + }; + + i2c@13870000 { + #address-cells = <1>; + #size-cells = <0>; + samsung,i2c-sda-delay = <100>; + samsung,i2c-max-bus-freq = <20000>; + + gpios = <&gpd1 2 2 3 0>, + <&gpd1 3 2 3 0>; + status = "okay"; + + codec: alc5625@1e { + compatible = "realtek,alc5625"; + reg = <0x1e>; + }; + }; + + i2c@138e0000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "samsung,s3c2440-hdmiphy-i2c"; + samsung,i2c-quirk-hdmiphy; + samsung,i2c-no-gpio; + reg = <0x138e0000 0x1000>; + interrupts = <0 93 0>; + status = "okay"; + + hdmiphy: phy@38 { + compatible = "samsung,hdmiphy-exynos4210"; + reg = <0x38>; + }; + }; + + tvmixer: tvmixer@12c10000 { + compatible = "samsung,s5pv210-tvmixer"; + reg = <0x12c10000 0x10000>, <0x12c00000 0x10000>; + reg-names = "mxr", "vp"; + interrupts = <0 91 0>; + interrupt-names = "irq"; + }; + + hdmi: hdmi@12d00000 { + compatible = "samsung,s5pv210-hdmi"; + reg = <0x12d00000 0x100000>; + interrupts = <0 92 0>; + phy = <&hdmiphy>; + + vdd_osc-supply = <&ldo8_reg>; + vdd_pll-supply = <&ldo3_reg>; + vdd-supply = <&ldo3_reg>; + }; + + pd_cam: pd-cam { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C00 0x10>; + }; + + pd_tv: pd-tv { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C20 0x10>; + }; + + pd_mfc: pd-mfc { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C40 0x10>; + }; + + pd_g3d: pd-g3d { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C60 0x10>; + }; + + pd_lcd0: pd-lcd0 { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C80 0x10>; + }; + + pd_lcd1: pd-lcd1 { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023CA0 0x10>; + }; + + pd_gps: pd-gps { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023CE0 0x10>; + samsung,exynos4210-pd_off; + }; + + usb@12480000 { + vusb_a-supply = <&ldo3_reg>; + vusb_d-supply = <&ldo3_reg>; + }; + + reg_hdmi_en: fixedregulator@0 { + compatible = "regulator-fixed"; + regulator-name = "hdmi-en"; + }; + + i2s_0: i2s@03830000 { + gpios = <&gpz 0 2 0 0>, + <&gpz 1 2 0 0>, + <&gpz 2 2 0 0>, + <&gpz 3 2 0 0>, + <&gpz 4 2 0 0>, + <&gpz 5 2 0 0>, + <&gpz 6 2 0 0>; + idma-addr = <0x03000000>; + }; + + asoc_dma { + compatible = "samsung,audio-dma"; + }; + }; diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index 214c557eda7f..0a6604aae452 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -20,15 +20,12 @@ */ /include/ "exynos4.dtsi" -/include/ "exynos4210-pinctrl.dtsi" / { compatible = "samsung,exynos4210"; aliases { - pinctrl0 = &pinctrl_0; - pinctrl1 = &pinctrl_1; - pinctrl2 = &pinctrl_2; + i2s0 = &i2s_0; }; gic:interrupt-controller@10490000 { @@ -42,37 +39,28 @@ <0 12 0>, <0 13 0>, <0 14 0>, <0 15 0>; }; - pinctrl_0: pinctrl@11400000 { - compatible = "samsung,pinctrl-exynos4210"; - reg = <0x11400000 0x1000>; - interrupts = <0 47 0>; - interrupt-controller; - #interrupt-cells = <2>; - }; - - pinctrl_1: pinctrl@11000000 { - compatible = "samsung,pinctrl-exynos4210"; + wakeup_eint: interrupt-controller-wakeup-eint { + compatible = "samsung,exynos4210-wakeup-eint"; reg = <0x11000000 0x1000>; - interrupts = <0 46 0>; - interrupt-controller; #interrupt-cells = <2>; + interrupt-controller; + interrupts = <0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>, + <0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>, + <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>, + <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>, + <0 32 0>; + }; - wakup_eint: wakeup-interrupt-controller { - compatible = "samsung,exynos4210-wakeup-eint"; - interrupt-parent = <&gic>; - interrupt-controller; - #interrupt-cells = <2>; - interrupts = <0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>, - <0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>, - <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>, - <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>, - <0 32 0>; - }; + power-domain-lcd0 { + compatible = "samsung,exynos4210-pd"; + reg = <0x10023C00 0x10>; }; - pinctrl_2: pinctrl@03860000 { - compatible = "samsung,pinctrl-exynos4210"; - reg = <0x03860000 0x1000>; + fimd@11C00000 { + compatible = "samsung,exynos4210-fimd"; + interrupt-parent = <&combiner>; + reg = <0x11C00000 0x8000>; + interrupts = <11 1>, <11 0>, <11 2>; }; gpio-controllers { @@ -303,4 +291,33 @@ #gpio-cells = <4>; }; }; + + usb@12580000 { + compatible = "samsung,exynos-ehci", "usb-ehci"; + reg = <0x12580000 0x100>; + interrupts = <0 70 0>; + }; + + usb@12590000 { + compatible = "samsung,exynos-ohci", "usb-ohci"; + reg = <0x12590000 0x100>; + interrupts = <0 70 0>; + }; + + usb@12480000 { + compatible = "samsung,exynos-hsotg"; + reg = <0x12480000 0x20000>; + interrupts = <0 71 0>; + }; + + i2s_0: i2s@03830000 { + compatible = "samsung,samsung-i2s"; + reg = <0x03830000 0x100>; + tx-dma-channel-secondary = <&pdma0 10>; + tx-dma-channel = <&pdma0 12>; + rx-dma-channel = <&pdma0 11>; + supports-6ch; + supports-rstclr; + supports-secdai; + }; }; diff --git a/arch/arm/configs/android_origen_defconfig b/arch/arm/configs/android_origen_defconfig new file mode 100644 index 000000000000..7fd3e66455d7 --- /dev/null +++ b/arch/arm/configs/android_origen_defconfig @@ -0,0 +1,209 @@ +CONFIG_EXPERIMENTAL=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=16 +CONFIG_BLK_DEV_INITRD=y +CONFIG_KALLSYMS_ALL=y +CONFIG_PERF_COUNTERS=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_GCOV_KERNEL=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_PARTITION_ADVANCED=y +CONFIG_BSD_DISKLABEL=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_ARCH_EXYNOS=y +CONFIG_S3C_LOWLEVEL_UART_PORT=2 +CONFIG_S3C24XX_PWM=y +CONFIG_MACH_SMDKC210=y +CONFIG_MACH_ARMLEX4210=y +CONFIG_MACH_UNIVERSAL_C210=y +CONFIG_MACH_NURI=y +CONFIG_MACH_ORIGEN=y +CONFIG_MACH_SMDK4412=y +CONFIG_MACH_EXYNOS4_DT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_SMP=y +CONFIG_SCHED_MC=y +CONFIG_SCHED_SMT=y +CONFIG_NR_CPUS=2 +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_CMDLINE="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc mem=256M" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_PM_RUNTIME=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IPV6=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_NETLINK_ACCT=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_XTABLES=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_CFG80211=y +CONFIG_RFKILL=y +CONFIG_RFKILL_GPIO=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_CMA=y +CONFIG_CMA_SIZE_MBYTES=32 +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_NETDEVICES=y +CONFIG_USB_PEGASUS=y +CONFIG_USB_USBNET=y +CONFIG_USB_NET_DM9601=y +CONFIG_USB_NET_MCS7830=y +CONFIG_USB_VIDEO_CLASS=y +CONFIG_ATH_COMMON=y +CONFIG_ATH_DEBUG=y +CONFIG_ATH6KL=m +CONFIG_ATH6KL_PLATFORM_DATA=y +CONFIG_ATH6KL_POLL=y +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_GPIO=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_UNIDISPLAY_TS=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_SAMSUNG=y +CONFIG_SERIAL_SAMSUNG_CONSOLE=y +CONFIG_HW_RANDOM=y +CONFIG_I2C=y +CONFIG_I2C_S3C2410=y +CONFIG_POWER_SUPPLY=y +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y +CONFIG_SENSORS_EXYNOS4_TMU=y +CONFIG_MFD_MAX8997=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_MAX8997=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_V4L_USB_DRIVERS=y +CONFIG_VIDEO_SAMSUNG_S5P_FIMC=y +CONFIG_VIDEO_S5P_FIMC=y +CONFIG_VIDEO_SAMSUNG_S5P_TV=y +CONFIG_VIDEO_SAMSUNG_S5P_HDMI=y +CONFIG_VIDEO_SAMSUNG_S5P_SDO=y +CONFIG_VIDEO_SAMSUNG_S5P_MIXER=y +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_VIDEO_SAMSUNG_S5P_G2D=y +CONFIG_VIDEO_SAMSUNG_S5P_JPEG=y +CONFIG_VIDEO_SAMSUNG_S5P_MFC=y +CONFIG_DRM=y +CONFIG_ION=y +CONFIG_MALI400MP=y +CONFIG_USING_PMM=y +CONFIG_UMP=y +CONFIG_FB=y +CONFIG_FB_S3C=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_PLATFORM=y +CONFIG_LCD_PWRCTRL=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_SAMSUNG=y +CONFIG_HID_KYE=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_S5P=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_EXYNOS=y +CONFIG_USB_STORAGE=y +CONFIG_USB_GADGET=y +CONFIG_USB_S3C_HSOTG=y +CONFIG_USB_G_ANDROID=y +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_S3C=y +CONFIG_MMC_SDHCI_S3C_DMA=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_S3C=y +CONFIG_STAGING=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_PERSISTENT_TRACER=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ANDROID_SWITCH=y +CONFIG_SND_SOC_ORIGEN_ALC5625=y +CONFIG_EXT2_FS=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_CRAMFS=y +CONFIG_ROMFS_FS=y +CONFIG_PSTORE=y +CONFIG_PSTORE_RAM=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_DEBUG_KERNEL=y +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEBUG_RT_MUTEXES=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_INFO=y +CONFIG_FUNCTION_PROFILER=y +CONFIG_DEBUG_USER=y +CONFIG_CRC_CCITT=y diff --git a/arch/arm/configs/exynos4_defconfig b/arch/arm/configs/exynos4_defconfig index bffe68e190a3..62dd948ecac8 100644 --- a/arch/arm/configs/exynos4_defconfig +++ b/arch/arm/configs/exynos4_defconfig @@ -5,43 +5,154 @@ CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set CONFIG_ARCH_EXYNOS=y -CONFIG_S3C_LOWLEVEL_UART_PORT=1 +CONFIG_S3C_LOWLEVEL_UART_PORT=2 +CONFIG_S3C24XX_PWM=y CONFIG_MACH_SMDKC210=y CONFIG_MACH_ARMLEX4210=y CONFIG_MACH_UNIVERSAL_C210=y CONFIG_MACH_NURI=y CONFIG_MACH_ORIGEN=y CONFIG_MACH_SMDK4412=y +CONFIG_MACH_EXYNOS4_DT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_SMP=y +CONFIG_SCHED_MC=y +CONFIG_SCHED_SMT=y CONFIG_NR_CPUS=2 CONFIG_PREEMPT=y CONFIG_AEABI=y -CONFIG_CMDLINE="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc mem=256M" +CONFIG_CMDLINE="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc mem=256M" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_IDLE=y CONFIG_VFP=y CONFIG_NEON=y +CONFIG_PM_RUNTIME=y +CONFIG_NET=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IPV6=y +CONFIG_CFG80211=y +CONFIG_RFKILL=y +CONFIG_RFKILL_GPIO=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_CMA=y +CONFIG_CMA_SIZE_MBYTES=32 CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_SG=y +CONFIG_NETDEVICES=y +CONFIG_USB_PEGASUS=y +CONFIG_USB_USBNET=y +CONFIG_USB_NET_DM9601=y +CONFIG_USB_NET_MCS7830=y +CONFIG_ATH_COMMON=y +CONFIG_ATH_DEBUG=y +CONFIG_ATH6KL=m +CONFIG_ATH6KL_PLATFORM_DATA=y +CONFIG_ATH6KL_POLL=y CONFIG_INPUT_EVDEV=y -# CONFIG_INPUT_KEYBOARD is not set +CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_UNIDISPLAY_TS=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_SAMSUNG=y CONFIG_SERIAL_SAMSUNG_CONSOLE=y CONFIG_HW_RANDOM=y CONFIG_I2C=y +CONFIG_I2C_S3C2410=y +CONFIG_POWER_SUPPLY=y # CONFIG_HWMON is not set -# CONFIG_MFD_SUPPORT is not set -# CONFIG_HID_SUPPORT is not set -# CONFIG_USB_SUPPORT is not set +CONFIG_MFD_MAX8997=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_MAX8997=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_SAMSUNG_S5P_FIMC=y +CONFIG_VIDEO_S5P_FIMC=y +CONFIG_VIDEO_SAMSUNG_S5P_TV=y +CONFIG_VIDEO_SAMSUNG_S5P_HDMI=y +CONFIG_VIDEO_SAMSUNG_S5P_SDO=y +CONFIG_VIDEO_SAMSUNG_S5P_MIXER=y +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_VIDEO_SAMSUNG_S5P_G2D=y +CONFIG_VIDEO_SAMSUNG_S5P_JPEG=y +CONFIG_VIDEO_SAMSUNG_S5P_MFC=y +CONFIG_FB=y +CONFIG_FB_S3C=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_PLATFORM=y +CONFIG_LCD_PWRCTRL=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_SAMSUNG=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_S5P=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_EXYNOS=y +CONFIG_USB_STORAGE=y +CONFIG_USB_GADGET=y +CONFIG_USB_S3C_HSOTG=y +CONFIG_USB_ZERO=m +CONFIG_USB_ETH=m +CONFIG_USB_ETH_EEM=y +CONFIG_USB_G_NCM=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_FUNCTIONFS=m +CONFIG_USB_FUNCTIONFS_ETH=y +CONFIG_USB_FUNCTIONFS_RNDIS=y +CONFIG_USB_FUNCTIONFS_GENERIC=y +CONFIG_USB_FILE_STORAGE=m +CONFIG_USB_FILE_STORAGE_TEST=y +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_USB_G_PRINTER=m +CONFIG_USB_CDC_COMPOSITE=m +CONFIG_USB_G_ACM_MS=m +CONFIG_USB_G_MULTI=m +CONFIG_USB_G_MULTI_CDC=y +CONFIG_USB_G_HID=m +CONFIG_USB_G_DBGP=m +CONFIG_USB_G_DBGP_PRINTK=y +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_S3C=y +CONFIG_MMC_SDHCI_S3C_DMA=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_S3C=y +CONFIG_STAGING=y +CONFIG_SND_SOC_ORIGEN_ALC5625=y CONFIG_EXT2_FS=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y @@ -54,7 +165,9 @@ CONFIG_SOLARIS_X86_PARTITION=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_SECTION_MISMATCH=y CONFIG_DEBUG_KERNEL=y CONFIG_DETECT_HUNG_TASK=y CONFIG_DEBUG_RT_MUTEXES=y @@ -63,6 +176,4 @@ CONFIG_DEBUG_MUTEXES=y CONFIG_DEBUG_INFO=y CONFIG_SYSCTL_SYSCALL_CHECK=y CONFIG_DEBUG_USER=y -CONFIG_DEBUG_LL=y -CONFIG_EARLY_PRINTK=y CONFIG_CRC_CCITT=y diff --git a/arch/arm/configs/ubuntu_origen_defconfig b/arch/arm/configs/ubuntu_origen_defconfig new file mode 100644 index 000000000000..6bcf64c06dbe --- /dev/null +++ b/arch/arm/configs/ubuntu_origen_defconfig @@ -0,0 +1,241 @@ +CONFIG_EXPERIMENTAL=y +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=16 +CONFIG_BLK_DEV_INITRD=y +CONFIG_EMBEDDED=y +CONFIG_PERF_COUNTERS=y +# CONFIG_COMPAT_BRK is not set +CONFIG_SLAB=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_MODULES=y +CONFIG_ARCH_EXYNOS=y +CONFIG_S3C_LOWLEVEL_UART_PORT=2 +CONFIG_S3C24XX_PWM=y +CONFIG_MACH_SMDKC210=y +CONFIG_MACH_ARMLEX4210=y +CONFIG_MACH_UNIVERSAL_C210=y +CONFIG_MACH_NURI=y +CONFIG_MACH_ORIGEN=y +CONFIG_MACH_SMDK4412=y +CONFIG_MACH_EXYNOS4_DT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_SMP=y +CONFIG_SCHED_MC=y +CONFIG_SCHED_SMT=y +CONFIG_NR_CPUS=2 +CONFIG_THUMB2_KERNEL=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_SECCOMP=y +CONFIG_CC_STACKPROTECTOR=y +CONFIG_CMDLINE="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc mem=256M" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_BINFMT_MISC=y +CONFIG_PM_RUNTIME=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_NET_KEY_MIGRATE=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_SYN_COOKIES=y +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_NETLABEL=y +CONFIG_NETFILTER=y +CONFIG_CFG80211=y +CONFIG_RFKILL=y +CONFIG_RFKILL_GPIO=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_CMA=y +CONFIG_CMA_SIZE_MBYTES=32 +CONFIG_CONNECTOR=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_OOPS=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_NAND=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=65536 +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_NETDEVICES=y +CONFIG_USB_PEGASUS=y +CONFIG_USB_USBNET=y +CONFIG_USB_NET_DM9601=y +CONFIG_USB_NET_MCS7830=y +CONFIG_ATH_COMMON=y +CONFIG_ATH_DEBUG=y +CONFIG_ATH6KL=m +CONFIG_ATH6KL_PLATFORM_DATA=y +CONFIG_ATH6KL_POLL=y +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_GPIO=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_UNIDISPLAY_TS=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_SAMSUNG=y +CONFIG_SERIAL_SAMSUNG_CONSOLE=y +CONFIG_HW_RANDOM=y +CONFIG_I2C=y +CONFIG_I2C_S3C2410=y +CONFIG_POWER_SUPPLY=y +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y +CONFIG_SENSORS_EXYNOS4_TMU=y +CONFIG_MFD_MAX8997=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_MAX8997=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_SAMSUNG_S5P_FIMC=y +CONFIG_VIDEO_S5P_FIMC=y +CONFIG_VIDEO_SAMSUNG_S5P_TV=y +CONFIG_VIDEO_SAMSUNG_S5P_HDMI=y +CONFIG_VIDEO_SAMSUNG_S5P_SDO=y +CONFIG_VIDEO_SAMSUNG_S5P_MIXER=y +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_VIDEO_SAMSUNG_S5P_G2D=y +CONFIG_VIDEO_SAMSUNG_S5P_JPEG=y +CONFIG_VIDEO_SAMSUNG_S5P_MFC=y +CONFIG_FB=y +CONFIG_FB_S3C=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_PLATFORM=y +CONFIG_LCD_PWRCTRL=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_SAMSUNG=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_S5P=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_EXYNOS=y +CONFIG_USB_STORAGE=y +CONFIG_USB_GADGET=y +CONFIG_USB_S3C_HSOTG=y +CONFIG_USB_ZERO=m +CONFIG_USB_ETH=m +CONFIG_USB_ETH_EEM=y +CONFIG_USB_G_NCM=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_FUNCTIONFS=m +CONFIG_USB_FUNCTIONFS_ETH=y +CONFIG_USB_FUNCTIONFS_RNDIS=y +CONFIG_USB_FUNCTIONFS_GENERIC=y +CONFIG_USB_FILE_STORAGE=m +CONFIG_USB_FILE_STORAGE_TEST=y +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_USB_G_PRINTER=m +CONFIG_USB_CDC_COMPOSITE=m +CONFIG_USB_G_ACM_MS=m +CONFIG_USB_G_MULTI=m +CONFIG_USB_G_MULTI_CDC=y +CONFIG_USB_G_HID=m +CONFIG_USB_G_DBGP=m +CONFIG_USB_G_DBGP_PRINTK=y +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_S3C=y +CONFIG_MMC_SDHCI_S3C_DMA=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_S3C=y +CONFIG_STAGING=y +CONFIG_SND_SOC_ORIGEN_ALC5625=y +CONFIG_EXT2_FS=y +CONFIG_EXT3_FS=y +CONFIG_EXT4_FS=y +CONFIG_BTRFS_FS=y +CONFIG_QUOTA=y +CONFIG_QFMT_V2=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_ECRYPT_FS=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_SUMMARY=y +CONFIG_JFFS2_FS_XATTR=y +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_LZO=y +CONFIG_JFFS2_RUBIN=y +CONFIG_CRAMFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_DEBUG_KERNEL=y +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEBUG_RT_MUTEXES=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_INFO=y +CONFIG_SYSCTL_SYSCALL_CHECK=y +CONFIG_DEBUG_USER=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_PROVE_LOCKING=y +CONFIG_ENABLE_DEFAULT_TRACERS=y +CONFIG_STRICT_DEVMEM=y +CONFIG_KEYS=y +CONFIG_SECURITY=y +CONFIG_LSM_MMAP_MIN_ADDR=0 +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SMACK=y +CONFIG_SECURITY_APPARMOR=y +CONFIG_DEFAULT_SECURITY_APPARMOR=y +CONFIG_CRYPTO_MICHAEL_MIC=y +CONFIG_CRC_CCITT=y +CONFIG_CRC_T10DIF=y +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index da55107033dd..68e00a76f2a0 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -325,6 +325,7 @@ config MACH_ORIGEN select CPU_EXYNOS4210 select EXYNOS4_DEV_USB_OHCI select EXYNOS4_SETUP_FIMD0 + select EXYNOS4_SETUP_I2C1 select EXYNOS4_SETUP_SDHCI select EXYNOS4_SETUP_USB_PHY select EXYNOS_DEV_DMA @@ -333,7 +334,9 @@ config MACH_ORIGEN select S3C24XX_PWM select S3C_DEV_HSMMC select S3C_DEV_HSMMC2 + select S3C_DEV_HSMMC3 select S3C_DEV_RTC + select S3C_DEV_I2C1 select S3C_DEV_USB_HSOTG select S3C_DEV_WDT select S5P_DEV_FIMC0 @@ -342,6 +345,7 @@ config MACH_ORIGEN select S5P_DEV_FIMC3 select S5P_DEV_FIMD0 select S5P_DEV_G2D + select S5P_DEV_G3D select S5P_DEV_I2C_HDMIPHY select S5P_DEV_JPEG select S5P_DEV_MFC @@ -406,9 +410,9 @@ config MACH_EXYNOS4_DT select ARM_AMBA select CPU_EXYNOS4210 select HAVE_SAMSUNG_KEYPAD if INPUT_KEYBOARD - select PINCTRL - select PINCTRL_EXYNOS4 select USE_OF + select SAMSUNG_DEV_BACKLIGHT + select SAMSUNG_DEV_PWM help Machine support for Samsung Exynos4 machine with device tree enabled. Select this if a fdt blob is available for the Exynos4 SoC based board. diff --git a/arch/arm/mach-exynos/clock-exynos4.c b/arch/arm/mach-exynos/clock-exynos4.c index 6a45c9a9abe9..b31815262164 100644 --- a/arch/arm/mach-exynos/clock-exynos4.c +++ b/arch/arm/mach-exynos/clock-exynos4.c @@ -23,6 +23,7 @@ #include <plat/pm.h> #include <mach/map.h> +#include <mach/regs-audss.h> #include <mach/regs-clock.h> #include <mach/sysmmu.h> @@ -31,6 +32,7 @@ #ifdef CONFIG_PM_SLEEP static struct sleep_save exynos4_clock_save[] = { + SAVE_ITEM(EXYNOS4_CLKSRC_AUDSS), SAVE_ITEM(EXYNOS4_CLKDIV_LEFTBUS), SAVE_ITEM(EXYNOS4_CLKGATE_IP_LEFTBUS), SAVE_ITEM(EXYNOS4_CLKDIV_RIGHTBUS), @@ -118,6 +120,10 @@ static struct clk dummy_apb_pclk = { .id = -1, }; +static struct clk exynos4_clk_xxti = { + .name = "xxti", +}; + static int exynos4_clksrc_mask_top_ctrl(struct clk *clk, int enable) { return s5p_gatectrl(EXYNOS4_CLKSRC_MASK_TOP, clk, enable); @@ -824,6 +830,24 @@ static struct clk exynos4_clk_fimd0 = { .ctrlbit = (1 << 0), }; +static struct clk *exynos4_clkset_mout_audss_list[] = { + &exynos4_clk_xxti, + &clk_fout_epll, +}; + +static struct clksrc_sources exynos4_clkset_mout_audss = { + .sources = exynos4_clkset_mout_audss_list, + .nr_sources = ARRAY_SIZE(exynos4_clkset_mout_audss_list), +}; + +static struct clksrc_clk exynos4_clk_mout_audss = { + .clk = { + .name = "busclk", + }, + .sources = &exynos4_clkset_mout_audss, + .reg_src = { .reg = EXYNOS4_CLKSRC_AUDSS, .shift = 0, .size = 1 }, +}; + struct clk *exynos4_clkset_group_list[] = { [0] = &clk_ext_xtal_mux, [1] = &clk_xusbxti, @@ -1324,6 +1348,7 @@ static struct clksrc_clk *exynos4_sysclks[] = { &exynos4_clk_aclk_100, &exynos4_clk_aclk_160, &exynos4_clk_aclk_133, + &exynos4_clk_mout_audss, &exynos4_clk_dout_mmc0, &exynos4_clk_dout_mmc1, &exynos4_clk_dout_mmc2, @@ -1373,6 +1398,7 @@ static struct clk_lookup exynos4_clk_lookup[] = { CLKDEV_INIT("exynos4210-spi.0", "spi_busclk0", &exynos4_clk_sclk_spi0.clk), CLKDEV_INIT("exynos4210-spi.1", "spi_busclk0", &exynos4_clk_sclk_spi1.clk), CLKDEV_INIT("exynos4210-spi.2", "spi_busclk0", &exynos4_clk_sclk_spi2.clk), + CLKDEV_INIT("samsung-i2s.0", "i2s_opclk0", &exynos4_clk_mout_audss.clk), }; static int xtal_rate; @@ -1388,6 +1414,78 @@ static unsigned long exynos4_fout_apll_get_rate(struct clk *clk) return 0; } +static u32 exynos4_epll_div[][6] = { + { 48000000, 0, 48, 3, 3, 0 }, + { 96000000, 0, 48, 3, 2, 0 }, + { 144000000, 1, 72, 3, 2, 0 }, + { 192000000, 0, 48, 3, 1, 0 }, + { 288000000, 1, 72, 3, 1, 0 }, + { 84000000, 0, 42, 3, 2, 0 }, + { 50000000, 0, 50, 3, 3, 0 }, + { 80000000, 1, 80, 3, 3, 0 }, + { 32750000, 1, 65, 3, 4, 35127 }, + { 32768000, 1, 65, 3, 4, 35127 }, + { 49152000, 0, 49, 3, 3, 9961 }, + { 67737600, 1, 67, 3, 3, 48366 }, + { 73728000, 1, 73, 3, 3, 47710 }, + { 45158400, 0, 45, 3, 3, 10381 }, + { 45000000, 0, 45, 3, 3, 10355 }, + { 45158000, 0, 45, 3, 3, 10355 }, + { 49125000, 0, 49, 3, 3, 9961 }, + { 67738000, 1, 67, 3, 3, 48366 }, + { 73800000, 1, 73, 3, 3, 47710 }, + { 36000000, 1, 32, 3, 4, 0 }, + { 60000000, 1, 60, 3, 3, 0 }, + { 72000000, 1, 72, 3, 3, 0 }, + { 191923200, 0, 47, 3, 1, 64278 }, + { 180633600, 0, 45, 3, 1, 10381 }, +}; + +static int exynos4_epll_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned int epll_con, epll_con_k; + unsigned int i; + + /* Return if nothing changed */ + if (clk->rate == rate) + return 0; + + epll_con = __raw_readl(EXYNOS4_EPLL_CON0); + epll_con &= ~(0x1 << 27 | \ + PLL46XX_MDIV_MASK << PLL46XX_MDIV_SHIFT | \ + PLL46XX_PDIV_MASK << PLL46XX_PDIV_SHIFT | \ + PLL46XX_SDIV_MASK << PLL46XX_SDIV_SHIFT); + + for (i = 0; i < ARRAY_SIZE(exynos4_epll_div); i++) { + if (exynos4_epll_div[i][0] == rate) { + epll_con_k = exynos4_epll_div[i][5] << 0; + epll_con |= exynos4_epll_div[i][1] << 27; + epll_con |= exynos4_epll_div[i][2] << PLL46XX_MDIV_SHIFT; + epll_con |= exynos4_epll_div[i][3] << PLL46XX_PDIV_SHIFT; + epll_con |= exynos4_epll_div[i][4] << PLL46XX_SDIV_SHIFT; + break; + } + } + + if (i == ARRAY_SIZE(exynos4_epll_div)) { + printk(KERN_ERR "%s: Invalid Clock EPLL Frequency\n", + __func__); + return -EINVAL; + } + + __raw_writel(epll_con, EXYNOS4_EPLL_CON0); + __raw_writel(epll_con_k, EXYNOS4_EPLL_CON1); + + clk->rate = rate; + + return 0; +} + +static struct clk_ops exynos4_epll_ops = { + .get_rate = s5p_epll_get_rate, + .set_rate = exynos4_epll_set_rate, +}; + static struct clk_ops exynos4_fout_apll_ops = { .get_rate = exynos4_fout_apll_get_rate, }; @@ -1517,6 +1615,10 @@ void __init_or_cpufreq exynos4_setup_clocks(void) clk_fout_vpll.ops = &exynos4_vpll_ops; clk_fout_vpll.rate = vpll; + clk_fout_epll.enable = s5p_epll_enable; + clk_fout_epll.ops = &exynos4_epll_ops; + clk_set_parent(&exynos4_clk_mout_audss.clk, &clk_fout_epll); + printk(KERN_INFO "EXYNOS4: PLL settings, A=%ld, M=%ld, E=%ld V=%ld", apll, mpll, epll, vpll); diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c index 1947be8e5f5b..9027f55f7519 100644 --- a/arch/arm/mach-exynos/common.c +++ b/arch/arm/mach-exynos/common.c @@ -54,6 +54,12 @@ #define L2_AUX_VAL 0x7C470001 #define L2_AUX_MASK 0xC200ffff +#ifdef CONFIG_PM +extern int s3c_irq_wake(struct irq_data *data, unsigned int state); +#else +#define s3c_irq_wake NULL +#endif + static const char name_exynos4210[] = "EXYNOS4210"; static const char name_exynos4212[] = "EXYNOS4212"; static const char name_exynos4412[] = "EXYNOS4412"; @@ -65,6 +71,8 @@ static void exynos4_init_clocks(int xtal); static void exynos5_init_clocks(int xtal); static void exynos_init_uarts(struct s3c2410_uartcfg *cfg, int no); static int exynos_init(void); +static int exynos_init_irq_eint(struct device_node *np, + struct device_node *parent); static struct cpu_table cpu_ids[] __initdata = { { @@ -190,6 +198,11 @@ static struct map_desc exynos4_iodesc[] __initdata = { .length = SZ_64K, .type = MT_DEVICE, }, { + .virtual = (unsigned long)S5P_VA_AUDSS, + .pfn = __phys_to_pfn(EXYNOS4_PA_AUDSS), + .length = SZ_4K, + .type = MT_DEVICE, + }, { .virtual = (unsigned long)S3C_VA_USB_HSPHY, .pfn = __phys_to_pfn(EXYNOS4_PA_HSPHY), .length = SZ_4K, @@ -608,6 +621,8 @@ static const struct of_device_id exynos4_dt_irq_match[] = { { .compatible = "arm,cortex-a9-gic", .data = gic_of_init, }, { .compatible = "samsung,exynos4210-combiner", .data = combiner_of_init, }, + { .compatible = "samsung,exynos4210-wakeup-eint", + .data = exynos_init_irq_eint, }, {}, }; #endif @@ -625,8 +640,10 @@ void __init exynos4_init_irq(void) of_irq_init(exynos4_dt_irq_match); #endif - if (!of_have_populated_dt()) + if (!of_have_populated_dt()) { combiner_init(S5P_VA_COMBINER_BASE, NULL); + exynos_init_irq_eint(NULL, NULL); + } /* * The parameters of s5p_init_irq() are for VIC init. @@ -634,14 +651,38 @@ void __init exynos4_init_irq(void) * uses GIC instead of VIC. */ s5p_init_irq(NULL, 0); +#ifdef CONFIG_PM + irq_get_chip(IRQ_RTC_ALARM)->irq_set_wake = s3c_irq_wake; +#endif } void __init exynos5_init_irq(void) { + int irq; + struct device_node *np; + #ifdef CONFIG_OF of_irq_init(exynos4_dt_irq_match); #endif /* + * The Exynos5 wakeup interrupt controller has two-interrupt parents, + * gic and combiner. Hence, a interrupt nexus node is used to translate + * interrupt specifers for the interrupts which wakeup interrupt + * controller deliver to the gic and combiner. + * + * When using a interrupt nexus node (which is child node of the wakeup + * controller node), the interrupt parent of the wakeup controller node + * is set as the nexus node and the nexus node does not have a + * 'interrupt-controller' property. Hence, the of_irq_init function + * will not be able to invoke the intializer function for wakeup + * interrupt controller. So call the initiazer explicitly here. + */ + np = of_find_compatible_node(NULL, NULL, + "samsung,exynos5210-wakeup-eint"); + if (np) + exynos_init_irq_eint(np, NULL); + + /* * The parameters of s5p_init_irq() are for VIC init. * Theses parameters should be NULL and 0 because EXYNOS4 * uses GIC instead of VIC. @@ -747,6 +788,9 @@ static DEFINE_SPINLOCK(eint_lock); static unsigned int eint0_15_data[16]; +#define EXYNOS_EINT_NR 32 +static struct irq_domain *irq_domain; + static inline int exynos4_irq_to_gpio(unsigned int irq) { if (irq < IRQ_EINT(0)) @@ -837,9 +881,9 @@ static inline void exynos_irq_eint_mask(struct irq_data *data) u32 mask; spin_lock(&eint_lock); - mask = __raw_readl(EINT_MASK(exynos_eint_base, data->irq)); - mask |= EINT_OFFSET_BIT(data->irq); - __raw_writel(mask, EINT_MASK(exynos_eint_base, data->irq)); + mask = __raw_readl(EINT_MASK(exynos_eint_base, data->hwirq)); + mask |= EINT_OFFSET_BIT(data->hwirq); + __raw_writel(mask, EINT_MASK(exynos_eint_base, data->hwirq)); spin_unlock(&eint_lock); } @@ -848,16 +892,16 @@ static void exynos_irq_eint_unmask(struct irq_data *data) u32 mask; spin_lock(&eint_lock); - mask = __raw_readl(EINT_MASK(exynos_eint_base, data->irq)); - mask &= ~(EINT_OFFSET_BIT(data->irq)); - __raw_writel(mask, EINT_MASK(exynos_eint_base, data->irq)); + mask = __raw_readl(EINT_MASK(exynos_eint_base, data->hwirq)); + mask &= ~(EINT_OFFSET_BIT(data->hwirq)); + __raw_writel(mask, EINT_MASK(exynos_eint_base, data->hwirq)); spin_unlock(&eint_lock); } static inline void exynos_irq_eint_ack(struct irq_data *data) { - __raw_writel(EINT_OFFSET_BIT(data->irq), - EINT_PEND(exynos_eint_base, data->irq)); + __raw_writel(EINT_OFFSET_BIT(data->hwirq), + EINT_PEND(exynos_eint_base, data->hwirq)); } static void exynos_irq_eint_maskack(struct irq_data *data) @@ -868,7 +912,7 @@ static void exynos_irq_eint_maskack(struct irq_data *data) static int exynos_irq_eint_set_type(struct irq_data *data, unsigned int type) { - int offs = EINT_OFFSET(data->irq); + int offs = data->hwirq; int shift; u32 ctrl, mask; u32 newvalue = 0; @@ -903,10 +947,10 @@ static int exynos_irq_eint_set_type(struct irq_data *data, unsigned int type) mask = 0x7 << shift; spin_lock(&eint_lock); - ctrl = __raw_readl(EINT_CON(exynos_eint_base, data->irq)); + ctrl = __raw_readl(EINT_CON(exynos_eint_base, data->hwirq)); ctrl &= ~mask; ctrl |= newvalue << shift; - __raw_writel(ctrl, EINT_CON(exynos_eint_base, data->irq)); + __raw_writel(ctrl, EINT_CON(exynos_eint_base, data->hwirq)); spin_unlock(&eint_lock); if (soc_is_exynos5250()) @@ -950,7 +994,7 @@ static inline void exynos_irq_demux_eint(unsigned int start) while (status) { irq = fls(status) - 1; - generic_handle_irq(irq + start); + generic_handle_irq(irq_find_mapping(irq_domain, irq + start)); status &= ~(1 << irq); } } @@ -959,8 +1003,8 @@ static void exynos_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc) { struct irq_chip *chip = irq_get_chip(irq); chained_irq_enter(chip, desc); - exynos_irq_demux_eint(IRQ_EINT(16)); - exynos_irq_demux_eint(IRQ_EINT(24)); + exynos_irq_demux_eint(16); + exynos_irq_demux_eint(24); chained_irq_exit(chip, desc); } @@ -968,6 +1012,7 @@ static void exynos_irq_eint0_15(unsigned int irq, struct irq_desc *desc) { u32 *irq_data = irq_get_handler_data(irq); struct irq_chip *chip = irq_get_chip(irq); + int eint_irq; chained_irq_enter(chip, desc); chip->irq_mask(&desc->irq_data); @@ -975,15 +1020,30 @@ static void exynos_irq_eint0_15(unsigned int irq, struct irq_desc *desc) if (chip->irq_ack) chip->irq_ack(&desc->irq_data); - generic_handle_irq(*irq_data); + eint_irq = irq_find_mapping(irq_domain, *irq_data); + generic_handle_irq(eint_irq); chip->irq_unmask(&desc->irq_data); chained_irq_exit(chip, desc); } -static int __init exynos_init_irq_eint(void) +static int exynos_eint_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) { - int irq; + irq_set_chip_and_handler(irq, &exynos_irq_eint, handle_level_irq); + set_irq_flags(irq, IRQF_VALID); + return 0; +} + +static struct irq_domain_ops exynos_eint_irq_domain_ops = { + .map = exynos_eint_irq_domain_map, +}; + +static int __init exynos_init_irq_eint(struct device_node *np, + struct device_node *parent) +{ + int irq, *src_int, irq_base, irq_eint; + unsigned int paddr; #ifdef CONFIG_PINCTRL_SAMSUNG /* @@ -1011,40 +1071,45 @@ static int __init exynos_init_irq_eint(void) } #endif - if (soc_is_exynos5250()) - exynos_eint_base = ioremap(EXYNOS5_PA_GPIO1, SZ_4K); - else - exynos_eint_base = ioremap(EXYNOS4_PA_GPIO2, SZ_4K); + if (!np) { + if (soc_is_exynos5250()) + exynos_eint_base = ioremap(EXYNOS5_PA_GPIO1, SZ_4K); + else + exynos_eint_base = ioremap(EXYNOS4_PA_GPIO2, SZ_4K); + } else { + exynos_eint_base = of_iomap(np, 0); + } if (exynos_eint_base == NULL) { pr_err("unable to ioremap for EINT base address\n"); - return -ENOMEM; + return -ENXIO; } - for (irq = 0 ; irq <= 31 ; irq++) { - irq_set_chip_and_handler(IRQ_EINT(irq), &exynos_irq_eint, - handle_level_irq); - set_irq_flags(IRQ_EINT(irq), IRQF_VALID); + irq_base = irq_alloc_descs(IRQ_EINT(0), 1, EXYNOS_EINT_NR, 0); + if (IS_ERR_VALUE(irq_base)) { + irq_base = IRQ_EINT(0); + pr_warning("%s: irq desc alloc failed. Continuing with %d as " + "linux irq base\n", __func__, irq_base); } - irq_set_chained_handler(EXYNOS_IRQ_EINT16_31, exynos_irq_demux_eint16_31); - - for (irq = 0 ; irq <= 15 ; irq++) { - eint0_15_data[irq] = IRQ_EINT(irq); - - if (soc_is_exynos5250()) { - irq_set_handler_data(exynos5_eint0_15_src_int[irq], - &eint0_15_data[irq]); - irq_set_chained_handler(exynos5_eint0_15_src_int[irq], - exynos_irq_eint0_15); - } else { - irq_set_handler_data(exynos4_eint0_15_src_int[irq], - &eint0_15_data[irq]); - irq_set_chained_handler(exynos4_eint0_15_src_int[irq], - exynos_irq_eint0_15); - } + irq_domain = irq_domain_add_legacy(np, EXYNOS_EINT_NR, irq_base, 0, + &exynos_eint_irq_domain_ops, NULL); + if (WARN_ON(!irq_domain)) { + pr_warning("%s: irq domain init failed\n", __func__); + return 0; + } + + irq_eint = np ? irq_of_parse_and_map(np, 16) : EXYNOS_IRQ_EINT16_31; + irq_set_chained_handler(irq_eint, exynos_irq_demux_eint16_31); + + for (irq = 0 ; irq <= 15; irq++) { + eint0_15_data[irq] = irq; + src_int = soc_is_exynos5250() ? exynos5_eint0_15_src_int : + exynos4_eint0_15_src_int; + irq_eint = np ? irq_of_parse_and_map(np, irq) : src_int[irq]; + irq_set_handler_data(irq_eint, &eint0_15_data[irq]); + irq_set_chained_handler(irq_eint, exynos_irq_eint0_15); } return 0; } -arch_initcall(exynos_init_irq_eint); diff --git a/arch/arm/mach-exynos/include/mach/irqs.h b/arch/arm/mach-exynos/include/mach/irqs.h index 35bced6f9092..e6af1c4df128 100644 --- a/arch/arm/mach-exynos/include/mach/irqs.h +++ b/arch/arm/mach-exynos/include/mach/irqs.h @@ -461,6 +461,7 @@ #define S5P_GPIOINT_BASE (S5P_EINT_BASE1 + 32) #define IRQ_GPIO_END (S5P_GPIOINT_BASE + S5P_GPIOINT_COUNT) #define IRQ_TIMER_BASE (IRQ_GPIO_END + 64) +#define IRQ_TS (S5P_EINT_BASE1 + 25) /* Set the default NR_IRQS */ diff --git a/arch/arm/mach-exynos/include/mach/mali/config.h b/arch/arm/mach-exynos/include/mach/mali/config.h new file mode 100644 index 000000000000..ccd3cf7d2450 --- /dev/null +++ b/arch/arm/mach-exynos/include/mach/mali/config.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARCH_CONFIG_H__ +#define __ARCH_CONFIG_H__ + +/* Configuration for the EB platform with ZBT memory enabled */ +/*zepplin added 2010.08.17 for orion configuration*/ +#define MALI_BASE_ADDR 0x13000000 +#define GP_ADDR MALI_BASE_ADDR +#define L2_ADDR MALI_BASE_ADDR+0x1000 +#define PMU_ADDR MALI_BASE_ADDR+0x2000 +#define GP_MMU_ADDR MALI_BASE_ADDR+0x3000 +#define PP0_MMU_ADDR MALI_BASE_ADDR+0x4000 +#define PP1_MMU_ADDR MALI_BASE_ADDR+0x5000 +#define PP2_MMU_ADDR MALI_BASE_ADDR+0x6000 +#define PP3_MMU_ADDR MALI_BASE_ADDR+0x7000 +#define PP0_ADDR MALI_BASE_ADDR+0x8000 +#define PP1_ADDR MALI_BASE_ADDR+0xA000 +#define PP2_ADDR MALI_BASE_ADDR+0xC000 +#define PP3_ADDR MALI_BASE_ADDR+0xE000 + +/*for mmu and os memory*/ +#define MEM_BASE_ADDR 0x40000000 +#define MEM_TOTAL_SIZE 0x40000000 +#define MEM_MALI_OS_SIZE 0x18000000 + +/*for dedicated memory*/ +//#define MEM_MALI_BASE 0x58000000 +//#define MEM_MALI_SIZE 0x08000000 +#define MEM_MALI_SIZE CONFIG_MALI_MEM_SIZE*1024*1024 +#define MEM_MALI_BASE 0x60000000 - MEM_MALI_SIZE + +//#define S5P_IRQ(x) (x+32) +//#define IRQ_SPI(x) S5P_IRQ(x+32) +#define MAX_IRQ_IN_COMBINER 8 +//#define COMBINER_GROUP(x) ((x) * MAX_IRQ_IN_COMBINER + IRQ_SPI(64)) +#define COMBINER_IRQ(x, y) (COMBINER_GROUP(x) + y) + +#define IRQ_PPMMU0_3D COMBINER_IRQ(13, 0) +#define IRQ_PPMMU1_3D COMBINER_IRQ(13, 1) +#define IRQ_PPMMU2_3D COMBINER_IRQ(13, 2) +#define IRQ_PPMMU3_3D COMBINER_IRQ(13, 3) +#define IRQ_GPMMU_3D COMBINER_IRQ(13, 4) + +#define IRQ_PP0_3D COMBINER_IRQ(14, 0) +#define IRQ_PP1_3D COMBINER_IRQ(14, 1) +#define IRQ_PP2_3D COMBINER_IRQ(14, 2) +#define IRQ_PP3_3D COMBINER_IRQ(14, 3) +#define IRQ_GP_3D COMBINER_IRQ(14, 4) +#define IRQ_PMU_3D COMBINER_IRQ(14, 5) + +static _mali_osk_resource_t arch_configuration [] = +{ + { + .type = MALI400GP, + .description = "Mali-400 GP", + .base = GP_ADDR, + .irq = IRQ_GP_3D, + .mmu_id = 1 + }, + { + .type = MALI400PP, + .base = PP0_ADDR, + .irq = IRQ_PP0_3D, + .description = "Mali-400 PP 0", + .mmu_id = 2 + }, + { + .type = MALI400PP, + .base = PP1_ADDR, + .irq = IRQ_PP1_3D, + .description = "Mali-400 PP 1", + .mmu_id = 3 + }, + { + .type = MALI400PP, + .base = PP2_ADDR, + .irq = IRQ_PP2_3D, + .description = "Mali-400 PP 2", + .mmu_id = 4 + }, + { + .type = MALI400PP, + .base = PP3_ADDR, + .irq = IRQ_PP3_3D, + .description = "Mali-400 PP 3", + .mmu_id = 5 + }, +#if USING_MMU + { + .type = MMU, + .base = GP_MMU_ADDR, + .irq = IRQ_GPMMU_3D, + .description = "Mali-400 MMU for GP", + .mmu_id = 1 + }, + { + .type = MMU, + .base = PP0_MMU_ADDR, + .irq = IRQ_PPMMU0_3D, + .description = "Mali-400 MMU for PP 0", + .mmu_id = 2 + }, + { + .type = MMU, + .base = PP1_MMU_ADDR, + .irq = IRQ_PPMMU1_3D, + .description = "Mali-400 MMU for PP 1", + .mmu_id = 3 + }, + { + .type = MMU, + .base = PP2_MMU_ADDR, + .irq = IRQ_PPMMU2_3D, + .description = "Mali-400 MMU for PP 2", + .mmu_id = 4 + }, + { + .type = MMU, + .base = PP3_MMU_ADDR, + .irq = IRQ_PPMMU3_3D, + .description = "Mali-400 MMU for PP 3", + .mmu_id = 5 + }, +#if USING_OS_MEMORY + { + .type = OS_MEMORY, + .description = "System Memory", + .size = MEM_MALI_OS_SIZE, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE | _MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, +#endif +#if USING_DED /* Dedicated Memory */ + { + .type = MEMORY, + .description = "Dedicated Memory", + .base = MEM_MALI_BASE, + .size = MEM_MALI_SIZE, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE | _MALI_GP_READABLE | _MALI_GP_WRITEABLE | _MALI_MMU_READABLE | _MALI_MMU_WRITEABLE + }, +#endif/* if USING_OS_MEMORY*/ + { + .type = MEM_VALIDATION, + .description = "memory validation", + .base = MEM_BASE_ADDR, + .size = MEM_TOTAL_SIZE, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE | _MALI_GP_READABLE | _MALI_GP_WRITEABLE | _MALI_MMU_READABLE | _MALI_MMU_WRITEABLE + }, +#else /* Not using MMU */ + { + .type = MEMORY, + .description = "Dedicated Memory", + .base = MEM_MALI_BASE, + .size = MEM_MALI_SIZE, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE | _MALI_GP_READABLE | _MALI_GP_WRITEABLE | _MALI_MMU_READABLE | _MALI_MMU_WRITEABLE + }, +#endif + { + .type = MALI400L2, + .base = L2_ADDR, + .description = "Mali-400 L2 cache" + }, +}; + +#endif /* __ARCH_CONFIG_H__ */ diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h index 8480849affb9..c17f1b490266 100644 --- a/arch/arm/mach-exynos/include/mach/map.h +++ b/arch/arm/mach-exynos/include/mach/map.h @@ -39,6 +39,8 @@ #define EXYNOS4_PA_G2D 0x12800000 +#define EXYNOS4_PA_G3D 0x13000000 + #define EXYNOS4_PA_I2S0 0x03830000 #define EXYNOS4_PA_I2S1 0xE3100000 #define EXYNOS4_PA_I2S2 0xE2A00000 @@ -212,6 +214,8 @@ #define EXYNOS4_PA_ADC 0x13910000 #define EXYNOS4_PA_ADC1 0x13911000 +#define EXYNOS4_PA_AUDSS 0x03810000 + #define EXYNOS4_PA_AC97 0x139A0000 #define EXYNOS4_PA_SPDIF 0x139B0000 @@ -250,6 +254,7 @@ #define S5P_PA_FIMC3 EXYNOS4_PA_FIMC3 #define S5P_PA_JPEG EXYNOS4_PA_JPEG #define S5P_PA_G2D EXYNOS4_PA_G2D +#define S5P_PA_G3D EXYNOS4_PA_G3D #define S5P_PA_FIMD0 EXYNOS4_PA_FIMD0 #define S5P_PA_HDMI EXYNOS4_PA_HDMI #define S5P_PA_IIC_HDMIPHY EXYNOS4_PA_IIC_HDMIPHY diff --git a/arch/arm/mach-exynos/include/mach/regs-audss.h b/arch/arm/mach-exynos/include/mach/regs-audss.h index ca5a8b64218a..71ceb08ae63b 100644 --- a/arch/arm/mach-exynos/include/mach/regs-audss.h +++ b/arch/arm/mach-exynos/include/mach/regs-audss.h @@ -1,5 +1,4 @@ -/* arch/arm/mach-exynos4/include/mach/regs-audss.h - * +/* * Copyright (c) 2011 Samsung Electronics * http://www.samsung.com * @@ -15,4 +14,12 @@ #define EXYNOS4_AUDSS_INT_MEM (0x03000000) +#define EXYNOS_AUDSSREG(x) (S5P_VA_AUDSS + (x)) + +#define EXYNOS4_CLKSRC_AUDSS EXYNOS_AUDSSREG(0x0) +#define EXYNOS4_CLKDIV_AUDSS EXYNOS_AUDSSREG(0x4) +#define EXYNOS4_CLKGATE_AUDSS EXYNOS_AUDSSREG(0x8) + +#define EXYNOS4_AUDSS_CLKGATE_I2SBUS (1<<2) + #endif /* _PLAT_REGS_AUDSS_H */ diff --git a/arch/arm/mach-exynos/include/mach/regs-gpio.h b/arch/arm/mach-exynos/include/mach/regs-gpio.h index e4b5b60dcb85..24bf4ec643eb 100644 --- a/arch/arm/mach-exynos/include/mach/regs-gpio.h +++ b/arch/arm/mach-exynos/include/mach/regs-gpio.h @@ -16,13 +16,13 @@ #include <mach/map.h> #include <mach/irqs.h> -#define EINT_REG_NR(x) (EINT_OFFSET(x) >> 3) +#define EINT_REG_NR(x) ((x) >> 3) #define EINT_CON(b, x) (b + 0xE00 + (EINT_REG_NR(x) * 4)) #define EINT_FLTCON(b, x) (b + 0xE80 + (EINT_REG_NR(x) * 4)) #define EINT_MASK(b, x) (b + 0xF00 + (EINT_REG_NR(x) * 4)) #define EINT_PEND(b, x) (b + 0xF40 + (EINT_REG_NR(x) * 4)) -#define EINT_OFFSET_BIT(x) (1 << (EINT_OFFSET(x) & 0x7)) +#define EINT_OFFSET_BIT(x) (1 << ((x) & 0x7)) /* compatibility for plat-s5p/irq-pm.c */ #define EXYNOS4_EINT40CON (S5P_VA_GPIO2 + 0xE00) diff --git a/arch/arm/mach-exynos/include/mach/ump/config.h b/arch/arm/mach-exynos/include/mach/ump/config.h new file mode 100644 index 000000000000..a82934f27990 --- /dev/null +++ b/arch/arm/mach-exynos/include/mach/ump/config.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARCH_CONFIG_H__ +#define __ARCH_CONFIG_H__ + +#define ARCH_UMP_BACKEND_DEFAULT UMP_MEMORY_TYPE +#define ARCH_UMP_MEMORY_ADDRESS_DEFAULT 0 +#define ARCH_UMP_MEMORY_SIZE_DEFAULT CONFIG_UMP_MEM_SIZE * 1024UL * 1024UL + +#endif /* __ARCH_CONFIG_H__ */ diff --git a/arch/arm/mach-exynos/mach-exynos4-dt.c b/arch/arm/mach-exynos/mach-exynos4-dt.c index eadf4b59e7d2..abe88d4e9a52 100644 --- a/arch/arm/mach-exynos/mach-exynos4-dt.c +++ b/arch/arm/mach-exynos/mach-exynos4-dt.c @@ -14,15 +14,58 @@ #include <linux/of_platform.h> #include <linux/serial_core.h> +#include <linux/platform_data/usb-ehci-s5p.h> +#include <linux/platform_data/usb-exynos.h> + +#include <linux/pwm_backlight.h> +#include <linux/gpio.h> +#include <linux/fb.h> +#include <linux/lcd.h> + +#include <video/samsung_fimd.h> + +#include <plat/backlight.h> +#include <plat/gpio-cfg.h> +#include <plat/fb.h> +#include <plat/devs.h> +#include <linux/platform_data/s3c-hsotg.h> + #include <asm/mach/arch.h> #include <asm/hardware/gic.h> #include <mach/map.h> #include <plat/cpu.h> #include <plat/regs-serial.h> +#include <plat/usb-phy.h> #include "common.h" +static struct s5p_ehci_platdata origen_ehci_pdata = { + .phy_init = s5p_usb_phy_init, + .phy_exit = s5p_usb_phy_exit, +}; + +static struct exynos4_ohci_platdata origen_ohci_pdata = { + .phy_init = s5p_usb_phy_init, + .phy_exit = s5p_usb_phy_exit, +}; + +static struct s3c_hsotg_plat origen_hsotg_pdata = { + .phy_init = s5p_usb_phy_init, + .phy_exit = s5p_usb_phy_exit, +}; + +/* LCD Backlight data */ +static struct samsung_bl_gpio_info origen_bl_gpio_info = { + .no = EXYNOS4_GPD0(0), + .func = S3C_GPIO_SFN(2), +}; + +static struct platform_pwm_backlight_data origen_bl_data = { + .pwm_id = 0, + .pwm_period_ns = 1000, +}; + /* * The following lookup table is used to override device names when devices * are registered from device tree. This is temporarily added to enable @@ -69,6 +112,12 @@ static const struct of_dev_auxdata exynos4_auxdata_lookup[] __initconst = { "s3c2440-i2c.6", NULL), OF_DEV_AUXDATA("samsung,s3c2440-i2c", EXYNOS4_PA_IIC(7), "s3c2440-i2c.7", NULL), + OF_DEV_AUXDATA("samsung,s3c2440-hdmiphy-i2c", EXYNOS4_PA_IIC_HDMIPHY, + "s3c2440-hdmiphy-i2c", NULL), + OF_DEV_AUXDATA("samsung,s5pv210-tvmixer", EXYNOS4_PA_MIXER, + "s5p-mixer", NULL), + OF_DEV_AUXDATA("samsung,s5pv210-hdmi", EXYNOS4_PA_HDMI, + "exynos4-hdmi", NULL), OF_DEV_AUXDATA("samsung,exynos4210-spi", EXYNOS4_PA_SPI0, "exynos4210-spi.0", NULL), OF_DEV_AUXDATA("samsung,exynos4210-spi", EXYNOS4_PA_SPI1, @@ -77,6 +126,17 @@ static const struct of_dev_auxdata exynos4_auxdata_lookup[] __initconst = { "exynos4210-spi.2", NULL), OF_DEV_AUXDATA("arm,pl330", EXYNOS4_PA_PDMA0, "dma-pl330.0", NULL), OF_DEV_AUXDATA("arm,pl330", EXYNOS4_PA_PDMA1, "dma-pl330.1", NULL), + OF_DEV_AUXDATA("samsung,exynos-ehci", EXYNOS4_PA_EHCI, "s5p-ehci", + &origen_ehci_pdata), + OF_DEV_AUXDATA("samsung,exynos-ohci", EXYNOS4_PA_OHCI, "exynos-ohci", + &origen_ohci_pdata), + OF_DEV_AUXDATA("samsung,exynos-hsotg", EXYNOS4_PA_HSOTG, "s3c-hsotg", + &origen_hsotg_pdata), + OF_DEV_AUXDATA("samsung,exynos4210-fimd", EXYNOS4_PA_FIMD0, + "exynos4-fb.0", NULL), + OF_DEV_AUXDATA("samsung,samsung-i2s", EXYNOS4_PA_I2S0, + "samsung-i2s.0", NULL), + OF_DEV_AUXDATA("samsung,audio-dma", 0, "samsung-audio", NULL), {}, }; @@ -86,10 +146,21 @@ static void __init exynos4_dt_map_io(void) s3c24xx_init_clocks(24000000); } +static void __init exynos4_setup_fimd(void) +{ + unsigned int reg; + + reg = __raw_readl(S3C_VA_SYS + 0x0210); + reg |= (1 << 1); + __raw_writel(reg, S3C_VA_SYS + 0x0210); +} + static void __init exynos4_dt_machine_init(void) { of_platform_populate(NULL, of_default_bus_match_table, exynos4_auxdata_lookup, NULL); + samsung_bl_set(&origen_bl_gpio_info, &origen_bl_data); + exynos4_setup_fimd(); } static char const *exynos4_dt_compat[] __initdata = { diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c index 9adf491674ea..c6fb8766a1b2 100644 --- a/arch/arm/mach-exynos/mach-origen.c +++ b/arch/arm/mach-exynos/mach-origen.c @@ -20,10 +20,13 @@ #include <linux/gpio_keys.h> #include <linux/i2c.h> #include <linux/regulator/machine.h> +#include <linux/regulator/fixed.h> #include <linux/mfd/max8997.h> #include <linux/lcd.h> #include <linux/rfkill-gpio.h> #include <linux/platform_data/s3c-hsotg.h> +#include <linux/ath6kl.h> +#include <linux/delay.h> #include <asm/mach/arch.h> #include <asm/hardware/gic.h> @@ -31,6 +34,7 @@ #include <video/platform_lcd.h> #include <video/samsung_fimd.h> +#include <video/lcd_pwrctrl.h> #include <plat/regs-serial.h> #include <plat/cpu.h> @@ -65,6 +69,10 @@ S5PV210_UFCON_TXTRIG4 | \ S5PV210_UFCON_RXTRIG4) +enum fixed_regulator_id { + FIXED_REG_5V = 0, +}; + static struct s3c2410_uartcfg origen_uartcfgs[] __initdata = { [0] = { .hwport = 0, @@ -96,10 +104,16 @@ static struct s3c2410_uartcfg origen_uartcfgs[] __initdata = { }, }; +static struct regulator_consumer_supply fixed_5v_regulators[] = { + REGULATOR_SUPPLY("hdmi-en", "exynos4-hdmi"), +}; + static struct regulator_consumer_supply __initdata ldo3_consumer[] = { REGULATOR_SUPPLY("vddcore", "s5p-mipi-csis.0"), /* MIPI */ REGULATOR_SUPPLY("vdd", "exynos4-hdmi"), /* HDMI */ REGULATOR_SUPPLY("vdd_pll", "exynos4-hdmi"), /* HDMI */ + REGULATOR_SUPPLY("vusb_d", "s3c-hsotg"), /* OTG */ + REGULATOR_SUPPLY("vusb_a", "s3c-hsotg"), /* OTG */ }; static struct regulator_consumer_supply __initdata ldo6_consumer[] = { REGULATOR_SUPPLY("vddio", "s5p-mipi-csis.0"), /* MIPI */ @@ -122,6 +136,7 @@ static struct regulator_consumer_supply __initdata ldo14_consumer[] = { }; static struct regulator_consumer_supply __initdata ldo17_consumer[] = { REGULATOR_SUPPLY("vdd33", "swb-a31"), /* AR6003 WLAN & CSR 8810 BT */ + REGULATOR_SUPPLY("vmmc", NULL), }; static struct regulator_consumer_supply __initdata buck1_consumer[] = { REGULATOR_SUPPLY("vdd_arm", NULL), /* CPUFREQ */ @@ -133,7 +148,7 @@ static struct regulator_consumer_supply __initdata buck3_consumer[] = { REGULATOR_SUPPLY("vdd_g3d", "mali_drm"), /* G3D */ }; static struct regulator_consumer_supply __initdata buck7_consumer[] = { - REGULATOR_SUPPLY("vcc", "platform-lcd"), /* LCD */ + REGULATOR_SUPPLY("vcc-lcd", "lcd-pwrctrl.0"), /* LCD */ }; static struct regulator_init_data __initdata max8997_ldo1_data = { @@ -167,6 +182,7 @@ static struct regulator_init_data __initdata max8997_ldo3_data = { .min_uV = 1100000, .max_uV = 1100000, .apply_uV = 1, + .always_on = 1, .valid_ops_mask = REGULATOR_CHANGE_STATUS, .state_mem = { .disabled = 1, @@ -210,6 +226,7 @@ static struct regulator_init_data __initdata max8997_ldo7_data = { .min_uV = 1800000, .max_uV = 1800000, .apply_uV = 1, + .always_on = 1, .valid_ops_mask = REGULATOR_CHANGE_STATUS, .state_mem = { .disabled = 1, @@ -269,6 +286,7 @@ static struct regulator_init_data __initdata max8997_ldo11_data = { .min_uV = 3000000, .max_uV = 3000000, .apply_uV = 1, + .always_on = 1, .valid_ops_mask = REGULATOR_CHANGE_STATUS, .state_mem = { .disabled = 1, @@ -359,7 +377,9 @@ static struct regulator_init_data __initdata max8997_buck3_data = { .constraints = { .name = "VDD_G3D_1.1V", .min_uV = 900000, - .max_uV = 1100000, + .max_uV = 1200000, + .always_on = 1, + .boot_on = 1, .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS, .state_mem = { @@ -388,7 +408,7 @@ static struct regulator_init_data __initdata max8997_buck7_data = { .name = "VDD_LCD_3.3V", .min_uV = 3300000, .max_uV = 3300000, - .boot_on = 1, + .boot_on = 0, .apply_uV = 1, .valid_ops_mask = REGULATOR_CHANGE_STATUS, .state_mem = { @@ -471,6 +491,19 @@ static struct i2c_board_info i2c0_devs[] __initdata = { .platform_data = &origen_max8997_pdata, .irq = IRQ_EINT(4), }, +#ifdef CONFIG_TOUCHSCREEN_UNIDISPLAY_TS + { + I2C_BOARD_INFO("unidisplay_ts", 0x41), + .irq = IRQ_TS, + }, +#endif +}; + +/* I2C1 */ +static struct i2c_board_info i2c1_devs[] __initdata = { + { + I2C_BOARD_INFO("alc5625", 0x1E), + }, }; static struct s3c_sdhci_platdata origen_hsmmc0_pdata __initdata = { @@ -481,6 +514,100 @@ static struct s3c_sdhci_platdata origen_hsmmc2_pdata __initdata = { .cd_type = S3C_SDHCI_CD_INTERNAL, }; + +/* + * WLAN: SDIO Host will call this func at booting time + */ +static int origen_wifi_status_register(void (*notify_func) + (struct platform_device *, int state)); + +/* WLAN: MMC3-SDIO */ +static struct s3c_sdhci_platdata origen_hsmmc3_pdata __initdata = { + .max_width = 4, + .host_caps = MMC_CAP_4_BIT_DATA | + MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED, + .pm_caps = MMC_PM_KEEP_POWER, + .cd_type = S3C_SDHCI_CD_EXTERNAL, + .ext_cd_init = origen_wifi_status_register, +}; + +/* + * WLAN: Save SDIO Card detect func into this pointer + */ +static void (*wifi_status_cb)(struct platform_device *, int state); + +static int origen_wifi_status_register(void (*notify_func) + (struct platform_device *, int state)) +{ + /* Assign sdhci_s3c_notify_change to wifi_status_cb */ + if (!notify_func) + return -EAGAIN; + else + wifi_status_cb = notify_func; + + return 0; +} + +#define ORIGEN_WLAN_WOW EXYNOS4_GPX2(3) +#define ORIGEN_WLAN_RESET EXYNOS4_GPX2(4) + + +static void origen_wlan_setup_power(bool val) +{ + int err; + + if (val) { + err = gpio_request_one(ORIGEN_WLAN_RESET, + GPIOF_OUT_INIT_LOW, "GPX2_4"); + if (err) { + pr_warning("ORIGEN: Not obtain WIFI gpios\n"); + return; + } + s3c_gpio_cfgpin(ORIGEN_WLAN_RESET, S3C_GPIO_OUTPUT); + s3c_gpio_setpull(ORIGEN_WLAN_RESET, + S3C_GPIO_PULL_NONE); + /* VDD33,I/O Supply must be done */ + gpio_set_value(ORIGEN_WLAN_RESET, 0); + udelay(30); /*Tb */ + gpio_direction_output(ORIGEN_WLAN_RESET, 1); + } else { + gpio_direction_output(ORIGEN_WLAN_RESET, 0); + gpio_free(ORIGEN_WLAN_RESET); + } + + mdelay(100); + + return; +} + +/* + * This will be called at init time of WLAN driver + */ +static int origen_wifi_set_detect(bool val) +{ + + if (!wifi_status_cb) { + pr_warning("ORIGEN: WLAN: No callback \n" + "ORIGEN: WLAN: MMC should boot earlier than net \n"); + + return -EAGAIN; + } + if (true == val) { + origen_wlan_setup_power(true); + wifi_status_cb(&s3c_device_hsmmc3, 1); + } else { + origen_wlan_setup_power(false); + wifi_status_cb(&s3c_device_hsmmc3, 0); + } + + return 0; +} + +struct ath6kl_platform_data origen_wlan_data __initdata = { + .setup_power = origen_wifi_set_detect, +}; + + /* USB EHCI */ static struct s5p_ehci_platdata origen_ehci_pdata; @@ -588,29 +715,12 @@ static struct platform_device origen_device_gpiokeys = { }, }; -static void lcd_hv070wsa_set_power(struct plat_lcd_data *pd, unsigned int power) -{ - int ret; - - if (power) - ret = gpio_request_one(EXYNOS4_GPE3(4), - GPIOF_OUT_INIT_HIGH, "GPE3_4"); - else - ret = gpio_request_one(EXYNOS4_GPE3(4), - GPIOF_OUT_INIT_LOW, "GPE3_4"); - - gpio_free(EXYNOS4_GPE3(4)); - - if (ret) - pr_err("failed to request gpio for LCD power: %d\n", ret); -} - -static struct plat_lcd_data origen_lcd_hv070wsa_data = { - .set_power = lcd_hv070wsa_set_power, +static struct lcd_pwrctrl_data origen_lcd_hv070wsa_data = { + .gpio = EXYNOS4_GPE3(4), }; static struct platform_device origen_lcd_hv070wsa = { - .name = "platform-lcd", + .name = "lcd-pwrctrl", .dev.parent = &s5p_device_fimd0.dev, .dev.platform_data = &origen_lcd_hv070wsa_data, }; @@ -644,7 +754,25 @@ static struct s3c_fb_pd_win origen_fb_win0 = { .xres = 1024, .yres = 600, .max_bpp = 32, - .default_bpp = 24, + .default_bpp = 32, + .virtual_x = 1024, + .virtual_y = 2 * 600, +}; + +static struct s3c_fb_pd_win origen_fb_win1 = { + .xres = 1024, + .yres = 600, + .max_bpp = 32, + .default_bpp = 32, + .virtual_x = 1024, + .virtual_y = 2 * 600, +}; + +static struct s3c_fb_pd_win origen_fb_win2 = { + .xres = 1024, + .yres = 600, + .max_bpp = 32, + .default_bpp = 32, .virtual_x = 1024, .virtual_y = 2 * 600, }; @@ -662,6 +790,8 @@ static struct fb_videomode origen_lcd_timing = { static struct s3c_fb_platdata origen_lcd_pdata __initdata = { .win[0] = &origen_fb_win0, + .win[1] = &origen_fb_win1, + .win[2] = &origen_fb_win2, .vtiming = &origen_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC | @@ -690,7 +820,9 @@ static struct platform_device origen_device_bluetooth = { static struct platform_device *origen_devices[] __initdata = { &s3c_device_hsmmc2, &s3c_device_hsmmc0, + &s3c_device_hsmmc3, &s3c_device_i2c0, + &s3c_device_i2c1, &s3c_device_rtc, &s3c_device_usb_hsotg, &s3c_device_wdt, @@ -702,6 +834,7 @@ static struct platform_device *origen_devices[] __initdata = { &s5p_device_fimc_md, &s5p_device_fimd0, &s5p_device_g2d, + &s5p_device_g3d, &s5p_device_hdmi, &s5p_device_i2c_hdmiphy, &s5p_device_jpeg, @@ -709,6 +842,8 @@ static struct platform_device *origen_devices[] __initdata = { &s5p_device_mfc_l, &s5p_device_mfc_r, &s5p_device_mixer, + &samsung_asoc_dma, + &exynos4_device_i2s0, #ifdef CONFIG_DRM_EXYNOS &exynos_device_drm, #endif @@ -765,11 +900,14 @@ static void __init origen_power_init(void) gpio_request(EXYNOS4_GPX0(4), "PMIC_IRQ"); s3c_gpio_cfgpin(EXYNOS4_GPX0(4), S3C_GPIO_SFN(0xf)); s3c_gpio_setpull(EXYNOS4_GPX0(4), S3C_GPIO_PULL_NONE); + + regulator_register_always_on(FIXED_REG_5V, "Fixed 5V", + fixed_5v_regulators, ARRAY_SIZE(fixed_5v_regulators), 5000000); } static void __init origen_reserve(void) { - s5p_mfc_reserve_mem(0x43000000, 8 << 20, 0x51000000, 8 << 20); + s5p_mfc_reserve_mem(0x43000000, 32 << 20, 0x51000000, 32 << 20); } static void __init origen_machine_init(void) @@ -779,12 +917,16 @@ static void __init origen_machine_init(void) s3c_i2c0_set_platdata(NULL); i2c_register_board_info(0, i2c0_devs, ARRAY_SIZE(i2c0_devs)); + s3c_i2c1_set_platdata(NULL); + i2c_register_board_info(1, i2c1_devs, ARRAY_SIZE(i2c1_devs)); + /* * Since sdhci instance 2 can contain a bootable media, * sdhci instance 0 is registered after instance 2. */ s3c_sdhci2_set_platdata(&origen_hsmmc2_pdata); s3c_sdhci0_set_platdata(&origen_hsmmc0_pdata); + s3c_sdhci3_set_platdata(&origen_hsmmc3_pdata); origen_ehci_init(); origen_ohci_init(); @@ -807,6 +949,8 @@ static void __init origen_machine_init(void) samsung_bl_set(&origen_bl_gpio_info, &origen_bl_data); origen_bt_setup(); + + ath6kl_set_platform_data(&origen_wlan_data); } MACHINE_START(ORIGEN, "ORIGEN") diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index c06c992943a1..239045f198e5 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -312,6 +312,9 @@ static void exynos_pm_resume(void) } early_wakeup: + /* Clear INFORM Register */ + __raw_writel(0, S5P_INFORM1); + return; } diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index c0bc83a7663e..f9b229726a29 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -191,6 +191,11 @@ static __init int exynos4_pm_init_power_domain(void) #ifdef CONFIG_S5P_DEV_G2D exynos_pm_add_dev_to_genpd(&s5p_device_g2d, &exynos4_pd_lcd0); #endif +#ifdef CONFIG_S5P_DEV_G3D + /* MALI requires the PD to be always on. */ + exynos4_pd_g3d.pd.power_off = NULL; + exynos_pm_add_dev_to_genpd(&s5p_device_g3d, &exynos4_pd_g3d); +#endif #ifdef CONFIG_S5P_DEV_JPEG exynos_pm_add_dev_to_genpd(&s5p_device_jpeg, &exynos4_pd_cam); #endif @@ -200,6 +205,7 @@ arch_initcall(exynos4_pm_init_power_domain); int __init exynos_pm_late_initcall(void) { - pm_genpd_poweroff_unused(); + if (!of_have_populated_dt()) + pm_genpd_poweroff_unused(); return 0; } diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index 59401e1cc530..3d4c51a39a69 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -371,6 +371,11 @@ config S5P_DEV_G2D help Compile in platform device definitions for G2D device +config S5P_DEV_G3D + bool + help + Compile in platform device definitions for G3D device + config S5P_DEV_I2C_HDMIPHY bool help diff --git a/arch/arm/plat-samsung/devs.c b/arch/arm/plat-samsung/devs.c index 03f654d55eff..375d0af2a287 100644 --- a/arch/arm/plat-samsung/devs.c +++ b/arch/arm/plat-samsung/devs.c @@ -291,6 +291,25 @@ struct platform_device s5p_device_g2d = { }; #endif /* CONFIG_S5P_DEV_G2D */ +/* G3D */ + +#ifdef CONFIG_S5P_DEV_G3D +static struct resource s5p_g3d_resource[] = { + [0] = DEFINE_RES_MEM(S5P_PA_G3D, SZ_256K), +}; + +struct platform_device s5p_device_g3d = { + .name = "s5p-g3d", + .id = 0, + .num_resources = ARRAY_SIZE(s5p_g3d_resource), + .resource = s5p_g3d_resource, + .dev = { + .dma_mask = &samsung_device_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; +#endif /* CONFIG_S5P_DEV_G3D */ + #ifdef CONFIG_S5P_DEV_JPEG static struct resource s5p_jpeg_resource[] = { [0] = DEFINE_RES_MEM(S5P_PA_JPEG, SZ_4K), diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h index 5da4b4f38f40..5037810463e4 100644 --- a/arch/arm/plat-samsung/include/plat/devs.h +++ b/arch/arm/plat-samsung/include/plat/devs.h @@ -83,6 +83,7 @@ extern struct platform_device s5p_device_fimc3; extern struct platform_device s5p_device_fimc_md; extern struct platform_device s5p_device_jpeg; extern struct platform_device s5p_device_g2d; +extern struct platform_device s5p_device_g3d; extern struct platform_device s5p_device_fimd0; extern struct platform_device s5p_device_hdmi; extern struct platform_device s5p_device_i2c_hdmiphy; diff --git a/arch/arm/plat-samsung/include/plat/map-s5p.h b/arch/arm/plat-samsung/include/plat/map-s5p.h index c2d7bdae5891..b98be492ee0b 100644 --- a/arch/arm/plat-samsung/include/plat/map-s5p.h +++ b/arch/arm/plat-samsung/include/plat/map-s5p.h @@ -40,6 +40,8 @@ #define S5P_VA_GIC_CPU S3C_ADDR(0x02810000) #define S5P_VA_GIC_DIST S3C_ADDR(0x02820000) +#define S5P_VA_AUDSS S3C_ADDR(0X02A00000) + #define VA_VIC(x) (S3C_VA_IRQ + ((x) * 0x10000)) #define VA_VIC0 VA_VIC(0) #define VA_VIC1 VA_VIC(1) diff --git a/arch/arm/plat-samsung/s5p-dev-mfc.c b/arch/arm/plat-samsung/s5p-dev-mfc.c index ad6089465e2a..e3b54b1f1abd 100644 --- a/arch/arm/plat-samsung/s5p-dev-mfc.c +++ b/arch/arm/plat-samsung/s5p-dev-mfc.c @@ -12,6 +12,7 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> +#include <linux/dma-contiguous.h> #include <linux/memblock.h> #include <linux/ioport.h> @@ -20,52 +21,14 @@ #include <plat/irqs.h> #include <plat/mfc.h> -struct s5p_mfc_reserved_mem { - phys_addr_t base; - unsigned long size; - struct device *dev; -}; - -static struct s5p_mfc_reserved_mem s5p_mfc_mem[2] __initdata; - void __init s5p_mfc_reserve_mem(phys_addr_t rbase, unsigned int rsize, phys_addr_t lbase, unsigned int lsize) { - int i; - - s5p_mfc_mem[0].dev = &s5p_device_mfc_r.dev; - s5p_mfc_mem[0].base = rbase; - s5p_mfc_mem[0].size = rsize; - - s5p_mfc_mem[1].dev = &s5p_device_mfc_l.dev; - s5p_mfc_mem[1].base = lbase; - s5p_mfc_mem[1].size = lsize; - - for (i = 0; i < ARRAY_SIZE(s5p_mfc_mem); i++) { - struct s5p_mfc_reserved_mem *area = &s5p_mfc_mem[i]; - if (memblock_remove(area->base, area->size)) { - printk(KERN_ERR "Failed to reserve memory for MFC device (%ld bytes at 0x%08lx)\n", - area->size, (unsigned long) area->base); - area->base = 0; - } - } -} - -static int __init s5p_mfc_memory_init(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(s5p_mfc_mem); i++) { - struct s5p_mfc_reserved_mem *area = &s5p_mfc_mem[i]; - if (!area->base) - continue; + if (dma_declare_contiguous(&s5p_device_mfc_r.dev, rsize, rbase, 0)) + printk(KERN_ERR "Failed to reserve memory for MFC device (%u bytes at 0x%08lx)\n", + rsize, (unsigned long) rbase); - if (dma_declare_coherent_memory(area->dev, area->base, - area->base, area->size, - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) - printk(KERN_ERR "Failed to declare coherent memory for MFC device (%ld bytes at 0x%08lx)\n", - area->size, (unsigned long) area->base); - } - return 0; + if (dma_declare_contiguous(&s5p_device_mfc_l.dev, lsize, lbase, 0)) + printk(KERN_ERR "Failed to reserve memory for MFC device (%u bytes at 0x%08lx)\n", + rsize, (unsigned long) rbase); } -device_initcall(s5p_mfc_memory_init); diff --git a/drivers/Makefile b/drivers/Makefile index 487d933d1c66..c7719400ef97 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_TARGET_CORE) += target/ obj-$(CONFIG_MTD) += mtd/ obj-$(CONFIG_SPI) += spi/ obj-y += hsi/ +obj-y += mmc/ obj-y += net/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_FUSION) += message/ @@ -105,7 +106,6 @@ obj-$(CONFIG_EISA) += eisa/ obj-y += lguest/ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_CPU_IDLE) += cpuidle/ -obj-y += mmc/ obj-$(CONFIG_MEMSTICK) += memstick/ obj-y += leds/ obj-$(CONFIG_SWITCH) += switch/ diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 96b71b6536d6..8182ce808513 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1425,7 +1425,7 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, } ret = dev_pm_get_subsys_data(dev); - if (ret) + if (ret < 0) goto out; genpd->device_count++; @@ -1460,7 +1460,7 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, if (gpd_data != gpd_data_new) __pm_genpd_free_dev_data(dev, gpd_data_new); - return ret; + return (ret < 0) ? : 0; } /** diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index af2d81e10f71..85e22d037dfe 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -17,6 +17,8 @@ #include <linux/regulator/consumer.h> #include <linux/cpufreq.h> #include <linux/suspend.h> +#include <linux/notifier.h> +#include <linux/reboot.h> #include <mach/cpufreq.h> @@ -214,6 +216,31 @@ static struct notifier_block exynos_cpufreq_nb = { .notifier_call = exynos_cpufreq_pm_notifier, }; +static int exynos_cpufreq_reboot_notifier(struct notifier_block *this, + unsigned long code, void *_cmd) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */ + mutex_lock(&cpufreq_lock); + + if (frequency_locked) + goto out; + frequency_locked = true; + + if (locking_frequency) { + mutex_unlock(&cpufreq_lock); + exynos_target(policy, locking_frequency, CPUFREQ_RELATION_H); + mutex_lock(&cpufreq_lock); + } + +out: + mutex_unlock(&cpufreq_lock); + return NOTIFY_DONE; +} + +static struct notifier_block exynos_cpufreq_reboot_nb = { + .notifier_call = exynos_cpufreq_reboot_notifier, +}; + static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) { policy->cur = policy->min = policy->max = exynos_getspeed(policy->cpu); @@ -286,6 +313,7 @@ static int __init exynos_cpufreq_init(void) } register_pm_notifier(&exynos_cpufreq_nb); + register_reboot_notifier(&exynos_cpufreq_reboot_nb); if (cpufreq_register_driver(&exynos_driver)) { pr_err("%s: failed to register cpufreq driver\n", __func__); diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index ca2d3b34dbf5..633b375f4cf5 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -1 +1 @@ -obj-y += drm/ vga/ stub/ ion/ +obj-y += drm/ vga/ stub/ ion/ arm/ diff --git a/drivers/gpu/arm/Kconfig b/drivers/gpu/arm/Kconfig new file mode 100644 index 000000000000..efe78169ac0c --- /dev/null +++ b/drivers/gpu/arm/Kconfig @@ -0,0 +1,2 @@ +source "drivers/gpu/arm/mali/Kconfig" +source "drivers/gpu/arm/ump/Kconfig" diff --git a/drivers/gpu/arm/Makefile b/drivers/gpu/arm/Makefile new file mode 100644 index 000000000000..ece84b5fbcd3 --- /dev/null +++ b/drivers/gpu/arm/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_MALI400MP) +=mali/ +obj-$(CONFIG_UMP) +=ump/ diff --git a/drivers/gpu/arm/mali/Kconfig b/drivers/gpu/arm/mali/Kconfig new file mode 100644 index 000000000000..1404f59d3de4 --- /dev/null +++ b/drivers/gpu/arm/mali/Kconfig @@ -0,0 +1,75 @@ +config MALI400MP + bool "Enable MALI integration" + default n + ---help--- + This enables MALI 3D graphics driver. Required to use hardware accelerated OpenGL ES 2.0 and 1.1. + +config USING_MMU + bool "Using MMU" + depends on MALI400MP + default y + ---help--- + This enables MMU for Mali. Required hardware support for MMU. + +config USING_UMP + bool "Using UMP" + depends on MALI400MP && UMP + default y + ---help--- + Using UMP for Mali memory. + +config USING_OS_MEMORY + bool "Using OS memory" + depends on MALI400MP + default y + ---help--- + This enables for Mali to use OS memory. + +config USING_PMM + bool "Enable Power Management" + depends on MALI400MP + default n + ---help--- + This enables for Mali Power Management. + +config USING_GPU_UTILIZATION + bool "GPU utilization" + depends on MALI400MP + default n + ---help--- + This enables GPU utilization information. + +config USING_MALI_RUN_TIME_PM + bool "Using Run time Power Management" + depends on MALI400MP + default n + ---help--- + This enables Mali working with run time power management. + +config USING_MALI_PMM_TESTSUITE + bool "Power Management Test Suite" + depends on MALI400MP + default n + ---help--- + This enables Power management Test Suite. + +config USING_PROFILING + bool "Enable Profiling" + depends on MALI400MP + default n + ---help--- + This support Profiling on Mali. + +config DEBUG_BUILD + bool "Enables debug messages" + depends on MALI400MP + default n + ---help--- + This enables Mali driver debug messages. + +config MALI_MEM_SIZE + int "Mali memory size" + depends on MALI400MP + default "64" + ---help--- + This value is memory size of Mali GPU(unit is MByte). diff --git a/drivers/gpu/arm/mali/Makefile b/drivers/gpu/arm/mali/Makefile new file mode 100755 index 000000000000..263e076a4a9a --- /dev/null +++ b/drivers/gpu/arm/mali/Makefile @@ -0,0 +1,272 @@ +# +# Copyright (C) 2010-2012 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained from Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +USE_UMPV2=0 + +OSKOS :=linux +FILES_PREFIX= +MALI_FILE_PREFIX := $(srctree)/drivers/gpu/arm/mali +KBUILDROOT = + +# Add platform configuration file for Mali +#ifeq ($(CONFIG_ARCH_EXYNOS4),y) +MACHDIR := $(srctree)/arch/arm/mach-exynos +MALICONFIGDIR :=$(MACHDIR)/include/mach/mali +#endif + +USING_DED=0 + +ifeq ($(CONFIG_USING_MMU),y) +USING_MMU=1 +endif + +ifeq ($(CONFIG_USING_UMP),y) +USING_UMP=1 +endif + +ifeq ($(CONFIG_USING_MMU),y) +USING_MMU=1 +endif + +ifeq ($(CONFIG_USING_OS_MEMORY),y) +USING_OS_MEMORY=1 +endif + +ifeq ($(CONFIG_USING_PMM),y) +USING_PMM=1 +endif + +ifeq ($(CONFIG_USING_GPU_UTILIZATION),y) +USING_GPU_UTILIZATION =1 +endif + +ifeq ($(CONFIG_USING_MALI_RUN_TIME_PM),y) +USING_MALI_RUN_TIME_PM =1 +endif + +ifeq ($(CONFIG_USING_MALI_PMM_TESTSUITE),y) +USING_MALI_PMM_TESTSUITE=1 +endif + +ifeq ($(CONFIG_USING_PROFILING),y) +USING_PROFILING =1 +endif + +# set up defaults if not defined by the user +ARCH ?= arm +USING_MMU ?= 1 +USING_UMP ?= 0 +USING_OS_MEMORY ?= 0 +USING_PMM ?= 0 +USING_GPU_UTILIZATION ?= 0 +USING_MALI_RUN_TIME_PM ?= 0 +USING_MALI_PMM_TESTSUITE ?= 0 +OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB ?= 6 +USING_PROFILING ?= 0 +USING_INTERNAL_PROFILING ?= 0 +TIMESTAMP ?= default +TARGET_PLATFORM ?= default + +ifeq ($(USING_UMP),1) +ifeq ($(USE_UMPV2),1) + UMP_SYMVERS_FILE ?= ../umpv2/Module.symvers +else + UMP_SYMVERS_FILE ?= ../ump/Module.symvers +endif + KBUILD_EXTRA_SYMBOLS = $(KBUILD_EXTMOD)/$(UMP_SYMVERS_FILE) +endif + +# Check if a Mali Core sub module should be enabled, true or false returned +submodule_enabled = $(shell gcc $(DEFINES) -E $(MALICONFIGDIR)/config.h | grep type | grep -c $(2)) + +# Set up our defines, which will be passed to gcc +DEFINES += -DUSING_OS_MEMORY=$(USING_OS_MEMORY) +DEFINES += -DUSING_MMU=$(USING_MMU) +DEFINES += -DUSING_UMP=$(USING_UMP) +DEFINES += -D_MALI_OSK_SPECIFIC_INDIRECT_MMAP +DEFINES += -DMALI_TIMELINE_PROFILING_ENABLED=$(USING_PROFILING) +DEFINES += -DMALI_INTERNAL_TIMELINE_PROFILING_ENABLED=$(USING_INTERNAL_PROFILING) +DEFINES += -DMALI_POWER_MGMT_TEST_SUITE=$(USING_MALI_PMM_TESTSUITE) +DEFINES += -DMALI_PMM_RUNTIME_JOB_CONTROL_ON=$(USING_MALI_RUN_TIME_PM) + +# MALI_STATE_TRACKING is only supported on Linux kernels from version 2.6.32. +DEFINES += -DMALI_STATE_TRACKING=1 +DEFINES += -DMALI_STATE_TRACKING_USING_PROC=1 +DEFINES += -DMALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB=$(OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB) + +ifneq ($(call submodule_enabled, $M, PMU),0) + MALI_PLATFORM_FILE = platform/mali400-pmu/mali_platform.o +else + MALI_PLATFORM_FILE = platform/$(TARGET_PLATFORM)/mali_platform.o +endif + +DEFINES += -DUSING_MALI_PMM=$(USING_PMM) +DEFINES += -DMALI_GPU_UTILIZATION=$(USING_GPU_UTILIZATION) + +ifeq ($(CONFIG_DEBUG_BUILD),y) +DEFINES += -DDEBUG +endif +DEFINES += -DSVN_REV=$(SVN_REV) +DEFINES += -DSVN_REV_STRING=\"$(SVN_REV)\" + +# Linux has its own mmap cleanup handlers (see mali_kernel_mem_mmu.c) +DEFINES += -DMALI_UKK_HAS_IMPLICIT_MMAP_CLEANUP + +ifeq ($(USING_UMP),1) + DEFINES += -DMALI_USE_UNIFIED_MEMORY_PROVIDER=1 + EXTRA_CFLAGS += -I$(MALI_FILE_PREFIX)/../ump/include +else + DEFINES += -DMALI_USE_UNIFIED_MEMORY_PROVIDER=0 +endif + +# Target build file +obj-$(CONFIG_MALI400MP) += mali.o + +# Use our defines when compiling +EXTRA_CFLAGS += $(DEFINES) -I$(MALI_FILE_PREFIX) -I$(MALI_FILE_PREFIX)/common -I$(MALI_FILE_PREFIX)/linux -I$(MALI_FILE_PREFIX)/platform + +ifneq ($(CONFIG_UMP),y) +OSKFILES=$(FILES_PREFIX)$(OSKOS)/mali_osk_atomics.o \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_locks.o \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_math.o \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_memory.o \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_misc.o +endif +OSKFILES+=\ + $(FILES_PREFIX)$(OSKOS)/mali_osk_irq.o \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_low_level_mem.o \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_mali.o \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_notification.o \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_time.o \ + $(FILES_PREFIX)$(OSKOS)/mali_osk_timers.o + +UKKFILES=\ + $(FILES_PREFIX)$(OSKOS)/mali_ukk_mem.o \ + $(FILES_PREFIX)$(OSKOS)/mali_ukk_gp.o \ + $(FILES_PREFIX)$(OSKOS)/mali_ukk_pp.o \ + $(FILES_PREFIX)$(OSKOS)/mali_ukk_core.o + +ifeq ($(USING_PROFILING),1) +UKKFILES+=\ + $(FILES_PREFIX)$(OSKOS)/mali_ukk_profiling.o +endif + +# For customer releases the Linux Device Drivers will be provided as ARM proprietary and GPL releases: +# The ARM proprietary product will only include the license/proprietary directory +# The GPL product will only include the license/gpl directory + +ifeq ($(wildcard $(MALI_FILE_PREFIX)/linux/license/gpl/*),) +EXTRA_CFLAGS += -I$(MALI_FILE_PREFIX)/linux/license/proprietary +else +EXTRA_CFLAGS += -I$(MALI_FILE_PREFIX)/linux/license/gpl +endif +EXTRA_CFLAGS += -I$(MALI_FILE_PREFIX)/common/pmm + +# Source files which always are included in a build +mali-y := \ + common/mali_kernel_core.o \ + linux/mali_kernel_linux.o \ + $(OSKOS)/mali_osk_indir_mmap.o \ + common/mali_kernel_rendercore.o \ + common/mali_kernel_descriptor_mapping.o \ + common/mali_kernel_vsync.o \ + linux/mali_ukk_vsync.o \ + linux/mali_kernel_sysfs.o \ + $(MALI_PLATFORM_FILE) \ + $(OSKFILES) \ + $(UKKFILES) + +ifeq ($(USING_PROFILING),1) +mali-y += \ + linux/mali_osk_profiling_gator.o \ + timestamp-$(TIMESTAMP)/mali_timestamp.o +EXTRA_CFLAGS += -I$(MALI_FILE_PREFIX)/timestamp-$(TIMESTAMP) +endif + +ifeq ($(USING_INTERNAL_PROFILING),1) +mali-y += \ + common/mali_osk_profiling_internal.o \ + timestamp-$(TIMESTAMP)/mali_timestamp.o +EXTRA_CFLAGS += -I$(MALI_FILE_PREFIX)/timestamp-$(TIMESTAMP) +endif + + +# Selecting files to compile by parsing the config file + +ifeq ($(USING_PMM),1) +mali-y += \ + common/pmm/mali_pmm.o \ + common/pmm/mali_pmm_policy.o \ + common/pmm/mali_pmm_policy_alwayson.o \ + common/pmm/mali_pmm_policy_jobcontrol.o \ + common/pmm/mali_pmm_state.o \ + linux/mali_kernel_pm.o \ + linux/mali_osk_pm.o \ + linux/mali_device_pause_resume.o +endif + +ifeq ($(USING_GPU_UTILIZATION),1) +mali-y += \ + common/mali_kernel_utilization.o +endif + +ifneq ($(call submodule_enabled, $M, MALI400PP),0) + # Mali-400 PP in use + EXTRA_CFLAGS += -DUSING_MALI400 + mali-y += common/mali_kernel_MALI200.o +endif + +ifneq ($(call submodule_enabled, $M, MALI400GP),0) + # Mali-400 GP in use + mali-y += common/mali_kernel_GP2.o +endif + +ifneq ($(call submodule_enabled, $M, MALI300PP),0) + # Mali-400 PP in use + EXTRA_CFLAGS += -DUSING_MALI400 + mali-y += common/mali_kernel_MALI200.o +endif + +ifneq ($(call submodule_enabled, $M, MALI300GP),0) + # Mali-400 GP in use + mali-y += common/mali_kernel_GP2.o +endif + +ifneq ($(call submodule_enabled, $M, MALI200),0) + # Mali200 in use + EXTRA_CFLAGS += -DUSING_MALI200 + mali-y += common/mali_kernel_MALI200.o +endif + +ifneq ($(call submodule_enabled, $M, MALIGP2),0) + # MaliGP2 in use + mali-y += common/mali_kernel_GP2.o +endif + +ifneq ($(call submodule_enabled, $M, MMU),0) + # Mali MMU in use + mali-y += common/mali_kernel_mem_mmu.o common/mali_kernel_memory_engine.o common/mali_block_allocator.o common/mali_kernel_mem_os.o +else + # No Mali MMU in use + mali-y += common/mali_kernel_mem_buddy.o +endif + +ifneq ($(call submodule_enabled, $M, MALI400L2)$(),0) + # Mali Level2 cache in use + EXTRA_CFLAGS += -DUSING_MALI400_L2_CACHE + mali-y += common/mali_kernel_l2_cache.o +endif + +ifneq ($(call submodule_enabled, $M, MALI300L2)$(),0) + # Mali Level2 cache in use + EXTRA_CFLAGS += -DUSING_MALI400_L2_CACHE + mali-y += common/mali_kernel_l2_cache.o +endif + diff --git a/drivers/gpu/arm/mali/arch/config.h b/drivers/gpu/arm/mali/arch/config.h new file mode 100644 index 000000000000..ae2fcf81d736 --- /dev/null +++ b/drivers/gpu/arm/mali/arch/config.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARCH_CONFIG_H__ +#define __ARCH_CONFIG_H__ + +/* Configuration for the EB platform with ZBT memory enabled */ + +#define MEM_MALI_SIZE CONFIG_MALI_MEM_SIZE*1024*1024 + +static _mali_osk_resource_t arch_configuration [] = +{ + { + .type = MALI400GP, + .description = "Mali-400 GP", + .base = 0xC0000000, + .irq = -1, + .mmu_id = 1 + }, + { + .type = MALI400PP, + .base = 0xc0008000, + .irq = -1, + .description = "Mali-400 PP 0", + .mmu_id = 2 + }, + { + .type = MALI400PP, + .base = 0xc000A000, + .irq = -1, + .description = "Mali-400 PP 1", + .mmu_id = 3 + }, + { + .type = MALI400PP, + .base = 0xc000C000, + .irq = -1, + .description = "Mali-400 PP 2", + .mmu_id = 4 + }, + { + .type = MALI400PP, + .base = 0xc000E000, + .irq = -1, + .description = "Mali-400 PP 3", + .mmu_id = 5 + }, +#if USING_MMU + { + .type = MMU, + .base = 0xC0003000, + .irq = 102, + .description = "Mali-400 MMU for GP", + .mmu_id = 1 + }, + { + .type = MMU, + .base = 0xC0004000, + .irq = 102, + .description = "Mali-400 MMU for PP 0", + .mmu_id = 2 + }, + { + .type = MMU, + .base = 0xC0005000, + .irq = 102, + .description = "Mali-400 MMU for PP 1", + .mmu_id = 3 + }, + { + .type = MMU, + .base = 0xC0006000, + .irq = 102, + .description = "Mali-400 MMU for PP 2", + .mmu_id = 4 + }, + { + .type = MMU, + .base = 0xC0007000, + .irq = 102, + .description = "Mali-400 MMU for PP 3", + .mmu_id = 5 + }, +#endif +#if ! ONLY_ZBT + { + .type = MEMORY, + .description = "Mali SDRAM remapped to baseboard", + .cpu_usage_adjust = -0x50000000, + .alloc_order = 0, /* Highest preference for this memory */ +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 + .base = 0xD2000000, /* Reserving 32MB for UMP devicedriver */ + .size = MEM_MALI_SIZE, +#else + .base = 0xD0000000, + .size = MEM_MALI_SIZE, +#endif /* MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 */ + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, +#endif +#if USING_ZBT + { + .type = MEMORY, + .description = "Mali ZBT", + .alloc_order = 5, /* Medium preference for this memory */ + .base = 0xe1000000, + .size = MEM_MALI_SIZE, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE + }, +#endif + { + .type = MEM_VALIDATION, + .description = "Framebuffer", + .base = 0xe0000000, + .size = 0x01000000, + .flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_WRITEABLE | _MALI_PP_READABLE + }, + { + .type = MALI400L2, + .base = 0xC0001000, + .description = "Mali-400 L2 cache" + }, +}; + +#endif /* __ARCH_CONFIG_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_block_allocator.c b/drivers/gpu/arm/mali/common/mali_block_allocator.c new file mode 100644 index 000000000000..5c2c49a71a2b --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_block_allocator.c @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "mali_kernel_common.h" +#include "mali_kernel_core.h" +#include "mali_kernel_memory_engine.h" +#include "mali_block_allocator.h" +#include "mali_osk.h" + +#define MALI_BLOCK_SIZE (256UL * 1024UL) /* 256 kB, remember to keep the ()s */ + +typedef struct block_info +{ + struct block_info * next; +} block_info; + +/* The structure used as the handle produced by block_allocator_allocate, + * and removed by block_allocator_release */ +typedef struct block_allocator_allocation +{ + /* The list will be released in reverse order */ + block_info *last_allocated; + mali_allocation_engine * engine; + mali_memory_allocation * descriptor; + u32 start_offset; + u32 mapping_length; +} block_allocator_allocation; + + +typedef struct block_allocator +{ + _mali_osk_lock_t *mutex; + block_info * all_blocks; + block_info * first_free; + u32 base; + u32 cpu_usage_adjust; + u32 num_blocks; +} block_allocator; + +MALI_STATIC_INLINE u32 get_phys(block_allocator * info, block_info * block); +static mali_physical_memory_allocation_result block_allocator_allocate(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info); +static void block_allocator_release(void * ctx, void * handle); +static mali_physical_memory_allocation_result block_allocator_allocate_page_table_block(void * ctx, mali_page_table_block * block); +static void block_allocator_release_page_table_block( mali_page_table_block *page_table_block ); +static void block_allocator_destroy(mali_physical_memory_allocator * allocator); +static u32 block_allocator_stat(mali_physical_memory_allocator * allocator); + +mali_physical_memory_allocator * mali_block_allocator_create(u32 base_address, u32 cpu_usage_adjust, u32 size, const char *name) +{ + mali_physical_memory_allocator * allocator; + block_allocator * info; + u32 usable_size; + u32 num_blocks; + + usable_size = size & ~(MALI_BLOCK_SIZE - 1); + MALI_DEBUG_PRINT(3, ("Mali block allocator create for region starting at 0x%08X length 0x%08X\n", base_address, size)); + MALI_DEBUG_PRINT(4, ("%d usable bytes\n", usable_size)); + num_blocks = usable_size / MALI_BLOCK_SIZE; + MALI_DEBUG_PRINT(4, ("which becomes %d blocks\n", num_blocks)); + + if (usable_size == 0) + { + MALI_DEBUG_PRINT(1, ("Memory block of size %d is unusable\n", size)); + return NULL; + } + + allocator = _mali_osk_malloc(sizeof(mali_physical_memory_allocator)); + if (NULL != allocator) + { + info = _mali_osk_malloc(sizeof(block_allocator)); + if (NULL != info) + { + info->mutex = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_ORDERED, 0, 105); + if (NULL != info->mutex) + { + info->all_blocks = _mali_osk_malloc(sizeof(block_info) * num_blocks); + if (NULL != info->all_blocks) + { + u32 i; + info->first_free = NULL; + info->num_blocks = num_blocks; + + info->base = base_address; + info->cpu_usage_adjust = cpu_usage_adjust; + + for ( i = 0; i < num_blocks; i++) + { + info->all_blocks[i].next = info->first_free; + info->first_free = &info->all_blocks[i]; + } + + allocator->allocate = block_allocator_allocate; + allocator->allocate_page_table_block = block_allocator_allocate_page_table_block; + allocator->destroy = block_allocator_destroy; + allocator->stat = block_allocator_stat; + allocator->ctx = info; + allocator->name = name; + + return allocator; + } + _mali_osk_lock_term(info->mutex); + } + _mali_osk_free(info); + } + _mali_osk_free(allocator); + } + + return NULL; +} + +static void block_allocator_destroy(mali_physical_memory_allocator * allocator) +{ + block_allocator * info; + MALI_DEBUG_ASSERT_POINTER(allocator); + MALI_DEBUG_ASSERT_POINTER(allocator->ctx); + info = (block_allocator*)allocator->ctx; + + _mali_osk_free(info->all_blocks); + _mali_osk_lock_term(info->mutex); + _mali_osk_free(info); + _mali_osk_free(allocator); +} + +MALI_STATIC_INLINE u32 get_phys(block_allocator * info, block_info * block) +{ + return info->base + ((block - info->all_blocks) * MALI_BLOCK_SIZE); +} + +static mali_physical_memory_allocation_result block_allocator_allocate(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info) +{ + block_allocator * info; + u32 left; + block_info * last_allocated = NULL; + mali_physical_memory_allocation_result result = MALI_MEM_ALLOC_NONE; + block_allocator_allocation *ret_allocation; + + MALI_DEBUG_ASSERT_POINTER(ctx); + MALI_DEBUG_ASSERT_POINTER(descriptor); + MALI_DEBUG_ASSERT_POINTER(offset); + MALI_DEBUG_ASSERT_POINTER(alloc_info); + + info = (block_allocator*)ctx; + left = descriptor->size - *offset; + MALI_DEBUG_ASSERT(0 != left); + + if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) return MALI_MEM_ALLOC_INTERNAL_FAILURE; + + ret_allocation = _mali_osk_malloc( sizeof(block_allocator_allocation) ); + + if ( NULL == ret_allocation ) + { + /* Failure; try another allocator by returning MALI_MEM_ALLOC_NONE */ + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + return result; + } + + ret_allocation->start_offset = *offset; + ret_allocation->mapping_length = 0; + + while ((left > 0) && (info->first_free)) + { + block_info * block; + u32 phys_addr; + u32 padding; + u32 current_mapping_size; + + block = info->first_free; + info->first_free = info->first_free->next; + block->next = last_allocated; + last_allocated = block; + + phys_addr = get_phys(info, block); + + padding = *offset & (MALI_BLOCK_SIZE-1); + + if (MALI_BLOCK_SIZE - padding < left) + { + current_mapping_size = MALI_BLOCK_SIZE - padding; + } + else + { + current_mapping_size = left; + } + + if (_MALI_OSK_ERR_OK != mali_allocation_engine_map_physical(engine, descriptor, *offset, phys_addr + padding, info->cpu_usage_adjust, current_mapping_size)) + { + MALI_DEBUG_PRINT(1, ("Mapping of physical memory failed\n")); + result = MALI_MEM_ALLOC_INTERNAL_FAILURE; + mali_allocation_engine_unmap_physical(engine, descriptor, ret_allocation->start_offset, ret_allocation->mapping_length, (_mali_osk_mem_mapregion_flags_t)0); + + /* release all memory back to the pool */ + while (last_allocated) + { + /* This relinks every block we've just allocated back into the free-list */ + block = last_allocated->next; + last_allocated->next = info->first_free; + info->first_free = last_allocated; + last_allocated = block; + } + + break; + } + + *offset += current_mapping_size; + left -= current_mapping_size; + ret_allocation->mapping_length += current_mapping_size; + } + + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + + if (last_allocated) + { + if (left) result = MALI_MEM_ALLOC_PARTIAL; + else result = MALI_MEM_ALLOC_FINISHED; + + /* Record all the information about this allocation */ + ret_allocation->last_allocated = last_allocated; + ret_allocation->engine = engine; + ret_allocation->descriptor = descriptor; + + alloc_info->ctx = info; + alloc_info->handle = ret_allocation; + alloc_info->release = block_allocator_release; + } + else + { + /* Free the allocation information - nothing to be passed back */ + _mali_osk_free( ret_allocation ); + } + + return result; +} + +static void block_allocator_release(void * ctx, void * handle) +{ + block_allocator * info; + block_info * block, * next; + block_allocator_allocation *allocation; + + MALI_DEBUG_ASSERT_POINTER(ctx); + MALI_DEBUG_ASSERT_POINTER(handle); + + info = (block_allocator*)ctx; + allocation = (block_allocator_allocation*)handle; + block = allocation->last_allocated; + + MALI_DEBUG_ASSERT_POINTER(block); + + if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) + { + MALI_DEBUG_PRINT(1, ("allocator release: Failed to get mutex\n")); + return; + } + + /* unmap */ + mali_allocation_engine_unmap_physical(allocation->engine, allocation->descriptor, allocation->start_offset, allocation->mapping_length, (_mali_osk_mem_mapregion_flags_t)0); + + while (block) + { + MALI_DEBUG_ASSERT(!((block < info->all_blocks) || (block > (info->all_blocks + info->num_blocks)))); + + next = block->next; + + /* relink into free-list */ + block->next = info->first_free; + info->first_free = block; + + /* advance the loop */ + block = next; + } + + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + + _mali_osk_free( allocation ); +} + + +static mali_physical_memory_allocation_result block_allocator_allocate_page_table_block(void * ctx, mali_page_table_block * block) +{ + block_allocator * info; + mali_physical_memory_allocation_result result = MALI_MEM_ALLOC_INTERNAL_FAILURE; + + MALI_DEBUG_ASSERT_POINTER(ctx); + MALI_DEBUG_ASSERT_POINTER(block); + info = (block_allocator*)ctx; + + if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) return MALI_MEM_ALLOC_INTERNAL_FAILURE; + + if (NULL != info->first_free) + { + void * virt; + u32 phys; + u32 size; + block_info * alloc; + alloc = info->first_free; + + phys = get_phys(info, alloc); /* Does not modify info or alloc */ + size = MALI_BLOCK_SIZE; /* Must be multiple of MALI_MMU_PAGE_SIZE */ + virt = _mali_osk_mem_mapioregion( phys, size, "Mali block allocator page tables" ); + + /* Failure of _mali_osk_mem_mapioregion will result in MALI_MEM_ALLOC_INTERNAL_FAILURE, + * because it's unlikely another allocator will be able to map in. */ + + if ( NULL != virt ) + { + block->ctx = info; /* same as incoming ctx */ + block->handle = alloc; + block->phys_base = phys; + block->size = size; + block->release = block_allocator_release_page_table_block; + block->mapping = virt; + + info->first_free = alloc->next; + + alloc->next = NULL; /* Could potentially link many blocks together instead */ + + result = MALI_MEM_ALLOC_FINISHED; + } + } + else result = MALI_MEM_ALLOC_NONE; + + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + + return result; +} + + +static void block_allocator_release_page_table_block( mali_page_table_block *page_table_block ) +{ + block_allocator * info; + block_info * block, * next; + + MALI_DEBUG_ASSERT_POINTER( page_table_block ); + + info = (block_allocator*)page_table_block->ctx; + block = (block_info*)page_table_block->handle; + + MALI_DEBUG_ASSERT_POINTER(info); + MALI_DEBUG_ASSERT_POINTER(block); + + + if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) + { + MALI_DEBUG_PRINT(1, ("allocator release: Failed to get mutex\n")); + return; + } + + /* Unmap all the physical memory at once */ + _mali_osk_mem_unmapioregion( page_table_block->phys_base, page_table_block->size, page_table_block->mapping ); + + /** @note This loop handles the case where more than one block_info was linked. + * Probably unnecssary for page table block releasing. */ + while (block) + { + next = block->next; + + MALI_DEBUG_ASSERT(!((block < info->all_blocks) || (block > (info->all_blocks + info->num_blocks)))); + + block->next = info->first_free; + info->first_free = block; + + block = next; + } + + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); +} + +static u32 block_allocator_stat(mali_physical_memory_allocator * allocator) +{ + block_allocator * info; + block_info *block; + u32 free_blocks = 0; + + MALI_DEBUG_ASSERT_POINTER(allocator); + + info = (block_allocator*)allocator->ctx; + block = info->first_free; + + while(block) + { + free_blocks++; + block = block->next; + } + return (info->num_blocks - free_blocks) * MALI_BLOCK_SIZE; +} diff --git a/drivers/gpu/arm/mali/common/mali_block_allocator.h b/drivers/gpu/arm/mali/common/mali_block_allocator.h new file mode 100644 index 000000000000..6c6f13eec34a --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_block_allocator.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_BLOCK_ALLOCATOR_H__ +#define __MALI_BLOCK_ALLOCATOR_H__ + +#include "mali_kernel_memory_engine.h" + +mali_physical_memory_allocator * mali_block_allocator_create(u32 base_address, u32 cpu_usage_adjust, u32 size, const char *name); + +#endif /* __MALI_BLOCK_ALLOCATOR_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_cinstr_profiling_events_m200.h b/drivers/gpu/arm/mali/common/mali_cinstr_profiling_events_m200.h new file mode 100644 index 000000000000..47f37636a935 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_cinstr_profiling_events_m200.h @@ -0,0 +1,117 @@ +/** + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms of + * such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained + * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _CINSTR_PROFILING_EVENTS_M200_H_ +#define _CINSTR_PROFILING_EVENTS_M200_H_ + +/* + * The event ID is a 32 bit value consisting of different fields + * reserved, 4 bits, for future use + * event type, 4 bits, cinstr_profiling_event_type_t + * event channel, 8 bits, the source of the event. + * event data, 16 bit field, data depending on event type + */ + +/** + * Specifies what kind of event this is + */ +typedef enum +{ + MALI_PROFILING_EVENT_TYPE_SINGLE = 0 << 24, + MALI_PROFILING_EVENT_TYPE_START = 1 << 24, + MALI_PROFILING_EVENT_TYPE_STOP = 2 << 24, + MALI_PROFILING_EVENT_TYPE_SUSPEND = 3 << 24, + MALI_PROFILING_EVENT_TYPE_RESUME = 4 << 24, +} cinstr_profiling_event_type_t; + + +/** + * Secifies the channel/source of the event + */ +typedef enum +{ + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE = 0 << 16, + MALI_PROFILING_EVENT_CHANNEL_GP0 = 1 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP0 = 5 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP1 = 6 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP2 = 7 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP3 = 8 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP4 = 9 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP5 = 10 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP6 = 11 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP7 = 12 << 16, + MALI_PROFILING_EVENT_CHANNEL_GPU = 21 << 16, +} cinstr_profiling_event_channel_t; + + +#define MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(num) (((MALI_PROFILING_EVENT_CHANNEL_GP0 >> 16) + (num)) << 16) +#define MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(num) (((MALI_PROFILING_EVENT_CHANNEL_PP0 >> 16) + (num)) << 16) + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from software channel + */ +typedef enum +{ + MALI_PROFILING_EVENT_REASON_SINGLE_SW_NONE = 0, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_NEW_FRAME = 1, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_FLUSH = 2, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_SWAP_BUFFERS = 3, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_FB_EVENT = 4 +} cinstr_profiling_event_reason_single_sw_t; + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_START/STOP is used from software channel + */ +typedef enum +{ + MALI_PROFILING_EVENT_REASON_START_STOP_SW_NONE = 0, + MALI_PROFILING_EVENT_REASON_START_STOP_MALI = 1, +} cinstr_profiling_event_reason_start_stop_sw_t; + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SUSPEND/RESUME is used from software channel + */ +typedef enum +{ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_NONE = 0, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_PIPELINE_FULL = 1, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VSYNC = 26, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_FB_IFRAME_WAIT= 27, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_FB_IFRAME_SYNC= 28, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VG_WAIT_FILTER_CLEANUP = 29, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VG_WAIT_TEXTURE = 30, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_GLES_WAIT_MIPLEVEL = 31, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_GLES_WAIT_READPIXELS = 32, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_EGL_WAIT_SWAP_IMMEDIATE= 33, +} cinstr_profiling_event_reason_suspend_resume_sw_t; + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from a HW channel (GPx+PPx) + */ +typedef enum +{ + MALI_PROFILING_EVENT_REASON_SINGLE_HW_NONE = 0, + MALI_PROFILING_EVENT_REASON_SINGLE_HW_INTERRUPT = 1, + MALI_PROFILING_EVENT_REASON_SINGLE_HW_FLUSH = 2, +} cinstr_profiling_event_reason_single_hw_t; + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from the GPU channel + */ +typedef enum +{ + MALI_PROFILING_EVENT_REASON_SINGLE_GPU_NONE = 0, + MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE = 1, +} cinstr_profiling_event_reason_single_gpu_t; + +#endif /*_CINSTR_PROFILING_EVENTS_M200_H_*/ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_GP2.c b/drivers/gpu/arm/mali/common/mali_kernel_GP2.c new file mode 100644 index 000000000000..7ccdd8e2e570 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_GP2.c @@ -0,0 +1,1500 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_kernel_core.h" +#include "mali_kernel_subsystem.h" +#include "regs/mali_gp_regs.h" +#include "mali_kernel_rendercore.h" +#include "mali_osk.h" +#include "mali_osk_list.h" +#if MALI_TIMELINE_PROFILING_ENABLED +#include "mali_osk_profiling.h" +#endif +#if defined(USING_MALI400_L2_CACHE) +#include "mali_kernel_l2_cache.h" +#endif +#if USING_MMU +#include "mali_kernel_mem_mmu.h" /* Needed for mali_kernel_mmu_force_bus_reset() */ +#endif + +#if defined(USING_MALI200) +#define MALI_GP_SUBSYSTEM_NAME "MaliGP2" +#define MALI_GP_CORE_TYPE _MALI_GP2 +#elif defined(USING_MALI400) +#define MALI_GP_SUBSYSTEM_NAME "Mali-400 GP" +#define MALI_GP_CORE_TYPE _MALI_400_GP +#else +#error "No supported mali core defined" +#endif + +#define GET_JOB_EMBEDDED_PTR(job) (&((job)->embedded_core_job)) +#define GET_JOBGP2_PTR(job_extern) _MALI_OSK_CONTAINER_OF(job_extern, maligp_job, embedded_core_job) + +/* Initialized when this subsystem is initialized. This is determined by the + * position in subsystems[], and so the value used to initialize this is + * determined at compile time */ +static mali_kernel_subsystem_identifier mali_subsystem_gp_id = -1; + +static mali_core_renderunit * last_gp_core_cookie = NULL; + +/* Describing a maligp job settings */ +typedef struct maligp_job +{ + /* The general job struct common for all mali cores */ + mali_core_job embedded_core_job; + _mali_uk_gp_start_job_s user_input; + + u32 irq_status; + u32 status_reg_on_stop; + u32 perf_counter0; + u32 perf_counter1; + u32 vscl_stop_addr; + u32 plbcl_stop_addr; + u32 heap_current_addr; + + /* The data we will return back to the user */ + _mali_osk_notification_t *notification_obj; + + int is_stalled_waiting_for_more_memory; + + u32 active_mask; + /* progress checking */ + u32 last_vscl; + u32 last_plbcl; + /* extended progress checking, only enabled when we can use one of the performance counters */ + u32 have_extended_progress_checking; + u32 vertices; + +#if defined(USING_MALI400_L2_CACHE) + u32 perf_counter_l2_src0; + u32 perf_counter_l2_src1; + u32 perf_counter_l2_val0; + u32 perf_counter_l2_val1; +#endif + +#if MALI_TIMELINE_PROFILING_ENABLED + u32 pid; + u32 tid; +#endif +} maligp_job; + +/*Functions Exposed to the General External System through + function pointers.*/ + +static _mali_osk_errcode_t maligp_subsystem_startup(mali_kernel_subsystem_identifier id); +#if USING_MMU +static _mali_osk_errcode_t maligp_subsystem_mmu_connect(mali_kernel_subsystem_identifier id); +#endif +static void maligp_subsystem_terminate(mali_kernel_subsystem_identifier id); +static _mali_osk_errcode_t maligp_subsystem_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue); +static void maligp_subsystem_session_end(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot); +static _mali_osk_errcode_t maligp_subsystem_core_system_info_fill(_mali_system_info* info); +static _mali_osk_errcode_t maligp_renderunit_create(_mali_osk_resource_t * resource); +#if USING_MMU +static void maligp_subsystem_broadcast_notification(mali_core_notification_message message, u32 data); +#endif +#if MALI_STATE_TRACKING +u32 maligp_subsystem_dump_state(char *buf, u32 size); +#endif + +/* Internal support functions */ +static _mali_osk_errcode_t maligp_core_version_legal( mali_core_renderunit *core ); +static void maligp_raw_reset( mali_core_renderunit *core); +static void maligp_reset_hard(struct mali_core_renderunit * core); +static void maligp_reset(mali_core_renderunit *core); +static void maligp_initialize_registers_mgmt(mali_core_renderunit *core ); + +#ifdef DEBUG +static void maligp_print_regs(int debug_level, mali_core_renderunit *core); +#endif + +/* Functions exposed to mali_core system through functionpointers + in the subsystem struct. */ +static _mali_osk_errcode_t subsystem_maligp_start_job(mali_core_job * job, mali_core_renderunit * core); +static u32 subsystem_maligp_irq_handler_upper_half(mali_core_renderunit * core); +static int subsystem_maligp_irq_handler_bottom_half(mali_core_renderunit* core); +static _mali_osk_errcode_t subsystem_maligp_get_new_job_from_user(struct mali_core_session * session, void * argument); +static _mali_osk_errcode_t subsystem_maligp_suspend_response(struct mali_core_session * session, void * argument); +static void subsystem_maligp_return_job_to_user(mali_core_job * job, mali_subsystem_job_end_code end_status); +static void subsystem_maligp_renderunit_delete(mali_core_renderunit * core); +static void subsystem_maligp_renderunit_reset_core(struct mali_core_renderunit * core, mali_core_reset_style style ); +static void subsystem_maligp_renderunit_probe_core_irq_trigger(struct mali_core_renderunit* core); +static _mali_osk_errcode_t subsystem_maligp_renderunit_probe_core_irq_finished(struct mali_core_renderunit* core); +static void subsystem_maligp_renderunit_stop_bus(struct mali_core_renderunit* core); + +/* Variables */ +static register_address_and_value default_mgmt_regs[] = +{ + { MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_VAL_IRQ_MASK_USED } +}; + + +/* This will be one of the subsystems in the array of subsystems: + static struct mali_kernel_subsystem * subsystems[]; + found in file: mali_kernel_core.c +*/ + +struct mali_kernel_subsystem mali_subsystem_gp2= +{ + maligp_subsystem_startup, /* startup */ + maligp_subsystem_terminate, /* shutdown */ +#if USING_MMU + maligp_subsystem_mmu_connect, /* load_complete */ +#else + NULL, +#endif + maligp_subsystem_core_system_info_fill, /* system_info_fill */ + maligp_subsystem_session_begin, /* session_begin */ + maligp_subsystem_session_end, /* session_end */ +#if USING_MMU + maligp_subsystem_broadcast_notification, /* broadcast_notification */ +#else + NULL, +#endif +#if MALI_STATE_TRACKING + maligp_subsystem_dump_state, /* dump_state */ +#endif +} ; + +static mali_core_subsystem subsystem_maligp ; + +static _mali_osk_errcode_t maligp_subsystem_startup(mali_kernel_subsystem_identifier id) +{ + mali_core_subsystem * subsystem; + + MALI_DEBUG_PRINT(3, ("Mali GP: maligp_subsystem_startup\n") ) ; + + mali_subsystem_gp_id = id; + + /* All values get 0 as default */ + _mali_osk_memset(&subsystem_maligp, 0, sizeof(*subsystem)); + + subsystem = &subsystem_maligp; + subsystem->start_job = &subsystem_maligp_start_job; + subsystem->irq_handler_upper_half = &subsystem_maligp_irq_handler_upper_half; + subsystem->irq_handler_bottom_half = &subsystem_maligp_irq_handler_bottom_half; + subsystem->get_new_job_from_user = &subsystem_maligp_get_new_job_from_user; + subsystem->suspend_response = &subsystem_maligp_suspend_response; + subsystem->return_job_to_user = &subsystem_maligp_return_job_to_user; + subsystem->renderunit_delete = &subsystem_maligp_renderunit_delete; + subsystem->reset_core = &subsystem_maligp_renderunit_reset_core; + subsystem->stop_bus = &subsystem_maligp_renderunit_stop_bus; + subsystem->probe_core_irq_trigger = &subsystem_maligp_renderunit_probe_core_irq_trigger; + subsystem->probe_core_irq_acknowledge = &subsystem_maligp_renderunit_probe_core_irq_finished; + + /* Setting variables in the general core part of the subsystem.*/ + subsystem->name = MALI_GP_SUBSYSTEM_NAME; + subsystem->core_type = MALI_GP_CORE_TYPE; + subsystem->id = id; + + /* Initiates the rest of the general core part of the subsystem */ + MALI_CHECK_NO_ERROR(mali_core_subsystem_init( subsystem )); + + /* This will register the function for adding MALIGP2 cores to the subsystem */ +#if defined(USING_MALI200) + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(MALIGP2, maligp_renderunit_create)); +#endif +#if defined(USING_MALI400) + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(MALI400GP, maligp_renderunit_create)); +#endif + + MALI_DEBUG_PRINT(6, ("Mali GP: maligp_subsystem_startup\n") ) ; + + MALI_SUCCESS; +} + +#if USING_MMU +static _mali_osk_errcode_t maligp_subsystem_mmu_connect(mali_kernel_subsystem_identifier id) +{ + mali_core_subsystem_attach_mmu(&subsystem_maligp); + MALI_SUCCESS; /* OK */ +} +#endif + +static void maligp_subsystem_terminate(mali_kernel_subsystem_identifier id) +{ + MALI_DEBUG_PRINT(3, ("Mali GP: maligp_subsystem_terminate\n") ) ; + mali_core_subsystem_cleanup(&subsystem_maligp); +} + +static _mali_osk_errcode_t maligp_subsystem_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue) +{ + mali_core_session * session; + + MALI_DEBUG_PRINT(3, ("Mali GP: maligp_subsystem_session_begin\n") ) ; + MALI_CHECK_NON_NULL(session = _mali_osk_malloc( sizeof(*session) ), _MALI_OSK_ERR_FAULT); + + _mali_osk_memset(session, 0, sizeof(*session) ); + *slot = (mali_kernel_subsystem_session_slot)session; + + session->subsystem = &subsystem_maligp; + + session->notification_queue = queue; + +#if USING_MMU + session->mmu_session = mali_session_data; +#endif + + mali_core_session_begin(session); + + MALI_DEBUG_PRINT(6, ("Mali GP: maligp_subsystem_session_begin\n") ) ; + + MALI_SUCCESS; +} + +static void maligp_subsystem_session_end(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot) +{ + mali_core_session * session; + /** @note mali_session_data not needed here */ + + MALI_DEBUG_PRINT(3, ("Mali GP: maligp_subsystem_session_end\n") ) ; + if ( NULL==slot || NULL==*slot) + { + MALI_PRINT_ERROR(("Input slot==NULL")); + return; + } + session = (mali_core_session *)*slot; + mali_core_session_close(session); + + _mali_osk_free(session); + *slot = NULL; + + MALI_DEBUG_PRINT(6, ("Mali GP: maligp_subsystem_session_end\n") ) ; +} + +/** + * We fill in info about all the cores we have + * @param info Pointer to system info struct to update + * @return _MALI_OSK_ERR_OK on success, or another _mali_osk_errcode_t for errors. + */ +static _mali_osk_errcode_t maligp_subsystem_core_system_info_fill(_mali_system_info* info) +{ + return mali_core_subsystem_system_info_fill(&subsystem_maligp, info); +} + +static _mali_osk_errcode_t maligp_renderunit_create(_mali_osk_resource_t * resource) +{ + mali_core_renderunit *core; + int err; + + MALI_DEBUG_PRINT(3, ("Mali GP: maligp_renderunit_create\n") ) ; + /* Checking that the resource settings are correct */ +#if defined(USING_MALI200) + if(MALIGP2 != resource->type) + { + MALI_PRINT_ERROR(("Can not register this resource as a " MALI_GP_SUBSYSTEM_NAME " core.")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } +#elif defined(USING_MALI400) + if(MALI400GP != resource->type) + { + MALI_PRINT_ERROR(("Can not register this resource as a " MALI_GP_SUBSYSTEM_NAME " core.")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } +#endif + if ( 0 != resource->size ) + { + MALI_PRINT_ERROR(("Memory size set to " MALI_GP_SUBSYSTEM_NAME " core should be zero.")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + if ( NULL == resource->description ) + { + MALI_PRINT_ERROR(("A " MALI_GP_SUBSYSTEM_NAME " core needs a unique description field")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* Create a new core object */ + core = (mali_core_renderunit*) _mali_osk_malloc(sizeof(*core)); + if ( NULL == core ) + { + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* Variables set to be able to open and register the core */ + core->subsystem = &subsystem_maligp ; + core->registers_base_addr = resource->base ; + core->size = MALIGP2_REGISTER_ADDRESS_SPACE_SIZE ; + core->description = resource->description; + core->irq_nr = resource->irq ; +#if USING_MMU + core->mmu_id = resource->mmu_id; + core->mmu = NULL; +#endif +#if USING_MALI_PMM + /* Set up core's PMM id */ + core->pmm_id = MALI_PMM_CORE_GP; +#endif + + err = mali_core_renderunit_init( core ); + if (_MALI_OSK_ERR_OK != err) + { + MALI_DEBUG_PRINT(1, ("Failed to initialize renderunit\n")); + goto exit_on_error0; + } + + /* Map the new core object, setting: core->registers_mapped */ + err = mali_core_renderunit_map_registers(core); + if (_MALI_OSK_ERR_OK != err) goto exit_on_error1; + + /* Check that the register mapping of the core works. + Return 0 if maligp core is present and accessible. */ + if (mali_benchmark) { + core->core_version = MALI_GP_PRODUCT_ID << 16; + } else { + core->core_version = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_VERSION); + } + + err = maligp_core_version_legal(core); + if (_MALI_OSK_ERR_OK != err) goto exit_on_error2; + + /* Reset the core. Put the core into a state where it can start to render. */ + maligp_reset(core); + + /* Registering IRQ, init the work_queue_irq_handle */ + /* Adding this core as an available renderunit in the subsystem. */ + err = mali_core_subsystem_register_renderunit(&subsystem_maligp, core); + if (_MALI_OSK_ERR_OK != err) goto exit_on_error2; + +#ifdef DEBUG + MALI_DEBUG_PRINT(4, ("Mali GP: Initial Register settings:\n")); + maligp_print_regs(4, core); +#endif + + MALI_DEBUG_PRINT(6, ("Mali GP: maligp_renderunit_create\n") ) ; + + MALI_SUCCESS; + +exit_on_error2: + mali_core_renderunit_unmap_registers(core); +exit_on_error1: + mali_core_renderunit_term(core); +exit_on_error0: + _mali_osk_free( core ) ; + MALI_PRINT_ERROR(("Renderunit NOT created.")); + MALI_ERROR((_mali_osk_errcode_t)err); +} + +#if USING_MMU +/* Used currently only for signalling when MMU has a pagefault */ +static void maligp_subsystem_broadcast_notification(mali_core_notification_message message, u32 data) +{ + mali_core_subsystem_broadcast_notification(&subsystem_maligp, message, data); +} +#endif + +#ifdef DEBUG +static void maligp_print_regs(int debug_level, mali_core_renderunit *core) +{ + if (debug_level <= mali_debug_level) + { + MALI_DEBUG_PRINT(1, (" VS 0x%08X 0x%08X, PLBU 0x%08X 0x%08X ALLOC 0x%08X 0x%08X\n", + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_VSCL_START_ADDR), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_VSCL_END_ADDR), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBUCL_START_ADDR), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBUCL_END_ADDR), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_END_ADDR)) + ); + MALI_DEBUG_PRINT(1, (" IntRaw 0x%08X IntMask 0x%08X, Status 0x%02X Ver: 0x%08X \n", + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_INT_MASK), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_STATUS), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_VERSION))); + + MALI_DEBUG_PRINT(1, (" PERF_CNT Enbl:%d %d Src: %02d %02d VAL: 0x%08X 0x%08X\n", + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_ENABLE), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_ENABLE), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_SRC), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_SRC), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_VALUE), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE))); + + MALI_DEBUG_PRINT(1, (" VS_START 0x%08X PLBU_START 0x%08X AXI_ERR 0x%08X\n", + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_VSCL_START_ADDR_READ), + mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBCL_START_ADDR_READ), + mali_core_renderunit_register_read(core, MALIGP2_CONTR_AXI_BUS_ERROR_STAT))); + } +} +#endif + +static _mali_osk_errcode_t maligp_core_version_legal( mali_core_renderunit *core ) +{ + u32 mali_type; + + mali_type = core->core_version >> 16; + +#if defined(USING_MALI400) + if ( MALI400_GP_PRODUCT_ID != mali_type && MALI300_GP_PRODUCT_ID != mali_type ) +#else + if ( MALI_GP_PRODUCT_ID != mali_type ) +#endif + { + MALI_PRINT_ERROR(("Error: reading this from maligp version register: 0x%x\n", core->core_version)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + MALI_DEBUG_PRINT(3, ("Mali GP: core_version_legal: Reads correct mali version: %d\n", core->core_version )) ; + MALI_SUCCESS; +} + +static void subsystem_maligp_renderunit_stop_bus(struct mali_core_renderunit* core) +{ + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_CMD, MALIGP2_REG_VAL_CMD_STOP_BUS); +} + +static void maligp_reset( mali_core_renderunit *core ) +{ + if (!mali_benchmark) { + maligp_raw_reset(core); + maligp_initialize_registers_mgmt(core); + } +} + + +static void maligp_reset_hard( mali_core_renderunit *core ) +{ + const int reset_finished_loop_count = 15; + const u32 reset_wait_target_register = MALIGP2_REG_ADDR_MGMT_WRITE_BOUND_LOW; + const u32 reset_invalid_value = 0xC0FFE000; + const u32 reset_check_value = 0xC01A0000; + const u32 reset_default_value = 0; + int i; + + mali_core_renderunit_register_write(core, reset_wait_target_register, reset_invalid_value); + + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_CMD, MALIGP2_REG_VAL_CMD_RESET); + + for (i = 0; i < reset_finished_loop_count; i++) + { + mali_core_renderunit_register_write(core, reset_wait_target_register, reset_check_value); + if (reset_check_value == mali_core_renderunit_register_read(core, reset_wait_target_register)) + { + MALI_DEBUG_PRINT(5, ("Reset loop exiting after %d iterations\n", i)); + break; + } + } + + if (i == reset_finished_loop_count) + { + MALI_DEBUG_PRINT(1, ("The reset loop didn't work\n")); + } + + mali_core_renderunit_register_write(core, reset_wait_target_register, reset_default_value); /* set it back to the default */ + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALIGP2_REG_VAL_IRQ_MASK_ALL); + + +} + +static void maligp_raw_reset( mali_core_renderunit *core ) +{ + int i; + const int request_loop_count = 20; + + MALI_DEBUG_PRINT(4, ("Mali GP: maligp_raw_reset: %s\n", core->description)) ; + if (mali_benchmark) return; + + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_MASK, 0); /* disable the IRQs */ + +#if defined(USING_MALI200) + + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_CMD, MALIGP2_REG_VAL_CMD_STOP_BUS); + + for (i = 0; i < request_loop_count; i++) + { + if (mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_STATUS) & MALIGP2_REG_VAL_STATUS_BUS_STOPPED) break; + _mali_osk_time_ubusydelay(10); + } + + MALI_DEBUG_PRINT_IF(1, request_loop_count == i, ("Mali GP: Bus was never stopped during core reset\n")); + + if (request_loop_count==i) + { + /* Could not stop bus connections from core, probably because some of the already pending + bus request has had a page fault, and therefore can not complete before the MMU does PageFault + handling. This can be treated as a heavier reset function - which unfortunately reset all + the cores on this MMU in addition to the MMU itself */ +#if USING_MMU + if ((NULL!=core->mmu) && (MALI_FALSE == core->error_recovery)) + { + MALI_DEBUG_PRINT(1, ("Mali GP: Forcing MMU bus reset\n")); + mali_kernel_mmu_force_bus_reset(core->mmu); + return; + } +#endif + MALI_PRINT(("A MMU reset did not allow GP to stop its bus, system failure, unable to recover\n")); + return; + } + + /* the bus was stopped OK, complete the reset */ + /* use the hard reset routine to do the actual reset */ + maligp_reset_hard(core); + +#elif defined(USING_MALI400) + + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALI400GP_REG_VAL_IRQ_RESET_COMPLETED); + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_CMD, MALI400GP_REG_VAL_CMD_SOFT_RESET); + + for (i = 0; i < request_loop_count; i++) + { + if (mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT) & /*Bitwise OR*/ + MALI400GP_REG_VAL_IRQ_RESET_COMPLETED) break; + _mali_osk_time_ubusydelay(10); + } + + if ( request_loop_count==i ) + { +#if USING_MMU + /* Could not stop bus connections from core, probably because some of the already pending + bus request has had a page fault, and therefore can not complete before the MMU does PageFault + handling. This can be treated as a heavier reset function - which unfortunately reset all + the cores on this MMU in addition to the MMU itself */ + if ((NULL!=core->mmu) && (MALI_FALSE == core->error_recovery)) + { + MALI_DEBUG_PRINT(1, ("Mali GP: Forcing Bus reset\n")); + mali_kernel_mmu_force_bus_reset(core->mmu); + return; + } +#endif + MALI_PRINT(("A MMU reset did not allow GP to stop its bus, system failure, unable to recover\n")); + } + else + { + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALIGP2_REG_VAL_IRQ_MASK_ALL); + } + +#else +#error "no supported mali core defined" +#endif +} + +/* Sets the registers on maligp according to the const default_mgmt_regs array. */ +static void maligp_initialize_registers_mgmt(mali_core_renderunit *core ) +{ + int i; + + MALI_DEBUG_PRINT(6, ("Mali GP: maligp_initialize_registers_mgmt: %s\n", core->description)) ; + for(i=0 ; i< (sizeof(default_mgmt_regs)/sizeof(*default_mgmt_regs)) ; ++i) + { + mali_core_renderunit_register_write_relaxed(core, default_mgmt_regs[i].address, default_mgmt_regs[i].value); + } + _mali_osk_write_mem_barrier(); +} + + +/* Start this job on this core. Return MALI_TRUE if the job was started. */ +static _mali_osk_errcode_t subsystem_maligp_start_job(mali_core_job * job, mali_core_renderunit * core) +{ + maligp_job *jobgp; + u32 startcmd; + /* The local extended version of the general structs */ + jobgp = _MALI_OSK_CONTAINER_OF(job, maligp_job, embedded_core_job); + + startcmd = 0; + if ( jobgp->user_input.frame_registers[0] != jobgp->user_input.frame_registers[1] ) + { + startcmd |= (u32) MALIGP2_REG_VAL_CMD_START_VS; + } + + if ( jobgp->user_input.frame_registers[2] != jobgp->user_input.frame_registers[3] ) + { + startcmd |= (u32) MALIGP2_REG_VAL_CMD_START_PLBU; + } + + if(0 == startcmd) + { + MALI_DEBUG_PRINT(4, ("Mali GP: Job: 0x%08x WILL NOT START SINCE JOB HAS ILLEGAL ADDRESSES\n", + (u32)jobgp->user_input.user_job_ptr)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + +#ifdef DEBUG + MALI_DEBUG_PRINT(4, ("Mali GP: Registers Start\n")); + maligp_print_regs(4, core); +#endif + + + mali_core_renderunit_register_write_array( + core, + MALIGP2_REG_ADDR_MGMT_VSCL_START_ADDR, + &(jobgp->user_input.frame_registers[0]), + sizeof(jobgp->user_input.frame_registers)/sizeof(jobgp->user_input.frame_registers[0])); + +#if MALI_TIMELINE_PROFILING_ENABLED + /* + * If the hardware counters are not turned on, ask the external profiler + * if they should be. + */ + if (jobgp->user_input.perf_counter_flag == 0) + { + mali_bool src0_enabled = _mali_osk_profiling_query_hw_counter(COUNTER_VP_C0, + &(jobgp->user_input.perf_counter_src0)); + mali_bool src1_enabled = _mali_osk_profiling_query_hw_counter(COUNTER_VP_C1, + &(jobgp->user_input.perf_counter_src1)); + + if (src0_enabled == MALI_TRUE) + { + jobgp->user_input.perf_counter_flag |= + _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE; + } + + if (src1_enabled == MALI_TRUE) + { + jobgp->user_input.perf_counter_flag |= + _MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE; + } + } +#endif /* MALI_TIMELINE_PROFILING_ENABLED */ + + /* This selects which performance counters we are reading */ + if ( 0 != jobgp->user_input.perf_counter_flag ) + { + if ( jobgp->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE) + { + mali_core_renderunit_register_write_relaxed( + core, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_SRC, + jobgp->user_input.perf_counter_src0); + + mali_core_renderunit_register_write_relaxed( + core, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_ENABLE, + MALIGP2_REG_VAL_PERF_CNT_ENABLE); + } + + if ( jobgp->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE) + { + mali_core_renderunit_register_write_relaxed( + core, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_SRC, + jobgp->user_input.perf_counter_src1); + + mali_core_renderunit_register_write_relaxed( + core, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_ENABLE, + MALIGP2_REG_VAL_PERF_CNT_ENABLE); + } + +#if defined(USING_MALI400_L2_CACHE) + if ( jobgp->user_input.perf_counter_flag & (_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC0_ENABLE|_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC1_ENABLE) ) + { + int force_reset = ( jobgp->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_L2_RESET ) ? 1 : 0; + u32 src0 = 0; + u32 src1 = 0; + + if ( jobgp->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC0_ENABLE ) + { + src0 = jobgp->user_input.perf_counter_l2_src0; + } + if ( jobgp->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC1_ENABLE ) + { + src1 = jobgp->user_input.perf_counter_l2_src1; + } + + mali_kernel_l2_cache_set_perf_counters(src0, src1, force_reset); /* will activate and possibly reset counters */ + + /* Now, retrieve the current values, so we can substract them when the job has completed */ + mali_kernel_l2_cache_get_perf_counters(&jobgp->perf_counter_l2_src0, + &jobgp->perf_counter_l2_val0, + &jobgp->perf_counter_l2_src1, + &jobgp->perf_counter_l2_val1); + } +#endif + } + + if ( 0 == (jobgp->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE)) + { + /* extended progress checking can be enabled */ + + jobgp->have_extended_progress_checking = 1; + + mali_core_renderunit_register_write_relaxed( + core, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_SRC, + MALIGP2_REG_VAL_PERF_CNT1_SRC_NUMBER_OF_VERTICES_PROCESSED + ); + + mali_core_renderunit_register_write_relaxed( + core, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_ENABLE, + MALIGP2_REG_VAL_PERF_CNT_ENABLE); + } + + subsystem_flush_mapped_mem_cache(); + + MALI_DEBUG_PRINT(4, ("Mali GP: STARTING GP WITH CMD: 0x%x\n", startcmd)); +#if MALI_STATE_TRACKING + _mali_osk_atomic_inc(&job->session->jobs_started); +#endif + + /* This is the command that starts the Core */ + mali_core_renderunit_register_write(core, + MALIGP2_REG_ADDR_MGMT_CMD, + startcmd); + _mali_osk_write_mem_barrier(); + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE | MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(core->core_number) | MALI_PROFILING_EVENT_REASON_SINGLE_HW_FLUSH, + jobgp->user_input.frame_builder_id, jobgp->user_input.flush_id, 0, 0, 0); + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_START|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(core->core_number), jobgp->pid, jobgp->tid, 0, 0, 0); +#endif + + MALI_SUCCESS; +} + +/* Check if given core has an interrupt pending. Return MALI_TRUE and set mask to 0 if pending */ + +static u32 subsystem_maligp_irq_handler_upper_half(mali_core_renderunit * core) +{ + u32 irq_readout; + + if (mali_benchmark) { + return (core->current_job ? 1 : 0); /* simulate irq is pending when a job is pending */ + } + + irq_readout = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_INT_STAT); + + if ( MALIGP2_REG_VAL_IRQ_MASK_NONE != irq_readout ) + { + /* Mask out all IRQs from this core until IRQ is handled */ + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_VAL_IRQ_MASK_NONE); + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(core->core_number)|MALI_PROFILING_EVENT_REASON_SINGLE_HW_INTERRUPT, irq_readout, 0, 0, 0, 0); +#endif + + /* We do need to handle this in a bottom half, return 1 */ + return 1; + } + return 0; +} + +/* This function should check if the interrupt indicates that job was finished. +If so it should update the job-struct, reset the core registers, and return MALI_TRUE, . +If the job is still working after this function it should return MALI_FALSE. +The function must also enable the bits in the interrupt mask for the core. +Called by the bottom half interrupt function. */ +static int subsystem_maligp_irq_handler_bottom_half(mali_core_renderunit* core) +{ + mali_core_job * job; + maligp_job * jobgp; + u32 irq_readout; + u32 core_status; + u32 vscl; + u32 plbcl; + + job = core->current_job; + + if (mali_benchmark) { + MALI_DEBUG_PRINT(3, ("MaliGP: Job: Benchmark\n") ); + irq_readout = MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST | MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST; + core_status = 0; + } else { + irq_readout = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT) & MALIGP2_REG_VAL_IRQ_MASK_USED; + core_status = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_STATUS); + } + + if (NULL == job) + { + MALI_DEBUG_ASSERT(CORE_IDLE==core->state); + if ( 0 != irq_readout ) + { + MALI_PRINT_ERROR(("Interrupt from a core not running a job. IRQ: 0x%04x Status: 0x%04x", irq_readout, core_status)); + } + return JOB_STATUS_END_UNKNOWN_ERR; + } + MALI_DEBUG_ASSERT(CORE_IDLE!=core->state); + + jobgp = GET_JOBGP2_PTR(job); + + jobgp->heap_current_addr = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR); + + vscl = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_VSCL_START_ADDR); + plbcl = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBUCL_START_ADDR); + + MALI_DEBUG_PRINT(3, ("Mali GP: Job: 0x%08x IRQ RECEIVED Rawstat: 0x%x Status: 0x%x\n", + (u32)jobgp->user_input.user_job_ptr, irq_readout , core_status )) ; + + jobgp->irq_status |= irq_readout; + jobgp->status_reg_on_stop = core_status; + + if ( 0 != jobgp->is_stalled_waiting_for_more_memory ) + { + /* Readback the performance counters */ + if (jobgp->user_input.perf_counter_flag & (_MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE|_MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE) ) + { + jobgp->perf_counter0 = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_VALUE); + jobgp->perf_counter1 = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE); + +#if MALI_TIMELINE_PROFILING_ENABLED + /* Report the hardware counter values to the external profiler */ + _mali_osk_profiling_report_hw_counter(COUNTER_VP_C0, jobgp->perf_counter0); + _mali_osk_profiling_report_hw_counter(COUNTER_VP_C1, jobgp->perf_counter1); +#endif /* MALI_TIMELINE_PROFILING_ENABLED */ + } + +#if defined(USING_MALI400_L2_CACHE) + if (jobgp->user_input.perf_counter_flag & (_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC0_ENABLE|_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC1_ENABLE) ) + { + u32 src0; + u32 val0; + u32 src1; + u32 val1; + mali_kernel_l2_cache_get_perf_counters(&src0, &val0, &src1, &val1); + + if (jobgp->perf_counter_l2_src0 == src0) + { + jobgp->perf_counter_l2_val0 = val0 - jobgp->perf_counter_l2_val0; + } + else + { + jobgp->perf_counter_l2_val0 = 0; + } + + if (jobgp->perf_counter_l2_src1 == src1) + { + jobgp->perf_counter_l2_val1 = val1 - jobgp->perf_counter_l2_val1; + } + else + { + jobgp->perf_counter_l2_val1 = 0; + } + } +#endif + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(core->core_number), 0, 0, 0, 0, 0); /* add GP and L2 counters and return status? */ +#endif + + MALI_DEBUG_PRINT(2, ("Mali GP: Job aborted - userspace would not provide more heap memory.\n")); +#if MALI_STATE_TRACKING + _mali_osk_atomic_inc(&job->session->jobs_ended); +#endif + return JOB_STATUS_END_OOM; /* Core is ready for more jobs.*/ + } + /* finished ? */ + else if (0 == (core_status & MALIGP2_REG_VAL_STATUS_MASK_ACTIVE)) + { +#ifdef DEBUG + MALI_DEBUG_PRINT(4, ("Mali GP: Registers On job end:\n")); + maligp_print_regs(4, core); +#endif + MALI_DEBUG_PRINT_IF(5, irq_readout & 0x04, ("OOM when done, ignoring (reg.current = 0x%x, reg.end = 0x%x)\n", + (void*)mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR), + (void*)mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_END_ADDR)) + ); + + + if (0 != jobgp->user_input.perf_counter_flag ) + { + /* Readback the performance counters */ + if (jobgp->user_input.perf_counter_flag & (_MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE|_MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE) ) + { + jobgp->perf_counter0 = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_VALUE); + jobgp->perf_counter1 = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE); + +#if MALI_TIMELINE_PROFILING_ENABLED + /* Report the hardware counter values to the external profiler */ + _mali_osk_profiling_report_hw_counter(COUNTER_VP_C0, jobgp->perf_counter0); + _mali_osk_profiling_report_hw_counter(COUNTER_VP_C1, jobgp->perf_counter1); +#endif /* MALI_TIMELINE_PROFILING_ENABLED */ + } + +#if defined(USING_MALI400_L2_CACHE) + if (jobgp->user_input.perf_counter_flag & (_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC0_ENABLE|_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC1_ENABLE) ) + { + u32 src0; + u32 val0; + u32 src1; + u32 val1; + mali_kernel_l2_cache_get_perf_counters(&src0, &val0, &src1, &val1); + + if (jobgp->perf_counter_l2_src0 == src0) + { + jobgp->perf_counter_l2_val0 = val0 - jobgp->perf_counter_l2_val0; + } + else + { + jobgp->perf_counter_l2_val0 = 0; + } + + if (jobgp->perf_counter_l2_src1 == src1) + { + jobgp->perf_counter_l2_val1 = val1 - jobgp->perf_counter_l2_val1; + } + else + { + jobgp->perf_counter_l2_val1 = 0; + } + } +#endif + } + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(core->core_number), + jobgp->perf_counter0, jobgp->perf_counter1, + jobgp->user_input.perf_counter_src0 | (jobgp->user_input.perf_counter_src1 << 8) +#if defined(USING_MALI400_L2_CACHE) + | (jobgp->user_input.perf_counter_l2_src0 << 16) | (jobgp->user_input.perf_counter_l2_src1 << 24), + jobgp->perf_counter_l2_val0, + jobgp->perf_counter_l2_val1 +#else + ,0, 0 +#endif + ); +#endif + + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALIGP2_REG_VAL_IRQ_MASK_ALL); + +#if MALI_STATE_TRACKING + _mali_osk_atomic_inc(&job->session->jobs_ended); +#endif + return JOB_STATUS_END_SUCCESS; /* core idle */ + } + /* sw watchdog timeout handling or time to do hang checking ? */ + else if ( + (CORE_WATCHDOG_TIMEOUT == core->state) || + ( + (CORE_HANG_CHECK_TIMEOUT == core->state) && + ( + (jobgp->have_extended_progress_checking ? (mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE) == jobgp->vertices) : 1/*TRUE*/) && + ((core_status & MALIGP2_REG_VAL_STATUS_VS_ACTIVE) ? (vscl == jobgp->last_vscl) : 1/*TRUE*/) && + ((core_status & MALIGP2_REG_VAL_STATUS_PLBU_ACTIVE) ? (plbcl == jobgp->last_plbcl) : 1/*TRUE*/) + ) + ) + ) + { + /* no progress detected, killed by the watchdog */ + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(core->core_number), 0, 0, 0, 0, 0); /* add GP and L2 counters and return status? */ +#endif + + MALI_DEBUG_PRINT(1, ("Mali GP: SW-Timeout. Regs:\n")); + if (core_status & MALIGP2_REG_VAL_STATUS_VS_ACTIVE) MALI_DEBUG_PRINT(1, ("vscl current = 0x%x last = 0x%x\n", (void*)vscl, (void*)jobgp->last_vscl)); + if (core_status & MALIGP2_REG_VAL_STATUS_PLBU_ACTIVE) MALI_DEBUG_PRINT(1, ("plbcl current = 0x%x last = 0x%x\n", (void*)plbcl, (void*)jobgp->last_plbcl)); + if (jobgp->have_extended_progress_checking) MALI_DEBUG_PRINT(1, ("vertices processed = %d, last = %d\n", mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE), + jobgp->vertices)); +#ifdef DEBUG + maligp_print_regs(2, core); +#endif + +#if MALI_STATE_TRACKING + _mali_osk_atomic_inc(&job->session->jobs_ended); +#endif + + return JOB_STATUS_END_HANG; + } + /* if hang timeout checking was enabled and we detected progress, will be fall down to this check */ + /* check for PLBU OOM before the hang check to avoid the race condition of the hw wd trigging while waiting for us to handle the OOM interrupt */ + else if ( 0 != (irq_readout & MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM)) + { + mali_core_session *session; + _mali_osk_notification_t *notific; + _mali_uk_gp_job_suspended_s * suspended_job; + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SUSPEND|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(core->core_number), 0, 0, 0, 0, 0); /* add GP and L2 counters and return status? */ +#endif + + session = job->session; + + MALI_DEBUG_PRINT(4, ("OOM, new heap requested by GP\n")); + MALI_DEBUG_PRINT(4, ("Status when OOM: current = 0x%x, end = 0x%x\n", + (void*)mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR), + (void*)mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_END_ADDR)) + ); + + notific = _mali_osk_notification_create( + + _MALI_NOTIFICATION_GP_STALLED, + sizeof( _mali_uk_gp_job_suspended_s ) + ); + if ( NULL == notific) + { + MALI_PRINT_ERROR( ("Mali GP: Could not get notification object\n")) ; + return JOB_STATUS_END_OOM; /* Core is ready for more jobs.*/ + } + + core->state = CORE_WORKING; + jobgp->is_stalled_waiting_for_more_memory = 1; + suspended_job = (_mali_uk_gp_job_suspended_s *)notific->result_buffer; /* this is ok - result_buffer was malloc'd */ + + suspended_job->user_job_ptr = jobgp->user_input.user_job_ptr; + suspended_job->reason = _MALIGP_JOB_SUSPENDED_OUT_OF_MEMORY ; + suspended_job->cookie = (u32) core; + last_gp_core_cookie = core; + + _mali_osk_notification_queue_send( session->notification_queue, notific); + +#ifdef DEBUG + maligp_print_regs(4, core); +#endif + + /* stop all active timers */ + _mali_osk_timer_del( core->timer); + _mali_osk_timer_del( core->timer_hang_detection); + MALI_DEBUG_PRINT(4, ("Mali GP: PLBU heap empty, sending memory request to userspace\n")); + /* save to watchdog_jiffies what was remaining WD timeout value when OOM was triggered */ + job->watchdog_jiffies = (long)job->watchdog_jiffies - (long)_mali_osk_time_tickcount(); + /* reuse core->timer as the userspace response timeout handler */ + _mali_osk_timer_add( core->timer, _mali_osk_time_mstoticks(1000) ); /* wait max 1 sec for userspace to respond */ + return JOB_STATUS_CONTINUE_RUN; /* The core is NOT available for new jobs. */ + } + /* hw watchdog is reporting a new hang or an existing progress-during-hang check passed? */ + else if ((CORE_HANG_CHECK_TIMEOUT == core->state) || (irq_readout & jobgp->active_mask & MALIGP2_REG_VAL_IRQ_HANG)) + { + /* check interval in ms */ + u32 timeout = mali_core_hang_check_timeout_get(); + MALI_DEBUG_PRINT(3, ("Mali GP: HW/SW Watchdog triggered, checking for progress in %d ms\n", timeout)); + core->state = CORE_WORKING; + + /* save state for the progress checking */ + jobgp->last_vscl = vscl; + jobgp->last_plbcl = plbcl; + if (jobgp->have_extended_progress_checking) + { + jobgp->vertices = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE); + } + /* hw watchdog triggered, set up a progress checker every HANGCHECK ms */ + _mali_osk_timer_add( core->timer_hang_detection, _mali_osk_time_mstoticks(timeout)); + jobgp->active_mask &= ~MALIGP2_REG_VAL_IRQ_HANG; /* ignore the hw watchdog from now on */ + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, irq_readout); + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_MASK, jobgp->active_mask); + return JOB_STATUS_CONTINUE_RUN; /* not finihsed */ } + /* no errors, but still working */ + else if ( ( 0 == (core_status & MALIGP2_REG_VAL_STATUS_MASK_ERROR)) && + ( 0 != (core_status & MALIGP2_REG_VAL_STATUS_MASK_ACTIVE )) + ) + { + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, irq_readout); + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_MASK, jobgp->active_mask); + return JOB_STATUS_CONTINUE_RUN; + } + /* Else there must be some error */ + else + { +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(core->core_number), 0, 0, 0, 0, 0); /* add GP and L2 counters and return status? */ +#endif + + MALI_DEBUG_PRINT(1, ("Mali GP: Core crashed? *IRQ: 0x%x Status: 0x%x\n", irq_readout, core_status )); + #ifdef DEBUG + MALI_DEBUG_PRINT(1, ("Mali GP: Registers Before reset:\n")); + maligp_print_regs(1, core); + #endif +#if MALI_STATE_TRACKING + _mali_osk_atomic_inc(&job->session->jobs_ended); +#endif + return JOB_STATUS_END_UNKNOWN_ERR; + } +} + + +/* This function is called from the ioctl function and should return a mali_core_job pointer +to a created mali_core_job object with the data given from userspace */ +static _mali_osk_errcode_t subsystem_maligp_get_new_job_from_user(struct mali_core_session * session, void * argument) +{ + maligp_job *jobgp; + mali_core_job *job = NULL; + mali_core_job *previous_replaced_job; + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + _mali_uk_gp_start_job_s * user_ptr_job_input; + + user_ptr_job_input = (_mali_uk_gp_start_job_s *)argument; + + MALI_CHECK_NON_NULL(jobgp = (maligp_job *) _mali_osk_calloc(1, sizeof(maligp_job)), _MALI_OSK_ERR_FAULT); + + /* Copy the job data from the U/K interface */ + if ( NULL == _mali_osk_memcpy(&jobgp->user_input, user_ptr_job_input, sizeof(_mali_uk_gp_start_job_s) ) ) + { + MALI_PRINT_ERROR( ("Mali GP: Could not copy data from U/K interface.\n")) ; + err = _MALI_OSK_ERR_FAULT; + goto function_exit; + } + + MALI_DEBUG_PRINT(3, ("Mali GP: subsystem_maligp_get_new_job_from_user 0x%x\n", (void*)jobgp->user_input.user_job_ptr)); + + MALI_DEBUG_PRINT(3, ("Mali GP: Job Regs: 0x%08X 0x%08X, 0x%08X 0x%08X 0x%08X 0x%08X\n", + jobgp->user_input.frame_registers[0], + jobgp->user_input.frame_registers[1], + jobgp->user_input.frame_registers[2], + jobgp->user_input.frame_registers[3], + jobgp->user_input.frame_registers[4], + jobgp->user_input.frame_registers[5]) ); + + + job = GET_JOB_EMBEDDED_PTR(jobgp); + + job->session = session; + job->flags = MALI_UK_START_JOB_FLAG_DEFAULT; /* Current flags only make sence for PP jobs */ + job_priority_set(job, jobgp->user_input.priority); + job_watchdog_set(job, jobgp->user_input.watchdog_msecs ); + jobgp->heap_current_addr = jobgp->user_input.frame_registers[4]; + + job->abort_id = jobgp->user_input.abort_id; + + jobgp->is_stalled_waiting_for_more_memory = 0; + +#if MALI_TIMELINE_PROFILING_ENABLED + jobgp->pid = _mali_osk_get_pid(); + jobgp->tid = _mali_osk_get_tid(); +#endif + + if (mali_job_queue_full(session)) + { + /* Cause jobgp to free: */ + user_ptr_job_input->status = _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE; + goto function_exit; + } + + /* We now know that we have a job, and a slot to put it in */ + + jobgp->active_mask = MALIGP2_REG_VAL_IRQ_MASK_USED; + + /* Allocating User Return Data */ + jobgp->notification_obj = _mali_osk_notification_create( + _MALI_NOTIFICATION_GP_FINISHED, + sizeof(_mali_uk_gp_job_finished_s) ); + + if ( NULL == jobgp->notification_obj ) + { + MALI_PRINT_ERROR( ("Mali GP: Could not get notification_obj.\n")) ; + err = _MALI_OSK_ERR_NOMEM; + goto function_exit; + } + + _MALI_OSK_INIT_LIST_HEAD( &(job->list) ) ; + + MALI_DEBUG_PRINT(4, ("Mali GP: Job: 0x%08x INPUT from user.\n", (u32)jobgp->user_input.user_job_ptr)) ; + + /* This should not happen since we have the checking of priority above */ + err = mali_core_session_add_job(session, job, &previous_replaced_job); + if ( _MALI_OSK_ERR_OK != err ) + { + MALI_PRINT_ERROR( ("Mali GP: Internal error\n")) ; + /* Cause jobgp to free: */ + user_ptr_job_input->status = _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE; + _mali_osk_notification_delete( jobgp->notification_obj ); + goto function_exit; + } + + /* If MALI_TRUE: This session had a job with lower priority which were removed. + This replaced job is given back to userspace. */ + if ( NULL != previous_replaced_job ) + { + maligp_job *previous_replaced_jobgp; + + previous_replaced_jobgp = GET_JOBGP2_PTR(previous_replaced_job); + + MALI_DEBUG_PRINT(4, ("Mali GP: Replacing job: 0x%08x\n", (u32)previous_replaced_jobgp->user_input.user_job_ptr)) ; + + /* Copy to the input data (which also is output data) the + pointer to the job that were replaced, so that the userspace + driver can put this job in the front of its job-queue */ + user_ptr_job_input->returned_user_job_ptr = previous_replaced_jobgp->user_input.user_job_ptr; + + /** @note failure to 'copy to user' at this point must not free jobgp, + * and so no transaction rollback required in the U/K interface */ + + /* This does not cause jobgp to free: */ + user_ptr_job_input->status = _MALI_UK_START_JOB_STARTED_LOW_PRI_JOB_RETURNED; + MALI_DEBUG_PRINT(5, ("subsystem_maligp_get_new_job_from_user: Job added, prev returned\n")) ; + } + else + { + /* This does not cause jobgp to free: */ + user_ptr_job_input->status = _MALI_UK_START_JOB_STARTED; + MALI_DEBUG_PRINT(5, ("subsystem_maligp_get_new_job_from_user: Job added\n")) ; + } + +function_exit: + if ( _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE == user_ptr_job_input->status + || _MALI_OSK_ERR_OK != err ) + { + _mali_osk_free(jobgp); + } +#if MALI_STATE_TRACKING + if (_MALI_UK_START_JOB_STARTED==user_ptr_job_input->status) + { + if(job) + { + job->job_nr=_mali_osk_atomic_inc_return(&session->jobs_received); + } + } +#endif + + MALI_ERROR(err); +} + + +static _mali_osk_errcode_t subsystem_maligp_suspend_response(struct mali_core_session * session, void * argument) +{ + mali_core_renderunit *core; + maligp_job *jobgp; + mali_core_job *job; + + _mali_uk_gp_suspend_response_s * suspend_response; + + MALI_DEBUG_PRINT(5, ("subsystem_maligp_suspend_response\n")); + + suspend_response = (_mali_uk_gp_suspend_response_s *)argument; + + /* We read job data from User */ + /* On a single mali_gp system we can only have one Stalled GP, + and therefore one stalled request with a cookie. This checks + that we get the correct cookie */ + if ( last_gp_core_cookie != (mali_core_renderunit *)suspend_response->cookie ) + { + MALI_DEBUG_PRINT(2, ("Mali GP: Got an illegal cookie from Userspace.\n")) ; + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + core = (mali_core_renderunit *)suspend_response->cookie; + last_gp_core_cookie = NULL; + job = core->current_job; + jobgp = GET_JOBGP2_PTR(job); + + switch( suspend_response->code ) + { + case _MALIGP_JOB_RESUME_WITH_NEW_HEAP : + MALI_DEBUG_PRINT(5, ("MALIGP_JOB_RESUME_WITH_NEW_HEAP jiffies: %li\n", _mali_osk_time_tickcount())); + MALI_DEBUG_PRINT(4, ("New Heap addr 0x%08x - 0x%08x\n", suspend_response->arguments[0], suspend_response->arguments[1])); + + jobgp->is_stalled_waiting_for_more_memory = 0; + job->watchdog_jiffies += _mali_osk_time_tickcount(); /* convert to absolute time again */ + _mali_osk_timer_mod( core->timer, job->watchdog_jiffies); /* update the timer */ + + + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, (MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM | MALIGP2_REG_VAL_IRQ_HANG)); + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_MASK, jobgp->active_mask); + mali_core_renderunit_register_write_relaxed(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR, suspend_response->arguments[0]); + mali_core_renderunit_register_write_relaxed(core, MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_END_ADDR, suspend_response->arguments[1]); + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_CMD, MALIGP2_REG_VAL_CMD_UPDATE_PLBU_ALLOC); + _mali_osk_write_mem_barrier(); + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_RESUME|MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(core->core_number), 0, 0, 0, 0, 0); +#endif + + MALI_DEBUG_PRINT(4, ("GP resumed with new heap\n")); + + break; + + case _MALIGP_JOB_ABORT: + MALI_DEBUG_PRINT(3, ("MALIGP_JOB_ABORT on heap extend request\n")); + _mali_osk_irq_schedulework( core->irq ); + break; + + default: + MALI_PRINT_ERROR(("Wrong Suspend response from userspace\n")); + } + MALI_SUCCESS; +} + +/* This function is called from the ioctl function and should write the necessary data +to userspace telling which job was finished and the status and debuginfo for this job. +The function must also free and cleanup the input job object. */ +static void subsystem_maligp_return_job_to_user( mali_core_job * job, mali_subsystem_job_end_code end_status ) +{ + maligp_job *jobgp; + _mali_uk_gp_job_finished_s * job_out; + _mali_uk_gp_start_job_s* job_input; + mali_core_session *session; + + + jobgp = _MALI_OSK_CONTAINER_OF(job, maligp_job, embedded_core_job); + job_out = (_mali_uk_gp_job_finished_s *)jobgp->notification_obj->result_buffer; /* OK - this should've been malloc'd */ + job_input= &(jobgp->user_input); + session = job->session; + + MALI_DEBUG_PRINT(5, ("Mali GP: Job: 0x%08x OUTPUT to user. Runtime: %d us, irq readout %x\n", + (u32)jobgp->user_input.user_job_ptr, + job->render_time_usecs, + jobgp->irq_status)) ; + + _mali_osk_memset(job_out, 0 , sizeof(_mali_uk_gp_job_finished_s)); + + job_out->user_job_ptr = job_input->user_job_ptr; + + switch( end_status ) + { + case JOB_STATUS_CONTINUE_RUN: + case JOB_STATUS_END_SUCCESS: + case JOB_STATUS_END_OOM: + case JOB_STATUS_END_ABORT: + case JOB_STATUS_END_TIMEOUT_SW: + case JOB_STATUS_END_HANG: + case JOB_STATUS_END_SEG_FAULT: + case JOB_STATUS_END_ILLEGAL_JOB: + case JOB_STATUS_END_UNKNOWN_ERR: + case JOB_STATUS_END_SHUTDOWN: + case JOB_STATUS_END_SYSTEM_UNUSABLE: + job_out->status = (mali_subsystem_job_end_code) end_status; + break; + default: + job_out->status = JOB_STATUS_END_UNKNOWN_ERR ; + } + + job_out->irq_status = jobgp->irq_status; + job_out->status_reg_on_stop = jobgp->status_reg_on_stop; + job_out->vscl_stop_addr = 0; + job_out->plbcl_stop_addr = 0; + job_out->heap_current_addr = jobgp->heap_current_addr; + job_out->perf_counter0 = jobgp->perf_counter0; + job_out->perf_counter1 = jobgp->perf_counter1; + job_out->perf_counter_src0 = jobgp->user_input.perf_counter_src0 ; + job_out->perf_counter_src1 = jobgp->user_input.perf_counter_src1 ; + job_out->render_time = job->render_time_usecs; +#if defined(USING_MALI400_L2_CACHE) + job_out->perf_counter_l2_src0 = jobgp->perf_counter_l2_src0; + job_out->perf_counter_l2_src1 = jobgp->perf_counter_l2_src1; + job_out->perf_counter_l2_val0 = jobgp->perf_counter_l2_val0; + job_out->perf_counter_l2_val1 = jobgp->perf_counter_l2_val1; +#endif + +#if MALI_STATE_TRACKING + _mali_osk_atomic_inc(&session->jobs_returned); +#endif + _mali_osk_notification_queue_send( session->notification_queue, jobgp->notification_obj); + jobgp->notification_obj = NULL; + + _mali_osk_free(jobgp); + + last_gp_core_cookie = NULL; +} + +static void subsystem_maligp_renderunit_delete(mali_core_renderunit * core) +{ + MALI_DEBUG_PRINT(5, ("Mali GP: maligp_renderunit_delete\n")); + _mali_osk_free(core); +} + +static void subsystem_maligp_renderunit_reset_core(struct mali_core_renderunit * core, mali_core_reset_style style) +{ + MALI_DEBUG_PRINT(5, ("Mali GP: renderunit_reset_core\n")); + + switch (style) + { + case MALI_CORE_RESET_STYLE_RUNABLE: + maligp_reset(core); + break; + case MALI_CORE_RESET_STYLE_DISABLE: + maligp_raw_reset(core); /* do the raw reset */ + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_MASK, 0); /* then disable the IRQs */ + break; + case MALI_CORE_RESET_STYLE_HARD: + maligp_reset_hard(core); + maligp_initialize_registers_mgmt(core); + break; + default: + MALI_DEBUG_PRINT(1, ("Unknown reset type %d\n", style)); + break; + } +} + +static void subsystem_maligp_renderunit_probe_core_irq_trigger(struct mali_core_renderunit* core) +{ + mali_core_renderunit_register_write(core , MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_VAL_IRQ_MASK_USED); + mali_core_renderunit_register_write(core , MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT, MALIGP2_REG_VAL_CMD_FORCE_HANG ); + _mali_osk_mem_barrier(); +} + +static _mali_osk_errcode_t subsystem_maligp_renderunit_probe_core_irq_finished(struct mali_core_renderunit* core) +{ + u32 irq_readout; + + irq_readout = mali_core_renderunit_register_read(core, MALIGP2_REG_ADDR_MGMT_INT_STAT); + + if ( MALIGP2_REG_VAL_IRQ_FORCE_HANG & irq_readout ) + { + mali_core_renderunit_register_write(core, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, MALIGP2_REG_VAL_IRQ_FORCE_HANG); + _mali_osk_mem_barrier(); + MALI_SUCCESS; + } + + MALI_ERROR(_MALI_OSK_ERR_FAULT); +} + +_mali_osk_errcode_t _mali_ukk_gp_start_job( _mali_uk_gp_start_job_s *args ) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_gp_id); + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_FAULT); + return mali_core_subsystem_ioctl_start_job(session, args); +} + +_mali_osk_errcode_t _mali_ukk_get_gp_number_of_cores( _mali_uk_get_gp_number_of_cores_s *args ) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_gp_id); + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_FAULT); + return mali_core_subsystem_ioctl_number_of_cores_get(session, &args->number_of_cores); +} + +_mali_osk_errcode_t _mali_ukk_get_gp_core_version( _mali_uk_get_gp_core_version_s *args ) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_gp_id); + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_FAULT); + return mali_core_subsystem_ioctl_core_version_get(session, &args->version); +} + +_mali_osk_errcode_t _mali_ukk_gp_suspend_response( _mali_uk_gp_suspend_response_s *args ) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_gp_id); + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_FAULT); + return mali_core_subsystem_ioctl_suspend_response(session, args); +} + +void _mali_ukk_gp_abort_job( _mali_uk_gp_abort_job_s * args) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + if (NULL == args->ctx) return; + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_gp_id); + if (NULL == session) return; + mali_core_subsystem_ioctl_abort_job(session, args->abort_id); + +} + +#if USING_MALI_PMM + +_mali_osk_errcode_t maligp_signal_power_up( mali_bool queue_only ) +{ + MALI_DEBUG_PRINT(4, ("Mali GP: signal power up core - queue_only: %d\n", queue_only )); + + return( mali_core_subsystem_signal_power_up( &subsystem_maligp, 0, queue_only ) ); +} + +_mali_osk_errcode_t maligp_signal_power_down( mali_bool immediate_only ) +{ + MALI_DEBUG_PRINT(4, ("Mali GP: signal power down core - immediate_only: %d\n", immediate_only )); + + return( mali_core_subsystem_signal_power_down( &subsystem_maligp, 0, immediate_only ) ); +} + +#endif + +#if MALI_STATE_TRACKING +u32 maligp_subsystem_dump_state(char *buf, u32 size) +{ + return mali_core_renderunit_dump_state(&subsystem_maligp, buf, size); +} +#endif diff --git a/drivers/gpu/arm/mali/common/mali_kernel_MALI200.c b/drivers/gpu/arm/mali/common/mali_kernel_MALI200.c new file mode 100644 index 000000000000..7bce5862a9d4 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_MALI200.c @@ -0,0 +1,1286 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_kernel_pp.h" +#include "mali_kernel_subsystem.h" +#include "mali_kernel_core.h" +#include "regs/mali_200_regs.h" +#include "mali_kernel_rendercore.h" +#if MALI_TIMELINE_PROFILING_ENABLED +#include "mali_osk_profiling.h" +#endif +#ifdef USING_MALI400_L2_CACHE +#include "mali_kernel_l2_cache.h" +#endif +#if USING_MMU +#include "mali_kernel_mem_mmu.h" /* Needed for mali_kernel_mmu_force_bus_reset() */ +#endif + +#include "mali_osk_list.h" + +#if defined(USING_MALI200) +#define MALI_PP_SUBSYSTEM_NAME "Mali200" +#define MALI_PP_CORE_TYPE _MALI_200 +#elif defined(USING_MALI400) +#define MALI_PP_SUBSYSTEM_NAME "Mali-400 PP" +#define MALI_PP_CORE_TYPE _MALI_400_PP +#else +#error "No supported mali core defined" +#endif + +#define GET_JOB_EMBEDDED_PTR(job) (&((job)->embedded_core_job)) +#define GET_JOB200_PTR(job_extern) _MALI_OSK_CONTAINER_OF(job_extern, mali200_job, embedded_core_job) + +/* Initialized when this subsystem is initialized. This is determined by the + * position in subsystems[], and so the value used to initialize this is + * determined at compile time */ +static mali_kernel_subsystem_identifier mali_subsystem_mali200_id = -1; + +/* Describing a mali200 job settings */ +typedef struct mali200_job +{ + /* The general job struct common for all mali cores */ + mali_core_job embedded_core_job; + _mali_uk_pp_start_job_s user_input; + + u32 irq_status; + u32 perf_counter0; + u32 perf_counter1; + u32 last_tile_list_addr; /* Neccessary to continue a stopped job */ + + u32 active_mask; + + /* The data we will return back to the user */ + _mali_osk_notification_t *notification_obj; + +#if defined(USING_MALI400_L2_CACHE) + u32 perf_counter_l2_src0; + u32 perf_counter_l2_src1; + u32 perf_counter_l2_val0; + u32 perf_counter_l2_val1; + u32 perf_counter_l2_val0_raw; + u32 perf_counter_l2_val1_raw; +#endif + +#if MALI_TIMELINE_PROFILING_ENABLED + u32 pid; + u32 tid; +#endif +} mali200_job; + + +/*Functions Exposed to the General External System through + funciont pointers.*/ + +static _mali_osk_errcode_t mali200_subsystem_startup(mali_kernel_subsystem_identifier id); +#if USING_MMU +static _mali_osk_errcode_t mali200_subsystem_mmu_connect(mali_kernel_subsystem_identifier id); +#endif +static void mali200_subsystem_terminate(mali_kernel_subsystem_identifier id); +static _mali_osk_errcode_t mali200_subsystem_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue); +static void mali200_subsystem_session_end(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot); +static _mali_osk_errcode_t mali200_subsystem_core_system_info_fill(_mali_system_info* info); +static _mali_osk_errcode_t mali200_renderunit_create(_mali_osk_resource_t * resource); +#if USING_MMU +static void mali200_subsystem_broadcast_notification(mali_core_notification_message message, u32 data); +#endif +#if MALI_STATE_TRACKING +u32 mali200_subsystem_dump_state(char *buf, u32 size); +#endif + +/* Internal support functions */ +static _mali_osk_errcode_t mali200_core_version_legal( mali_core_renderunit *core ); +static void mali200_reset(mali_core_renderunit *core); +static void mali200_reset_hard(struct mali_core_renderunit * core); +static void mali200_raw_reset(mali_core_renderunit * core); +static void mali200_initialize_registers_mgmt(mali_core_renderunit *core ); + +/* Functions exposed to mali_core system through functionpointers + in the subsystem struct. */ +static _mali_osk_errcode_t subsystem_mali200_start_job(mali_core_job * job, mali_core_renderunit * core); +static _mali_osk_errcode_t subsystem_mali200_get_new_job_from_user(struct mali_core_session * session, void * argument); +static void subsystem_mali200_return_job_to_user( mali_core_job * job, mali_subsystem_job_end_code end_status); +static void subsystem_mali200_renderunit_delete(mali_core_renderunit * core); +static void subsystem_mali200_renderunit_reset_core(struct mali_core_renderunit * core, mali_core_reset_style style); +static void subsystem_mali200_renderunit_probe_core_irq_trigger(struct mali_core_renderunit* core); +static _mali_osk_errcode_t subsystem_mali200_renderunit_probe_core_irq_finished(struct mali_core_renderunit* core); + +static void subsystem_mali200_renderunit_stop_bus(struct mali_core_renderunit* core); +static u32 subsystem_mali200_irq_handler_upper_half(struct mali_core_renderunit * core); +static int subsystem_mali200_irq_handler_bottom_half(struct mali_core_renderunit* core); + +/* This will be one of the subsystems in the array of subsystems: + static struct mali_kernel_subsystem * subsystems[]; + found in file: mali_kernel_core.c +*/ + +struct mali_kernel_subsystem mali_subsystem_mali200= +{ + mali200_subsystem_startup, /* startup */ + mali200_subsystem_terminate, /* shutdown */ +#if USING_MMU + mali200_subsystem_mmu_connect, /* load_complete */ +#else + NULL, +#endif + mali200_subsystem_core_system_info_fill, /* system_info_fill */ + mali200_subsystem_session_begin, /* session_begin */ + mali200_subsystem_session_end, /* session_end */ +#if USING_MMU + mali200_subsystem_broadcast_notification, /* broadcast_notification */ +#else + NULL, +#endif +#if MALI_STATE_TRACKING + mali200_subsystem_dump_state, /* dump_state */ +#endif +} ; + +static mali_core_subsystem subsystem_mali200 ; + +static _mali_osk_errcode_t mali200_subsystem_startup(mali_kernel_subsystem_identifier id) +{ + mali_core_subsystem * subsystem; + + MALI_DEBUG_PRINT(3, ("Mali PP: mali200_subsystem_startup\n") ) ; + + mali_subsystem_mali200_id = id; + + /* All values get 0 as default */ + _mali_osk_memset(&subsystem_mali200, 0, sizeof(subsystem_mali200)); + + subsystem = &subsystem_mali200; + subsystem->start_job = &subsystem_mali200_start_job; + subsystem->irq_handler_upper_half = &subsystem_mali200_irq_handler_upper_half; + subsystem->irq_handler_bottom_half = &subsystem_mali200_irq_handler_bottom_half; + subsystem->get_new_job_from_user = &subsystem_mali200_get_new_job_from_user; + subsystem->return_job_to_user = &subsystem_mali200_return_job_to_user; + subsystem->renderunit_delete = &subsystem_mali200_renderunit_delete; + subsystem->reset_core = &subsystem_mali200_renderunit_reset_core; + subsystem->stop_bus = &subsystem_mali200_renderunit_stop_bus; + subsystem->probe_core_irq_trigger = &subsystem_mali200_renderunit_probe_core_irq_trigger; + subsystem->probe_core_irq_acknowledge = &subsystem_mali200_renderunit_probe_core_irq_finished; + + /* Setting variables in the general core part of the subsystem.*/ + subsystem->name = MALI_PP_SUBSYSTEM_NAME; + subsystem->core_type = MALI_PP_CORE_TYPE; + subsystem->id = id; + + /* Initiates the rest of the general core part of the subsystem */ + MALI_CHECK_NO_ERROR(mali_core_subsystem_init( subsystem )); + + /* This will register the function for adding MALI200 cores to the subsystem */ +#if defined(USING_MALI200) + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(MALI200, mali200_renderunit_create)); +#endif +#if defined(USING_MALI400) + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(MALI400PP, mali200_renderunit_create)); +#endif + + MALI_DEBUG_PRINT(6, ("Mali PP: mali200_subsystem_startup\n") ) ; + + MALI_SUCCESS; +} + +#if USING_MMU +static _mali_osk_errcode_t mali200_subsystem_mmu_connect(mali_kernel_subsystem_identifier id) +{ + mali_core_subsystem_attach_mmu(&subsystem_mali200); + MALI_SUCCESS; /* OK */ +} +#endif + +static void mali200_subsystem_terminate(mali_kernel_subsystem_identifier id) +{ + MALI_DEBUG_PRINT(3, ("Mali PP: mali200_subsystem_terminate\n") ) ; + mali_core_subsystem_cleanup(&subsystem_mali200); +} + +static _mali_osk_errcode_t mali200_subsystem_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue) +{ + mali_core_session * session; + + MALI_DEBUG_PRINT(3, ("Mali PP: mali200_subsystem_session_begin\n") ) ; + MALI_CHECK_NON_NULL(session = _mali_osk_malloc( sizeof(mali_core_session) ), _MALI_OSK_ERR_NOMEM); + + _mali_osk_memset(session, 0, sizeof(*session) ); + *slot = (mali_kernel_subsystem_session_slot)session; + + session->subsystem = &subsystem_mali200; + + session->notification_queue = queue; + +#if USING_MMU + session->mmu_session = mali_session_data; +#endif + + mali_core_session_begin(session); + + MALI_DEBUG_PRINT(6, ("Mali PP: mali200_subsystem_session_begin\n") ) ; + + MALI_SUCCESS; +} + +static void mali200_subsystem_session_end(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot) +{ + mali_core_session * session; + + MALI_DEBUG_PRINT(3, ("Mali PP: mali200_subsystem_session_end\n") ) ; + if ( NULL==slot || NULL==*slot) + { + MALI_PRINT_ERROR(("Input slot==NULL")); + return; + } + session = (mali_core_session*) *slot; + mali_core_session_close(session); + + _mali_osk_free(session); + *slot = NULL; + + MALI_DEBUG_PRINT(6, ("Mali PP: mali200_subsystem_session_end\n") ) ; +} + +/** + * We fill in info about all the cores we have + * @param info Pointer to system info struct to update + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali200_subsystem_core_system_info_fill(_mali_system_info* info) +{ + return mali_core_subsystem_system_info_fill(&subsystem_mali200, info); +} + + +static _mali_osk_errcode_t mali200_renderunit_create(_mali_osk_resource_t * resource) +{ + mali_core_renderunit *core; + _mali_osk_errcode_t err; + + MALI_DEBUG_PRINT(3, ("Mali PP: mali200_renderunit_create\n") ) ; + /* Checking that the resource settings are correct */ +#if defined(USING_MALI200) + if(MALI200 != resource->type) + { + MALI_PRINT_ERROR(("Can not register this resource as a " MALI_PP_SUBSYSTEM_NAME " core.")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } +#elif defined(USING_MALI400) + if(MALI400PP != resource->type) + { + MALI_PRINT_ERROR(("Can not register this resource as a " MALI_PP_SUBSYSTEM_NAME " core.")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } +#endif + if ( 0 != resource->size ) + { + MALI_PRINT_ERROR(("Memory size set to " MALI_PP_SUBSYSTEM_NAME " core should be zero.")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + if ( NULL == resource->description ) + { + MALI_PRINT_ERROR(("A " MALI_PP_SUBSYSTEM_NAME " core needs a unique description field")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* Create a new core object */ + core = (mali_core_renderunit*) _mali_osk_malloc(sizeof(*core)); + if ( NULL == core ) + { + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + /* Variables set to be able to open and register the core */ + core->subsystem = &subsystem_mali200 ; + core->registers_base_addr = resource->base ; + core->size = MALI200_REG_SIZEOF_REGISTER_BANK ; + core->irq_nr = resource->irq ; + core->description = resource->description; +#if USING_MMU + core->mmu_id = resource->mmu_id; + core->mmu = NULL; +#endif +#if USING_MALI_PMM + /* Set up core's PMM id */ + switch( subsystem_mali200.number_of_cores ) + { + case 0: + core->pmm_id = MALI_PMM_CORE_PP0; + break; + case 1: + core->pmm_id = MALI_PMM_CORE_PP1; + break; + case 2: + core->pmm_id = MALI_PMM_CORE_PP2; + break; + case 3: + core->pmm_id = MALI_PMM_CORE_PP3; + break; + default: + MALI_DEBUG_PRINT(1, ("Unknown supported core for PMM\n")); + err = _MALI_OSK_ERR_FAULT; + goto exit_on_error0; + } +#endif + + err = mali_core_renderunit_init( core ); + if (_MALI_OSK_ERR_OK != err) + { + MALI_DEBUG_PRINT(1, ("Failed to initialize renderunit\n")); + goto exit_on_error0; + } + + /* Map the new core object, setting: core->registers_mapped */ + err = mali_core_renderunit_map_registers(core); + if (_MALI_OSK_ERR_OK != err) + { + MALI_DEBUG_PRINT(1, ("Failed to map register\n")); + goto exit_on_error1; + } + + /* Check that the register mapping of the core works. + Return 0 if Mali PP core is present and accessible. */ + if (mali_benchmark) { +#if defined(USING_MALI200) + core->core_version = (((u32)MALI_PP_PRODUCT_ID) << 16) | 5 /* Fake Mali200-r0p5 */; +#elif defined(USING_MALI400) + core->core_version = (((u32)MALI_PP_PRODUCT_ID) << 16) | 0x0101 /* Fake Mali400-r1p1 */; +#else +#error "No supported mali core defined" +#endif + } else { + core->core_version = mali_core_renderunit_register_read( + core, + MALI200_REG_ADDR_MGMT_VERSION); + } + + err = mali200_core_version_legal(core); + if (_MALI_OSK_ERR_OK != err) + { + MALI_DEBUG_PRINT(1, ("Invalid core\n")); + goto exit_on_error2; + } + + /* Reset the core. Put the core into a state where it can start to render. */ + mali200_reset(core); + + /* Registering IRQ, init the work_queue_irq_handle */ + /* Adding this core as an available renderunit in the subsystem. */ + err = mali_core_subsystem_register_renderunit(&subsystem_mali200, core); + if (_MALI_OSK_ERR_OK != err) + { + MALI_DEBUG_PRINT(1, ("Failed to register with core\n")); + goto exit_on_error2; + } + MALI_DEBUG_PRINT(6, ("Mali PP: mali200_renderunit_create\n") ) ; + + MALI_SUCCESS; + +exit_on_error2: + mali_core_renderunit_unmap_registers(core); +exit_on_error1: + mali_core_renderunit_term(core); +exit_on_error0: + _mali_osk_free( core ) ; + MALI_PRINT_ERROR(("Renderunit NOT created.")); + MALI_ERROR(err); +} + +#if USING_MMU +/* Used currently only for signalling when MMU has a pagefault */ +static void mali200_subsystem_broadcast_notification(mali_core_notification_message message, u32 data) +{ + mali_core_subsystem_broadcast_notification(&subsystem_mali200, message, data); +} +#endif + +static _mali_osk_errcode_t mali200_core_version_legal( mali_core_renderunit *core ) +{ + u32 mali_type; + + mali_type = core->core_version >> 16; +#if defined(USING_MALI400) + /* Mali300 and Mali400 is compatible, accept either core. */ + if (MALI400_PP_PRODUCT_ID != mali_type && MALI300_PP_PRODUCT_ID != mali_type) +#else + if (MALI_PP_PRODUCT_ID != mali_type) +#endif + { + MALI_PRINT_ERROR(("Error: reading this from " MALI_PP_SUBSYSTEM_NAME " version register: 0x%x\n", core->core_version)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + MALI_DEBUG_PRINT(3, ("Mali PP: core_version_legal: Reads correct mali version: %d\n", mali_type) ) ; + MALI_SUCCESS; +} + +static void subsystem_mali200_renderunit_stop_bus(struct mali_core_renderunit* core) +{ + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI200_REG_VAL_CTRL_MGMT_STOP_BUS); +} + +static void mali200_raw_reset( mali_core_renderunit *core ) +{ + int i; + const int request_loop_count = 20; + + MALI_DEBUG_PRINT(4, ("Mali PP: mali200_raw_reset: %s\n", core->description)); + if (mali_benchmark) return; + + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_MASK, 0); /* disable IRQs */ + +#if defined(USING_MALI200) + + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI200_REG_VAL_CTRL_MGMT_STOP_BUS); + + for (i = 0; i < request_loop_count; i++) + { + if (mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_STATUS) & MALI200_REG_VAL_STATUS_BUS_STOPPED) break; + _mali_osk_time_ubusydelay(10); + } + + MALI_DEBUG_PRINT_IF(1, request_loop_count == i, ("Mali PP: Bus was never stopped during core reset\n")); + + + if (request_loop_count==i) + { +#if USING_MMU + if ((NULL!=core->mmu) && (MALI_FALSE == core->error_recovery)) + { + /* Could not stop bus connections from core, probably because some of the already pending + bus request has had a page fault, and therefore can not complete before the MMU does PageFault + handling. This can be treated as a heavier reset function - which unfortunately reset all + the cores on this MMU in addition to the MMU itself */ + MALI_DEBUG_PRINT(1, ("Mali PP: Forcing Bus reset\n")); + mali_kernel_mmu_force_bus_reset(core->mmu); + return; + } +#endif + MALI_PRINT(("A MMU reset did not allow PP to stop its bus, system failure, unable to recover\n")); + return; + } + + /* use the hard reset routine to do the actual reset */ + mali200_reset_hard(core); + +#elif defined(USING_MALI400) + + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_CLEAR, MALI400PP_REG_VAL_IRQ_RESET_COMPLETED); + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI400PP_REG_VAL_CTRL_MGMT_SOFT_RESET); + + for (i = 0; i < request_loop_count; i++) + { + if (mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_INT_RAWSTAT) & MALI400PP_REG_VAL_IRQ_RESET_COMPLETED) break; + _mali_osk_time_ubusydelay(10); + } + + if (request_loop_count==i) + { +#if USING_MMU + if ((NULL!=core->mmu) && (MALI_FALSE == core->error_recovery)) + { + /* Could not stop bus connections from core, probably because some of the already pending + bus request has had a page fault, and therefore can not complete before the MMU does PageFault + handling. This can be treated as a heavier reset function - which unfortunately reset all + the cores on this MMU in addition to the MMU itself */ + MALI_DEBUG_PRINT(1, ("Mali PP: Forcing Bus reset\n")); + mali_kernel_mmu_force_bus_reset(core->mmu); + return; + } +#endif + MALI_PRINT(("A MMU reset did not allow PP to stop its bus, system failure, unable to recover\n")); + return; + } + else + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_CLEAR, MALI200_REG_VAL_IRQ_MASK_ALL); + +#else +#error "no supported mali core defined" +#endif +} + +static void mali200_reset( mali_core_renderunit *core ) +{ + if (!mali_benchmark) { + mali200_raw_reset(core); + mali200_initialize_registers_mgmt(core); + } +} + +/* Sets the registers on mali200 according to the const default_mgmt_regs array. */ +static void mali200_initialize_registers_mgmt(mali_core_renderunit *core ) +{ + MALI_DEBUG_PRINT(6, ("Mali PP: mali200_initialize_registers_mgmt: %s\n", core->description)) ; + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_USED); +} + +/* Start this job on this core. Return MALI_TRUE if the job was started. */ +static _mali_osk_errcode_t subsystem_mali200_start_job(mali_core_job * job, mali_core_renderunit * core) +{ + mali200_job *job200; + + /* The local extended version of the general structs */ + job200 = _MALI_OSK_CONTAINER_OF(job, mali200_job, embedded_core_job); + + if ( (0 == job200->user_input.frame_registers[0]) || + (0 == job200->user_input.frame_registers[1]) ) + { + MALI_DEBUG_PRINT(4, ("Mali PP: Job: 0x%08x WILL NOT START SINCE JOB HAS ILLEGAL ADDRESSES\n", + (u32)job200->user_input.user_job_ptr)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + MALI_DEBUG_PRINT(4, ("Mali PP: Job: 0x%08x START_RENDER Tile_list: 0x%08x\n", + (u32)job200->user_input.user_job_ptr, + job200->user_input.frame_registers[0])); + MALI_DEBUG_PRINT(6, ("Mali PP: RSW base addr: 0x%08x Vertex base addr: 0x%08x\n", + job200->user_input.frame_registers[1], job200->user_input.frame_registers[2])); + + /* Frame registers. Copy from mem to physical registers */ + mali_core_renderunit_register_write_array( + core, + MALI200_REG_ADDR_FRAME, + &(job200->user_input.frame_registers[0]), + MALI200_NUM_REGS_FRAME); + + /* Write Back unit 0. Copy from mem to physical registers only if the WB unit will be used. */ + if (job200->user_input.wb0_registers[0]) + { + mali_core_renderunit_register_write_array( + core, + MALI200_REG_ADDR_WB0, + &(job200->user_input.wb0_registers[0]), + MALI200_NUM_REGS_WBx); + } + + /* Write Back unit 1. Copy from mem to physical registers only if the WB unit will be used. */ + if (job200->user_input.wb1_registers[0]) + { + mali_core_renderunit_register_write_array( + core, + MALI200_REG_ADDR_WB1, + &(job200->user_input.wb1_registers[0]), + MALI200_NUM_REGS_WBx); + } + + /* Write Back unit 2. Copy from mem to physical registers only if the WB unit will be used. */ + if (job200->user_input.wb2_registers[0]) + { + mali_core_renderunit_register_write_array( + core, + MALI200_REG_ADDR_WB2, + &(job200->user_input.wb2_registers[0]), + MALI200_NUM_REGS_WBx); + } + +#if MALI_TIMELINE_PROFILING_ENABLED + /* + * If the hardware counters are not turned on, ask the external profiler + * if they should be. + */ + if (job200->user_input.perf_counter_flag == 0) + { + /* + * Work out the correct counter offset to use. Each fragment processor + * has two hardware counters. + */ + u32 counter0_offset = MALI_PROFILING_PP_CORE_COUNTER0_OFFSET(core->core_number); + u32 counter1_offset = MALI_PROFILING_PP_CORE_COUNTER1_OFFSET(core->core_number); + + mali_bool src0_enabled = _mali_osk_profiling_query_hw_counter(counter0_offset, + &(job200->user_input.perf_counter_src0)); + mali_bool src1_enabled = _mali_osk_profiling_query_hw_counter(counter1_offset, + &(job200->user_input.perf_counter_src1)); + + if (src0_enabled == MALI_TRUE) + { + job200->user_input.perf_counter_flag |= + _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE; + } + + if (src1_enabled == MALI_TRUE) + { + job200->user_input.perf_counter_flag |= + _MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE; + } + } +#endif /* MALI_TIMELINE_PROFILING_ENABLED */ + + /* This selects which performance counters we are reading */ + if ( 0 != job200->user_input.perf_counter_flag ) + { + if ( job200->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE) + { + mali_core_renderunit_register_write_relaxed( + core, + MALI200_REG_ADDR_MGMT_PERF_CNT_0_ENABLE, + MALI200_REG_VAL_PERF_CNT_ENABLE); + mali_core_renderunit_register_write_relaxed( + core, + MALI200_REG_ADDR_MGMT_PERF_CNT_0_SRC, + job200->user_input.perf_counter_src0); + + } + + if ( job200->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE) + { + mali_core_renderunit_register_write_relaxed( + core, + MALI200_REG_ADDR_MGMT_PERF_CNT_1_ENABLE, + MALI200_REG_VAL_PERF_CNT_ENABLE); + mali_core_renderunit_register_write_relaxed( + core, + MALI200_REG_ADDR_MGMT_PERF_CNT_1_SRC, + job200->user_input.perf_counter_src1); + + } + +#if defined(USING_MALI400_L2_CACHE) + if ( job200->user_input.perf_counter_flag & (_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC0_ENABLE|_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC1_ENABLE) ) + { + int force_reset = ( job200->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_L2_RESET ) ? 1 : 0; + u32 src0 = 0; + u32 src1 = 0; + + if ( job200->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC0_ENABLE ) + { + src0 = job200->user_input.perf_counter_l2_src0; + } + if ( job200->user_input.perf_counter_flag & _MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC1_ENABLE ) + { + src1 = job200->user_input.perf_counter_l2_src1; + } + + mali_kernel_l2_cache_set_perf_counters(src0, src1, force_reset); /* will activate and possibly reset counters */ + + /* Now, retrieve the current values, so we can substract them when the job has completed */ + mali_kernel_l2_cache_get_perf_counters(&job200->perf_counter_l2_src0, + &job200->perf_counter_l2_val0, + &job200->perf_counter_l2_src1, + &job200->perf_counter_l2_val1); + } +#endif + } + + subsystem_flush_mapped_mem_cache(); + +#if MALI_STATE_TRACKING + _mali_osk_atomic_inc(&job->session->jobs_started); +#endif + + /* This is the command that starts the Core */ + mali_core_renderunit_register_write( + core, + MALI200_REG_ADDR_MGMT_CTRL_MGMT, + MALI200_REG_VAL_CTRL_MGMT_START_RENDERING); + _mali_osk_write_mem_barrier(); + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE | MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(core->core_number) | MALI_PROFILING_EVENT_REASON_SINGLE_HW_FLUSH, job200->user_input.frame_builder_id, job200->user_input.flush_id, 0, 0, 0); + _mali_profiling_add_event(MALI_PROFILING_EVENT_TYPE_START|MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(core->core_number), job200->pid, job200->tid, +#if defined(USING_MALI400_L2_CACHE) + (job200->user_input.perf_counter_l2_src0 << 16) | (job200->user_input.perf_counter_l2_src1 << 24), + job200->perf_counter_l2_val0, job200->perf_counter_l2_val1 +#else + 0, 0, 0 +#endif + ); +#endif + + MALI_SUCCESS; +} + +static u32 subsystem_mali200_irq_handler_upper_half(mali_core_renderunit * core) +{ + u32 irq_readout; + + if (mali_benchmark) { + return (core->current_job ? 1 : 0); /* simulate irq is pending when a job is pending */ + } + + irq_readout = mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_INT_STATUS); + + if ( MALI200_REG_VAL_IRQ_MASK_NONE != irq_readout ) + { + /* Mask out all IRQs from this core until IRQ is handled */ + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_NONE); + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE|MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(core->core_number)|MALI_PROFILING_EVENT_REASON_SINGLE_HW_INTERRUPT, irq_readout, 0, 0, 0, 0); +#endif + + return 1; + } + return 0; +} + +static int subsystem_mali200_irq_handler_bottom_half(struct mali_core_renderunit* core) +{ + u32 irq_readout; + u32 current_tile_addr; + u32 core_status; + mali_core_job * job; + mali200_job * job200; + + job = core->current_job; + job200 = GET_JOB200_PTR(job); + + + if (mali_benchmark) { + irq_readout = MALI200_REG_VAL_IRQ_END_OF_FRAME; + current_tile_addr = 0; + core_status = 0; + } else { + irq_readout = mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_INT_RAWSTAT) & MALI200_REG_VAL_IRQ_MASK_USED; + current_tile_addr = mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_CURRENT_REND_LIST_ADDR); + core_status = mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_STATUS); + } + + if (NULL == job) + { + MALI_DEBUG_ASSERT(CORE_IDLE==core->state); + if ( 0 != irq_readout ) + { + MALI_PRINT_ERROR(("Interrupt from a core not running a job. IRQ: 0x%04x Status: 0x%04x", irq_readout, core_status)); + } + return JOB_STATUS_END_UNKNOWN_ERR; + } + MALI_DEBUG_ASSERT(CORE_IDLE!=core->state); + + job200->irq_status |= irq_readout; + + MALI_DEBUG_PRINT_IF( 3, ( 0 != irq_readout ), + ("Mali PP: Job: 0x%08x IRQ RECEIVED Rawstat: 0x%x Tile_addr: 0x%x Status: 0x%x\n", + (u32)job200->user_input.user_job_ptr, irq_readout ,current_tile_addr ,core_status)); + + if ( MALI200_REG_VAL_IRQ_END_OF_FRAME & irq_readout) + { +#if defined(USING_MALI200) + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_CTRL_MGMT, MALI200_REG_VAL_CTRL_MGMT_FLUSH_CACHES); +#endif + + if (0 != job200->user_input.perf_counter_flag ) + { + if (job200->user_input.perf_counter_flag & (_MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE|_MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE) ) + { +#if MALI_TIMELINE_PROFILING_ENABLED + /* Work out the counter offsets for the core number */ + u32 counter0_offset = MALI_PROFILING_PP_CORE_COUNTER0_OFFSET(core->core_number); + u32 counter1_offset = MALI_PROFILING_PP_CORE_COUNTER1_OFFSET(core->core_number); +#endif /* MALI_TIMELINE_PROFILING_ENABLED */ + + job200->perf_counter0 = mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_PERF_CNT_0_VALUE); + job200->perf_counter1 = mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_PERF_CNT_1_VALUE); + +#if MALI_TIMELINE_PROFILING_ENABLED + /* Report the counter values */ + _mali_osk_profiling_report_hw_counter(counter0_offset, job200->perf_counter0); + _mali_osk_profiling_report_hw_counter(counter1_offset, job200->perf_counter1); +#endif /* MALI_TIMELINE_PROFILING_ENABLED */ + } + +#if defined(USING_MALI400_L2_CACHE) + if (job200->user_input.perf_counter_flag & (_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC0_ENABLE|_MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC1_ENABLE) ) + { + u32 src0; + u32 val0; + u32 src1; + u32 val1; + mali_kernel_l2_cache_get_perf_counters(&src0, &val0, &src1, &val1); + + if (job200->perf_counter_l2_src0 == src0) + { + job200->perf_counter_l2_val0_raw = val0; + job200->perf_counter_l2_val0 = val0 - job200->perf_counter_l2_val0; + } + else + { + job200->perf_counter_l2_val0_raw = 0; + job200->perf_counter_l2_val0 = 0; + } + + if (job200->perf_counter_l2_src1 == src1) + { + job200->perf_counter_l2_val1_raw = val1; + job200->perf_counter_l2_val1 = val1 - job200->perf_counter_l2_val1; + } + else + { + job200->perf_counter_l2_val1_raw = 0; + job200->perf_counter_l2_val1 = 0; + } + } +#endif + + } + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(core->core_number), + job200->perf_counter0, job200->perf_counter1, + job200->user_input.perf_counter_src0 | (job200->user_input.perf_counter_src1 << 8) +#if defined(USING_MALI400_L2_CACHE) + | (job200->user_input.perf_counter_l2_src0 << 16) | (job200->user_input.perf_counter_l2_src1 << 24), + job200->perf_counter_l2_val0_raw, job200->perf_counter_l2_val1_raw +#else + , 0, 0 +#endif + ); +#endif + + +#if MALI_STATE_TRACKING + _mali_osk_atomic_inc(&job->session->jobs_ended); +#endif + return JOB_STATUS_END_SUCCESS; /* reschedule */ + } + /* Overall SW watchdog timeout or (time to do hang checking and progress detected)? */ + else if ( + (CORE_WATCHDOG_TIMEOUT == core->state) || + ((CORE_HANG_CHECK_TIMEOUT == core->state) && (current_tile_addr == job200->last_tile_list_addr)) + ) + { +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(core->core_number), 0, 0, 0, 0, 0); /* add GP and L2 counters and return status */ +#endif + /* no progress detected, killed by the watchdog */ + MALI_DEBUG_PRINT(2, ("M200: SW-Timeout Rawstat: 0x%x Tile_addr: 0x%x Status: 0x%x.\n", irq_readout ,current_tile_addr ,core_status) ); + /* In this case will the system outside cleanup and reset the core */ + +#if MALI_STATE_TRACKING + _mali_osk_atomic_inc(&job->session->jobs_ended); +#endif + + return JOB_STATUS_END_HANG; + } + /* HW watchdog triggered or an existing hang check passed? */ + else if ((CORE_HANG_CHECK_TIMEOUT == core->state) || (irq_readout & job200->active_mask & MALI200_REG_VAL_IRQ_HANG)) + { + /* check interval in ms */ + u32 timeout = mali_core_hang_check_timeout_get(); + MALI_DEBUG_PRINT(3, ("M200: HW/SW Watchdog triggered, checking for progress in %d ms\n", timeout)); + job200->last_tile_list_addr = current_tile_addr; + /* hw watchdog triggered, set up a progress checker every HANGCHECK ms */ + _mali_osk_timer_add(core->timer_hang_detection, _mali_osk_time_mstoticks(timeout)); + job200->active_mask &= ~MALI200_REG_VAL_IRQ_HANG; /* ignore the hw watchdoig from now on */ + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_CLEAR, irq_readout & ~MALI200_REG_VAL_IRQ_HANG); + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_MASK, job200->active_mask); + return JOB_STATUS_CONTINUE_RUN; /* not finished */ + } + /* No irq pending, core still busy */ + else if ((0 == (irq_readout & MALI200_REG_VAL_IRQ_MASK_USED)) && ( 0 != (core_status & MALI200_REG_VAL_STATUS_RENDERING_ACTIVE))) + { + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_CLEAR, irq_readout); + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_MASK, job200->active_mask); + return JOB_STATUS_CONTINUE_RUN; /* Not finished */ + } + else + { +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_STOP|MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(core->core_number), 0, 0, 0, 0, 0); /* add GP and L2 counters and return status */ +#endif + + MALI_DEBUG_PRINT(1, ("Mali PP: Job: 0x%08x CRASH? Rawstat: 0x%x Tile_addr: 0x%x Status: 0x%x\n", + (u32)job200->user_input.user_job_ptr, irq_readout ,current_tile_addr ,core_status) ) ; + + if (irq_readout & MALI200_REG_VAL_IRQ_BUS_ERROR) + { + u32 bus_error = mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_BUS_ERROR_STATUS); + + MALI_DEBUG_PRINT(1, ("Bus error status: 0x%08X\n", bus_error)); + MALI_DEBUG_PRINT_IF(1, (bus_error & 0x01), ("Bus write error from id 0x%02x\n", (bus_error>>2) & 0x0F)); + MALI_DEBUG_PRINT_IF(1, (bus_error & 0x02), ("Bus read error from id 0x%02x\n", (bus_error>>6) & 0x0F)); + MALI_DEBUG_PRINT_IF(1, (0 == (bus_error & 0x03)), ("Bus error but neither read or write was set as the error reason\n")); + (void)bus_error; + } + +#if MALI_STATE_TRACKING + _mali_osk_atomic_inc(&job->session->jobs_ended); +#endif + return JOB_STATUS_END_UNKNOWN_ERR; /* reschedule */ + } +} + + +/* This function is called from the ioctl function and should return a mali_core_job pointer +to a created mali_core_job object with the data given from userspace */ +static _mali_osk_errcode_t subsystem_mali200_get_new_job_from_user(struct mali_core_session * session, void * argument) +{ + mali200_job *job200; + mali_core_job *job = NULL; + mali_core_job *previous_replaced_job; + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + _mali_uk_pp_start_job_s * user_ptr_job_input; + + user_ptr_job_input = (_mali_uk_pp_start_job_s *)argument; + + MALI_CHECK_NON_NULL(job200 = (mali200_job *) _mali_osk_malloc(sizeof(mali200_job)), _MALI_OSK_ERR_NOMEM); + _mali_osk_memset(job200, 0 , sizeof(mali200_job) ); + + /* We read job data from Userspace pointer */ + if ( NULL == _mali_osk_memcpy((void*)&job200->user_input, user_ptr_job_input, sizeof(job200->user_input)) ) + { + MALI_PRINT_ERROR( ("Mali PP: Could not copy data from U/K interface.\n")) ; + err = _MALI_OSK_ERR_FAULT; + goto function_exit; + } + + MALI_DEBUG_PRINT(5, ("Mali PP: subsystem_mali200_get_new_job_from_user 0x%x\n", (void*)job200->user_input.user_job_ptr)); + + MALI_DEBUG_PRINT(5, ("Mali PP: Frameregs: 0x%x 0x%x 0x%x Writeback[1] 0x%x, Pri:%d; Watchd:%d\n", + job200->user_input.frame_registers[0], job200->user_input.frame_registers[1], job200->user_input.frame_registers[2], + job200->user_input.wb0_registers[1], job200->user_input.priority, + job200->user_input.watchdog_msecs)); + + if ( job200->user_input.perf_counter_flag) + { +#if defined(USING_MALI400_L2_CACHE) + MALI_DEBUG_PRINT(5, ("Mali PP: Performance counters: flag:0x%x src0:0x%x src1:0x%x l2_src0:0x%x l2_src1:0x%x\n", + job200->user_input.perf_counter_flag, + job200->user_input.perf_counter_src0, + job200->user_input.perf_counter_src1, + job200->user_input.perf_counter_l2_src0, + job200->user_input.perf_counter_l2_src1)); +#else + MALI_DEBUG_PRINT(5, ("Mali PP: Performance counters: flag:0x%x src0:0x%x src1:0x%x\n", + job200->user_input.perf_counter_flag, + job200->user_input.perf_counter_src0, + job200->user_input.perf_counter_src1)); +#endif + } + + job = GET_JOB_EMBEDDED_PTR(job200); + + job->session = session; + job->flags = user_ptr_job_input->flags; + job_priority_set(job, job200->user_input.priority); + job_watchdog_set(job, job200->user_input.watchdog_msecs ); + +#if MALI_TIMELINE_PROFILING_ENABLED + job200->pid = _mali_osk_get_pid(); + job200->tid = _mali_osk_get_tid(); +#endif + + job->abort_id = job200->user_input.abort_id; + if (mali_job_queue_full(session)) + { + user_ptr_job_input->status = _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE; + goto function_exit; + } + + /* We now know that we has a job, and a empty session slot to put it in */ + + job200->active_mask = MALI200_REG_VAL_IRQ_MASK_USED; + + /* Allocating User Return Data */ + job200->notification_obj = _mali_osk_notification_create( + _MALI_NOTIFICATION_PP_FINISHED, + sizeof(_mali_uk_pp_job_finished_s) ); + + if ( NULL == job200->notification_obj ) + { + MALI_PRINT_ERROR( ("Mali PP: Could not get notification_obj.\n")) ; + err = _MALI_OSK_ERR_NOMEM; + goto function_exit; + } + + _MALI_OSK_INIT_LIST_HEAD( &(job->list) ) ; + + MALI_DEBUG_PRINT(4, ("Mali PP: Job: 0x%08x INPUT from user.\n", (u32)job200->user_input.user_job_ptr)) ; + + /* This should not happen since we have the checking of priority above */ + if ( _MALI_OSK_ERR_OK != mali_core_session_add_job(session, job, &previous_replaced_job)) + { + MALI_PRINT_ERROR( ("Mali PP: Internal error\n")) ; + user_ptr_job_input->status = _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE; + _mali_osk_notification_delete( job200->notification_obj ); + goto function_exit; + } + + /* If MALI_TRUE: This session had a job with lower priority which were removed. + This replaced job is given back to userspace. */ + if ( NULL != previous_replaced_job ) + { + mali200_job *previous_replaced_job200; + + previous_replaced_job200 = GET_JOB200_PTR(previous_replaced_job); + + MALI_DEBUG_PRINT(4, ("Mali PP: Replacing job: 0x%08x\n", (u32)previous_replaced_job200->user_input.user_job_ptr)) ; + + /* Copy to the input data (which also is output data) the + pointer to the job that were replaced, so that the userspace + driver can put this job in the front of its job-queue */ + + user_ptr_job_input->returned_user_job_ptr = previous_replaced_job200->user_input.user_job_ptr; + + /** @note failure to 'copy to user' at this point must not free job200, + * and so no transaction rollback required in the U/K interface */ + + /* This does not cause job200 to free: */ + user_ptr_job_input->status = _MALI_UK_START_JOB_STARTED_LOW_PRI_JOB_RETURNED; + MALI_DEBUG_PRINT(5, ("subsystem_mali200_get_new_job_from_user: Job added, prev returned\n")) ; + } + else + { + /* This does not cause job200 to free: */ + user_ptr_job_input->status = _MALI_UK_START_JOB_STARTED; + MALI_DEBUG_PRINT(5, ("subsystem_mali200_get_new_job_from_user: Job added\n")) ; + } + +function_exit: + if (_MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE == user_ptr_job_input->status + || _MALI_OSK_ERR_OK != err ) + { + _mali_osk_free(job200); + } +#if MALI_STATE_TRACKING + if (_MALI_UK_START_JOB_STARTED==user_ptr_job_input->status) + { + if(job) + { + job->job_nr=_mali_osk_atomic_inc_return(&session->jobs_received); + } + } +#endif + + MALI_ERROR(err); +} + +/* This function is called from the ioctl function and should write the necessary data +to userspace telling which job was finished and the status and debuginfo for this job. +The function must also free and cleanup the input job object. */ +static void subsystem_mali200_return_job_to_user( mali_core_job * job, mali_subsystem_job_end_code end_status) +{ + mali200_job *job200; + _mali_uk_pp_job_finished_s * job_out; + _mali_uk_pp_start_job_s * job_input; + mali_core_session *session; + + if (NULL == job) + { + MALI_DEBUG_PRINT(1, ("subsystem_mali200_return_job_to_user received a NULL ptr\n")); + return; + } + + job200 = _MALI_OSK_CONTAINER_OF(job, mali200_job, embedded_core_job); + + if (NULL == job200->notification_obj) + { + MALI_DEBUG_PRINT(1, ("Found job200 with NULL notification object, abandoning userspace sending\n")); + return; + } + + job_out = job200->notification_obj->result_buffer; + job_input= &(job200->user_input); + session = job->session; + + MALI_DEBUG_PRINT(4, ("Mali PP: Job: 0x%08x OUTPUT to user. Runtime: %dus\n", + (u32)job200->user_input.user_job_ptr, + job->render_time_usecs)) ; + + _mali_osk_memset(job_out, 0 , sizeof(_mali_uk_pp_job_finished_s)); + + job_out->user_job_ptr = job_input->user_job_ptr; + + switch( end_status ) + { + case JOB_STATUS_CONTINUE_RUN: + case JOB_STATUS_END_SUCCESS: + case JOB_STATUS_END_OOM: + case JOB_STATUS_END_ABORT: + case JOB_STATUS_END_TIMEOUT_SW: + case JOB_STATUS_END_HANG: + case JOB_STATUS_END_SEG_FAULT: + case JOB_STATUS_END_ILLEGAL_JOB: + case JOB_STATUS_END_UNKNOWN_ERR: + case JOB_STATUS_END_SHUTDOWN: + case JOB_STATUS_END_SYSTEM_UNUSABLE: + job_out->status = (mali_subsystem_job_end_code) end_status; + break; + + default: + job_out->status = JOB_STATUS_END_UNKNOWN_ERR ; + } + job_out->irq_status = job200->irq_status; + job_out->perf_counter0 = job200->perf_counter0; + job_out->perf_counter1 = job200->perf_counter1; + job_out->render_time = job->render_time_usecs; + +#if defined(USING_MALI400_L2_CACHE) + job_out->perf_counter_l2_src0 = job200->perf_counter_l2_src0; + job_out->perf_counter_l2_src1 = job200->perf_counter_l2_src1; + job_out->perf_counter_l2_val0 = job200->perf_counter_l2_val0; + job_out->perf_counter_l2_val1 = job200->perf_counter_l2_val1; + job_out->perf_counter_l2_val0_raw = job200->perf_counter_l2_val0_raw; + job_out->perf_counter_l2_val1_raw = job200->perf_counter_l2_val1_raw; +#endif + +#if MALI_STATE_TRACKING + _mali_osk_atomic_inc(&session->jobs_returned); +#endif + _mali_osk_notification_queue_send( session->notification_queue, job200->notification_obj); + job200->notification_obj = NULL; + + _mali_osk_free(job200); +} + +static void subsystem_mali200_renderunit_delete(mali_core_renderunit * core) +{ + MALI_DEBUG_PRINT(5, ("Mali PP: mali200_renderunit_delete\n")); + _mali_osk_free(core); +} + +static void mali200_reset_hard(struct mali_core_renderunit * core) +{ + const int reset_finished_loop_count = 15; + const u32 reset_wait_target_register = MALI200_REG_ADDR_MGMT_WRITE_BOUNDARY_LOW; + const u32 reset_invalid_value = 0xC0FFE000; + const u32 reset_check_value = 0xC01A0000; + const u32 reset_default_value = 0; + int i; + + MALI_DEBUG_PRINT(5, ("subsystem_mali200_renderunit_reset_core_hard called for core %s\n", core->description)); + + mali_core_renderunit_register_write(core, reset_wait_target_register, reset_invalid_value); + + mali_core_renderunit_register_write( + core, + MALI200_REG_ADDR_MGMT_CTRL_MGMT, + MALI200_REG_VAL_CTRL_MGMT_FORCE_RESET); + + for (i = 0; i < reset_finished_loop_count; i++) + { + mali_core_renderunit_register_write(core, reset_wait_target_register, reset_check_value); + if (reset_check_value == mali_core_renderunit_register_read(core, reset_wait_target_register)) + { + MALI_DEBUG_PRINT(5, ("Reset loop exiting after %d iterations\n", i)); + break; + } + _mali_osk_time_ubusydelay(10); + } + + if (i == reset_finished_loop_count) + { + MALI_DEBUG_PRINT(1, ("The reset loop didn't work\n")); + } + + mali_core_renderunit_register_write(core, reset_wait_target_register, reset_default_value); /* set it back to the default */ + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_CLEAR, MALI200_REG_VAL_IRQ_MASK_ALL); +} + +static void subsystem_mali200_renderunit_reset_core(struct mali_core_renderunit * core, mali_core_reset_style style) +{ + MALI_DEBUG_PRINT(5, ("Mali PP: renderunit_reset_core\n")); + + switch (style) + { + case MALI_CORE_RESET_STYLE_RUNABLE: + mali200_reset(core); + break; + case MALI_CORE_RESET_STYLE_DISABLE: + mali200_raw_reset(core); /* do the raw reset */ + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_MASK, 0); /* then disable the IRQs */ + break; + case MALI_CORE_RESET_STYLE_HARD: + mali200_reset_hard(core); + break; + default: + MALI_DEBUG_PRINT(1, ("Unknown reset type %d\n", style)); + } +} + +static void subsystem_mali200_renderunit_probe_core_irq_trigger(struct mali_core_renderunit* core) +{ + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_MASK, MALI200_REG_VAL_IRQ_MASK_USED); + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_RAWSTAT, MALI200_REG_VAL_IRQ_FORCE_HANG); + _mali_osk_mem_barrier(); +} + +static _mali_osk_errcode_t subsystem_mali200_renderunit_probe_core_irq_finished(struct mali_core_renderunit* core) +{ + u32 irq_readout; + + irq_readout = mali_core_renderunit_register_read(core, MALI200_REG_ADDR_MGMT_INT_STATUS); + + if ( MALI200_REG_VAL_IRQ_FORCE_HANG & irq_readout ) + { + mali_core_renderunit_register_write(core, MALI200_REG_ADDR_MGMT_INT_CLEAR, MALI200_REG_VAL_IRQ_FORCE_HANG); + _mali_osk_mem_barrier(); + MALI_SUCCESS; + } + + MALI_ERROR(_MALI_OSK_ERR_FAULT); +} + +_mali_osk_errcode_t _mali_ukk_pp_start_job( _mali_uk_pp_start_job_s *args ) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_mali200_id); + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_FAULT); + return mali_core_subsystem_ioctl_start_job(session, args); +} + +_mali_osk_errcode_t _mali_ukk_get_pp_number_of_cores( _mali_uk_get_pp_number_of_cores_s *args ) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_mali200_id); + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_FAULT); + return mali_core_subsystem_ioctl_number_of_cores_get(session, &args->number_of_cores); +} + +_mali_osk_errcode_t _mali_ukk_get_pp_core_version( _mali_uk_get_pp_core_version_s *args ) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_mali200_id); + MALI_CHECK_NON_NULL(session, _MALI_OSK_ERR_FAULT); + return mali_core_subsystem_ioctl_core_version_get(session, &args->version); +} + +void _mali_ukk_pp_abort_job( _mali_uk_pp_abort_job_s * args) +{ + mali_core_session * session; + MALI_DEBUG_ASSERT_POINTER(args); + if (NULL == args->ctx) return; + session = (mali_core_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_mali200_id); + if (NULL == session) return; + mali_core_subsystem_ioctl_abort_job(session, args->abort_id); + +} + +#if USING_MALI_PMM + +_mali_osk_errcode_t malipp_signal_power_up( u32 core_num, mali_bool queue_only ) +{ + MALI_DEBUG_PRINT(4, ("Mali PP: signal power up core: %d - queue_only: %d\n", core_num, queue_only )); + + return( mali_core_subsystem_signal_power_up( &subsystem_mali200, core_num, queue_only ) ); +} + +_mali_osk_errcode_t malipp_signal_power_down( u32 core_num, mali_bool immediate_only ) +{ + MALI_DEBUG_PRINT(4, ("Mali PP: signal power down core: %d - immediate_only: %d\n", core_num, immediate_only )); + + return( mali_core_subsystem_signal_power_down( &subsystem_mali200, core_num, immediate_only ) ); +} + +#endif + +#if MALI_STATE_TRACKING +u32 mali200_subsystem_dump_state(char *buf, u32 size) +{ + return mali_core_renderunit_dump_state(&subsystem_mali200, buf, size); +} +#endif diff --git a/drivers/gpu/arm/mali/common/mali_kernel_common.h b/drivers/gpu/arm/mali/common/mali_kernel_common.h new file mode 100644 index 000000000000..d9388ff18a23 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_common.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_COMMON_H__ +#define __MALI_KERNEL_COMMON_H__ + +/* Make sure debug is defined when it should be */ +#ifndef DEBUG + #if defined(_DEBUG) + #define DEBUG + #endif +#endif + +/* The file include several useful macros for error checking, debugging and printing. + * - MALI_PRINTF(...) Do not use this function: Will be included in Release builds. + * - MALI_DEBUG_PRINT(nr, (X) ) Prints the second argument if nr<=MALI_DEBUG_LEVEL. + * - MALI_DEBUG_ERROR( (X) ) Prints an errortext, a source trace, and the given error message. + * - MALI_DEBUG_ASSERT(exp,(X)) If the asserted expr is false, the program will exit. + * - MALI_DEBUG_ASSERT_POINTER(pointer) Triggers if the pointer is a zero pointer. + * - MALI_DEBUG_CODE( X ) The code inside the macro is only compiled in Debug builds. + * + * The (X) means that you must add an extra parenthesis around the argumentlist. + * + * The printf function: MALI_PRINTF(...) is routed to _mali_osk_debugmsg + * + * Suggested range for the DEBUG-LEVEL is [1:6] where + * [1:2] Is messages with highest priority, indicate possible errors. + * [3:4] Is messages with medium priority, output important variables. + * [5:6] Is messages with low priority, used during extensive debugging. + */ + + /** + * Fundamental error macro. Reports an error code. This is abstracted to allow us to + * easily switch to a different error reporting method if we want, and also to allow + * us to search for error returns easily. + * + * Note no closing semicolon - this is supplied in typical usage: + * + * MALI_ERROR(MALI_ERROR_OUT_OF_MEMORY); + */ +#define MALI_ERROR(error_code) return (error_code) + +/** + * Basic error macro, to indicate success. + * Note no closing semicolon - this is supplied in typical usage: + * + * MALI_SUCCESS; + */ +#define MALI_SUCCESS MALI_ERROR(_MALI_OSK_ERR_OK) + +/** + * Basic error macro. This checks whether the given condition is true, and if not returns + * from this function with the supplied error code. This is a macro so that we can override it + * for stress testing. + * + * Note that this uses the do-while-0 wrapping to ensure that we don't get problems with dangling + * else clauses. Note also no closing semicolon - this is supplied in typical usage: + * + * MALI_CHECK((p!=NULL), ERROR_NO_OBJECT); + */ +#define MALI_CHECK(condition, error_code) do { if(!(condition)) MALI_ERROR(error_code); } while(0) + +/** + * Error propagation macro. If the expression given is anything other than _MALI_OSK_NO_ERROR, + * then the value is returned from the enclosing function as an error code. This effectively + * acts as a guard clause, and propagates error values up the call stack. This uses a + * temporary value to ensure that the error expression is not evaluated twice. + * If the counter for forcing a failure has been set using _mali_force_error, this error will be + * returned without evaluating the expression in MALI_CHECK_NO_ERROR + */ +#define MALI_CHECK_NO_ERROR(expression) \ + do { _mali_osk_errcode_t _check_no_error_result=(expression); \ + if(_check_no_error_result != _MALI_OSK_ERR_OK) \ + MALI_ERROR(_check_no_error_result); \ + } while(0) + +/** + * Pointer check macro. Checks non-null pointer. + */ +#define MALI_CHECK_NON_NULL(pointer, error_code) MALI_CHECK( ((pointer)!=NULL), (error_code) ) + +/** + * Error macro with goto. This checks whether the given condition is true, and if not jumps + * to the specified label using a goto. The label must therefore be local to the function in + * which this macro appears. This is most usually used to execute some clean-up code before + * exiting with a call to ERROR. + * + * Like the other macros, this is a macro to allow us to override the condition if we wish, + * e.g. to force an error during stress testing. + */ +#define MALI_CHECK_GOTO(condition, label) do { if(!(condition)) goto label; } while(0) + +/** + * Explicitly ignore a parameter passed into a function, to suppress compiler warnings. + * Should only be used with parameter names. + */ +#define MALI_IGNORE(x) x=x + +#define MALI_PRINTF(args) _mali_osk_dbgmsg args; + +#define MALI_PRINT_ERROR(args) do{ \ + MALI_PRINTF(("Mali: ERR: %s\n" ,__FILE__)); \ + MALI_PRINTF((" %s()%4d\n ", __FUNCTION__, __LINE__)) ; \ + MALI_PRINTF(args); \ + MALI_PRINTF(("\n")); \ + } while(0) + +#define MALI_PRINT(args) do{ \ + MALI_PRINTF(("Mali: ")); \ + MALI_PRINTF(args); \ + } while (0) + +#ifdef DEBUG +extern int mali_debug_level; + +#define MALI_DEBUG_CODE(code) code +#define MALI_DEBUG_PRINT(level, args) do { \ + if((level) <= mali_debug_level)\ + {MALI_PRINTF(("Mali<" #level ">: ")); MALI_PRINTF(args); } \ + } while (0) + +#define MALI_DEBUG_PRINT_ERROR(args) MALI_PRINT_ERROR(args) + +#define MALI_DEBUG_PRINT_IF(level,condition,args) \ + if((condition)&&((level) <= mali_debug_level))\ + {MALI_PRINTF(("Mali<" #level ">: ")); MALI_PRINTF(args); } + +#define MALI_DEBUG_PRINT_ELSE(level, args)\ + else if((level) <= mali_debug_level)\ + { MALI_PRINTF(("Mali<" #level ">: ")); MALI_PRINTF(args); } + +/** + * @note these variants of DEBUG ASSERTS will cause a debugger breakpoint + * to be entered (see _mali_osk_break() ). An alternative would be to call + * _mali_osk_abort(), on OSs that support it. + */ +#define MALI_DEBUG_PRINT_ASSERT(condition, args) do {if( !(condition)) { MALI_PRINT_ERROR(args); _mali_osk_break(); } } while(0) +#define MALI_DEBUG_ASSERT_POINTER(pointer) do {if( (pointer)== NULL) {MALI_PRINT_ERROR(("NULL pointer " #pointer)); _mali_osk_break();} } while(0) +#define MALI_DEBUG_ASSERT(condition) do {if( !(condition)) {MALI_PRINT_ERROR(("ASSERT failed: " #condition )); _mali_osk_break();} } while(0) + +#else /* DEBUG */ + +#define MALI_DEBUG_CODE(code) +#define MALI_DEBUG_PRINT(string,args) do {} while(0) +#define MALI_DEBUG_PRINT_ERROR(args) do {} while(0) +#define MALI_DEBUG_PRINT_IF(level,condition,args) do {} while(0) +#define MALI_DEBUG_PRINT_ELSE(level,condition,args) do {} while(0) +#define MALI_DEBUG_PRINT_ASSERT(condition,args) do {} while(0) +#define MALI_DEBUG_ASSERT_POINTER(pointer) do {} while(0) +#define MALI_DEBUG_ASSERT(condition) do {} while(0) + +#endif /* DEBUG */ + +/** + * variables from user space cannot be dereferenced from kernel space; tagging them + * with __user allows the GCC compiler to generate a warning. Other compilers may + * not support this so we define it here as an empty macro if the compiler doesn't + * define it. + */ +#ifndef __user +#define __user +#endif + +#endif /* __MALI_KERNEL_COMMON_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_core.c b/drivers/gpu/arm/mali/common/mali_kernel_core.c new file mode 100644 index 000000000000..5e3626dece44 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_core.c @@ -0,0 +1,911 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_subsystem.h" +#include "mali_kernel_mem.h" +#include "mali_kernel_session_manager.h" +#include "mali_kernel_pp.h" +#include "mali_kernel_gp.h" +#include "mali_osk.h" +#include "mali_osk_mali.h" +#include "mali_ukk.h" +#include "mali_kernel_core.h" +#include "mali_kernel_rendercore.h" +#if defined USING_MALI400_L2_CACHE +#include "mali_kernel_l2_cache.h" +#endif +#if USING_MALI_PMM +#include "mali_pmm.h" +#endif /* USING_MALI_PMM */ + +/* platform specific set up */ +#include "mali_platform.h" + +/* Initialized when this subsystem is initialized. This is determined by the + * position in subsystems[], and so the value used to initialize this is + * determined at compile time */ +static mali_kernel_subsystem_identifier mali_subsystem_core_id = -1; + +/** Pointer to table of resource definitions available to the Mali driver. + * _mali_osk_resources_init() sets up the pointer to this table. + */ +static _mali_osk_resource_t *arch_configuration = NULL; + +/** Number of resources initialized by _mali_osk_resources_init() */ +static u32 num_resources; + +static _mali_osk_errcode_t register_resources( _mali_osk_resource_t **arch_configuration, u32 num_resources ); + +static _mali_osk_errcode_t initialize_subsystems(void); +static void terminate_subsystems(void); + +static _mali_osk_errcode_t mali_kernel_subsystem_core_setup(mali_kernel_subsystem_identifier id); +static void mali_kernel_subsystem_core_cleanup(mali_kernel_subsystem_identifier id); +static _mali_osk_errcode_t mali_kernel_subsystem_core_system_info_fill(_mali_system_info* info); +static _mali_osk_errcode_t mali_kernel_subsystem_core_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue); + +static _mali_osk_errcode_t build_system_info(void); +static void cleanup_system_info(_mali_system_info *cleanup); + +/** + * @brief handler for MEM_VALIDATION resources + * + * This resource handler is common to all memory systems. It provides a default + * means for validating requests to map in external memory via + * _mali_ukk_map_external_mem. In addition, if _mali_ukk_va_to_pa is + * implemented, then _mali_ukk_va_to_pa can make use of this MEM_VALIDATION + * resource. + * + * MEM_VALIDATION also provide a CPU physical to Mali physical address + * translation, for use by _mali_ukk_map_external_mem. + * + * @note MEM_VALIDATION resources are only to handle simple cases where a + * certain physical address range is allowed to be mapped in by any process, + * e.g. a framebuffer at a fixed location. If the implementor has more complex + * mapping requirements, then they must either: + * - implement their own memory validation function + * - or, integrate with UMP. + * + * @param resource The resource to handle (type MEM_VALIDATION) + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +static _mali_osk_errcode_t mali_kernel_core_resource_mem_validation(_mali_osk_resource_t * resource); + +/* MEM_VALIDATION handler state */ +typedef struct +{ + u32 phys_base; /**< Mali physical base of the memory, page aligned */ + u32 size; /**< size in bytes of the memory, multiple of page size */ + s32 cpu_usage_adjust; /**< Offset to add to Mali Physical address to obtain CPU physical address */ +} _mali_mem_validation_t; + +#define INVALID_MEM 0xffffffff + +static _mali_mem_validation_t mem_validator = { INVALID_MEM, INVALID_MEM, -1 }; + +static struct mali_kernel_subsystem mali_subsystem_core = +{ + mali_kernel_subsystem_core_setup, /* startup */ + mali_kernel_subsystem_core_cleanup, /* shutdown */ + NULL, /* load_complete */ + mali_kernel_subsystem_core_system_info_fill, /* system_info_fill */ + mali_kernel_subsystem_core_session_begin, /* session_begin */ + NULL, /* session_end */ + NULL, /* broadcast_notification */ +#if MALI_STATE_TRACKING + NULL, /* dump_state */ +#endif +}; + +static struct mali_kernel_subsystem * subsystems[] = +{ + +#if USING_MALI_PMM + /* The PMM must be initialized before any cores - including L2 cache */ + &mali_subsystem_pmm, +#endif + + /* always included */ + &mali_subsystem_memory, + + /* The rendercore subsystem must be initialized before any subsystem based on the + * rendercores is started e.g. mali_subsystem_mali200 and mali_subsystem_gp2 */ + &mali_subsystem_rendercore, + + /* add reference to the subsystem */ + &mali_subsystem_mali200, + + /* add reference to the subsystem */ + &mali_subsystem_gp2, + +#if defined USING_MALI400_L2_CACHE + &mali_subsystem_l2_cache, +#endif + + /* always included */ + /* NOTE Keep the core entry at the tail of the list */ + &mali_subsystem_core +}; + +#define SUBSYSTEMS_COUNT ( sizeof(subsystems) / sizeof(subsystems[0]) ) + +/* Pointers to this type available as incomplete struct in mali_kernel_session_manager.h */ +struct mali_session_data +{ + void * subsystem_data[SUBSYSTEMS_COUNT]; + _mali_osk_notification_queue_t * ioctl_queue; +}; + +static mali_kernel_resource_registrator resource_handler[RESOURCE_TYPE_COUNT] = { NULL, }; + +/* system info variables */ +static _mali_osk_lock_t *system_info_lock = NULL; +static _mali_system_info * system_info = NULL; +static u32 system_info_size = 0; + +/* is called from OS specific driver entry point */ +_mali_osk_errcode_t mali_kernel_constructor( void ) +{ + _mali_osk_errcode_t err; + + err = mali_platform_init(); + if (_MALI_OSK_ERR_OK != err) goto error1; + + err = _mali_osk_init(); + if (_MALI_OSK_ERR_OK != err) goto error2; + + MALI_DEBUG_PRINT(2, ("\n")); + MALI_DEBUG_PRINT(2, ("Inserting Mali v%d device driver. \n",_MALI_API_VERSION)); + MALI_DEBUG_PRINT(2, ("Compiled: %s, time: %s.\n", __DATE__, __TIME__)); + MALI_DEBUG_PRINT(2, ("Svn revision: %s\n", SVN_REV_STRING)); + + err = initialize_subsystems(); + if (_MALI_OSK_ERR_OK != err) goto error3; + + MALI_PRINT(("Mali device driver %s loaded\n", SVN_REV_STRING)); + + MALI_SUCCESS; + +error3: + MALI_PRINT(("Mali subsystems failed\n")); + _mali_osk_term(); +error2: + MALI_PRINT(("Mali device driver init failed\n")); + if (_MALI_OSK_ERR_OK != mali_platform_deinit()) + { + MALI_PRINT(("Failed to deinit platform\n")); + } +error1: + MALI_PRINT(("Failed to init platform\n")); + MALI_ERROR(err); +} + +/* is called from OS specific driver exit point */ +void mali_kernel_destructor( void ) +{ + MALI_DEBUG_PRINT(2, ("\n")); + MALI_DEBUG_PRINT(2, ("Unloading Mali v%d device driver.\n",_MALI_API_VERSION)); +#if USING_MALI_PMM + malipmm_force_powerup(); +#endif + terminate_subsystems(); /* subsystems are responsible for their registered resources */ + _mali_osk_term(); + + if (_MALI_OSK_ERR_OK != mali_platform_deinit()) + { + MALI_PRINT(("Failed to deinit platform\n")); + } + MALI_DEBUG_PRINT(2, ("Module unloaded.\n")); +} + +_mali_osk_errcode_t register_resources( _mali_osk_resource_t **arch_configuration, u32 num_resources ) +{ + _mali_osk_resource_t *arch_resource = *arch_configuration; + u32 i; +#if USING_MALI_PMM + u32 is_pmu_first_resource = 1; +#endif /* USING_MALI_PMM */ + + /* loop over arch configuration */ + for (i = 0; i < num_resources; ++i, arch_resource++) + { + if ( (arch_resource->type >= RESOURCE_TYPE_FIRST) && + (arch_resource->type < RESOURCE_TYPE_COUNT) && + (NULL != resource_handler[arch_resource->type]) + ) + { +#if USING_MALI_PMM + if((arch_resource->type != PMU) && (is_pmu_first_resource == 1)) + { + _mali_osk_resource_t mali_pmu_virtual_resource; + mali_pmu_virtual_resource.type = PMU; + mali_pmu_virtual_resource.description = "Virtual PMU"; + mali_pmu_virtual_resource.base = 0x00000000; + mali_pmu_virtual_resource.cpu_usage_adjust = 0; + mali_pmu_virtual_resource.size = 0; + mali_pmu_virtual_resource.irq = 0; + mali_pmu_virtual_resource.flags = 0; + mali_pmu_virtual_resource.mmu_id = 0; + mali_pmu_virtual_resource.alloc_order = 0; + MALI_CHECK_NO_ERROR(resource_handler[mali_pmu_virtual_resource.type](&mali_pmu_virtual_resource)); + } + is_pmu_first_resource = 0; +#endif /* USING_MALI_PMM */ + + MALI_CHECK_NO_ERROR(resource_handler[arch_resource->type](arch_resource)); + /* the subsystem shutdown process will release all the resources already registered */ + } + else + { + MALI_DEBUG_PRINT(1, ("No handler installed for resource %s, type %d\n", arch_resource->description, arch_resource->type)); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + } + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t initialize_subsystems(void) +{ + int i, j; + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; /* default error code */ + + MALI_CHECK_NON_NULL(system_info_lock = _mali_osk_lock_init( (_mali_osk_lock_flags_t)(_MALI_OSK_LOCKFLAG_SPINLOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE), 0, 0 ), _MALI_OSK_ERR_FAULT); + + for (i = 0; i < (int)SUBSYSTEMS_COUNT; ++i) + { + if (NULL != subsystems[i]->startup) + { + /* the subsystem has a startup function defined */ + err = subsystems[i]->startup(i); /* the subsystem identifier is the offset in our subsystems array */ + if (_MALI_OSK_ERR_OK != err) goto cleanup; + } + } + + for (j = 0; j < (int)SUBSYSTEMS_COUNT; ++j) + { + if (NULL != subsystems[j]->load_complete) + { + /* the subsystem has a load_complete function defined */ + err = subsystems[j]->load_complete(j); + if (_MALI_OSK_ERR_OK != err) goto cleanup; + } + } + + /* All systems loaded and resources registered */ + /* Build system info */ + if (_MALI_OSK_ERR_OK != build_system_info()) goto cleanup; + + MALI_SUCCESS; /* all ok */ + +cleanup: + /* i is index of subsystem which failed to start, all indices before that has to be shut down */ + for (i = i - 1; i >= 0; --i) + { + /* the subsystem identifier is the offset in our subsystems array */ + /* Call possible shutdown notficiation functions */ + if (NULL != subsystems[i]->shutdown) subsystems[i]->shutdown(i); + } + + _mali_osk_lock_term( system_info_lock ); + MALI_ERROR(err); /* err is what the module which failed its startup returned, or the default */ +} + +static void terminate_subsystems(void) +{ + int i; + /* shut down subsystems in reverse order from startup */ + for (i = SUBSYSTEMS_COUNT - 1; i >= 0; --i) + { + /* the subsystem identifier is the offset in our subsystems array */ + if (NULL != subsystems[i]->shutdown) subsystems[i]->shutdown(i); + } + if (system_info_lock) _mali_osk_lock_term( system_info_lock ); + + /* Free _mali_system_info struct */ + cleanup_system_info(system_info); +} + +void _mali_kernel_core_broadcast_subsystem_message(mali_core_notification_message message, u32 data) +{ + int i; + + for (i = 0; i < (int)SUBSYSTEMS_COUNT; ++i) + { + if (NULL != subsystems[i]->broadcast_notification) + { + subsystems[i]->broadcast_notification(message, data); + } + } +} + +static _mali_osk_errcode_t mali_kernel_subsystem_core_setup(mali_kernel_subsystem_identifier id) +{ + mali_subsystem_core_id = id; + + /* Register our own resources */ + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(MEM_VALIDATION, mali_kernel_core_resource_mem_validation)); + + /* parse the arch resource definition and tell all the subsystems */ + /* this is why the core subsystem has to be specified last in the subsystem array */ + MALI_CHECK_NO_ERROR(_mali_osk_resources_init(&arch_configuration, &num_resources)); + + MALI_CHECK_NO_ERROR(register_resources(&arch_configuration, num_resources)); + + /* resource parsing succeeded and the subsystem have corretly accepted their resources */ + MALI_SUCCESS; +} + +static void mali_kernel_subsystem_core_cleanup(mali_kernel_subsystem_identifier id) +{ + _mali_osk_resources_term(&arch_configuration, num_resources); +} + +static void cleanup_system_info(_mali_system_info *cleanup) +{ + _mali_core_info * current_core; + _mali_mem_info * current_mem; + + /* delete all the core info structs */ + while (NULL != cleanup->core_info) + { + current_core = cleanup->core_info; + cleanup->core_info = cleanup->core_info->next; + _mali_osk_free(current_core); + } + + /* delete all the mem info struct */ + while (NULL != cleanup->mem_info) + { + current_mem = cleanup->mem_info; + cleanup->mem_info = cleanup->mem_info->next; + _mali_osk_free(current_mem); + } + + /* delete the system info struct itself */ + _mali_osk_free(cleanup); +} + +static _mali_osk_errcode_t build_system_info(void) +{ + unsigned int i; + int err = _MALI_OSK_ERR_FAULT; + _mali_system_info * new_info, * cleanup; + _mali_core_info * current_core; + _mali_mem_info * current_mem; + u32 new_size = 0; + + /* create a new system info struct */ + MALI_CHECK_NON_NULL(new_info = (_mali_system_info *)_mali_osk_malloc(sizeof(_mali_system_info)), _MALI_OSK_ERR_NOMEM); + + _mali_osk_memset(new_info, 0, sizeof(_mali_system_info)); + + /* if an error happens during any of the system_info_fill calls cleanup the new info structs */ + cleanup = new_info; + + /* ask each subsystems to fill in their info */ + for (i = 0; i < SUBSYSTEMS_COUNT; ++i) + { + if (NULL != subsystems[i]->system_info_fill) + { + err = subsystems[i]->system_info_fill(new_info); + if (_MALI_OSK_ERR_OK != err) goto error_exit; + } + } + + /* building succeeded, calculate the size */ + + /* size needed of the system info struct itself */ + new_size = sizeof(_mali_system_info); + + /* size needed for the cores */ + for (current_core = new_info->core_info; NULL != current_core; current_core = current_core->next) + { + new_size += sizeof(_mali_core_info); + } + + /* size needed for the memory banks */ + for (current_mem = new_info->mem_info; NULL != current_mem; current_mem = current_mem->next) + { + new_size += sizeof(_mali_mem_info); + } + + /* lock system info access so a user wont't get a corrupted version */ + _mali_osk_lock_wait( system_info_lock, _MALI_OSK_LOCKMODE_RW ); + + /* cleanup the old one */ + cleanup = system_info; + /* set new info */ + system_info = new_info; + system_info_size = new_size; + + /* we're safe */ + _mali_osk_lock_signal( system_info_lock, _MALI_OSK_LOCKMODE_RW ); + + /* ok result */ + err = _MALI_OSK_ERR_OK; + + /* we share the cleanup routine with the error case */ +error_exit: + if (NULL == cleanup) MALI_ERROR((_mali_osk_errcode_t)err); /* no cleanup needed, return what err contains */ + + /* cleanup */ + cleanup_system_info(cleanup); + + /* return whatever err is, we could end up here in both the error and success cases */ + MALI_ERROR((_mali_osk_errcode_t)err); +} + +_mali_osk_errcode_t _mali_ukk_get_api_version( _mali_uk_get_api_version_s *args ) +{ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + /* check compatability */ + if ( args->version == _MALI_UK_API_VERSION ) + { + args->compatible = 1; + } + else + { + args->compatible = 0; + } + + args->version = _MALI_UK_API_VERSION; /* report our version */ + + /* success regardless of being compatible or not */ + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_get_system_info_size(_mali_uk_get_system_info_size_s *args) +{ + MALI_DEBUG_ASSERT_POINTER(args); + args->size = system_info_size; + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_get_system_info( _mali_uk_get_system_info_s *args ) +{ + _mali_core_info * current_core; + _mali_mem_info * current_mem; + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; + void * current_write_pos, ** current_patch_pos; + u32 adjust_ptr_base; + + /* check input */ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + MALI_CHECK_NON_NULL(args->system_info, _MALI_OSK_ERR_INVALID_ARGS); + + /* lock the system info */ + _mali_osk_lock_wait( system_info_lock, _MALI_OSK_LOCKMODE_RW ); + + /* first check size */ + if (args->size < system_info_size) goto exit_when_locked; + + /* we build a copy of system_info in the user space buffer specified by the user and + * patch up the pointers. The ukk_private members of _mali_uk_get_system_info_s may + * indicate a different base address for patching the pointers (normally the + * address of the provided system_info buffer would be used). This is helpful when + * the system_info buffer needs to get copied to user space and the pointers need + * to be in user space. + */ + if (0 == args->ukk_private) + { + adjust_ptr_base = (u32)args->system_info; + } + else + { + adjust_ptr_base = args->ukk_private; + } + + /* copy each struct into the buffer, and update its pointers */ + current_write_pos = (void *)args->system_info; + + /* first, the master struct */ + _mali_osk_memcpy(current_write_pos, system_info, sizeof(_mali_system_info)); + + /* advance write pointer */ + current_write_pos = (void *)((u32)current_write_pos + sizeof(_mali_system_info)); + + /* first we write the core info structs, patch starts at master's core_info pointer */ + current_patch_pos = (void **)((u32)args->system_info + offsetof(_mali_system_info, core_info)); + + for (current_core = system_info->core_info; NULL != current_core; current_core = current_core->next) + { + + /* patch the pointer pointing to this core */ + *current_patch_pos = (void*)(adjust_ptr_base + ((u32)current_write_pos - (u32)args->system_info)); + + /* copy the core info */ + _mali_osk_memcpy(current_write_pos, current_core, sizeof(_mali_core_info)); + + /* update patch pos */ + current_patch_pos = (void **)((u32)current_write_pos + offsetof(_mali_core_info, next)); + + /* advance write pos in memory */ + current_write_pos = (void *)((u32)current_write_pos + sizeof(_mali_core_info)); + } + /* patching of last patch pos is not needed, since we wrote NULL there in the first place */ + + /* then we write the mem info structs, patch starts at master's mem_info pointer */ + current_patch_pos = (void **)((u32)args->system_info + offsetof(_mali_system_info, mem_info)); + + for (current_mem = system_info->mem_info; NULL != current_mem; current_mem = current_mem->next) + { + /* patch the pointer pointing to this core */ + *current_patch_pos = (void*)(adjust_ptr_base + ((u32)current_write_pos - (u32)args->system_info)); + + /* copy the core info */ + _mali_osk_memcpy(current_write_pos, current_mem, sizeof(_mali_mem_info)); + + /* update patch pos */ + current_patch_pos = (void **)((u32)current_write_pos + offsetof(_mali_mem_info, next)); + + /* advance write pos in memory */ + current_write_pos = (void *)((u32)current_write_pos + sizeof(_mali_mem_info)); + } + /* patching of last patch pos is not needed, since we wrote NULL there in the first place */ + + err = _MALI_OSK_ERR_OK; +exit_when_locked: + _mali_osk_lock_signal( system_info_lock, _MALI_OSK_LOCKMODE_RW ); + MALI_ERROR(err); +} + +_mali_osk_errcode_t _mali_ukk_wait_for_notification( _mali_uk_wait_for_notification_s *args ) +{ + _mali_osk_errcode_t err; + _mali_osk_notification_t * notification; + _mali_osk_notification_queue_t *queue; + + /* check input */ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + queue = (_mali_osk_notification_queue_t *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_core_id); + + /* if the queue does not exist we're currently shutting down */ + if (NULL == queue) + { + MALI_DEBUG_PRINT(1, ("No notification queue registered with the session. Asking userspace to stop querying\n")); + args->type = _MALI_NOTIFICATION_CORE_SHUTDOWN_IN_PROGRESS; + MALI_SUCCESS; + } + + /* receive a notification, might sleep */ + err = _mali_osk_notification_queue_receive(queue, ¬ification); + if (_MALI_OSK_ERR_OK != err) + { + MALI_ERROR(err); /* errcode returned, pass on to caller */ + } + + /* copy the buffer to the user */ + args->type = (_mali_uk_notification_type)notification->notification_type; + _mali_osk_memcpy(&args->data, notification->result_buffer, notification->result_buffer_size); + + /* finished with the notification */ + _mali_osk_notification_delete( notification ); + + MALI_SUCCESS; /* all ok */ +} + +_mali_osk_errcode_t _mali_ukk_post_notification( _mali_uk_post_notification_s *args ) +{ + _mali_osk_notification_t * notification; + _mali_osk_notification_queue_t *queue; + + /* check input */ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + queue = (_mali_osk_notification_queue_t *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_core_id); + + /* if the queue does not exist we're currently shutting down */ + if (NULL == queue) + { + MALI_DEBUG_PRINT(1, ("No notification queue registered with the session. Asking userspace to stop querying\n")); + MALI_SUCCESS; + } + + notification = _mali_osk_notification_create(args->type, 0); + if ( NULL == notification) + { + MALI_PRINT_ERROR( ("Failed to create notification object\n")) ; + return _MALI_OSK_ERR_NOMEM; + } + + _mali_osk_notification_queue_send(queue, notification); + + MALI_SUCCESS; /* all ok */ +} + +static _mali_osk_errcode_t mali_kernel_subsystem_core_system_info_fill(_mali_system_info* info) +{ + MALI_CHECK_NON_NULL(info, _MALI_OSK_ERR_INVALID_ARGS); + + info->drivermode = _MALI_DRIVER_MODE_NORMAL; + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_kernel_subsystem_core_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue) +{ + MALI_CHECK_NON_NULL(slot, _MALI_OSK_ERR_INVALID_ARGS); + *slot = queue; + MALI_SUCCESS; +} + +/* MEM_VALIDATION resource handler */ +static _mali_osk_errcode_t mali_kernel_core_resource_mem_validation(_mali_osk_resource_t * resource) +{ + /* Check that no other MEM_VALIDATION resources exist */ + MALI_CHECK( ((u32)-1) == mem_validator.phys_base, _MALI_OSK_ERR_FAULT ); + + /* Check restrictions on page alignment */ + MALI_CHECK( 0 == (resource->base & (~_MALI_OSK_CPU_PAGE_MASK)), _MALI_OSK_ERR_FAULT ); + MALI_CHECK( 0 == (resource->size & (~_MALI_OSK_CPU_PAGE_MASK)), _MALI_OSK_ERR_FAULT ); + MALI_CHECK( 0 == (resource->cpu_usage_adjust & (~_MALI_OSK_CPU_PAGE_MASK)), _MALI_OSK_ERR_FAULT ); + + mem_validator.phys_base = resource->base; + mem_validator.size = resource->size; + mem_validator.cpu_usage_adjust = resource->cpu_usage_adjust; + MALI_DEBUG_PRINT( 2, ("Memory Validator '%s' installed for Mali physical address base==0x%08X, size==0x%08X, cpu_adjust==0x%08X\n", + resource->description, mem_validator.phys_base, mem_validator.size, mem_validator.cpu_usage_adjust )); + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_kernel_core_translate_cpu_to_mali_phys_range( u32 *phys_base, u32 size ) +{ + u32 mali_phys_base; + + mali_phys_base = *phys_base - mem_validator.cpu_usage_adjust; + + MALI_CHECK( 0 == ( mali_phys_base & (~_MALI_OSK_CPU_PAGE_MASK)), _MALI_OSK_ERR_FAULT ); + MALI_CHECK( 0 == ( size & (~_MALI_OSK_CPU_PAGE_MASK)), _MALI_OSK_ERR_FAULT ); + + MALI_CHECK_NO_ERROR( mali_kernel_core_validate_mali_phys_range( mali_phys_base, size ) ); + + *phys_base = mali_phys_base; + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_kernel_core_validate_mali_phys_range( u32 phys_base, u32 size ) +{ + MALI_CHECK_GOTO( 0 == ( phys_base & (~_MALI_OSK_CPU_PAGE_MASK)), failure ); + MALI_CHECK_GOTO( 0 == ( size & (~_MALI_OSK_CPU_PAGE_MASK)), failure ); + + if ( phys_base >= mem_validator.phys_base + && (phys_base + size) >= mem_validator.phys_base + && phys_base <= (mem_validator.phys_base + mem_validator.size) + && (phys_base + size) <= (mem_validator.phys_base + mem_validator.size) ) + { + MALI_SUCCESS; + } + + failure: + MALI_PRINTF( ("*******************************************************************************\n") ); + MALI_PRINTF( ("MALI PHYSICAL RANGE VALIDATION ERROR!\n") ); + MALI_PRINTF( ("\n") ); + MALI_PRINTF( ("We failed to validate a Mali-Physical range that the user-side wished to map in\n") ); + MALI_PRINTF( ("\n") ); + MALI_PRINTF( ("It is likely that the user-side wished to do Direct Rendering, but a suitable\n") ); + MALI_PRINTF( ("address range validation mechanism has not been correctly setup\n") ); + MALI_PRINTF( ("\n") ); + MALI_PRINTF( ("The range supplied was: phys_base=0x%08X, size=0x%08X\n", phys_base, size) ); + MALI_PRINTF( ("\n") ); + MALI_PRINTF( ("Please refer to the ARM Mali Software Integration Guide for more information.\n") ); + MALI_PRINTF( ("\n") ); + MALI_PRINTF( ("*******************************************************************************\n") ); + + MALI_ERROR( _MALI_OSK_ERR_FAULT ); +} + + +_mali_osk_errcode_t _mali_kernel_core_register_resource_handler(_mali_osk_resource_type_t type, mali_kernel_resource_registrator handler) +{ + MALI_CHECK(type < RESOURCE_TYPE_COUNT, _MALI_OSK_ERR_INVALID_ARGS); + MALI_DEBUG_ASSERT(NULL == resource_handler[type]); /* A handler for resource already exists */ + resource_handler[type] = handler; + MALI_SUCCESS; +} + +void * mali_kernel_session_manager_slot_get(struct mali_session_data * session_data, int id) +{ + MALI_DEBUG_ASSERT_POINTER(session_data); + if(id >= SUBSYSTEMS_COUNT) { MALI_DEBUG_PRINT(3, ("mali_kernel_session_manager_slot_get: id %d out of range\n", id)); return NULL; } + + if (NULL == session_data) { MALI_DEBUG_PRINT(3, ("mali_kernel_session_manager_slot_get: got NULL session data\n")); return NULL; } + return session_data->subsystem_data[id]; +} + +_mali_osk_errcode_t _mali_ukk_open(void **context) +{ + int i; + _mali_osk_errcode_t err; + struct mali_session_data * session_data; + + /* allocated struct to track this session */ + session_data = (struct mali_session_data *)_mali_osk_malloc(sizeof(struct mali_session_data)); + MALI_CHECK_NON_NULL(session_data, _MALI_OSK_ERR_NOMEM); + + _mali_osk_memset(session_data->subsystem_data, 0, sizeof(session_data->subsystem_data)); + + /* create a response queue for this session */ + session_data->ioctl_queue = _mali_osk_notification_queue_init(); + if (NULL == session_data->ioctl_queue) + { + _mali_osk_free(session_data); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + MALI_DEBUG_PRINT(3, ("Session starting\n")); + + /* call session_begin on all subsystems */ + for (i = 0; i < (int)SUBSYSTEMS_COUNT; ++i) + { + if (NULL != subsystems[i]->session_begin) + { + /* subsystem has a session_begin */ + err = subsystems[i]->session_begin(session_data, &session_data->subsystem_data[i], session_data->ioctl_queue); + MALI_CHECK_GOTO(err == _MALI_OSK_ERR_OK, cleanup); + } + } + + *context = (void*)session_data; + + MALI_DEBUG_PRINT(3, ("Session started\n")); + MALI_SUCCESS; + +cleanup: + MALI_DEBUG_PRINT(2, ("Session startup failed\n")); + /* i is index of subsystem which failed session begin, all indices before that has to be ended */ + /* end subsystem sessions in the reverse order they where started in */ + for (i = i - 1; i >= 0; --i) + { + if (NULL != subsystems[i]->session_end) subsystems[i]->session_end(session_data, &session_data->subsystem_data[i]); + } + + _mali_osk_notification_queue_term(session_data->ioctl_queue); + _mali_osk_free(session_data); + + /* return what the subsystem which failed session start returned */ + MALI_ERROR(err); +} + +_mali_osk_errcode_t _mali_ukk_close(void **context) +{ + int i; + struct mali_session_data * session_data; + + MALI_CHECK_NON_NULL(context, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = (struct mali_session_data *)*context; + + MALI_DEBUG_PRINT(2, ("Session ending\n")); + + /* end subsystem sessions in the reverse order they where started in */ + for (i = SUBSYSTEMS_COUNT - 1; i >= 0; --i) + { + if (NULL != subsystems[i]->session_end) subsystems[i]->session_end(session_data, &session_data->subsystem_data[i]); + } + + _mali_osk_notification_queue_term(session_data->ioctl_queue); + _mali_osk_free(session_data); + + *context = NULL; + + MALI_DEBUG_PRINT(2, ("Session has ended\n")); + + MALI_SUCCESS; +} + +#if USING_MALI_PMM + +_mali_osk_errcode_t mali_core_signal_power_up( mali_pmm_core_id core, mali_bool queue_only ) +{ + switch( core ) + { + case MALI_PMM_CORE_GP: + MALI_CHECK_NO_ERROR(maligp_signal_power_up(queue_only)); + break; +#if defined USING_MALI400_L2_CACHE + case MALI_PMM_CORE_L2: + if( !queue_only ) + { + /* Enable L2 cache due to power up */ + mali_kernel_l2_cache_do_enable(); + + /* Invalidate the cache on power up */ + MALI_DEBUG_PRINT(5, ("L2 Cache: Invalidate all\n")); + MALI_CHECK_NO_ERROR(mali_kernel_l2_cache_invalidate_all()); + } + break; +#endif + case MALI_PMM_CORE_PP0: + MALI_CHECK_NO_ERROR(malipp_signal_power_up(0, queue_only)); + break; + case MALI_PMM_CORE_PP1: + MALI_CHECK_NO_ERROR(malipp_signal_power_up(1, queue_only)); + break; + case MALI_PMM_CORE_PP2: + MALI_CHECK_NO_ERROR(malipp_signal_power_up(2, queue_only)); + break; + case MALI_PMM_CORE_PP3: + MALI_CHECK_NO_ERROR(malipp_signal_power_up(3, queue_only)); + break; + default: + /* Unknown core */ + MALI_DEBUG_PRINT_ERROR( ("Unknown core signalled with power up: %d\n", core) ); + MALI_ERROR( _MALI_OSK_ERR_INVALID_ARGS ); + } + + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_core_signal_power_down( mali_pmm_core_id core, mali_bool immediate_only ) +{ + switch( core ) + { + case MALI_PMM_CORE_GP: + MALI_CHECK_NO_ERROR(maligp_signal_power_down(immediate_only)); + break; +#if defined USING_MALI400_L2_CACHE + case MALI_PMM_CORE_L2: + /* Nothing to do */ + break; +#endif + case MALI_PMM_CORE_PP0: + MALI_CHECK_NO_ERROR(malipp_signal_power_down(0, immediate_only)); + break; + case MALI_PMM_CORE_PP1: + MALI_CHECK_NO_ERROR(malipp_signal_power_down(1, immediate_only)); + break; + case MALI_PMM_CORE_PP2: + MALI_CHECK_NO_ERROR(malipp_signal_power_down(2, immediate_only)); + break; + case MALI_PMM_CORE_PP3: + MALI_CHECK_NO_ERROR(malipp_signal_power_down(3, immediate_only)); + break; + default: + /* Unknown core */ + MALI_DEBUG_PRINT_ERROR( ("Unknown core signalled with power down: %d\n", core) ); + MALI_ERROR( _MALI_OSK_ERR_INVALID_ARGS ); + } + + MALI_SUCCESS; +} + +#endif + + +#if MALI_STATE_TRACKING +u32 _mali_kernel_core_dump_state(char* buf, u32 size) +{ + int i, n; + char *original_buf = buf; + for (i = 0; i < SUBSYSTEMS_COUNT; ++i) + { + if (NULL != subsystems[i]->dump_state) + { + n = subsystems[i]->dump_state(buf, size); + size -= n; + buf += n; + } + } +#if USING_MALI_PMM + n = mali_pmm_dump_os_thread_state(buf, size); + size -= n; + buf += n; +#endif + /* Return number of bytes written to buf */ + return (u32)(buf - original_buf); +} +#endif diff --git a/drivers/gpu/arm/mali/common/mali_kernel_core.h b/drivers/gpu/arm/mali/common/mali_kernel_core.h new file mode 100644 index 000000000000..b2fc3f0e388b --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_core.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_CORE_H__ +#define __MALI_KERNEL_CORE_H__ + +#include "mali_osk.h" + +#if USING_MALI_PMM +#include "mali_ukk.h" +#include "mali_pmm.h" +#include "mali_pmm_system.h" +#endif + +_mali_osk_errcode_t mali_kernel_constructor( void ); +void mali_kernel_destructor( void ); + +/** + * @brief Tranlate CPU physical to Mali physical addresses. + * + * This function is used to convert CPU physical addresses to Mali Physical + * addresses, such that _mali_ukk_map_external_mem may be used to map them + * into Mali. This will be used by _mali_ukk_va_to_mali_pa. + * + * This function only supports physically contiguous regions. + * + * A default implementation is provided, which uses a registered MEM_VALIDATION + * resource to do a static translation. Only an address range which will lie + * in the range specified by MEM_VALIDATION will be successfully translated. + * + * If a more complex, or non-static translation is required, then the + * implementor has the following options: + * - Rewrite this function to provide such a translation + * - Integrate the provider of the memory with UMP. + * + * @param[in,out] phys_base pointer to the page-aligned base address of the + * physical range to be translated + * + * @param[in] size size of the address range to be translated, which must be a + * multiple of the physical page size. + * + * @return on success, _MALI_OSK_ERR_OK and *phys_base is translated. If the + * cpu physical address range is not in the valid range, then a suitable + * _mali_osk_errcode_t error. + * + */ +_mali_osk_errcode_t mali_kernel_core_translate_cpu_to_mali_phys_range( u32 *phys_base, u32 size ); + + +/** + * @brief Validate a Mali physical address range. + * + * This function is used to ensure that an address range passed to + * _mali_ukk_map_external_mem is allowed to be mapped into Mali. + * + * This function only supports physically contiguous regions. + * + * A default implementation is provided, which uses a registered MEM_VALIDATION + * resource to do a static translation. Only an address range which will lie + * in the range specified by MEM_VALIDATION will be successfully validated. + * + * If a more complex, or non-static validation is required, then the + * implementor has the following options: + * - Rewrite this function to provide such a validation + * - Integrate the provider of the memory with UMP. + * + * @param phys_base page-aligned base address of the Mali physical range to be + * validated. + * + * @param size size of the address range to be validated, which must be a + * multiple of the physical page size. + * + * @return _MALI_OSK_ERR_OK if the Mali physical range is valid. Otherwise, a + * suitable _mali_osk_errcode_t error. + * + */ +_mali_osk_errcode_t mali_kernel_core_validate_mali_phys_range( u32 phys_base, u32 size ); + +#if USING_MALI_PMM +/** + * @brief Signal a power up on a Mali core. + * + * This function flags a core as powered up. + * For PP and GP cores it calls functions that move the core from a power off + * queue into the idle queue ready to run jobs. It also tries to schedule any + * pending jobs to run on it. + * + * This function will fail if the core is not powered off - either running or + * already idle. + * + * @param core The PMM core id to power up. + * @param queue_only When MALI_TRUE only re-queue the core - do not reset. + * + * @return _MALI_OSK_ERR_OK if the core has been powered up. Otherwise a + * suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_core_signal_power_up( mali_pmm_core_id core, mali_bool queue_only ); + +/** + * @brief Signal a power down on a Mali core. + * + * This function flags a core as powered down. + * For PP and GP cores it calls functions that move the core from an idle + * queue into the power off queue. + * + * This function will fail if the core is not idle - either running or + * already powered down. + * + * @param core The PMM core id to power up. + * @param immediate_only Do not set the core to pending power down if it can't + * power down immediately + * + * @return _MALI_OSK_ERR_OK if the core has been powered up. Otherwise a + * suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_core_signal_power_down( mali_pmm_core_id core, mali_bool immediate_only ); + +#endif + +/** + * Flag to indicate whether or not mali_benchmark is turned on. + */ +extern int mali_benchmark; + + +#endif /* __MALI_KERNEL_CORE_H__ */ + diff --git a/drivers/gpu/arm/mali/common/mali_kernel_descriptor_mapping.c b/drivers/gpu/arm/mali/common/mali_kernel_descriptor_mapping.c new file mode 100644 index 000000000000..2ea2d3d319eb --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_descriptor_mapping.c @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_kernel_descriptor_mapping.h" +#include "mali_osk.h" +#include "mali_osk_bitops.h" + +#define MALI_PAD_INT(x) (((x) + (BITS_PER_LONG - 1)) & ~(BITS_PER_LONG - 1)) + +/** + * Allocate a descriptor table capable of holding 'count' mappings + * @param count Number of mappings in the table + * @return Pointer to a new table, NULL on error + */ +static mali_descriptor_table * descriptor_table_alloc(int count); + +/** + * Free a descriptor table + * @param table The table to free + */ +static void descriptor_table_free(mali_descriptor_table * table); + +mali_descriptor_mapping * mali_descriptor_mapping_create(int init_entries, int max_entries) +{ + mali_descriptor_mapping * map = _mali_osk_calloc(1, sizeof(mali_descriptor_mapping)); + + init_entries = MALI_PAD_INT(init_entries); + max_entries = MALI_PAD_INT(max_entries); + + if (NULL != map) + { + map->table = descriptor_table_alloc(init_entries); + if (NULL != map->table) + { +#if !USING_MMU + map->lock = _mali_osk_lock_init( (_mali_osk_lock_flags_t)(_MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_READERWRITER | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE), 0, 20); +#else + map->lock = _mali_osk_lock_init( (_mali_osk_lock_flags_t)(_MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_READERWRITER | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE), 0, 116); +#endif + if (NULL != map->lock) + { + _mali_osk_set_nonatomic_bit(0, map->table->usage); /* reserve bit 0 to prevent NULL/zero logic to kick in */ + map->max_nr_mappings_allowed = max_entries; + map->current_nr_mappings = init_entries; + return map; + } + descriptor_table_free(map->table); + } + _mali_osk_free(map); + } + return NULL; +} + +void mali_descriptor_mapping_destroy(mali_descriptor_mapping * map) +{ + descriptor_table_free(map->table); + _mali_osk_lock_term(map->lock); + _mali_osk_free(map); +} + +_mali_osk_errcode_t mali_descriptor_mapping_allocate_mapping(mali_descriptor_mapping * map, void * target, int *odescriptor) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; + int new_descriptor; + + MALI_DEBUG_ASSERT_POINTER(map); + MALI_DEBUG_ASSERT_POINTER(odescriptor); + + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RW); + new_descriptor = _mali_osk_find_first_zero_bit(map->table->usage, map->current_nr_mappings); + if (new_descriptor == map->current_nr_mappings) + { + /* no free descriptor, try to expand the table */ + mali_descriptor_table * new_table, * old_table; + if (map->current_nr_mappings >= map->max_nr_mappings_allowed) goto unlock_and_exit; + + map->current_nr_mappings += BITS_PER_LONG; + new_table = descriptor_table_alloc(map->current_nr_mappings); + if (NULL == new_table) goto unlock_and_exit; + + old_table = map->table; + _mali_osk_memcpy(new_table->usage, old_table->usage, (sizeof(unsigned long)*map->current_nr_mappings) / BITS_PER_LONG); + _mali_osk_memcpy(new_table->mappings, old_table->mappings, map->current_nr_mappings * sizeof(void*)); + map->table = new_table; + descriptor_table_free(old_table); + } + + /* we have found a valid descriptor, set the value and usage bit */ + _mali_osk_set_nonatomic_bit(new_descriptor, map->table->usage); + map->table->mappings[new_descriptor] = target; + *odescriptor = new_descriptor; + err = _MALI_OSK_ERR_OK; + +unlock_and_exit: + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RW); + MALI_ERROR(err); +} + +void mali_descriptor_mapping_call_for_each(mali_descriptor_mapping * map, void (*callback)(int, void*)) +{ + int i; + + MALI_DEBUG_ASSERT_POINTER(map); + MALI_DEBUG_ASSERT_POINTER(callback); + + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + /* id 0 is skipped as it's an reserved ID not mapping to anything */ + for (i = 1; i < map->current_nr_mappings; ++i) + { + if (_mali_osk_test_bit(i, map->table->usage)) + { + callback(i, map->table->mappings[i]); + } + } + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RO); +} + +_mali_osk_errcode_t mali_descriptor_mapping_get(mali_descriptor_mapping * map, int descriptor, void** target) +{ + _mali_osk_errcode_t result = _MALI_OSK_ERR_FAULT; + MALI_DEBUG_ASSERT_POINTER(map); + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) + { + *target = map->table->mappings[descriptor]; + result = _MALI_OSK_ERR_OK; + } + else *target = NULL; + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RO); + MALI_ERROR(result); +} + +_mali_osk_errcode_t mali_descriptor_mapping_set(mali_descriptor_mapping * map, int descriptor, void * target) +{ + _mali_osk_errcode_t result = _MALI_OSK_ERR_FAULT; + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) + { + map->table->mappings[descriptor] = target; + result = _MALI_OSK_ERR_OK; + } + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RO); + MALI_ERROR(result); +} + +void mali_descriptor_mapping_free(mali_descriptor_mapping * map, int descriptor) +{ + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RW); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) + { + map->table->mappings[descriptor] = NULL; + _mali_osk_clear_nonatomic_bit(descriptor, map->table->usage); + } + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RW); +} + +static mali_descriptor_table * descriptor_table_alloc(int count) +{ + mali_descriptor_table * table; + + table = _mali_osk_calloc(1, sizeof(mali_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG) + (sizeof(void*) * count)); + + if (NULL != table) + { + table->usage = (u32*)((u8*)table + sizeof(mali_descriptor_table)); + table->mappings = (void**)((u8*)table + sizeof(mali_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG)); + } + + return table; +} + +static void descriptor_table_free(mali_descriptor_table * table) +{ + _mali_osk_free(table); +} diff --git a/drivers/gpu/arm/mali/common/mali_kernel_descriptor_mapping.h b/drivers/gpu/arm/mali/common/mali_kernel_descriptor_mapping.h new file mode 100644 index 000000000000..b02f16fbeb59 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_descriptor_mapping.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_kernel_descriptor_mapping.h + */ + +#ifndef __MALI_KERNEL_DESCRIPTOR_MAPPING_H__ +#define __MALI_KERNEL_DESCRIPTOR_MAPPING_H__ + +#include "mali_osk.h" + +/** + * The actual descriptor mapping table, never directly accessed by clients + */ +typedef struct mali_descriptor_table +{ + u32 * usage; /**< Pointer to bitpattern indicating if a descriptor is valid/used or not */ + void** mappings; /**< Array of the pointers the descriptors map to */ +} mali_descriptor_table; + +/** + * The descriptor mapping object + * Provides a separate namespace where we can map an integer to a pointer + */ +typedef struct mali_descriptor_mapping +{ + _mali_osk_lock_t *lock; /**< Lock protecting access to the mapping object */ + int max_nr_mappings_allowed; /**< Max number of mappings to support in this namespace */ + int current_nr_mappings; /**< Current number of possible mappings */ + mali_descriptor_table * table; /**< Pointer to the current mapping table */ +} mali_descriptor_mapping; + +/** + * Create a descriptor mapping object + * Create a descriptor mapping capable of holding init_entries growable to max_entries + * @param init_entries Number of entries to preallocate memory for + * @param max_entries Number of entries to max support + * @return Pointer to a descriptor mapping object, NULL on failure + */ +mali_descriptor_mapping * mali_descriptor_mapping_create(int init_entries, int max_entries); + +/** + * Destroy a descriptor mapping object + * @param map The map to free + */ +void mali_descriptor_mapping_destroy(mali_descriptor_mapping * map); + +/** + * Allocate a new mapping entry (descriptor ID) + * Allocates a new entry in the map. + * @param map The map to allocate a new entry in + * @param target The value to map to + * @return The descriptor allocated, a negative value on error + */ +_mali_osk_errcode_t mali_descriptor_mapping_allocate_mapping(mali_descriptor_mapping * map, void * target, int *descriptor); + +/** + * Get the value mapped to by a descriptor ID + * @param map The map to lookup the descriptor id in + * @param descriptor The descriptor ID to lookup + * @param target Pointer to a pointer which will receive the stored value + * @return 0 on successful lookup, negative on error + */ +_mali_osk_errcode_t mali_descriptor_mapping_get(mali_descriptor_mapping * map, int descriptor, void** target); + +/** + * Set the value mapped to by a descriptor ID + * @param map The map to lookup the descriptor id in + * @param descriptor The descriptor ID to lookup + * @param target Pointer to replace the current value with + * @return 0 on successful lookup, negative on error + */ +_mali_osk_errcode_t mali_descriptor_mapping_set(mali_descriptor_mapping * map, int descriptor, void * target); + +/** + * Call the specified callback function for each descriptor in map. + * Entire function is mutex protected. + * @param map The map to do callbacks for + * @param callback A callback function which will be calle for each entry in map + */ +void mali_descriptor_mapping_call_for_each(mali_descriptor_mapping * map, void (*callback)(int, void*)); + +/** + * Free the descriptor ID + * For the descriptor to be reused it has to be freed + * @param map The map to free the descriptor from + * @param descriptor The descriptor ID to free + */ +void mali_descriptor_mapping_free(mali_descriptor_mapping * map, int descriptor); + +#endif /* __MALI_KERNEL_DESCRIPTOR_MAPPING_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_gp.h b/drivers/gpu/arm/mali/common/mali_kernel_gp.h new file mode 100644 index 000000000000..ecd10e84d403 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_gp.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_GP2_H__ +#define __MALI_KERNEL_GP2_H__ + +extern struct mali_kernel_subsystem mali_subsystem_gp2; + +#if USING_MALI_PMM +_mali_osk_errcode_t maligp_signal_power_up( mali_bool queue_only ); +_mali_osk_errcode_t maligp_signal_power_down( mali_bool immediate_only ); +#endif + +#endif /* __MALI_KERNEL_GP2_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_l2_cache.c b/drivers/gpu/arm/mali/common/mali_kernel_l2_cache.c new file mode 100644 index 000000000000..7177979ee647 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_l2_cache.c @@ -0,0 +1,517 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_list.h" + +#include "mali_kernel_core.h" +#include "mali_kernel_pp.h" +#include "mali_kernel_subsystem.h" +#include "regs/mali_200_regs.h" +#include "mali_kernel_rendercore.h" +#include "mali_kernel_l2_cache.h" + +/** + * Size of the Mali L2 cache registers in bytes + */ +#define MALI400_L2_CACHE_REGISTERS_SIZE 0x30 + +/** + * Mali L2 cache register numbers + * Used in the register read/write routines. + * See the hardware documentation for more information about each register + */ +typedef enum mali_l2_cache_register { + MALI400_L2_CACHE_REGISTER_STATUS = 0x0002, + /*unused = 0x0003 */ + MALI400_L2_CACHE_REGISTER_COMMAND = 0x0004, /**< Misc cache commands, e.g. clear */ + MALI400_L2_CACHE_REGISTER_CLEAR_PAGE = 0x0005, + MALI400_L2_CACHE_REGISTER_MAX_READS = 0x0006, /**< Limit of outstanding read requests */ + MALI400_L2_CACHE_REGISTER_ENABLE = 0x0007, /**< Enable misc cache features */ + MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0 = 0x0008, + MALI400_L2_CACHE_REGISTER_PERFCNT_VAL0 = 0x0009, + MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1 = 0x000A, + MALI400_L2_CACHE_REGISTER_PERFCNT_VAL1 = 0x000B, +} mali_l2_cache_register; + + +/** + * Mali L2 cache commands + * These are the commands that can be sent to the Mali L2 cache unit + */ +typedef enum mali_l2_cache_command +{ + MALI400_L2_CACHE_COMMAND_CLEAR_ALL = 0x01, /**< Clear the entire cache */ + /* Read HW TRM carefully before adding/using other commands than the clear above */ +} mali_l2_cache_command; + +/** + * Mali L2 cache commands + * These are the commands that can be sent to the Mali L2 cache unit + */ +typedef enum mali_l2_cache_enable +{ + MALI400_L2_CACHE_ENABLE_DEFAULT = 0x0, /**< Default state of enable register */ + MALI400_L2_CACHE_ENABLE_ACCESS = 0x01, /**< Permit cacheable accesses */ + MALI400_L2_CACHE_ENABLE_READ_ALLOCATE = 0x02, /**< Permit cache read allocate */ +} mali_l2_cache_enable; + +/** + * Mali L2 cache status bits + */ +typedef enum mali_l2_cache_status +{ + MALI400_L2_CACHE_STATUS_COMMAND_BUSY = 0x01, /**< Command handler of L2 cache is busy */ + MALI400_L2_CACHE_STATUS_DATA_BUSY = 0x02, /**< L2 cache is busy handling data requests */ +} mali_l2_cache_status; + + +/** + * Definition of the L2 cache core struct + * Used to track a L2 cache unit in the system. + * Contains information about the mapping of the registers + */ +typedef struct mali_kernel_l2_cache_core +{ + unsigned long base; /**< Physical address of the registers */ + mali_io_address mapped_registers; /**< Virtual mapping of the registers */ + u32 mapping_size; /**< Size of registers in bytes */ + _mali_osk_list_t list; /**< Used to link multiple cache cores into a list */ + _mali_osk_lock_t *lock; /**< Serialize all L2 cache commands */ +} mali_kernel_l2_cache_core; + + +#define MALI400_L2_MAX_READS_DEFAULT 0x1C + +int mali_l2_max_reads = MALI400_L2_MAX_READS_DEFAULT; + + +/** + * Mali L2 cache subsystem startup function + * Called by the driver core when the driver is loaded. + * + * @param id Identifier assigned by the core to the L2 cache subsystem + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_l2_cache_initialize(mali_kernel_subsystem_identifier id); + +/** + * Mali L2 cache subsystem shutdown function + * Called by the driver core when the driver is unloaded. + * Cleans up + * @param id Identifier assigned by the core to the L2 cache subsystem + */ +static void mali_l2_cache_terminate(mali_kernel_subsystem_identifier id); + +/** + * L2 cache subsystem complete notification function. + * Called by the driver core when all drivers have loaded and all resources has been registered + * @param id Identifier assigned by the core to the L2 cache subsystem + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_l2_cache_load_complete(mali_kernel_subsystem_identifier id); + +/** + * Mali L2 cache subsystem's notification handler for a Mali L2 cache resource instances. + * Registered with the core during startup. + * Called by the core for each Mali L2 cache described in the active architecture's config.h file. + * @param resource The resource to handle (type MALI400L2) + * @return 0 if the Mali L2 cache was found and initialized, negative on error + */ +static _mali_osk_errcode_t mali_l2_cache_core_create(_mali_osk_resource_t * resource); + +/** + * Write to a L2 cache register + * Writes the given value to the specified register + * @param unit The L2 cache to write to + * @param reg The register to write to + * @param val The value to write to the register + */ +static void mali_l2_cache_register_write(mali_kernel_l2_cache_core * unit, mali_l2_cache_register reg, u32 val); + + + +/** + * Invalidate specified L2 cache + * @param cache The L2 cache to invalidate + * @return 0 if Mali L2 cache was successfully invalidated, otherwise error + */ +static _mali_osk_errcode_t mali_kernel_l2_cache_invalidate_all_cache(mali_kernel_l2_cache_core *cache); + + +/* + The fixed Mali L2 cache system's mali subsystem interface implementation. + We currently handle module and session life-time management. +*/ +struct mali_kernel_subsystem mali_subsystem_l2_cache = +{ + mali_l2_cache_initialize, /**< startup */ + mali_l2_cache_terminate, /**< shutdown */ + mali_l2_cache_load_complete, /**< load_complete */ + NULL, /**< system_info_fill */ + NULL, /**< session_begin */ + NULL, /**< session_end */ + NULL, /**< broadcast_notification */ +#if MALI_STATE_TRACKING + NULL, /**< dump_state */ +#endif +}; + + + +static _MALI_OSK_LIST_HEAD(caches_head); + + + + +/* called during module init */ +static _mali_osk_errcode_t mali_l2_cache_initialize(mali_kernel_subsystem_identifier id) +{ + _mali_osk_errcode_t err; + + MALI_IGNORE( id ); + + MALI_DEBUG_PRINT(2, ( "Mali L2 cache system initializing\n")); + + _MALI_OSK_INIT_LIST_HEAD(&caches_head); + + /* This will register the function for adding Mali L2 cache cores to the subsystem */ + err = _mali_kernel_core_register_resource_handler(MALI400L2, mali_l2_cache_core_create); + + MALI_ERROR(err); +} + + + +/* called if/when our module is unloaded */ +static void mali_l2_cache_terminate(mali_kernel_subsystem_identifier id) +{ + mali_kernel_l2_cache_core * cache, *temp_cache; + + MALI_DEBUG_PRINT(2, ( "Mali L2 cache system terminating\n")); + + /* loop over all L2 cache units and shut them down */ + _MALI_OSK_LIST_FOREACHENTRY( cache, temp_cache, &caches_head, mali_kernel_l2_cache_core, list ) + { + /* reset to defaults */ + mali_l2_cache_register_write(cache, MALI400_L2_CACHE_REGISTER_MAX_READS, (u32)MALI400_L2_MAX_READS_DEFAULT); + mali_l2_cache_register_write(cache, MALI400_L2_CACHE_REGISTER_ENABLE, (u32)MALI400_L2_CACHE_ENABLE_DEFAULT); + + /* remove from the list of cacges on the system */ + _mali_osk_list_del( &cache->list ); + + /* release resources */ + _mali_osk_mem_unmapioregion( cache->base, cache->mapping_size, cache->mapped_registers ); + _mali_osk_mem_unreqregion( cache->base, cache->mapping_size ); + _mali_osk_lock_term( cache->lock ); + _mali_osk_free( cache ); + + #if USING_MALI_PMM + /* Unregister the L2 cache with the PMM */ + malipmm_core_unregister( MALI_PMM_CORE_L2 ); + #endif + } +} + +static _mali_osk_errcode_t mali_l2_cache_core_create(_mali_osk_resource_t * resource) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT ; + mali_kernel_l2_cache_core * cache = NULL; + + MALI_DEBUG_PRINT(2, ( "Creating Mali L2 cache: %s\n", resource->description)); + +#if USING_MALI_PMM + /* Register the L2 cache with the PMM */ + err = malipmm_core_register( MALI_PMM_CORE_L2 ); + if( _MALI_OSK_ERR_OK != err ) + { + MALI_DEBUG_PRINT(1, ( "Failed to register L2 cache unit with PMM")); + return err; + } +#endif + + err = _mali_osk_mem_reqregion( resource->base, MALI400_L2_CACHE_REGISTERS_SIZE, resource->description); + + MALI_CHECK_GOTO( _MALI_OSK_ERR_OK == err, err_cleanup_requestmem_failed); + + /* Reset error that might be passed out */ + err = _MALI_OSK_ERR_FAULT; + + cache = _mali_osk_malloc(sizeof(mali_kernel_l2_cache_core)); + + MALI_CHECK_GOTO( NULL != cache, err_cleanup); + + cache->lock = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_SPINLOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, 104 ); + + MALI_CHECK_GOTO( NULL != cache->lock, err_cleanup); + + /* basic setup */ + _MALI_OSK_INIT_LIST_HEAD(&cache->list); + + cache->base = resource->base; + cache->mapping_size = MALI400_L2_CACHE_REGISTERS_SIZE; + + /* map the registers */ + cache->mapped_registers = _mali_osk_mem_mapioregion( cache->base, cache->mapping_size, resource->description ); + + MALI_CHECK_GOTO( NULL != cache->mapped_registers, err_cleanup); + + /* Invalidate cache (just to keep it in a known state at startup) */ + err = mali_kernel_l2_cache_invalidate_all_cache(cache); + + MALI_CHECK_GOTO( _MALI_OSK_ERR_OK == err, err_cleanup); + + /* add to our list of L2 caches */ + _mali_osk_list_add( &cache->list, &caches_head ); + + MALI_SUCCESS; + +err_cleanup: + /* This cleanup used when resources have been requested successfully */ + + if ( NULL != cache ) + { + if (NULL != cache->mapped_registers) + { + _mali_osk_mem_unmapioregion( cache->base, cache->mapping_size, cache->mapped_registers); + } + else + { + MALI_DEBUG_PRINT(1, ( "Failed to map Mali L2 cache registers at 0x%08lX\n", cache->base)); + } + + if( NULL != cache->lock ) + { + _mali_osk_lock_term( cache->lock ); + } + else + { + MALI_DEBUG_PRINT(1, ( "Failed to allocate a lock for handling a L2 cache unit")); + } + + _mali_osk_free( cache ); + } + else + { + MALI_DEBUG_PRINT(1, ( "Failed to allocate memory for handling a L2 cache unit")); + } + + /* A call is to request region, so this must always be reversed */ + _mali_osk_mem_unreqregion( resource->base, MALI400_L2_CACHE_REGISTERS_SIZE); +#if USING_MALI_PMM + malipmm_core_unregister( MALI_PMM_CORE_L2 ); +#endif + return err; + +err_cleanup_requestmem_failed: + MALI_DEBUG_PRINT(1, ("Failed to request Mali L2 cache '%s' register address space at (0x%08X - 0x%08X)\n", + resource->description, resource->base, resource->base + MALI400_L2_CACHE_REGISTERS_SIZE - 1) ); +#if USING_MALI_PMM + malipmm_core_unregister( MALI_PMM_CORE_L2 ); +#endif + return err; + +} + + +static void mali_l2_cache_register_write(mali_kernel_l2_cache_core * unit, mali_l2_cache_register reg, u32 val) +{ + _mali_osk_mem_iowrite32(unit->mapped_registers, (u32)reg * sizeof(u32), val); +} + + +static u32 mali_l2_cache_register_read(mali_kernel_l2_cache_core * unit, mali_l2_cache_register reg) +{ + return _mali_osk_mem_ioread32(unit->mapped_registers, (u32)reg * sizeof(u32)); +} + +void mali_kernel_l2_cache_do_enable(void) +{ + mali_kernel_l2_cache_core * cache, *temp_cache; + + /* loop over all L2 cache units and enable them*/ + _MALI_OSK_LIST_FOREACHENTRY( cache, temp_cache, &caches_head, mali_kernel_l2_cache_core, list) + { + mali_l2_cache_register_write(cache, MALI400_L2_CACHE_REGISTER_ENABLE, (u32)MALI400_L2_CACHE_ENABLE_ACCESS | (u32)MALI400_L2_CACHE_ENABLE_READ_ALLOCATE); + mali_l2_cache_register_write(cache, MALI400_L2_CACHE_REGISTER_MAX_READS, (u32)mali_l2_max_reads); + } +} + + +static _mali_osk_errcode_t mali_l2_cache_load_complete(mali_kernel_subsystem_identifier id) +{ + mali_kernel_l2_cache_do_enable(); + MALI_DEBUG_PRINT(2, ( "Mali L2 cache system load complete\n")); + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_kernel_l2_cache_send_command(mali_kernel_l2_cache_core *cache, u32 reg, u32 val) +{ + int i = 0; + const int loop_count = 100000; + + /* + * Grab lock in order to send commands to the L2 cache in a serialized fashion. + * The L2 cache will ignore commands if it is busy. + */ + _mali_osk_lock_wait(cache->lock, _MALI_OSK_LOCKMODE_RW); + + /* First, wait for L2 cache command handler to go idle */ + + for (i = 0; i < loop_count; i++) + { + if (!(_mali_osk_mem_ioread32(cache->mapped_registers , (u32)MALI400_L2_CACHE_REGISTER_STATUS * sizeof(u32)) & (u32)MALI400_L2_CACHE_STATUS_COMMAND_BUSY)) + { + break; + } + } + + if (i == loop_count) + { + _mali_osk_lock_signal(cache->lock, _MALI_OSK_LOCKMODE_RW); + MALI_DEBUG_PRINT(1, ( "Mali L2 cache: aborting wait for command interface to go idle\n")); + MALI_ERROR( _MALI_OSK_ERR_FAULT ); + } + + /* then issue the command */ + mali_l2_cache_register_write(cache, reg, val); + + _mali_osk_lock_signal(cache->lock, _MALI_OSK_LOCKMODE_RW); + MALI_SUCCESS; +} + + +static _mali_osk_errcode_t mali_kernel_l2_cache_invalidate_all_cache(mali_kernel_l2_cache_core *cache) +{ + return mali_kernel_l2_cache_send_command(cache, MALI400_L2_CACHE_REGISTER_COMMAND, MALI400_L2_CACHE_COMMAND_CLEAR_ALL); +} + +_mali_osk_errcode_t mali_kernel_l2_cache_invalidate_all(void) +{ + mali_kernel_l2_cache_core * cache, *temp_cache; + + /* loop over all L2 cache units and invalidate them */ + + _MALI_OSK_LIST_FOREACHENTRY( cache, temp_cache, &caches_head, mali_kernel_l2_cache_core, list) + { + MALI_CHECK_NO_ERROR( mali_kernel_l2_cache_invalidate_all_cache(cache) ); + } + + MALI_SUCCESS; +} + + +static _mali_osk_errcode_t mali_kernel_l2_cache_invalidate_page_cache(mali_kernel_l2_cache_core *cache, u32 page) +{ + return mali_kernel_l2_cache_send_command(cache, MALI400_L2_CACHE_REGISTER_CLEAR_PAGE, page); +} + +_mali_osk_errcode_t mali_kernel_l2_cache_invalidate_page(u32 page) +{ + mali_kernel_l2_cache_core * cache, *temp_cache; + + /* loop over all L2 cache units and invalidate them */ + + _MALI_OSK_LIST_FOREACHENTRY( cache, temp_cache, &caches_head, mali_kernel_l2_cache_core, list) + { + MALI_CHECK_NO_ERROR( mali_kernel_l2_cache_invalidate_page_cache(cache, page) ); + } + + MALI_SUCCESS; +} + + +void mali_kernel_l2_cache_set_perf_counters(u32 src0, u32 src1, int force_reset) +{ + mali_kernel_l2_cache_core * cache, *temp_cache; + int reset0 = force_reset; + int reset1 = force_reset; + MALI_DEBUG_CODE( + int changed0 = 0; + int changed1 = 0; + ) + + /* loop over all L2 cache units and activate the counters on them */ + _MALI_OSK_LIST_FOREACHENTRY(cache, temp_cache, &caches_head, mali_kernel_l2_cache_core, list) + { + u32 cur_src0 = mali_l2_cache_register_read(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0); + u32 cur_src1 = mali_l2_cache_register_read(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1); + + if (src0 != cur_src0) + { + mali_l2_cache_register_write(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0, src0); + MALI_DEBUG_CODE(changed0 = 1;) + reset0 = 1; + } + + if (src1 != cur_src1) + { + mali_l2_cache_register_write(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1, src1); + MALI_DEBUG_CODE(changed1 = 1;) + reset1 = 1; + } + + if (reset0) + { + mali_l2_cache_register_write(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_VAL0, 0); + } + + if (reset1) + { + mali_l2_cache_register_write(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_VAL1, 0); + } + + MALI_DEBUG_PRINT(5, ("L2 cache counters set: SRC0=%u, CHANGED0=%d, RESET0=%d, SRC1=%u, CHANGED1=%d, RESET1=%d\n", + src0, changed0, reset0, + src1, changed1, reset1)); + } +} + + +void mali_kernel_l2_cache_get_perf_counters(u32 *src0, u32 *val0, u32 *src1, u32 *val1) +{ + mali_kernel_l2_cache_core * cache, *temp_cache; + int first_time = 1; + *src0 = 0; + *src1 = 0; + *val0 = 0; + *val1 = 0; + + /* loop over all L2 cache units and read the counters */ + _MALI_OSK_LIST_FOREACHENTRY(cache, temp_cache, &caches_head, mali_kernel_l2_cache_core, list) + { + u32 cur_src0 = mali_l2_cache_register_read(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC0); + u32 cur_src1 = mali_l2_cache_register_read(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_SRC1); + u32 cur_val0 = mali_l2_cache_register_read(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_VAL0); + u32 cur_val1 = mali_l2_cache_register_read(cache, MALI400_L2_CACHE_REGISTER_PERFCNT_VAL1); + + MALI_DEBUG_PRINT(5, ("L2 cache counters get: SRC0=%u, VAL0=%u, SRC1=%u, VAL1=%u\n", cur_src0, cur_val0, cur_src1, cur_val1)); + + /* Only update the counter source once, with the value from the first L2 cache unit. */ + if (first_time) + { + *src0 = cur_src0; + *src1 = cur_src1; + first_time = 0; + } + + /* Bail out if the L2 cache units have different counters set. */ + if (*src0 == cur_src0 && *src1 == cur_src1) + { + *val0 += cur_val0; + *val1 += cur_val1; + } + else + { + MALI_DEBUG_PRINT(1, ("Warning: Mali L2 caches has different performance counters set, not retrieving data\n")); + } + } +} diff --git a/drivers/gpu/arm/mali/common/mali_kernel_l2_cache.h b/drivers/gpu/arm/mali/common/mali_kernel_l2_cache.h new file mode 100644 index 000000000000..1588850c1c72 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_l2_cache.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_L2_CACHE_H__ +#define __MALI_KERNEL_L2_CACHE_H__ + +#include "mali_osk.h" +#include "mali_kernel_subsystem.h" +extern struct mali_kernel_subsystem mali_subsystem_l2_cache; + +_mali_osk_errcode_t mali_kernel_l2_cache_invalidate_all(void); +_mali_osk_errcode_t mali_kernel_l2_cache_invalidate_page(u32 page); + +void mali_kernel_l2_cache_do_enable(void); +void mali_kernel_l2_cache_set_perf_counters(u32 src0, u32 src1, int force_reset); +void mali_kernel_l2_cache_get_perf_counters(u32 *src0, u32 *val0, u32 *src1, u32 *val1); + +#endif /* __MALI_KERNEL_L2_CACHE_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_mem.h b/drivers/gpu/arm/mali/common/mali_kernel_mem.h new file mode 100644 index 000000000000..dddf16223312 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_mem.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_MEM_H__ +#define __MALI_KERNEL_MEM_H__ + +#include "mali_kernel_subsystem.h" +extern struct mali_kernel_subsystem mali_subsystem_memory; + +#endif /* __MALI_KERNEL_MEM_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_mem_buddy.c b/drivers/gpu/arm/mali/common/mali_kernel_mem_buddy.c new file mode 100644 index 000000000000..49190db7ada9 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_mem_buddy.c @@ -0,0 +1,1426 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_core.h" +#include "mali_kernel_subsystem.h" +#include "mali_kernel_mem.h" +#include "mali_kernel_descriptor_mapping.h" +#include "mali_kernel_session_manager.h" + +/* kernel side OS functions and user-kernel interface */ +#include "mali_osk.h" +#include "mali_osk_mali.h" +#include "mali_osk_list.h" +#include "mali_ukk.h" + +#ifdef _MALI_OSK_SPECIFIC_INDIRECT_MMAP +#include "mali_osk_indir_mmap.h" +#endif + +#error Support for non-MMU builds is no longer supported and is planned for removal. + +/** + * Minimum memory allocation size + */ +#define MIN_BLOCK_SIZE (1024*1024UL) + +/** + * Per-session memory descriptor mapping table sizes + */ +#define MALI_MEM_DESCRIPTORS_INIT 64 +#define MALI_MEM_DESCRIPTORS_MAX 4096 + +/** + * Enum uses to store multiple fields in one u32 to keep the memory block struct small + */ +enum MISC_SHIFT { MISC_SHIFT_FREE = 0, MISC_SHIFT_ORDER = 1, MISC_SHIFT_TOPLEVEL = 6 }; +enum MISC_MASK { MISC_MASK_FREE = 0x01, MISC_MASK_ORDER = 0x1F, MISC_MASK_TOPLEVEL = 0x1F }; + +/* forward declaration of the block struct */ +struct mali_memory_block; + +/** + * Definition of memory bank type. + * Represents a memory bank (separate address space) + * Each bank keeps track of its block usage. + * A buddy system used to track the usage +*/ +typedef struct mali_memory_bank +{ + _mali_osk_list_t list; /* links multiple banks together */ + _mali_osk_lock_t *lock; + u32 base_addr; /* Mali seen address of bank */ + u32 cpu_usage_adjust; /* Adjustment factor for what the CPU sees */ + u32 size; /* the effective size */ + u32 real_size; /* the real size of the bank, as given by to the subsystem */ + int min_order; + int max_order; + struct mali_memory_block * blocklist; + _mali_osk_list_t *freelist; + _mali_osk_atomic_t num_active_allocations; + u32 used_for_flags; + u32 alloc_order; /**< Order in which the bank will be used for allocations */ + const char *name; /**< Descriptive name of the bank */ +} mali_memory_bank; + +/** + * Definition of the memory block type + * Represents a memory block, which is the smallest memory unit operated on. + * A block keeps info about its mapping, if in use by a user process + */ +typedef struct mali_memory_block +{ + _mali_osk_list_t link; /* used for freelist and process usage list*/ + mali_memory_bank * bank; /* the bank it belongs to */ + void __user * mapping; /* possible user space mapping of this block */ + u32 misc; /* used while a block is free to track the number blocks it represents */ + int descriptor; + u32 mmap_cookie; /**< necessary for interaction with _mali_ukk_mem_mmap/munmap */ +} mali_memory_block; + +/** + * Defintion of the type used to represent memory used by a session. + * Containts the head of the list of memory currently in use by a session. + */ +typedef struct memory_session +{ + _mali_osk_lock_t *lock; + _mali_osk_list_t memory_head; /* List of the memory blocks used by this session. */ + mali_descriptor_mapping * descriptor_mapping; /**< Mapping between userspace descriptors and our pointers */ +} memory_session; + +/* + Subsystem interface implementation +*/ +/** + * Buddy block memory subsystem startup function + * Called by the driver core when the driver is loaded. + * Registers the memory systems ioctl handler, resource handlers and memory map function with the core. + * + * @param id Identifier assigned by the core to the memory subsystem + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_initialize(mali_kernel_subsystem_identifier id); + +/** + * Buddy block memory subsystem shutdown function + * Called by the driver core when the driver is unloaded. + * Cleans up + * @param id Identifier assigned by the core to the memory subsystem + */ +static void mali_memory_core_terminate(mali_kernel_subsystem_identifier id); + +/** + * Buddy block memory load complete notification function. + * Called by the driver core when all drivers have loaded and all resources has been registered + * Reports on the memory resources registered + * @param id Identifier assigned by the core to the memory subsystem + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_load_complete(mali_kernel_subsystem_identifier id); + + +/** + * Buddy block memory subsystem session begin notification + * Called by the core when a new session to the driver is started. + * Creates a memory session object and sets it as the subsystem slot data for this session + * @param slot Pointer to the slot to use for storing per-session data + * @param queue The user space event sink + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue); + +/** + * Buddy block memory subsystem session end notification + * Called by the core when a session to the driver has ended. + * Cleans up per session data, which includes checking and fixing memory leaks + * + * @param slot Pointer to the slot to use for storing per-session data + */ +static void mali_memory_core_session_end(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot); + +/** + * Buddy block memory subsystem system info filler + * Called by the core when a system info update is needed + * We fill in info about all the memory types we have + * @param info Pointer to system info struct to update + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_system_info_fill(_mali_system_info* info); + +/* our registered resource handlers */ +/** + * Buddy block memory subsystem's notification handler for MEMORY resource instances. + * Registered with the core during startup. + * Called by the core for each memory bank described in the active architecture's config.h file. + * Requests memory region ownership and calls backend. + * @param resource The resource to handle (type MEMORY) + * @return 0 if the memory was claimed and accepted, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_resource_memory(_mali_osk_resource_t * resource); + +/** + * Buddy block memory subsystem's notification handler for MMU resource instances. + * Registered with the core during startup. + * Called by the core for each mmu described in the active architecture's config.h file. + * @param resource The resource to handle (type MMU) + * @return 0 if the MMU was found and initialized, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_resource_mmu(_mali_osk_resource_t * resource); + +/** + * Buddy block memory subsystem's notification handler for FPGA_FRAMEWORK resource instances. + * Registered with the core during startup. + * Called by the core for each fpga framework described in the active architecture's config.h file. + * @param resource The resource to handle (type FPGA_FRAMEWORK) + * @return 0 if the FPGA framework was found and initialized, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_resource_fpga(_mali_osk_resource_t * resource); + +/* ioctl command implementations */ +/** + * Buddy block memory subsystem's handler for MALI_IOC_MEM_GET_BIG_BLOCK ioctl + * Called by the generic ioctl handler when the MALI_IOC_MEM_GET_BIG_BLOCK command is received. + * Finds an available memory block and maps into the current process' address space. + * @param ukk_private private word for use by the User/Kernel interface + * @param session_data Pointer to the per-session object which will track the memory usage + * @param argument The argument from the user. A pointer to an struct mali_dd_get_big_block in user space + * @return Zero if successful, a standard Linux error value value on error (a negative value) + */ +_mali_osk_errcode_t _mali_ukk_get_big_block( _mali_uk_get_big_block_s *args ); + +/** + * Buddy block memory subsystem's handler for MALI_IOC_MEM_FREE_BIG_BLOCK ioctl + * Called by the generic ioctl handler when the MALI_IOC_MEM_FREE_BIG_BLOCK command is received. + * Unmaps the memory from the process' address space and marks the block as free. + * @param session_data Pointer to the per-session object which tracks the memory usage + * @param argument The argument from the user. A pointer to an struct mali_dd_get_big_block in user space + * @return Zero if successful, a standard Linux error value value on error (a negative value) + */ + +/* this static version allows us to make use of it while holding the memory_session lock. + * This is required for the session_end code */ +static _mali_osk_errcode_t _mali_ukk_free_big_block_internal( struct mali_session_data * mali_session_data, memory_session * session_data, _mali_uk_free_big_block_s *args); + +_mali_osk_errcode_t _mali_ukk_free_big_block( _mali_uk_free_big_block_s *args ); + +/** + * Buddy block memory subsystem's memory bank registration routine + * Called when a MEMORY resource has been found. + * The memory region has already been reserved for use by this driver. + * Create a bank object to represent this region and initialize its slots. + * @note Can only be called in an module atomic scope, i.e. during module init since no locking is performed + * @param phys_base Physical base address of this bank + * @param cpu_usage_adjust Adjustment factor for CPU seen address + * @param size Size of the bank in bytes + * @param flags Memory type bits + * @param alloc_order Order in which the bank will be used for allocations + * @param name descriptive name of the bank + * @return Zero on success, negative on error + */ +static int mali_memory_bank_register(u32 phys_base, u32 cpu_usage_adjust, u32 size, u32 flags, u32 alloc_order, const char *name); + +/** + * Get a block of mali memory of at least the given size and of the given type + * This is the backend for get_big_block. + * @param type_id The type id of memory requested. + * @param minimum_size The size requested + * @return Pointer to a block on success, NULL on failure + */ +static mali_memory_block * mali_memory_block_get(u32 type_id, u32 minimum_size); + +/** + * Get the mali seen address of the memory described by the block + * @param block The memory block to return the address of + * @return The mali seen address of the memory block + */ +MALI_STATIC_INLINE u32 block_mali_addr_get(mali_memory_block * block); + +/** + * Get the cpu seen address of the memory described by the block + * The cpu_usage_adjust will be used to change the mali seen phys address + * @param block The memory block to return the address of + * @return The mali seen address of the memory block + */ +MALI_STATIC_INLINE u32 block_cpu_addr_get(mali_memory_block * block); + +/** + * Get the size of the memory described by the given block + * @param block The memory block to return the size of + * @return The size of the memory block described by the object + */ +MALI_STATIC_INLINE u32 block_size_get(mali_memory_block * block); + +/** + * Get the user space accessible mapping the memory described by the given memory block + * Returns a pointer in user space to the memory, if one has been created. + * @param block The memory block to return the mapping of + * @return User space pointer to cpu accessible memory or NULL if not mapped + */ +MALI_STATIC_INLINE void __user * block_mapping_get(mali_memory_block * block); + +/** + * Set the user space accessible mapping the memory described by the given memory block. + * Sets the stored pointer to user space for the memory described by this block. + * @param block The memory block to set mapping info for + * @param ptr User space pointer to cpu accessible memory or NULL if not mapped + */ +MALI_STATIC_INLINE void block_mapping_set(mali_memory_block * block, void __user * ptr); + +/** + * Get the cookie for use with _mali_ukk_mem_munmap(). + * @param block The memory block to get the cookie from + * @return the cookie. A return of 0 is still a valid cookie. + */ +MALI_STATIC_INLINE u32 block_mmap_cookie_get(mali_memory_block * block); + +/** + * Set the cookie returned via _mali_ukk_mem_mmap(). + * @param block The memory block to set the cookie for + * @param cookie the cookie + */ +MALI_STATIC_INLINE void block_mmap_cookie_set(mali_memory_block * block, u32 cookie); + + +/** + * Get a memory block's free status + * @param block The block to get the state of + */ +MALI_STATIC_INLINE u32 get_block_free(mali_memory_block * block); + +/** + * Set a memory block's free status + * @param block The block to set the state for + * @param state The state to set + */ +MALI_STATIC_INLINE void set_block_free(mali_memory_block * block, int state); + +/** + * Set a memory block's order + * @param block The block to set the order for + * @param order The order to set + */ +MALI_STATIC_INLINE void set_block_order(mali_memory_block * block, u32 order); + +/** + * Get a memory block's order + * @param block The block to get the order for + * @return The order this block exists on + */ +MALI_STATIC_INLINE u32 get_block_order(mali_memory_block * block); + +/** + * Tag a block as being a toplevel block. + * A toplevel block has no buddy and no parent + * @param block The block to tag as being toplevel + */ +MALI_STATIC_INLINE void set_block_toplevel(mali_memory_block * block, u32 level); + +/** + * Check if a block is a toplevel block + * @param block The block to check + * @return 1 if toplevel, 0 else + */ +MALI_STATIC_INLINE u32 get_block_toplevel(mali_memory_block * block); + +/** + * Checks if the given block is a buddy at the given order and that it's free + * @param block The block to check + * @param order The order to check against + * @return 0 if not valid, else 1 + */ +MALI_STATIC_INLINE int block_is_valid_buddy(mali_memory_block * block, int order); + +/* + The buddy system uses the following rules to quickly find a blocks buddy + and parent (block representing this block at a higher order level): + - Given a block with index i the blocks buddy is at index i ^ ( 1 << order) + - Given a block with index i the blocks parent is at i & ~(1 << order) +*/ + +/** + * Get a blocks buddy + * @param block The block to find the buddy for + * @param order The order to operate on + * @return Pointer to the buddy block + */ +MALI_STATIC_INLINE mali_memory_block * block_get_buddy(mali_memory_block * block, u32 order); + +/** + * Get a blocks parent + * @param block The block to find the parent for + * @param order The order to operate on + * @return Pointer to the parent block + */ +MALI_STATIC_INLINE mali_memory_block * block_get_parent(mali_memory_block * block, u32 order); + +/** + * Release mali memory + * Backend for free_big_block. + * Will release the mali memory described by the given block struct. + * @param block Memory block to free + */ +static void block_release(mali_memory_block * block); + +/* end interface implementation */ + +/** + * List of all the memory banks registerd with the subsystem. + * Access to this list is NOT synchronized since it's only + * written to during module init and termination. + */ +static _MALI_OSK_LIST_HEAD(memory_banks_list); + +/* + The buddy memory system's mali subsystem interface implementation. + We currently handle module and session life-time management. +*/ +struct mali_kernel_subsystem mali_subsystem_memory = +{ + mali_memory_core_initialize, /* startup */ + mali_memory_core_terminate, /* shutdown */ + mali_memory_core_load_complete, /* load_complete */ + mali_memory_core_system_info_fill, /* system_info_fill */ + mali_memory_core_session_begin, /* session_begin */ + mali_memory_core_session_end, /* session_end */ + NULL, /* broadcast_notification */ +#if MALI_STATE_TRACKING + NULL, /* dump_state */ +#endif +}; + +/* Initialized when this subsystem is initialized. This is determined by the + * position in subsystems[], and so the value used to initialize this is + * determined at compile time */ +static mali_kernel_subsystem_identifier mali_subsystem_memory_id = -1; + +/* called during module init */ +static _mali_osk_errcode_t mali_memory_core_initialize(mali_kernel_subsystem_identifier id) +{ + _MALI_OSK_INIT_LIST_HEAD(&memory_banks_list); + + mali_subsystem_memory_id = id; + + /* register our handlers */ + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(MEMORY, mali_memory_core_resource_memory)); + + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(MMU, mali_memory_core_resource_mmu)); + + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(FPGA_FRAMEWORK, mali_memory_core_resource_fpga)); + + MALI_SUCCESS; +} + +/* called if/when our module is unloaded */ +static void mali_memory_core_terminate(mali_kernel_subsystem_identifier id) +{ + mali_memory_bank * bank, *temp; + + /* loop over all memory banks to free them */ + /* we use the safe version since we delete the current bank in the body */ + _MALI_OSK_LIST_FOREACHENTRY(bank, temp, &memory_banks_list, mali_memory_bank, list) + { + MALI_DEBUG_CODE(int usage_count = _mali_osk_atomic_read(&bank->num_active_allocations)); + /* + Report leaked memory + */ + MALI_DEBUG_PRINT_IF(1, 0 != usage_count, ("%d allocation(s) from memory bank at 0x%X still in use\n", usage_count, bank->base_addr)); + + _mali_osk_atomic_term(&bank->num_active_allocations); + + _mali_osk_lock_term(bank->lock); + + /* unlink from bank list */ + _mali_osk_list_del(&bank->list); + + /* release kernel resources used by the bank */ + _mali_osk_mem_unreqregion(bank->base_addr, bank->real_size); + + /* remove all resources used to represent this bank*/ + _mali_osk_free(bank->freelist); + _mali_osk_free(bank->blocklist); + + /* destroy the bank object itself */ + _mali_osk_free(bank); + } + + /* No need to de-initialize mali_subsystem_memory_id - it could only be + * re-initialized to the same value */ +} + +/* load_complete handler */ +static _mali_osk_errcode_t mali_memory_core_load_complete(mali_kernel_subsystem_identifier id) +{ + mali_memory_bank * bank, *temp; + + MALI_DEBUG_PRINT( 1, ("Mali memory allocators will be used in this order of preference (lowest number first) :\n")); + + _MALI_OSK_LIST_FOREACHENTRY(bank, temp, &memory_banks_list, mali_memory_bank, list) + { + if ( NULL != bank->name ) + { + MALI_DEBUG_PRINT( 1, ("\t%d: %s\n", bank->alloc_order, bank->name) ); + } + else + { + MALI_DEBUG_PRINT( 1, ("\t%d: (UNNAMED ALLOCATOR)\n", bank->alloc_order ) ); + } + } + MALI_SUCCESS; +} + +MALI_STATIC_INLINE u32 order_needed_for_size(u32 size, struct mali_memory_bank * bank) +{ + u32 order = 0; + + if (0 < size) + { + for ( order = sizeof(u32)*8 - 1; ((1UL<<order) & size) == 0; --order) + /* nothing */; + + /* check if size is pow2, if not we need increment order by one */ + if (0 != (size & ((1UL<<order)-1))) ++order; + } + + if ((NULL != bank) && (order < bank->min_order)) order = bank->min_order; + /* Not capped to max order, that doesn't make sense */ + + return order; +} + +MALI_STATIC_INLINE u32 maximum_order_which_fits(u32 size) +{ + u32 order = 0; + u32 powsize = 1; + while (powsize < size) + { + powsize <<= 1; + if (powsize > size) break; + order++; + } + + return order; +} + +/* called for new MEMORY resources */ +static _mali_osk_errcode_t mali_memory_bank_register(u32 phys_base, u32 cpu_usage_adjust, u32 size, u32 flags, u32 alloc_order, const char *name) +{ + /* no locking performed due to function contract */ + int i; + u32 left, offset; + mali_memory_bank * bank; + mali_memory_bank * bank_enum, *temp; + + _mali_osk_errcode_t err; + + /* Only a multiple of MIN_BLOCK_SIZE is usable */ + u32 usable_size = size & ~(MIN_BLOCK_SIZE - 1); + + /* handle zero sized banks and bank smaller than the fixed block size */ + if (0 == usable_size) + { + MALI_PRINT(("Usable size == 0\n")); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + /* warn for banks not a muliple of the block size */ + MALI_DEBUG_PRINT_IF(1, usable_size != size, ("Memory bank @ 0x%X not a multiple of minimum block size. %d bytes wasted\n", phys_base, size - usable_size)); + + /* check against previous registrations */ + MALI_DEBUG_CODE( + { + _MALI_OSK_LIST_FOREACHENTRY(bank, temp, &memory_banks_list, mali_memory_bank, list) + { + /* duplicate ? */ + if (bank->base_addr == phys_base) + { + MALI_PRINT(("Duplicate registration of a memory bank at 0x%X detected\n", phys_base)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + /* overlapping ? */ + else if ( + ( (phys_base > bank->base_addr) && (phys_base < (bank->base_addr + bank->real_size)) ) || + ( (phys_base + size) > bank->base_addr && ((phys_base + size) < (bank->base_addr + bank->real_size)) ) + ) + { + MALI_PRINT(("Overlapping memory blocks found. Memory at 0x%X overlaps with memory at 0x%X size 0x%X\n", bank->base_addr, phys_base, size)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + } + } + ); + + /* create an object to represent this memory bank */ + MALI_CHECK_NON_NULL(bank = (mali_memory_bank*)_mali_osk_malloc(sizeof(mali_memory_bank)), _MALI_OSK_ERR_NOMEM); + + /* init the fields */ + _MALI_OSK_INIT_LIST_HEAD(&bank->list); + bank->base_addr = phys_base; + bank->cpu_usage_adjust = cpu_usage_adjust; + bank->size = usable_size; + bank->real_size = size; + bank->alloc_order = alloc_order; + bank->name = name; + + err = _mali_osk_atomic_init(&bank->num_active_allocations, 0); + if (err != _MALI_OSK_ERR_OK) + { + _mali_osk_free(bank); + MALI_ERROR(err); + } + + bank->used_for_flags = flags; + bank->min_order = order_needed_for_size(MIN_BLOCK_SIZE, NULL); + bank->max_order = maximum_order_which_fits(usable_size); + bank->lock = _mali_osk_lock_init((_mali_osk_lock_flags_t)(_MALI_OSK_LOCKFLAG_SPINLOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE), 0, 0); + if (NULL == bank->lock) + { + _mali_osk_atomic_term(&bank->num_active_allocations); + _mali_osk_free(bank); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + bank->blocklist = _mali_osk_calloc(1, sizeof(struct mali_memory_block) * (usable_size / MIN_BLOCK_SIZE)); + if (NULL == bank->blocklist) + { + _mali_osk_lock_term(bank->lock); + _mali_osk_atomic_term(&bank->num_active_allocations); + _mali_osk_free(bank); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + for (i = 0; i < (usable_size / MIN_BLOCK_SIZE); i++) + { + bank->blocklist[i].bank = bank; + } + + bank->freelist = _mali_osk_calloc(1, sizeof(_mali_osk_list_t) * (bank->max_order - bank->min_order + 1)); + if (NULL == bank->freelist) + { + _mali_osk_lock_term(bank->lock); + _mali_osk_free(bank->blocklist); + _mali_osk_atomic_term(&bank->num_active_allocations); + _mali_osk_free(bank); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + for (i = 0; i < (bank->max_order - bank->min_order + 1); i++) _MALI_OSK_INIT_LIST_HEAD(&bank->freelist[i]); + + /* init slot info */ + for (offset = 0, left = usable_size; offset < (usable_size / MIN_BLOCK_SIZE); /* updated inside the body */) + { + u32 block_order; + mali_memory_block * block; + + /* the maximum order which fits in the remaining area */ + block_order = maximum_order_which_fits(left); + + /* find the block pointer */ + block = &bank->blocklist[offset]; + + /* tag the block as being toplevel */ + set_block_toplevel(block, block_order); + + /* tag it as being free */ + set_block_free(block, 1); + + /* set the order */ + set_block_order(block, block_order); + + _mali_osk_list_addtail(&block->link, bank->freelist + (block_order - bank->min_order)); + + left -= (1 << block_order); + offset += ((1 << block_order) / MIN_BLOCK_SIZE); + } + + /* add bank to list of banks on the system */ + _MALI_OSK_LIST_FOREACHENTRY( bank_enum, temp, &memory_banks_list, mali_memory_bank, list ) + { + if ( bank_enum->alloc_order >= alloc_order ) + { + /* Found insertion point - our item must go before this one */ + break; + } + } + _mali_osk_list_addtail(&bank->list, &bank_enum->list); + + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_memory_mmu_register(u32 type, u32 phys_base) +{ + /* not supported */ + return _MALI_OSK_ERR_INVALID_FUNC; +} + +void mali_memory_mmu_unregister(u32 phys_base) +{ + /* not supported */ + return; +} + +static mali_memory_block * mali_memory_block_get(u32 type_id, u32 minimum_size) +{ + mali_memory_bank * bank; + mali_memory_block * block = NULL; + u32 requested_order, current_order; + + /* input validation */ + if (0 == minimum_size) + { + /* bad size */ + MALI_DEBUG_PRINT(2, ("Zero size block requested by mali_memory_block_get\n")); + return NULL; + } + + bank = (mali_memory_bank*)type_id; + + requested_order = order_needed_for_size(minimum_size, bank); + + MALI_DEBUG_PRINT(4, ("For size %d we need order %d (%d)\n", minimum_size, requested_order, 1 << requested_order)); + + _mali_osk_lock_wait(bank->lock, _MALI_OSK_LOCKMODE_RW); + /* ! critical section begin */ + + MALI_DEBUG_PRINT(7, ("Bank 0x%x locked\n", bank)); + + for (current_order = requested_order; current_order <= bank->max_order; ++current_order) + { + _mali_osk_list_t * list = bank->freelist + (current_order - bank->min_order); + MALI_DEBUG_PRINT(7, ("Checking freelist 0x%x for order %d\n", list, current_order)); + if (0 != _mali_osk_list_empty(list)) continue; /* empty list */ + + MALI_DEBUG_PRINT(7, ("Found an entry on the freelist for order %d\n", current_order)); + + + block = _MALI_OSK_LIST_ENTRY(list->next, mali_memory_block, link); + _mali_osk_list_delinit(&block->link); + + while (current_order > requested_order) + { + mali_memory_block * buddy_block; + MALI_DEBUG_PRINT(7, ("Splitting block 0x%x\n", block)); + current_order--; + list--; + buddy_block = block_get_buddy(block, current_order - bank->min_order); + set_block_order(buddy_block, current_order); + set_block_free(buddy_block, 1); + _mali_osk_list_add(&buddy_block->link, list); + } + + set_block_order(block, current_order); + set_block_free(block, 0); + + /* update usage count */ + _mali_osk_atomic_inc(&bank->num_active_allocations); + + break; + } + + /* ! critical section end */ + _mali_osk_lock_signal(bank->lock, _MALI_OSK_LOCKMODE_RW); + + MALI_DEBUG_PRINT(7, ("Lock released for bank 0x%x\n", bank)); + + MALI_DEBUG_PRINT_IF(7, NULL != block, ("Block 0x%x allocated\n", block)); + + return block; +} + + +static void block_release(mali_memory_block * block) +{ + mali_memory_bank * bank; + u32 current_order; + + if (NULL == block) return; + + bank = block->bank; + + /* we're manipulating the free list, so we need to lock it */ + _mali_osk_lock_wait(bank->lock, _MALI_OSK_LOCKMODE_RW); + /* ! critical section begin */ + + set_block_free(block, 1); + current_order = get_block_order(block); + + while (current_order <= bank->max_order) + { + mali_memory_block * buddy_block; + buddy_block = block_get_buddy(block, current_order - bank->min_order); + if (!block_is_valid_buddy(buddy_block, current_order)) break; + _mali_osk_list_delinit(&buddy_block->link); /* remove from free list */ + /* clear tracked data in both blocks */ + set_block_order(block, 0); + set_block_free(block, 0); + set_block_order(buddy_block, 0); + set_block_free(buddy_block, 0); + /* make the parent control the new state */ + block = block_get_parent(block, current_order - bank->min_order); + set_block_order(block, current_order + 1); /* merged has a higher order */ + set_block_free(block, 1); /* mark it as free */ + current_order++; + if (get_block_toplevel(block) == current_order) break; /* stop the merge if we've arrived at a toplevel block */ + } + + _mali_osk_list_add(&block->link, &bank->freelist[current_order - bank->min_order]); + + /* update bank usage statistics */ + _mali_osk_atomic_dec(&block->bank->num_active_allocations); + + /* !critical section end */ + _mali_osk_lock_signal(bank->lock, _MALI_OSK_LOCKMODE_RW); + + return; +} + +MALI_STATIC_INLINE u32 block_get_offset(mali_memory_block * block) +{ + return block - block->bank->blocklist; +} + +MALI_STATIC_INLINE u32 block_mali_addr_get(mali_memory_block * block) +{ + if (NULL != block) return block->bank->base_addr + MIN_BLOCK_SIZE * block_get_offset(block); + else return 0; +} + +MALI_STATIC_INLINE u32 block_cpu_addr_get(mali_memory_block * block) +{ + if (NULL != block) return (block->bank->base_addr + MIN_BLOCK_SIZE * block_get_offset(block)) + block->bank->cpu_usage_adjust; + else return 0; +} + +MALI_STATIC_INLINE u32 block_size_get(mali_memory_block * block) +{ + if (NULL != block) return 1 << get_block_order(block); + else return 0; +} + +MALI_STATIC_INLINE void __user * block_mapping_get(mali_memory_block * block) +{ + if (NULL != block) return block->mapping; + else return NULL; +} + +MALI_STATIC_INLINE void block_mapping_set(mali_memory_block * block, void __user * ptr) +{ + if (NULL != block) block->mapping = ptr; +} + +MALI_STATIC_INLINE u32 block_mmap_cookie_get(mali_memory_block * block) +{ + if (NULL != block) return block->mmap_cookie; + else return 0; +} + +/** + * Set the cookie returned via _mali_ukk_mem_mmap(). + * @param block The memory block to set the cookie for + * @param cookie the cookie + */ +MALI_STATIC_INLINE void block_mmap_cookie_set(mali_memory_block * block, u32 cookie) +{ + if (NULL != block) block->mmap_cookie = cookie; +} + + +static _mali_osk_errcode_t mali_memory_core_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue) +{ + memory_session * session_data; + + /* validate input */ + if (NULL == slot) + { + MALI_DEBUG_PRINT(1, ("NULL slot given to memory session begin\n")); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + if (NULL != *slot) + { + MALI_DEBUG_PRINT(1, ("The slot given to memory session begin already contains data")); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + /* create the session data object */ + MALI_CHECK_NON_NULL(session_data = _mali_osk_malloc(sizeof(memory_session)), _MALI_OSK_ERR_NOMEM); + + /* create descriptor mapping table */ + session_data->descriptor_mapping = mali_descriptor_mapping_create(MALI_MEM_DESCRIPTORS_INIT, MALI_MEM_DESCRIPTORS_MAX); + + if (NULL == session_data->descriptor_mapping) + { + _mali_osk_free(session_data); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + _MALI_OSK_INIT_LIST_HEAD(&session_data->memory_head); /* no memory in use */ + session_data->lock = _mali_osk_lock_init((_mali_osk_lock_flags_t)(_MALI_OSK_LOCKFLAG_ONELOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE), 0, 0); + if (NULL == session_data->lock) + { + _mali_osk_free(session_data); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + *slot = session_data; /* slot will point to our data object */ + + MALI_SUCCESS; +} + +static void mali_memory_core_session_end(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot) +{ + memory_session * session_data; + + /* validate input */ + if (NULL == slot) + { + MALI_DEBUG_PRINT(1, ("NULL slot given to memory session begin\n")); + return; + } + + if (NULL == *slot) + { + MALI_DEBUG_PRINT(1, ("NULL memory_session found in current session object")); + return; + } + + _mali_osk_lock_wait(((memory_session*)*slot)->lock, _MALI_OSK_LOCKMODE_RW); + session_data = (memory_session *)*slot; + /* clear our slot */ + *slot = NULL; + + /* First free all memory still being used. This can happen if the + * caller has leaked memory or the application has crashed forcing an + * auto-session end. */ + if (0 == _mali_osk_list_empty(&session_data->memory_head)) + { + mali_memory_block * block, * temp; + MALI_DEBUG_PRINT(1, ("Memory found on session usage list during session termination\n")); + + /* use the _safe version since fre_big_block removes the active block from the list we're iterating */ + _MALI_OSK_LIST_FOREACHENTRY(block, temp, &session_data->memory_head, mali_memory_block, link) + { + _mali_osk_errcode_t err; + _mali_uk_free_big_block_s uk_args; + + MALI_DEBUG_PRINT(4, ("Freeing block 0x%x with mali address 0x%x size %d mapped in user space at 0x%x\n", + block, + (void*)block_mali_addr_get(block), + block_size_get(block), + block_mapping_get(block)) + ); + + /* free the block */ + /** @note manual type safety check-point */ + uk_args.ctx = mali_session_data; + uk_args.cookie = (u32)block->descriptor; + err = _mali_ukk_free_big_block_internal( mali_session_data, session_data, &uk_args ); + + if ( _MALI_OSK_ERR_OK != err ) + { + MALI_DEBUG_PRINT_ERROR(("_mali_ukk_free_big_block_internal() failed during session termination on block with cookie==0x%X\n", + uk_args.cookie) + ); + } + } + } + + if (NULL != session_data->descriptor_mapping) + { + mali_descriptor_mapping_destroy(session_data->descriptor_mapping); + session_data->descriptor_mapping = NULL; + } + + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_lock_term(session_data->lock); + + /* free the session data object */ + _mali_osk_free(session_data); + + return; +} + +static _mali_osk_errcode_t mali_memory_core_system_info_fill(_mali_system_info* info) +{ + mali_memory_bank * bank, *temp; + _mali_mem_info **mem_info_tail; + + /* check input */ + MALI_CHECK_NON_NULL(info, _MALI_OSK_ERR_INVALID_ARGS); + + /* Make sure we won't leak any memory. It could also be that it's an + * uninitialized variable, but the caller should have zeroed the + * variable. */ + MALI_DEBUG_ASSERT(NULL == info->mem_info); + + mem_info_tail = &info->mem_info; + + _MALI_OSK_LIST_FOREACHENTRY(bank, temp, &memory_banks_list, mali_memory_bank, list) + { + _mali_mem_info * mem_info; + + mem_info = (_mali_mem_info *)_mali_osk_calloc(1, sizeof(_mali_mem_info)); + if (NULL == mem_info) return _MALI_OSK_ERR_NOMEM; /* memory already allocated will be freed by the caller */ + + /* set info */ + mem_info->size = bank->size; + mem_info->flags = (_mali_bus_usage)bank->used_for_flags; + mem_info->maximum_order_supported = bank->max_order; + mem_info->identifier = (u32)bank; + + /* add to system info linked list */ + (*mem_info_tail) = mem_info; + mem_info_tail = &mem_info->next; + } + + /* all OK */ + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_memory_core_resource_memory(_mali_osk_resource_t * resource) +{ + _mali_osk_errcode_t err; + + /* Request ownership of the memory */ + if (_MALI_OSK_ERR_OK != _mali_osk_mem_reqregion(resource->base, resource->size, resource->description)) + { + MALI_DEBUG_PRINT(1, ("Failed to request memory region %s (0x%08X - 0x%08X)\n", resource->description, resource->base, resource->base + resource->size - 1)); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + /* call backend */ + err = mali_memory_bank_register(resource->base, resource->cpu_usage_adjust, resource->size, resource->flags, resource->alloc_order, resource->description); + if (_MALI_OSK_ERR_OK != err) + { + /* if backend refused the memory we have to release the region again */ + MALI_DEBUG_PRINT(1, ("Memory bank registration failed\n")); + _mali_osk_mem_unreqregion(resource->base, resource->size); + MALI_ERROR(err); + } + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_memory_core_resource_mmu(_mali_osk_resource_t * resource) +{ + /* Not supported by the fixed block memory system */ + MALI_DEBUG_PRINT(1, ("MMU resource not supported by non-MMU driver!\n")); + MALI_ERROR(_MALI_OSK_ERR_INVALID_FUNC); +} + +static _mali_osk_errcode_t mali_memory_core_resource_fpga(_mali_osk_resource_t * resource) +{ + mali_io_address mapping; + + MALI_DEBUG_PRINT(5, ("FPGA framework '%s' @ (0x%08X - 0x%08X)\n", + resource->description, resource->base, resource->base + sizeof(u32) * 2 - 1 + )); + + mapping = _mali_osk_mem_mapioregion(resource->base + 0x1000, sizeof(u32) * 2, "fpga framework"); + if (mapping) + { + u32 data; + data = _mali_osk_mem_ioread32(mapping, 0); + MALI_DEBUG_PRINT(2, ("FPGA framwork '%s' @ 0x%08X:\n", resource->description, resource->base)); + MALI_DEBUG_PRINT(2, ("\tBitfile date: %d%02d%02d_%02d%02d\n", + (data >> 20), + (data >> 16) & 0xF, + (data >> 11) & 0x1F, + (data >> 6) & 0x1F, + (data >> 0) & 0x3F)); + data = _mali_osk_mem_ioread32(mapping, sizeof(u32)); + MALI_DEBUG_PRINT(2, ("\tBitfile SCCS rev: %d\n", data)); + + _mali_osk_mem_unmapioregion(resource->base + 0x1000, sizeof(u32) *2, mapping); + } + else MALI_DEBUG_PRINT(1, ("Failed to access FPGA framwork '%s' @ 0x%08X\n", resource->description, resource->base)); + + MALI_SUCCESS; +} + +/* static _mali_osk_errcode_t get_big_block(void * ukk_private, struct mali_session_data * mali_session_data, void __user * argument) */ +_mali_osk_errcode_t _mali_ukk_get_big_block( _mali_uk_get_big_block_s *args ) +{ + _mali_uk_mem_mmap_s args_mmap = {0, }; + int md; + mali_memory_block * block; + _mali_osk_errcode_t err; + memory_session * session_data; + + MALI_DEBUG_ASSERT_POINTER( args ); + + MALI_DEBUG_ASSERT_POINTER( args->ctx ); + + /** @note manual type safety check-point */ + session_data = (memory_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_memory_id); + + MALI_CHECK_NON_NULL(session_data, _MALI_OSK_ERR_INVALID_ARGS); + + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + if (!args->type_id) + { + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* at least min block size */ + if (MIN_BLOCK_SIZE > args->minimum_size_requested) args->minimum_size_requested = MIN_BLOCK_SIZE; + + /* perform the actual allocation */ + block = mali_memory_block_get(args->type_id, args->minimum_size_requested); + if ( NULL == block ) + { + /* no memory available with requested type_id */ + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_allocate_mapping(session_data->descriptor_mapping, block, &md)) + { + block_release(block); + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + block->descriptor = md; + + + /* fill in response */ + args->mali_address = block_mali_addr_get(block); + args->block_size = block_size_get(block); + args->cookie = (u32)md; + args->flags = block->bank->used_for_flags; + + /* map the block into the process' address space */ + + /** @note manual type safety check-point */ + args_mmap.ukk_private = (void *)args->ukk_private; + args_mmap.ctx = args->ctx; + args_mmap.size = args->block_size; + args_mmap.phys_addr = block_cpu_addr_get(block); + +#ifndef _MALI_OSK_SPECIFIC_INDIRECT_MMAP + err = _mali_ukk_mem_mmap( &args_mmap ); +#else + err = _mali_osk_specific_indirect_mmap( &args_mmap ); +#endif + + /* check if the mapping failed */ + if ( _MALI_OSK_ERR_OK != err ) + { + MALI_DEBUG_PRINT(1, ("Memory mapping failed 0x%x\n", args->cpuptr)); + /* mapping failed */ + + /* remove descriptor entry */ + mali_descriptor_mapping_free(session_data->descriptor_mapping, md); + + /* free the mali memory */ + block_release(block); + + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + return err; + } + + args->cpuptr = args_mmap.mapping; + block_mmap_cookie_set(block, args_mmap.cookie); + block_mapping_set(block, args->cpuptr); + + MALI_DEBUG_PRINT(2, ("Mali memory 0x%x (size %d) mapped in process memory space at 0x%x\n", (void*)args->mali_address, args->block_size, args->cpuptr)); + + /* track memory in use for the session */ + _mali_osk_list_addtail(&block->link, &session_data->memory_head); + + /* memory assigned to the session, memory mapped into the process' view */ + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + MALI_SUCCESS; +} + +/* Internal code that assumes the memory session lock is held */ +static _mali_osk_errcode_t _mali_ukk_free_big_block_internal( struct mali_session_data * mali_session_data, memory_session * session_data, _mali_uk_free_big_block_s *args) +{ + mali_memory_block * block = NULL; + _mali_osk_errcode_t err; + _mali_uk_mem_munmap_s args_munmap = {0,}; + + MALI_DEBUG_ASSERT_POINTER( mali_session_data ); + MALI_DEBUG_ASSERT_POINTER( session_data ); + MALI_DEBUG_ASSERT_POINTER( args ); + + err = mali_descriptor_mapping_get(session_data->descriptor_mapping, (int)args->cookie, (void**)&block); + if (_MALI_OSK_ERR_OK != err) + { + MALI_DEBUG_PRINT(1, ("Invalid memory descriptor %d used to release memory pages\n", (int)args->cookie)); + MALI_ERROR(err); + } + + MALI_DEBUG_ASSERT_POINTER(block); + + MALI_DEBUG_PRINT(4, ("Asked to free block 0x%x with mali address 0x%x size %d mapped in user space at 0x%x\n", + block, + (void*)block_mali_addr_get(block), + block_size_get(block), + block_mapping_get(block)) + ); + + /** @note manual type safety check-point */ + args_munmap.ctx = (void*)mali_session_data; + args_munmap.mapping = block_mapping_get( block ); + args_munmap.size = block_size_get( block ); + args_munmap.cookie = block_mmap_cookie_get( block ); + +#ifndef _MALI_OSK_SPECIFIC_INDIRECT_MMAP + _mali_ukk_mem_munmap( &args_munmap ); +#else + _mali_osk_specific_indirect_munmap( &args_munmap ); +#endif + + MALI_DEBUG_PRINT(6, ("Session data 0x%x, lock 0x%x\n", session_data, &session_data->lock)); + + /* unlink from session usage list */ + MALI_DEBUG_PRINT(5, ("unlink from session usage list\n")); + _mali_osk_list_delinit(&block->link); + + /* remove descriptor entry */ + mali_descriptor_mapping_free(session_data->descriptor_mapping, (int)args->cookie); + + /* free the mali memory */ + block_release(block); + MALI_DEBUG_PRINT(5, ("Block freed\n")); + + MALI_SUCCESS; +} + +/* static _mali_osk_errcode_t free_big_block( struct mali_session_data * mali_session_data, void __user * argument) */ +_mali_osk_errcode_t _mali_ukk_free_big_block( _mali_uk_free_big_block_s *args ) +{ + _mali_osk_errcode_t err; + struct mali_session_data * mali_session_data; + memory_session * session_data; + + MALI_DEBUG_ASSERT_POINTER( args ); + + MALI_DEBUG_ASSERT_POINTER( args->ctx ); + + /** @note manual type safety check-point */ + mali_session_data = (struct mali_session_data *)args->ctx; + + /* Must always verify this, since these are provided by the user */ + MALI_CHECK_NON_NULL(mali_session_data, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = mali_kernel_session_manager_slot_get(mali_session_data, mali_subsystem_memory_id); + + MALI_CHECK_NON_NULL(session_data, _MALI_OSK_ERR_INVALID_ARGS); + + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + /** @note this has been separated out so that the session_end handler can call this while it has the memory_session lock held */ + err = _mali_ukk_free_big_block_internal( mali_session_data, session_data, args ); + + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + return err; +} + +MALI_STATIC_INLINE u32 get_block_free(mali_memory_block * block) +{ + return (block->misc >> MISC_SHIFT_FREE) & MISC_MASK_FREE; +} + +MALI_STATIC_INLINE void set_block_free(mali_memory_block * block, int state) +{ + if (state) block->misc |= (MISC_MASK_FREE << MISC_SHIFT_FREE); + else block->misc &= ~(MISC_MASK_FREE << MISC_SHIFT_FREE); +} + +MALI_STATIC_INLINE void set_block_order(mali_memory_block * block, u32 order) +{ + block->misc &= ~(MISC_MASK_ORDER << MISC_SHIFT_ORDER); + block->misc |= ((order & MISC_MASK_ORDER) << MISC_SHIFT_ORDER); +} + +MALI_STATIC_INLINE u32 get_block_order(mali_memory_block * block) +{ + return (block->misc >> MISC_SHIFT_ORDER) & MISC_MASK_ORDER; +} + +MALI_STATIC_INLINE void set_block_toplevel(mali_memory_block * block, u32 level) +{ + block->misc |= ((level & MISC_MASK_TOPLEVEL) << MISC_SHIFT_TOPLEVEL); +} + +MALI_STATIC_INLINE u32 get_block_toplevel(mali_memory_block * block) +{ + return (block->misc >> MISC_SHIFT_TOPLEVEL) & MISC_MASK_TOPLEVEL; +} + +MALI_STATIC_INLINE int block_is_valid_buddy(mali_memory_block * block, int order) +{ + if (get_block_free(block) && (get_block_order(block) == order)) return 1; + else return 0; +} + +MALI_STATIC_INLINE mali_memory_block * block_get_buddy(mali_memory_block * block, u32 order) +{ + return block + ( (block_get_offset(block) ^ (1 << order)) - block_get_offset(block)); +} + +MALI_STATIC_INLINE mali_memory_block * block_get_parent(mali_memory_block * block, u32 order) +{ + return block + ((block_get_offset(block) & ~(1 << order)) - block_get_offset(block)); +} + +/* This handler registered to mali_mmap for non-MMU builds */ +_mali_osk_errcode_t _mali_ukk_mem_mmap( _mali_uk_mem_mmap_s *args ) +{ + _mali_osk_errcode_t ret; + struct mali_session_data * mali_session_data; + mali_memory_allocation * descriptor; + memory_session * session_data; + + /* validate input */ + if (NULL == args) { MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: args was NULL\n")); MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); } + + /* Unpack arguments */ + mali_session_data = (struct mali_session_data *)args->ctx; + + if (NULL == mali_session_data) { MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: mali_session data was NULL\n")); MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); } + + MALI_DEBUG_ASSERT( mali_subsystem_memory_id >= 0 ); + + session_data = mali_kernel_session_manager_slot_get(mali_session_data, mali_subsystem_memory_id); + /* validate input */ + if (NULL == session_data) { MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: session data was NULL\n")); MALI_ERROR(_MALI_OSK_ERR_FAULT); } + + descriptor = (mali_memory_allocation*) _mali_osk_calloc( 1, sizeof(mali_memory_allocation) ); + if (NULL == descriptor) { MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: descriptor was NULL\n")); MALI_ERROR(_MALI_OSK_ERR_NOMEM); } + + descriptor->size = args->size; + descriptor->mali_address = args->phys_addr; + descriptor->mali_addr_mapping_info = (void*)session_data; + descriptor->process_addr_mapping_info = args->ukk_private; /* save to be used during physical manager callback */ + descriptor->flags = MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE; + + ret = _mali_osk_mem_mapregion_init( descriptor ); + if ( _MALI_OSK_ERR_OK != ret ) + { + MALI_DEBUG_PRINT(3, ("_mali_osk_mem_mapregion_init() failed\n")); + _mali_osk_free(descriptor); + MALI_ERROR(ret); + } + + ret = _mali_osk_mem_mapregion_map( descriptor, 0, &descriptor->mali_address, descriptor->size ); + if ( _MALI_OSK_ERR_OK != ret ) + { + MALI_DEBUG_PRINT(3, ("_mali_osk_mem_mapregion_map() failed\n")); + _mali_osk_mem_mapregion_term( descriptor ); + _mali_osk_free(descriptor); + MALI_ERROR(ret); + } + + args->mapping = descriptor->mapping; + + /** + * @note we do not require use of mali_descriptor_mapping here: + * the cookie gets stored in the mali_memory_block struct, which itself is + * protected by mali_descriptor_mapping, and so this cookie never leaves + * kernel space (on any OS). + * + * In the MMU case, we must use a mali_descriptor_mapping, since on _some_ + * OSs, the cookie leaves kernel space. + */ + args->cookie = (u32)descriptor; + MALI_SUCCESS; +} + +/* This handler registered to mali_munmap for non-MMU builds */ +_mali_osk_errcode_t _mali_ukk_mem_munmap( _mali_uk_mem_munmap_s *args ) +{ + mali_memory_allocation * descriptor; + + /** see note in _mali_ukk_mem_mmap() - no need to use descriptor mapping */ + descriptor = (mali_memory_allocation *)args->cookie; + MALI_DEBUG_ASSERT_POINTER(descriptor); + + /* args->mapping and args->size are also discarded. They are only necessary for certain do_munmap implementations. However, they could be used to check the descriptor at this point. */ + _mali_osk_mem_mapregion_unmap( descriptor, 0, descriptor->size, (_mali_osk_mem_mapregion_flags_t)0 ); + + _mali_osk_mem_mapregion_term( descriptor ); + + _mali_osk_free(descriptor); + + return _MALI_OSK_ERR_OK; +} + +/** + * Stub function to satisfy UDD interface exclusion requirement. + * This is because the Base code compiles in \b both MMU and non-MMU calls, + * so both sets must be declared (but the 'unused' set may be stub) + */ +_mali_osk_errcode_t _mali_ukk_init_mem( _mali_uk_init_mem_s *args ) +{ + MALI_IGNORE( args ); + return _MALI_OSK_ERR_FAULT; +} + +/** + * Stub function to satisfy UDD interface exclusion requirement. + * This is because the Base code compiles in \b both MMU and non-MMU calls, + * so both sets must be declared (but the 'unused' set may be stub) + */ +_mali_osk_errcode_t _mali_ukk_term_mem( _mali_uk_term_mem_s *args ) +{ + MALI_IGNORE( args ); + return _MALI_OSK_ERR_FAULT; +} + +/** + * Stub function to satisfy UDD interface exclusion requirement. + * This is because the Base code compiles in \b both MMU and non-MMU calls, + * so both sets must be declared (but the 'unused' set may be stub) + */ +_mali_osk_errcode_t _mali_ukk_map_external_mem( _mali_uk_map_external_mem_s *args ) +{ + MALI_IGNORE( args ); + return _MALI_OSK_ERR_FAULT; +} + +/** + * Stub function to satisfy UDD interface exclusion requirement. + * This is because the Base code compiles in \b both MMU and non-MMU calls, + * so both sets must be declared (but the 'unused' set may be stub) + */ +_mali_osk_errcode_t _mali_ukk_unmap_external_mem( _mali_uk_unmap_external_mem_s *args ) +{ + MALI_IGNORE( args ); + return _MALI_OSK_ERR_FAULT; +} + +/** + * Stub function to satisfy UDD interface exclusion requirement. + * This is because the Base code compiles in \b both MMU and non-MMU calls, + * so both sets must be declared (but the 'unused' set may be stub) + */ +_mali_osk_errcode_t _mali_ukk_query_mmu_page_table_dump_size( _mali_uk_query_mmu_page_table_dump_size_s *args ) +{ + MALI_IGNORE( args ); + return _MALI_OSK_ERR_FAULT; +} + +/** + * Stub function to satisfy UDD interface exclusion requirement. + * This is because the Base code compiles in \b both MMU and non-MMU calls, + * so both sets must be declared (but the 'unused' set may be stub) + */ +_mali_osk_errcode_t _mali_ukk_dump_mmu_page_table( _mali_uk_dump_mmu_page_table_s * args ) +{ + MALI_IGNORE( args ); + return _MALI_OSK_ERR_FAULT; +} diff --git a/drivers/gpu/arm/mali/common/mali_kernel_mem_mmu.c b/drivers/gpu/arm/mali/common/mali_kernel_mem_mmu.c new file mode 100644 index 000000000000..be0cc6702e88 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_mem_mmu.c @@ -0,0 +1,3158 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_subsystem.h" +#include "mali_kernel_mem.h" +#include "mali_kernel_ioctl.h" +#include "mali_kernel_descriptor_mapping.h" +#include "mali_kernel_mem_mmu.h" +#include "mali_kernel_memory_engine.h" +#include "mali_block_allocator.h" +#include "mali_kernel_mem_os.h" +#include "mali_kernel_session_manager.h" +#include "mali_kernel_core.h" +#include "mali_kernel_rendercore.h" + +#if defined USING_MALI400_L2_CACHE +#include "mali_kernel_l2_cache.h" +#endif + +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 +#include "ump_kernel_interface.h" +#endif + +/* kernel side OS functions and user-kernel interface */ +#include "mali_osk.h" +#include "mali_osk_mali.h" +#include "mali_ukk.h" +#include "mali_osk_bitops.h" +#include "mali_osk_list.h" + +/** + * Size of the MMU registers in bytes + */ +#define MALI_MMU_REGISTERS_SIZE 0x24 + +/** + * Size of an MMU page in bytes + */ +#define MALI_MMU_PAGE_SIZE 0x1000 + +/** + * Page directory index from address + * Calculates the page directory index from the given address + */ +#define MALI_MMU_PDE_ENTRY(address) (((address)>>22) & 0x03FF) + +/** + * Page table index from address + * Calculates the page table index from the given address + */ +#define MALI_MMU_PTE_ENTRY(address) (((address)>>12) & 0x03FF) + +/** + * Extract the memory address from an PDE/PTE entry + */ +#define MALI_MMU_ENTRY_ADDRESS(value) ((value) & 0xFFFFFC00) + +/** + * Calculate memory address from PDE and PTE + */ +#define MALI_MMU_ADDRESS(pde, pte) (((pde)<<22) | ((pte)<<12)) + +/** + * Linux kernel version has marked SA_SHIRQ as deprecated, IRQF_SHARED should be used. + * This is to handle older kernels which haven't done this swap. + */ +#ifndef IRQF_SHARED +#define IRQF_SHARED SA_SHIRQ +#endif /* IRQF_SHARED */ + +/** + * Per-session memory descriptor mapping table sizes + */ +#define MALI_MEM_DESCRIPTORS_INIT 64 +#define MALI_MEM_DESCRIPTORS_MAX 65536 + +/** + * Used to disallow more than one core to run a MMU at the same time + * + * @note This value is hardwired into some systems' configuration files, + * which \em might not be a header file (e.g. some external data configuration + * file). Therefore, if this value is modified, its occurance must be + * \b manually checked for in the entire driver source tree. + */ +#define MALI_MMU_DISALLOW_PARALLELL_WORK_OF_MALI_CORES 1 + +#define MALI_INVALID_PAGE ((u32)(~0)) + +/** + * + */ +typedef enum mali_mmu_entry_flags +{ + MALI_MMU_FLAGS_PRESENT = 0x01, + MALI_MMU_FLAGS_READ_PERMISSION = 0x02, + MALI_MMU_FLAGS_WRITE_PERMISSION = 0x04, + MALI_MMU_FLAGS_MASK = 0x07 +} mali_mmu_entry_flags; + +/** + * MMU register numbers + * Used in the register read/write routines. + * See the hardware documentation for more information about each register + */ +typedef enum mali_mmu_register { + MALI_MMU_REGISTER_DTE_ADDR = 0x0000, /**< Current Page Directory Pointer */ + MALI_MMU_REGISTER_STATUS = 0x0001, /**< Status of the MMU */ + MALI_MMU_REGISTER_COMMAND = 0x0002, /**< Command register, used to control the MMU */ + MALI_MMU_REGISTER_PAGE_FAULT_ADDR = 0x0003, /**< Logical address of the last page fault */ + MALI_MMU_REGISTER_ZAP_ONE_LINE = 0x004, /**< Used to invalidate the mapping of a single page from the MMU */ + MALI_MMU_REGISTER_INT_RAWSTAT = 0x0005, /**< Raw interrupt status, all interrupts visible */ + MALI_MMU_REGISTER_INT_CLEAR = 0x0006, /**< Indicate to the MMU that the interrupt has been received */ + MALI_MMU_REGISTER_INT_MASK = 0x0007, /**< Enable/disable types of interrupts */ + MALI_MMU_REGISTER_INT_STATUS = 0x0008 /**< Interrupt status based on the mask */ +} mali_mmu_register; + +/** + * MMU interrupt register bits + * Each cause of the interrupt is reported + * through the (raw) interrupt status registers. + * Multiple interrupts can be pending, so multiple bits + * can be set at once. + */ +typedef enum mali_mmu_interrupt +{ + MALI_MMU_INTERRUPT_PAGE_FAULT = 0x01, /**< A page fault occured */ + MALI_MMU_INTERRUPT_READ_BUS_ERROR = 0x02 /**< A bus read error occured */ +} mali_mmu_interrupt; + +/** + * MMU commands + * These are the commands that can be sent + * to the MMU unit. + */ +typedef enum mali_mmu_command +{ + MALI_MMU_COMMAND_ENABLE_PAGING = 0x00, /**< Enable paging (memory translation) */ + MALI_MMU_COMMAND_DISABLE_PAGING = 0x01, /**< Disable paging (memory translation) */ + MALI_MMU_COMMAND_ENABLE_STALL = 0x02, /**< Enable stall on page fault */ + MALI_MMU_COMMAND_DISABLE_STALL = 0x03, /**< Disable stall on page fault */ + MALI_MMU_COMMAND_ZAP_CACHE = 0x04, /**< Zap the entire page table cache */ + MALI_MMU_COMMAND_PAGE_FAULT_DONE = 0x05, /**< Page fault processed */ + MALI_MMU_COMMAND_SOFT_RESET = 0x06 /**< Reset the MMU back to power-on settings */ +} mali_mmu_command; + +typedef enum mali_mmu_status_bits +{ + MALI_MMU_STATUS_BIT_PAGING_ENABLED = 1 << 0, + MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE = 1 << 1, + MALI_MMU_STATUS_BIT_STALL_ACTIVE = 1 << 2, + MALI_MMU_STATUS_BIT_IDLE = 1 << 3, + MALI_MMU_STATUS_BIT_REPLAY_BUFFER_EMPTY = 1 << 4, + MALI_MMU_STATUS_BIT_PAGE_FAULT_IS_WRITE = 1 << 5, +} mali_mmu_status_bits; + +/** + * Defintion of the type used to represent memory used by a session. + * Containts the pointer to the huge user space virtual memory area + * used to access the Mali memory. + */ +typedef struct memory_session +{ + _mali_osk_lock_t *lock; /**< Lock protecting the vm manipulation */ + + u32 mali_base_address; /**< Mali virtual memory area used by this session */ + mali_descriptor_mapping * descriptor_mapping; /**< Mapping between userspace descriptors and our pointers */ + + u32 page_directory; /**< Physical address of the memory session's page directory */ + + mali_io_address page_directory_mapped; /**< Pointer to the mapped version of the page directory into the kernel's address space */ + mali_io_address page_entries_mapped[1024]; /**< Pointers to the page tables which exists in the page directory mapped into the kernel's address space */ + u32 page_entries_usage_count[1024]; /**< Tracks usage count of the page table pages, so they can be releases on the last reference */ + + _mali_osk_list_t active_mmus; /**< The MMUs in this session, in increasing order of ID (so we can lock them in the correct order when necessary) */ + _mali_osk_list_t memory_head; /**< Track all the memory allocated in this session, for freeing on abnormal termination */ +} memory_session; + +typedef struct mali_kernel_memory_mmu_idle_callback +{ + _mali_osk_list_t link; + void (*callback)(void*); + void * callback_argument; +} mali_kernel_memory_mmu_idle_callback; + +/** + * Definition of the MMU struct + * Used to track a MMU unit in the system. + * Contains information about the mapping of the registers + */ +typedef struct mali_kernel_memory_mmu +{ + int id; /**< ID of the MMU, no duplicate IDs may exist on the system */ + const char * description; /**< Description text received from the resource manager to help identify the resource for people */ + int irq_nr; /**< IRQ number */ + u32 base; /**< Physical address of the registers */ + mali_io_address mapped_registers; /**< Virtual mapping of the registers */ + u32 mapping_size; /**< Size of registers in bytes */ + _mali_osk_list_t list; /**< Used to link multiple MMU's into a list */ + _mali_osk_irq_t *irq; + u32 flags; /**< Used to store if there is something special with this mmu. */ + + _mali_osk_lock_t *lock; /**< Lock protecting access to the usage fields */ + /* usage fields */ + memory_session * active_session; /**< Active session, NULL if no session is active */ + u32 usage_count; /**< Number of nested activations of the active session */ + _mali_osk_list_t callbacks; /**< Callback registered for MMU idle notification */ + void *core; + + int in_page_fault_handler; + + _mali_osk_list_t session_link; +} mali_kernel_memory_mmu; + +typedef struct dedicated_memory_info +{ + u32 base; + u32 size; + struct dedicated_memory_info * next; +} dedicated_memory_info; + +/* types used for external_memory and ump_memory physical memory allocators, which are using the mali_allocation_engine */ +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 +typedef struct ump_mem_allocation +{ + mali_allocation_engine * engine; + mali_memory_allocation * descriptor; + u32 initial_offset; + u32 size_allocated; + ump_dd_handle ump_mem; +} ump_mem_allocation ; +#endif + +typedef struct external_mem_allocation +{ + mali_allocation_engine * engine; + mali_memory_allocation * descriptor; + u32 initial_offset; + u32 size; +} external_mem_allocation; + +/* + Subsystem interface implementation +*/ +/** + * Fixed block memory subsystem startup function. + * Called by the driver core when the driver is loaded. + * Registers the memory systems ioctl handler, resource handlers and memory map function with the core. + * + * @param id Identifier assigned by the core to the memory subsystem + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_initialize(mali_kernel_subsystem_identifier id); + +/** + * Fixed block memory subsystem shutdown function. + * Called by the driver core when the driver is unloaded. + * Cleans up + * @param id Identifier assigned by the core to the memory subsystem + */ +static void mali_memory_core_terminate(mali_kernel_subsystem_identifier id); + +/** + * MMU Memory load complete notification function. + * Called by the driver core when all drivers have loaded and all resources has been registered + * Builds the memory overall memory list + * @param id Identifier assigned by the core to the memory subsystem + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_load_complete(mali_kernel_subsystem_identifier id); + +/** + * Fixed block memory subsystem session begin notification + * Called by the core when a new session to the driver is started. + * Creates a memory session object and sets it as the subsystem slot data for this session + * @param slot Pointer to the slot to use for storing per-session data + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue); + +/** + * Fixed block memory subsystem session end notification + * Called by the core when a session to the driver has ended. + * Cleans up per session data, which includes checking and fixing memory leaks + * + * @param slot Pointer to the slot to use for storing per-session data + */ +static void mali_memory_core_session_end(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot); + +/** + * Fixed block memory subsystem system info filler + * Called by the core when a system info update is needed + * We fill in info about all the memory types we have + * @param info Pointer to system info struct to update + * @return 0 on success, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_system_info_fill(_mali_system_info* info); + +/* our registered resource handlers */ + +/** + * Fixed block memory subsystem's notification handler for MMU resource instances. + * Registered with the core during startup. + * Called by the core for each mmu described in the active architecture's config.h file. + * @param resource The resource to handle (type MMU) + * @return 0 if the MMU was found and initialized, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_resource_mmu(_mali_osk_resource_t * resource); + +/** + * Fixed block memory subsystem's notification handler for FPGA_FRAMEWORK resource instances. + * Registered with the core during startup. + * Called by the core for each fpga framework described in the active architecture's config.h file. + * @param resource The resource to handle (type FPGA_FRAMEWORK) + * @return 0 if the FPGA framework was found and initialized, negative on error + */ +static _mali_osk_errcode_t mali_memory_core_resource_fpga(_mali_osk_resource_t * resource); + + +static _mali_osk_errcode_t mali_memory_core_resource_dedicated_memory(_mali_osk_resource_t * resource); +static _mali_osk_errcode_t mali_memory_core_resource_os_memory(_mali_osk_resource_t * resource); + +/** + * @brief Internal function for unmapping memory + * + * Worker function for unmapping memory from a user-process. We assume that the + * session/descriptor's lock was obtained before entry. For example, the + * wrapper _mali_ukk_mem_munmap() will lock the descriptor, then call this + * function to do the actual unmapping. mali_memory_core_session_end() could + * also call this directly (depending on compilation options), having locked + * the descriptor. + * + * This function will fail if it is unable to put the MMU in stall mode (which + * might be the case if a page fault is also being processed). + * + * @param args see _mali_uk_mem_munmap_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +static _mali_osk_errcode_t _mali_ukk_mem_munmap_internal( _mali_uk_mem_munmap_s *args ); + +/** + * The MMU interrupt handler + * Upper half of the MMU interrupt processing. + * Called by the kernel when the MMU has triggered an interrupt. + * The interrupt function supports IRQ sharing. So it'll probe the MMU in question + * @param irq The irq number (not used) + * @param dev_id Points to the MMU object being handled + * @param regs Registers of interrupted process (not used) + * @return Standard Linux interrupt result. + * Subset used by the driver is IRQ_HANDLED processed + * IRQ_NONE Not processed + */ +static _mali_osk_errcode_t mali_kernel_memory_mmu_interrupt_handler_upper_half(void * data); + +/** + * The MMU reset hander + * Bottom half of the MMU interrupt processing for page faults and bus errors + * @param work The item to operate on, NULL in our case + */ +static void mali_kernel_memory_mmu_interrupt_handler_bottom_half ( void *data ); + +/** + * Read MMU register value + * Reads the contents of the specified register. + * @param unit The MMU to read from + * @param reg The register to read + * @return The contents of the register + */ +static u32 mali_mmu_register_read(mali_kernel_memory_mmu * unit, mali_mmu_register reg); + +/** + * Write to a MMU register + * Writes the given value to the specified register + * @param unit The MMU to write to + * @param reg The register to write to + * @param val The value to write to the register + */ +static void mali_mmu_register_write(mali_kernel_memory_mmu * unit, mali_mmu_register reg, u32 val); + +/** + * Issues the reset command to the MMU and waits for HW to be ready again + * @param mmu The MMU to reset + */ +static void mali_mmu_raw_reset(mali_kernel_memory_mmu * mmu); + +/** + * Issues the enable paging command to the MMU and waits for HW to complete the request + * @param mmu The MMU to enable paging for + */ +static void mali_mmu_enable_paging(mali_kernel_memory_mmu * mmu); + +/** + * Issues the enable stall command to the MMU and waits for HW to complete the request + * @param mmu The MMU to enable paging for + * @return MALI_TRUE if HW stall was successfully engaged, otherwise MALI_FALSE (req timed out) + */ +static mali_bool mali_mmu_enable_stall(mali_kernel_memory_mmu * mmu); + +/** + * Issues the disable stall command to the MMU and waits for HW to complete the request + * @param mmu The MMU to enable paging for + */ +static void mali_mmu_disable_stall(mali_kernel_memory_mmu * mmu); + +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 +static void ump_memory_release(void * ctx, void * handle); +static mali_physical_memory_allocation_result ump_memory_commit(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info); +#endif /* MALI_USE_UNIFIED_MEMORY_PROVIDER != 0*/ + + +static void external_memory_release(void * ctx, void * handle); +static mali_physical_memory_allocation_result external_memory_commit(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info); + + + + +/* nop functions */ + +/* mali address manager needs to allocate page tables on allocate, write to page table(s) on map, write to page table(s) and release page tables on release */ +static _mali_osk_errcode_t mali_address_manager_allocate(mali_memory_allocation * descriptor); /* validates the range, allocates memory for the page tables if needed */ +static _mali_osk_errcode_t mali_address_manager_map(mali_memory_allocation * descriptor, u32 offset, u32 *phys_addr, u32 size); +static void mali_address_manager_release(mali_memory_allocation * descriptor); + +static void mali_mmu_activate_address_space(mali_kernel_memory_mmu * mmu, u32 page_directory); + +_mali_osk_errcode_t mali_mmu_page_table_cache_create(void); +void mali_mmu_page_table_cache_destroy(void); + +_mali_osk_errcode_t mali_mmu_get_table_page(u32 *table_page, mali_io_address *mapping); +void mali_mmu_release_table_page(u32 pa); + +static _mali_osk_errcode_t mali_allocate_empty_page_directory(void); + +static void mali_free_empty_page_directory(void); + +static _mali_osk_errcode_t fill_page(mali_io_address mapping, u32 data); + +static _mali_osk_errcode_t mali_allocate_fault_flush_pages(void); + +static void mali_free_fault_flush_pages(void); + +static void mali_mmu_probe_irq_trigger(mali_kernel_memory_mmu * mmu); +static _mali_osk_errcode_t mali_mmu_probe_irq_acknowledge(mali_kernel_memory_mmu * mmu); + +/* MMU variables */ + +typedef struct mali_mmu_page_table_allocation +{ + _mali_osk_list_t list; + u32 * usage_map; + u32 usage_count; + u32 num_pages; + mali_page_table_block pages; +} mali_mmu_page_table_allocation; + +typedef struct mali_mmu_page_table_allocations +{ + _mali_osk_lock_t *lock; + _mali_osk_list_t partial; + _mali_osk_list_t full; + /* we never hold on to a empty allocation */ +} mali_mmu_page_table_allocations; + +/* Head of the list of MMUs */ +static _MALI_OSK_LIST_HEAD(mmu_head); + +/* the mmu page table cache */ +static struct mali_mmu_page_table_allocations page_table_cache; + +/* page fault queue flush helper pages + * note that the mapping pointers are currently unused outside of the initialization functions */ +static u32 mali_page_fault_flush_page_directory = MALI_INVALID_PAGE; +static mali_io_address mali_page_fault_flush_page_directory_mapping = NULL; +static u32 mali_page_fault_flush_page_table = MALI_INVALID_PAGE; +static mali_io_address mali_page_fault_flush_page_table_mapping = NULL; +static u32 mali_page_fault_flush_data_page = MALI_INVALID_PAGE; +static mali_io_address mali_page_fault_flush_data_page_mapping = NULL; + +/* an empty page directory (no address valid) which is active on any MMU not currently marked as in use */ +static u32 mali_empty_page_directory = MALI_INVALID_PAGE; + +/* + The fixed memory system's mali subsystem interface implementation. + We currently handle module and session life-time management. +*/ +struct mali_kernel_subsystem mali_subsystem_memory = +{ + mali_memory_core_initialize, /* startup */ + mali_memory_core_terminate, /* shutdown */ + mali_memory_core_load_complete, /* load_complete */ + mali_memory_core_system_info_fill, /* system_info_fill */ + mali_memory_core_session_begin, /* session_begin */ + mali_memory_core_session_end, /* session_end */ + NULL, /* broadcast_notification */ +#if MALI_STATE_TRACKING + NULL, /* dump_state */ +#endif +}; + +static mali_kernel_mem_address_manager mali_address_manager = +{ + mali_address_manager_allocate, /* allocate */ + mali_address_manager_release, /* release */ + mali_address_manager_map, /* map_physical */ + NULL /* unmap_physical not present*/ +}; + +static mali_kernel_mem_address_manager process_address_manager = +{ + _mali_osk_mem_mapregion_init, /* allocate */ + _mali_osk_mem_mapregion_term, /* release */ + _mali_osk_mem_mapregion_map, /* map_physical */ + _mali_osk_mem_mapregion_unmap /* unmap_physical */ +}; + +static mali_allocation_engine memory_engine = NULL; +static mali_physical_memory_allocator * physical_memory_allocators = NULL; + +static dedicated_memory_info * mem_region_registrations = NULL; + +/* Initialized when this subsystem is initialized. This is determined by the + * position in subsystems[], and so the value used to initialize this is + * determined at compile time */ +static mali_kernel_subsystem_identifier mali_subsystem_memory_id = (mali_kernel_subsystem_identifier)-1; + +/* called during module init */ +static _mali_osk_errcode_t mali_memory_core_initialize(mali_kernel_subsystem_identifier id) +{ + MALI_DEBUG_PRINT(2, ("MMU memory system initializing\n")); + + /* save our subsystem id for later for use in slot lookup during session activation */ + mali_subsystem_memory_id = id; + + _MALI_OSK_INIT_LIST_HEAD(&mmu_head); + + MALI_CHECK_NO_ERROR( mali_mmu_page_table_cache_create() ); + + /* register our handlers */ + MALI_CHECK_NO_ERROR( _mali_kernel_core_register_resource_handler(MMU, mali_memory_core_resource_mmu) ); + + MALI_CHECK_NO_ERROR( _mali_kernel_core_register_resource_handler(FPGA_FRAMEWORK, mali_memory_core_resource_fpga) ); + + MALI_CHECK_NO_ERROR( _mali_kernel_core_register_resource_handler(MEMORY, mali_memory_core_resource_dedicated_memory) ); + + MALI_CHECK_NO_ERROR( _mali_kernel_core_register_resource_handler(OS_MEMORY, mali_memory_core_resource_os_memory) ); + + memory_engine = mali_allocation_engine_create(&mali_address_manager, &process_address_manager); + MALI_CHECK_NON_NULL( memory_engine, _MALI_OSK_ERR_FAULT); + + MALI_SUCCESS; +} + +/* called if/when our module is unloaded */ +static void mali_memory_core_terminate(mali_kernel_subsystem_identifier id) +{ + mali_kernel_memory_mmu * mmu, *temp_mmu; + + MALI_DEBUG_PRINT(2, ("MMU memory system terminating\n")); + + /* loop over all MMU units and shut them down */ + _MALI_OSK_LIST_FOREACHENTRY(mmu, temp_mmu, &mmu_head, mali_kernel_memory_mmu, list) + { + /* reset to defaults */ + mali_mmu_raw_reset(mmu); + + /* unregister the irq */ + _mali_osk_irq_term(mmu->irq); + + /* remove from the list of MMU's on the system */ + _mali_osk_list_del(&mmu->list); + + /* release resources */ + _mali_osk_mem_unmapioregion(mmu->base, mmu->mapping_size, mmu->mapped_registers); + _mali_osk_mem_unreqregion(mmu->base, mmu->mapping_size); + _mali_osk_lock_term(mmu->lock); + _mali_osk_free(mmu); + } + + /* free global helper pages */ + mali_free_empty_page_directory(); + mali_free_fault_flush_pages(); + + /* destroy the page table cache before shutting down backends in case we have a page table leak to report */ + mali_mmu_page_table_cache_destroy(); + + while ( NULL != mem_region_registrations) + { + dedicated_memory_info * m; + m = mem_region_registrations; + mem_region_registrations = m->next; + _mali_osk_mem_unreqregion(m->base, m->size); + _mali_osk_free(m); + } + + while ( NULL != physical_memory_allocators) + { + mali_physical_memory_allocator * m; + m = physical_memory_allocators; + physical_memory_allocators = m->next; + m->destroy(m); + } + + if (NULL != memory_engine) + { + mali_allocation_engine_destroy(memory_engine); + memory_engine = NULL; + } + +} + +static _mali_osk_errcode_t mali_memory_core_session_begin(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue) +{ + memory_session * session_data; + _mali_osk_errcode_t err; + int i; + mali_io_address pd_mapped; + + /* validate input */ + if (NULL == slot) + { + MALI_DEBUG_PRINT(1, ("NULL slot given to memory session begin\n")); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + if (NULL != *slot) + { + MALI_DEBUG_PRINT(1, ("The slot given to memory session begin already contains data")); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + MALI_DEBUG_PRINT(2, ("MMU session begin\n")); + + /* create the session data object */ + session_data = _mali_osk_calloc(1, sizeof(memory_session)); + MALI_CHECK_NON_NULL( session_data, _MALI_OSK_ERR_NOMEM ); + + /* create descriptor mapping table */ + session_data->descriptor_mapping = mali_descriptor_mapping_create(MALI_MEM_DESCRIPTORS_INIT, MALI_MEM_DESCRIPTORS_MAX); + + if (NULL == session_data->descriptor_mapping) + { + _mali_osk_free(session_data); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + err = mali_mmu_get_table_page(&session_data->page_directory, &pd_mapped); + + session_data->page_directory_mapped = pd_mapped; + if (_MALI_OSK_ERR_OK != err) + { + mali_descriptor_mapping_destroy(session_data->descriptor_mapping); + _mali_osk_free(session_data); + MALI_ERROR(err); + } + MALI_DEBUG_ASSERT_POINTER( session_data->page_directory_mapped ); + + MALI_DEBUG_PRINT(2, ("Page directory for session 0x%x placed at physical address 0x%08X\n", mali_session_data, session_data->page_directory)); + + for (i = 0; i < MALI_MMU_PAGE_SIZE/4; i++) + { + /* mark each page table as not present */ + _mali_osk_mem_iowrite32_relaxed(session_data->page_directory_mapped, sizeof(u32) * i, 0); + } + _mali_osk_write_mem_barrier(); + + /* page_table_mapped[] is already set to NULL by _mali_osk_calloc call */ + + _MALI_OSK_INIT_LIST_HEAD(&session_data->active_mmus); + session_data->lock = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_ONELOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, 128); + if (NULL == session_data->lock) + { + mali_mmu_release_table_page(session_data->page_directory); + mali_descriptor_mapping_destroy(session_data->descriptor_mapping); + _mali_osk_free(session_data); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* Init the session's memory allocation list */ + _MALI_OSK_INIT_LIST_HEAD( &session_data->memory_head ); + + *slot = session_data; /* slot will point to our data object */ + MALI_DEBUG_PRINT(2, ("MMU session begin: success\n")); + MALI_SUCCESS; +} + +static void descriptor_table_cleanup_callback(int descriptor_id, void* map_target) +{ + mali_memory_allocation * descriptor; + + descriptor = (mali_memory_allocation*)map_target; + + MALI_DEBUG_PRINT(1, ("Cleanup of descriptor %d mapping to 0x%x in descriptor table\n", descriptor_id, map_target)); + MALI_DEBUG_ASSERT(descriptor); + + mali_allocation_engine_release_memory(memory_engine, descriptor); + _mali_osk_free(descriptor); +} + +static void mali_memory_core_session_end(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot) +{ + memory_session * session_data; + int i; + const int num_page_table_entries = sizeof(session_data->page_entries_mapped) / sizeof(session_data->page_entries_mapped[0]); + + MALI_DEBUG_PRINT(2, ("MMU session end\n")); + + /* validate input */ + if (NULL == slot) + { + MALI_DEBUG_PRINT(1, ("NULL slot given to memory session begin\n")); + return; + } + + session_data = (memory_session *)*slot; + + if (NULL == session_data) + { + MALI_DEBUG_PRINT(1, ("No session data found during session end\n")); + return; + } + /* Lock the session so we can modify the memory list */ + _mali_osk_lock_wait( session_data->lock, _MALI_OSK_LOCKMODE_RW ); + /* Noninterruptable spinlock type, so must always have locked. Checking should've been done in OSK function. */ + +#ifndef MALI_UKK_HAS_IMPLICIT_MMAP_CLEANUP +#if _MALI_OSK_SPECIFIC_INDIRECT_MMAP +#error Indirect MMAP specified, but UKK does not have implicit MMAP cleanup. Current implementation does not handle this. +#else + + /* Free all memory engine allocations */ + if (0 == _mali_osk_list_empty(&session_data->memory_head)) + { + mali_memory_allocation *descriptor; + mali_memory_allocation *temp; + _mali_uk_mem_munmap_s unmap_args; + + MALI_DEBUG_PRINT(1, ("Memory found on session usage list during session termination\n")); + + unmap_args.ctx = mali_session_data; + + /* use the 'safe' list iterator, since freeing removes the active block from the list we're iterating */ + _MALI_OSK_LIST_FOREACHENTRY(descriptor, temp, &session_data->memory_head, mali_memory_allocation, list) + { + MALI_DEBUG_PRINT(4, ("Freeing block with mali address 0x%x size %d mapped in user space at 0x%x\n", + descriptor->mali_address, descriptor->size, descriptor->size, descriptor->mapping) + ); + /* ASSERT that the descriptor's lock references the correct thing */ + MALI_DEBUG_ASSERT( descriptor->lock == session_data->lock ); + /* Therefore, we have already locked the descriptor */ + + unmap_args.size = descriptor->size; + unmap_args.mapping = descriptor->mapping; + unmap_args.cookie = (u32)descriptor; + + /* + * This removes the descriptor from the list, and frees the descriptor + * + * Does not handle the _MALI_OSK_SPECIFIC_INDIRECT_MMAP case, since + * the only OS we are aware of that requires indirect MMAP also has + * implicit mmap cleanup. + */ + _mali_ukk_mem_munmap_internal( &unmap_args ); + } + } + + /* Assert that we really did free everything */ + MALI_DEBUG_ASSERT( _mali_osk_list_empty(&session_data->memory_head) ); +#endif /* _MALI_OSK_SPECIFIC_INDIRECT_MMAP */ +#endif /* MALI_UKK_HAS_IMPLICIT_MMAP_CLEANUP */ + + if (NULL != session_data->descriptor_mapping) + { + mali_descriptor_mapping_call_for_each(session_data->descriptor_mapping, descriptor_table_cleanup_callback); + mali_descriptor_mapping_destroy(session_data->descriptor_mapping); + session_data->descriptor_mapping = NULL; + } + + for (i = 0; i < num_page_table_entries; i++) + { + /* free PTE memory */ + if (session_data->page_directory_mapped && (_mali_osk_mem_ioread32(session_data->page_directory_mapped, sizeof(u32)*i) & MALI_MMU_FLAGS_PRESENT)) + { + mali_mmu_release_table_page( _mali_osk_mem_ioread32(session_data->page_directory_mapped, i*sizeof(u32)) & ~MALI_MMU_FLAGS_MASK); + _mali_osk_mem_iowrite32(session_data->page_directory_mapped, i * sizeof(u32), 0); + } + } + + if (MALI_INVALID_PAGE != session_data->page_directory) + { + mali_mmu_release_table_page(session_data->page_directory); + session_data->page_directory = MALI_INVALID_PAGE; + } + + _mali_osk_lock_signal( session_data->lock, _MALI_OSK_LOCKMODE_RW ); + + /** + * @note Could the VMA close handler mean that we use the session data after it was freed? + * In which case, would need to refcount the session data, and free on VMA close + */ + + /* Free the lock */ + _mali_osk_lock_term( session_data->lock ); + /* free the session data object */ + _mali_osk_free(session_data); + + /* clear our slot */ + *slot = NULL; + + return; +} + +static _mali_osk_errcode_t mali_allocate_empty_page_directory(void) +{ + _mali_osk_errcode_t err; + mali_io_address mapping; + + MALI_CHECK_NO_ERROR(mali_mmu_get_table_page(&mali_empty_page_directory, &mapping)); + + MALI_DEBUG_ASSERT_POINTER( mapping ); + + err = fill_page(mapping, 0); + if (_MALI_OSK_ERR_OK != err) + { + mali_mmu_release_table_page(mali_empty_page_directory); + mali_empty_page_directory = MALI_INVALID_PAGE; + } + return err; +} + +static void mali_free_empty_page_directory(void) +{ + if (MALI_INVALID_PAGE != mali_empty_page_directory) + { + mali_mmu_release_table_page(mali_empty_page_directory); + mali_empty_page_directory = MALI_INVALID_PAGE; + } +} + +static _mali_osk_errcode_t fill_page(mali_io_address mapping, u32 data) +{ + int i; + MALI_DEBUG_ASSERT_POINTER( mapping ); + + for(i = 0; i < MALI_MMU_PAGE_SIZE/4; i++) + { + _mali_osk_mem_iowrite32_relaxed( mapping, i * sizeof(u32), data); + } + _mali_osk_mem_barrier(); + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_allocate_fault_flush_pages(void) +{ + _mali_osk_errcode_t err; + + err = mali_mmu_get_table_page(&mali_page_fault_flush_data_page, &mali_page_fault_flush_data_page_mapping); + if (_MALI_OSK_ERR_OK == err) + { + err = mali_mmu_get_table_page(&mali_page_fault_flush_page_table, &mali_page_fault_flush_page_table_mapping); + if (_MALI_OSK_ERR_OK == err) + { + err = mali_mmu_get_table_page(&mali_page_fault_flush_page_directory, &mali_page_fault_flush_page_directory_mapping); + if (_MALI_OSK_ERR_OK == err) + { + fill_page(mali_page_fault_flush_data_page_mapping, 0); + fill_page(mali_page_fault_flush_page_table_mapping, mali_page_fault_flush_data_page | MALI_MMU_FLAGS_WRITE_PERMISSION | MALI_MMU_FLAGS_READ_PERMISSION | MALI_MMU_FLAGS_PRESENT); + fill_page(mali_page_fault_flush_page_directory_mapping, mali_page_fault_flush_page_table | MALI_MMU_FLAGS_PRESENT); + MALI_SUCCESS; + } + mali_mmu_release_table_page(mali_page_fault_flush_page_table); + mali_page_fault_flush_page_table = MALI_INVALID_PAGE; + mali_page_fault_flush_page_table_mapping = NULL; + } + mali_mmu_release_table_page(mali_page_fault_flush_data_page); + mali_page_fault_flush_data_page = MALI_INVALID_PAGE; + mali_page_fault_flush_data_page_mapping = NULL; + } + MALI_ERROR(err); +} + +static void mali_free_fault_flush_pages(void) +{ + if (MALI_INVALID_PAGE != mali_page_fault_flush_page_directory) + { + mali_mmu_release_table_page(mali_page_fault_flush_page_directory); + mali_page_fault_flush_page_directory = MALI_INVALID_PAGE; + } + + if (MALI_INVALID_PAGE != mali_page_fault_flush_page_table) + { + mali_mmu_release_table_page(mali_page_fault_flush_page_table); + mali_page_fault_flush_page_table = MALI_INVALID_PAGE; + } + + if (MALI_INVALID_PAGE != mali_page_fault_flush_data_page) + { + mali_mmu_release_table_page(mali_page_fault_flush_data_page); + mali_page_fault_flush_data_page = MALI_INVALID_PAGE; + } +} + +static _mali_osk_errcode_t mali_memory_core_load_complete(mali_kernel_subsystem_identifier id) +{ + mali_kernel_memory_mmu * mmu, * temp_mmu; + + /* Report the allocators */ + mali_allocation_engine_report_allocators( physical_memory_allocators ); + + /* allocate the helper pages */ + MALI_CHECK_NO_ERROR( mali_allocate_empty_page_directory() ); + if (_MALI_OSK_ERR_OK != mali_allocate_fault_flush_pages()) + { + mali_free_empty_page_directory(); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* activate the empty page directory on all MMU's */ + _MALI_OSK_LIST_FOREACHENTRY(mmu, temp_mmu, &mmu_head, mali_kernel_memory_mmu, list) + { + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_DTE_ADDR, mali_empty_page_directory); + mali_mmu_enable_paging(mmu); + } + + MALI_DEBUG_PRINT(4, ("MMUs activated\n")); + /* the MMU system is now active */ + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_memory_core_system_info_fill(_mali_system_info* info) +{ + _mali_mem_info * mem_info; + + /* Make sure we won't leak any memory. It could also be that it's an + * uninitialized variable, but the caller should have zeroed the + * variable. */ + MALI_DEBUG_ASSERT(NULL == info->mem_info); + + info->has_mmu = 1; + + mem_info = _mali_osk_calloc(1,sizeof(_mali_mem_info)); + MALI_CHECK_NON_NULL( mem_info, _MALI_OSK_ERR_NOMEM ); + + mem_info->size = 2048UL * 1024UL * 1024UL; + mem_info->maximum_order_supported = 30; + mem_info->flags = _MALI_CPU_WRITEABLE | _MALI_CPU_READABLE | _MALI_PP_READABLE | _MALI_PP_WRITEABLE |_MALI_GP_READABLE | _MALI_GP_WRITEABLE; + mem_info->identifier = 0; + + info->mem_info = mem_info; + + /* all OK */ + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_memory_core_resource_mmu(_mali_osk_resource_t * resource) +{ + mali_kernel_memory_mmu * mmu; + + MALI_DEBUG_PRINT(4, ("MMU '%s' @ (0x%08X - 0x%08X)\n", + resource->description, resource->base, resource->base + MALI_MMU_REGISTERS_SIZE - 1 + )); + + if (NULL != mali_memory_core_mmu_lookup(resource->mmu_id)) + { + MALI_DEBUG_PRINT(1, ("Duplicate MMU ids found. The id %d is already in use\n", resource->mmu_id)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + if (_MALI_OSK_ERR_OK != _mali_osk_mem_reqregion(resource->base, MALI_MMU_REGISTERS_SIZE, resource->description)) + { + /* specified addresses are already in used by another driver / the kernel */ + MALI_DEBUG_PRINT( + 1, ("Failed to request MMU '%s' register address space at (0x%08X - 0x%08X)\n", + resource->description, resource->base, resource->base + MALI_MMU_REGISTERS_SIZE - 1 + )); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + mmu = _mali_osk_calloc(1, sizeof(mali_kernel_memory_mmu)); + + if (NULL == mmu) + { + MALI_DEBUG_PRINT(1, ("Failed to allocate memory for handling a MMU unit")); + _mali_osk_mem_unreqregion(resource->base, MALI_MMU_REGISTERS_SIZE); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + /* basic setup */ + _MALI_OSK_INIT_LIST_HEAD(&mmu->list); + + mmu->id = resource->mmu_id; + mmu->irq_nr = resource->irq; + mmu->flags = resource->flags; + mmu->base = resource->base; + mmu->mapping_size = MALI_MMU_REGISTERS_SIZE; + mmu->description = resource->description; /* no need to copy */ + _MALI_OSK_INIT_LIST_HEAD(&mmu->callbacks); + _MALI_OSK_INIT_LIST_HEAD(&mmu->session_link); + mmu->in_page_fault_handler = 0; + + mmu->lock = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_ONELOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, 127-mmu->id); + if (NULL == mmu->lock) + { + MALI_DEBUG_PRINT(1, ("Failed to create mmu lock\n")); + _mali_osk_mem_unreqregion(mmu->base, mmu->mapping_size); + _mali_osk_free(mmu); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* map the registers */ + mmu->mapped_registers = _mali_osk_mem_mapioregion( mmu->base, mmu->mapping_size, mmu->description ); + if (NULL == mmu->mapped_registers) + { + /* failed to map the registers */ + MALI_DEBUG_PRINT(1, ("Failed to map MMU registers at 0x%08X\n", mmu->base)); + _mali_osk_lock_term(mmu->lock); + _mali_osk_mem_unreqregion(mmu->base, MALI_MMU_REGISTERS_SIZE); + _mali_osk_free(mmu); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + MALI_DEBUG_PRINT(4, ("MMU '%s' @ (0x%08X - 0x%08X) mapped to 0x%08X\n", + resource->description, resource->base, resource->base + MALI_MMU_REGISTERS_SIZE - 1, mmu->mapped_registers + )); + + /* setup MMU interrupt mask */ + /* set all values to known defaults */ + mali_mmu_raw_reset(mmu); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_MASK, MALI_MMU_INTERRUPT_PAGE_FAULT | MALI_MMU_INTERRUPT_READ_BUS_ERROR); + /* setup MMU page directory pointer */ + /* The mali_page_directory pointer is guaranteed to be 4kb aligned because we've used get_zeroed_page to accquire it */ + /* convert the kernel virtual address into a physical address and set */ + + /* add to our list of MMU's */ + _mali_osk_list_addtail(&mmu->list, &mmu_head); + + mmu->irq = _mali_osk_irq_init( + mmu->irq_nr, + mali_kernel_memory_mmu_interrupt_handler_upper_half, + mali_kernel_memory_mmu_interrupt_handler_bottom_half, + (_mali_osk_irq_trigger_t)mali_mmu_probe_irq_trigger, + (_mali_osk_irq_ack_t)mali_mmu_probe_irq_acknowledge, + mmu, + "mali_mmu_irq_handlers" + ); + if (NULL == mmu->irq) + { + _mali_osk_list_del(&mmu->list); + _mali_osk_lock_term(mmu->lock); + _mali_osk_mem_unmapioregion( mmu->base, mmu->mapping_size, mmu->mapped_registers ); + _mali_osk_mem_unreqregion(resource->base, MALI_MMU_REGISTERS_SIZE); + _mali_osk_free(mmu); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* set to a known state */ + mali_mmu_raw_reset(mmu); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_MASK, MALI_MMU_INTERRUPT_PAGE_FAULT | MALI_MMU_INTERRUPT_READ_BUS_ERROR); + + MALI_DEBUG_PRINT(2, ("MMU registered\n")); + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_memory_core_resource_fpga(_mali_osk_resource_t * resource) +{ + mali_io_address mapping; + + MALI_DEBUG_PRINT(5, ("FPGA framework '%s' @ (0x%08X - 0x%08X)\n", + resource->description, resource->base, resource->base + sizeof(u32) * 2 - 1 + )); + + mapping = _mali_osk_mem_mapioregion(resource->base + 0x1000, sizeof(u32) * 2, "fpga framework"); + if (mapping) + { + MALI_DEBUG_CODE(u32 data = ) + _mali_osk_mem_ioread32(mapping, 0); + MALI_DEBUG_PRINT(2, ("FPGA framwork '%s' @ 0x%08X:\n", resource->description, resource->base)); + MALI_DEBUG_PRINT(2, ("\tBitfile date: %d%02d%02d_%02d%02d\n", + (data >> 20), + (data >> 16) & 0xF, + (data >> 11) & 0x1F, + (data >> 6) & 0x1F, + (data >> 0) & 0x3F)); + MALI_DEBUG_CODE(data = ) + _mali_osk_mem_ioread32(mapping, sizeof(u32)); + MALI_DEBUG_PRINT(2, ("\tBitfile SCCS rev: %d\n", data)); + + _mali_osk_mem_unmapioregion(resource->base + 0x1000, sizeof(u32) *2, mapping); + } + else MALI_DEBUG_PRINT(1, ("Failed to access FPGA framwork '%s' @ 0x%08X\n", resource->description, resource->base)); + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_memory_core_resource_os_memory(_mali_osk_resource_t * resource) +{ + mali_physical_memory_allocator * allocator; + mali_physical_memory_allocator ** next_allocator_list; + + u32 alloc_order = resource->alloc_order; + + allocator = mali_os_allocator_create(resource->size, resource->cpu_usage_adjust, resource->description); + if (NULL == allocator) + { + MALI_DEBUG_PRINT(1, ("Failed to create OS memory allocator\n")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + allocator->alloc_order = alloc_order; + + /* link in the allocator: insertion into ordered list + * resources of the same alloc_order will be Last-in-first */ + next_allocator_list = &physical_memory_allocators; + + while ( NULL != *next_allocator_list && + (*next_allocator_list)->alloc_order < alloc_order ) + { + next_allocator_list = &((*next_allocator_list)->next); + } + + allocator->next = (*next_allocator_list); + (*next_allocator_list) = allocator; + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_memory_core_resource_dedicated_memory(_mali_osk_resource_t * resource) +{ + mali_physical_memory_allocator * allocator; + mali_physical_memory_allocator ** next_allocator_list; + dedicated_memory_info * cleanup_data; + + u32 alloc_order = resource->alloc_order; + + /* do the lowlevel linux operation first */ + + /* Request ownership of the memory */ + if (_MALI_OSK_ERR_OK != _mali_osk_mem_reqregion(resource->base, resource->size, resource->description)) + { + MALI_DEBUG_PRINT(1, ("Failed to request memory region %s (0x%08X - 0x%08X)\n", resource->description, resource->base, resource->base + resource->size - 1)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* create generic block allocator object to handle it */ + allocator = mali_block_allocator_create(resource->base, resource->cpu_usage_adjust, resource->size, resource->description ); + + if (NULL == allocator) + { + MALI_DEBUG_PRINT(1, ("Memory bank registration failed\n")); + _mali_osk_mem_unreqregion(resource->base, resource->size); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* save lowlevel cleanup info */ + allocator->alloc_order = alloc_order; + + cleanup_data = _mali_osk_malloc(sizeof(dedicated_memory_info)); + + if (NULL == cleanup_data) + { + _mali_osk_mem_unreqregion(resource->base, resource->size); + allocator->destroy(allocator); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + cleanup_data->base = resource->base; + cleanup_data->size = resource->size; + + cleanup_data->next = mem_region_registrations; + mem_region_registrations = cleanup_data; + + /* link in the allocator: insertion into ordered list + * resources of the same alloc_order will be Last-in-first */ + next_allocator_list = &physical_memory_allocators; + + while ( NULL != *next_allocator_list && + (*next_allocator_list)->alloc_order < alloc_order ) + { + next_allocator_list = &((*next_allocator_list)->next); + } + + allocator->next = (*next_allocator_list); + (*next_allocator_list) = allocator; + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t mali_kernel_memory_mmu_interrupt_handler_upper_half(void * data) +{ + mali_kernel_memory_mmu * mmu; + u32 int_stat; + mali_core_renderunit *core; + + if (mali_benchmark) MALI_SUCCESS; + + mmu = (mali_kernel_memory_mmu *)data; + + MALI_DEBUG_ASSERT_POINTER(mmu); + + /* Pointer to core holding this MMU */ + core = (mali_core_renderunit *)mmu->core; + + if(CORE_OFF == core->state) + { + MALI_SUCCESS; + } + + + /* check if it was our device which caused the interrupt (we could be sharing the IRQ line) */ + int_stat = mali_mmu_register_read(mmu, MALI_MMU_REGISTER_INT_STATUS); + if (0 == int_stat) + { + MALI_ERROR(_MALI_OSK_ERR_FAULT); /* no bits set, we are sharing the IRQ line and someone else caused the interrupt */ + } + + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_MASK, 0); + + mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS); + + if (int_stat & MALI_MMU_INTERRUPT_PAGE_FAULT) + { + _mali_osk_irq_schedulework(mmu->irq); + } + if (int_stat & MALI_MMU_INTERRUPT_READ_BUS_ERROR) + { + /* clear interrupt flag */ + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_CLEAR, MALI_MMU_INTERRUPT_READ_BUS_ERROR); + /* reenable it */ + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_MASK, mali_mmu_register_read(mmu, MALI_MMU_REGISTER_INT_MASK) | MALI_MMU_INTERRUPT_READ_BUS_ERROR); + } + + MALI_SUCCESS; +} + + +static void mali_kernel_mmu_bus_reset(mali_kernel_memory_mmu * mmu) +{ + +#if defined(USING_MALI200) + int i; + const int replay_buffer_check_interval = 10; /* must be below 1000 */ + const int replay_buffer_max_number_of_checks = 100; +#endif + + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + /* add an extra reference while handling the page fault */ + mmu->usage_count++; + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + + MALI_DEBUG_PRINT(4, ("Sending stop bus request to cores\n")); + /* request to stop the bus, but don't wait for it to actually stop */ + _mali_kernel_core_broadcast_subsystem_message(MMU_KILL_STEP1_STOP_BUS_FOR_ALL_CORES, (u32)mmu); + +#if defined(USING_MALI200) + /* no new request will come from any of the connected cores from now + * we must now flush the playback buffer for any requests queued already + */ + + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + + MALI_DEBUG_PRINT(4, ("Switching to the special page fault flush page directory\n")); + /* don't use the mali_mmu_activate_address_space function here as we can't stall the MMU */ + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_DTE_ADDR, mali_page_fault_flush_page_directory); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ZAP_CACHE); + /* resume the MMU */ + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_CLEAR, MALI_MMU_INTERRUPT_PAGE_FAULT); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_PAGE_FAULT_DONE); + /* the MMU will now play back all the requests, all going to our special page fault flush data page */ + + /* just to be safe, check that the playback buffer is empty before continuing */ + if (!mali_benchmark) { + for (i = 0; i < replay_buffer_max_number_of_checks; i++) + { + if (mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS) & MALI_MMU_STATUS_BIT_REPLAY_BUFFER_EMPTY) break; + _mali_osk_time_ubusydelay(replay_buffer_check_interval); + } + + MALI_DEBUG_PRINT_IF(1, i == replay_buffer_max_number_of_checks, ("MMU: %s: Failed to flush replay buffer on page fault\n", mmu->description)); + MALI_DEBUG_PRINT(1, ("Replay playback took %ld usec\n", i * replay_buffer_check_interval)); + } + + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + +#endif + /* notify all subsystems that the core should be reset once the bus is actually stopped */ + MALI_DEBUG_PRINT(4,("Sending job abort command to subsystems\n")); + _mali_kernel_core_broadcast_subsystem_message(MMU_KILL_STEP2_RESET_ALL_CORES_AND_ABORT_THEIR_JOBS, (u32)mmu); + + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + + /* reprogram the MMU */ + mali_mmu_raw_reset(mmu); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_MASK, MALI_MMU_INTERRUPT_PAGE_FAULT | MALI_MMU_INTERRUPT_READ_BUS_ERROR); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_DTE_ADDR, mali_empty_page_directory); /* no session is active, so just activate the empty page directory */ + mali_mmu_enable_paging(mmu); + + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + + /* release the extra address space reference, will schedule */ + mali_memory_core_mmu_release_address_space_reference(mmu); + + /* resume normal operation */ + _mali_kernel_core_broadcast_subsystem_message(MMU_KILL_STEP3_CONTINUE_JOB_HANDLING, (u32)mmu); + MALI_DEBUG_PRINT(4, ("Page fault handling complete\n")); +} + +static void mali_mmu_raw_reset(mali_kernel_memory_mmu * mmu) +{ + const int max_loop_count = 100; + const int delay_in_usecs = 1; + + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_DTE_ADDR, 0xCAFEBABE); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_SOFT_RESET); + + if (!mali_benchmark) + { + int i; + for (i = 0; i < max_loop_count; ++i) + { + if (mali_mmu_register_read(mmu, MALI_MMU_REGISTER_DTE_ADDR) == 0) + { + break; + } + _mali_osk_time_ubusydelay(delay_in_usecs); + } + MALI_DEBUG_PRINT_IF(1, (max_loop_count == i), ("Reset request failed, MMU status is 0x%08X\n", mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS))); + } +} + +static void mali_mmu_enable_paging(mali_kernel_memory_mmu * mmu) +{ + const int max_loop_count = 100; + const int delay_in_usecs = 1; + + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ENABLE_PAGING); + + if (!mali_benchmark) + { + int i; + for (i = 0; i < max_loop_count; ++i) + { + if (mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS) & MALI_MMU_STATUS_BIT_PAGING_ENABLED) + { + break; + } + _mali_osk_time_ubusydelay(delay_in_usecs); + } + MALI_DEBUG_PRINT_IF(1, (max_loop_count == i), ("Enable paging request failed, MMU status is 0x%08X\n", mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS))); + } +} + +static mali_bool mali_mmu_enable_stall(mali_kernel_memory_mmu * mmu) +{ + const int max_loop_count = 100; + const int delay_in_usecs = 999; + int i; + + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ENABLE_STALL); + + if (!mali_benchmark) + { + for (i = 0; i < max_loop_count; ++i) + { + if (mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS) & MALI_MMU_STATUS_BIT_STALL_ACTIVE) + { + break; + } + _mali_osk_time_ubusydelay(delay_in_usecs); + } + MALI_DEBUG_PRINT_IF(1, (max_loop_count == i), ("Enable stall request failed, MMU status is 0x%08X\n", mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS))); + if (max_loop_count == i) + { + return MALI_FALSE; + } + } + + return MALI_TRUE; +} + +static void mali_mmu_disable_stall(mali_kernel_memory_mmu * mmu) +{ + const int max_loop_count = 100; + const int delay_in_usecs = 1; + int i; + + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_DISABLE_STALL); + + if (!mali_benchmark) + { + for (i = 0; i < max_loop_count; ++i) + { + if ((mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS) & MALI_MMU_STATUS_BIT_STALL_ACTIVE) == 0) + { + break; + } + _mali_osk_time_ubusydelay(delay_in_usecs); + } + MALI_DEBUG_PRINT_IF(1, (max_loop_count == i), ("Disable stall request failed, MMU status is 0x%08X\n", mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS))); + } +} + +void mali_kernel_mmu_reset(void * input_mmu) +{ + mali_kernel_memory_mmu * mmu; + MALI_DEBUG_ASSERT_POINTER(input_mmu); + mmu = (mali_kernel_memory_mmu *)input_mmu; + + MALI_DEBUG_PRINT(4, ("Mali MMU: mali_kernel_mmu_reset: %s\n", mmu->description)); + + if ( 0 != mmu->in_page_fault_handler) + { + /* This is possible if the bus can never be stopped for some reason */ + MALI_PRINT_ERROR(("Stopping the Memory bus not possible. Mali reset could not be performed.")); + return; + } + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + mali_mmu_raw_reset(mmu); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_MASK, MALI_MMU_INTERRUPT_PAGE_FAULT | MALI_MMU_INTERRUPT_READ_BUS_ERROR); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_DTE_ADDR, mali_empty_page_directory); /* no session is active, so just activate the empty page directory */ + mali_mmu_enable_paging(mmu); + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + +} + +void mali_kernel_mmu_force_bus_reset(void * input_mmu) +{ + mali_kernel_memory_mmu * mmu; + MALI_DEBUG_ASSERT_POINTER(input_mmu); + mmu = (mali_kernel_memory_mmu *)input_mmu; + if ( 0 != mmu->in_page_fault_handler) + { + /* This is possible if the bus can never be stopped for some reason */ + MALI_PRINT_ERROR(("Stopping the Memory bus not possible. Mali reset could not be performed.")); + return; + } + MALI_DEBUG_PRINT(1, ("Mali MMU: Force_bus_reset.\n")); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_MASK, 0); + mali_kernel_mmu_bus_reset(mmu); +} + + +static void mali_kernel_memory_mmu_interrupt_handler_bottom_half(void * data) +{ + mali_kernel_memory_mmu *mmu; + u32 raw, fault_address, status; + mali_core_renderunit *core; + + MALI_DEBUG_PRINT(1, ("mali_kernel_memory_mmu_interrupt_handler_bottom_half\n")); + if (NULL == data) + { + MALI_PRINT_ERROR(("MMU IRQ work queue: NULL argument")); + return; /* Error */ + } + mmu = (mali_kernel_memory_mmu*)data; + + + MALI_DEBUG_PRINT(4, ("Locking subsystems\n")); + /* lock all subsystems */ + _mali_kernel_core_broadcast_subsystem_message(MMU_KILL_STEP0_LOCK_SUBSYSTEM, (u32)mmu); + + /* Pointer to core holding this MMU */ + core = (mali_core_renderunit *)mmu->core; + + if(CORE_OFF == core->state) + { + _mali_kernel_core_broadcast_subsystem_message(MMU_KILL_STEP4_UNLOCK_SUBSYSTEM, (u32)mmu); + return; + } + + raw = mali_mmu_register_read(mmu, MALI_MMU_REGISTER_INT_RAWSTAT); + status = mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS); + + if ( (0==(raw & MALI_MMU_INTERRUPT_PAGE_FAULT)) && (0==(status & MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE)) ) + { + MALI_DEBUG_PRINT(1, ("MMU: Page fault bottom half: No Irq found.\n")); + MALI_DEBUG_PRINT(4, ("Unlocking subsystems")); + _mali_kernel_core_broadcast_subsystem_message(MMU_KILL_STEP4_UNLOCK_SUBSYSTEM, (u32)mmu); + return; + } + + mmu->in_page_fault_handler = 1; + + fault_address = mali_mmu_register_read(mmu, MALI_MMU_REGISTER_PAGE_FAULT_ADDR); + MALI_PRINT(("Page fault detected at 0x%x from bus id %d of type %s on %s\n", + (void*)fault_address, + (status >> 6) & 0x1F, + (status & 32) ? "write" : "read", + mmu->description) + ); + + if (NULL == mmu->active_session) + { + MALI_PRINT(("Spurious memory access detected from MMU %s\n", mmu->description)); + } + else + { + MALI_PRINT(("Active page directory at 0x%08X\n", mmu->active_session->page_directory)); + MALI_PRINT(("Info from page table for VA 0x%x:\n", (void*)fault_address)); + MALI_PRINT(("DTE entry: PTE at 0x%x marked as %s\n", + (void*)(_mali_osk_mem_ioread32(mmu->active_session->page_directory_mapped, + MALI_MMU_PDE_ENTRY(fault_address) * sizeof(u32)) & ~MALI_MMU_FLAGS_MASK), + _mali_osk_mem_ioread32(mmu->active_session->page_directory_mapped, + MALI_MMU_PDE_ENTRY(fault_address) * sizeof(u32)) & MALI_MMU_FLAGS_PRESENT ? "present" : "not present" + )); + + if (_mali_osk_mem_ioread32(mmu->active_session->page_directory_mapped, MALI_MMU_PDE_ENTRY(fault_address) * sizeof(u32)) & MALI_MMU_FLAGS_PRESENT) + { + mali_io_address pte; + u32 data; + pte = mmu->active_session->page_entries_mapped[MALI_MMU_PDE_ENTRY(fault_address)]; + data = _mali_osk_mem_ioread32(pte, MALI_MMU_PTE_ENTRY(fault_address) * sizeof(u32)); + MALI_PRINT(("PTE entry: Page at 0x%x, %s %s %s\n", + (void*)(data & ~MALI_MMU_FLAGS_MASK), + data & MALI_MMU_FLAGS_PRESENT ? "present" : "not present", + data & MALI_MMU_FLAGS_READ_PERMISSION ? "readable" : "", + data & MALI_MMU_FLAGS_WRITE_PERMISSION ? "writable" : "" + )); + } + else + { + MALI_PRINT(("PTE entry: Not present\n")); + } + } + + + mali_kernel_mmu_bus_reset(mmu); + + mmu->in_page_fault_handler = 0; + + /* unlock all subsystems */ + MALI_DEBUG_PRINT(4, ("Unlocking subsystems")); + _mali_kernel_core_broadcast_subsystem_message(MMU_KILL_STEP4_UNLOCK_SUBSYSTEM, (u32)mmu); + +} + + +static u32 mali_mmu_register_read(mali_kernel_memory_mmu * unit, mali_mmu_register reg) +{ + u32 val; + + if (mali_benchmark) return 0; + + val = _mali_osk_mem_ioread32(unit->mapped_registers, (u32)reg * sizeof(u32)); + + MALI_DEBUG_PRINT(6, ("mali_mmu_register_read addr:0x%04X val:0x%08x\n", (u32)reg * sizeof(u32),val)); + + return val; +} + +static void mali_mmu_register_write(mali_kernel_memory_mmu * unit, mali_mmu_register reg, u32 val) +{ + if (mali_benchmark) return; + + MALI_DEBUG_PRINT(6, ("mali_mmu_register_write addr:0x%04X val:0x%08x\n", (u32)reg * sizeof(u32), val)); + + _mali_osk_mem_iowrite32(unit->mapped_registers, (u32)reg * sizeof(u32), val); +} + +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 +static mali_physical_memory_allocation_result ump_memory_commit(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info) +{ + ump_dd_handle ump_mem; + u32 nr_blocks; + u32 i; + ump_dd_physical_block * ump_blocks; + ump_mem_allocation *ret_allocation; + + MALI_DEBUG_ASSERT_POINTER(ctx); + MALI_DEBUG_ASSERT_POINTER(engine); + MALI_DEBUG_ASSERT_POINTER(descriptor); + MALI_DEBUG_ASSERT_POINTER(alloc_info); + + ret_allocation = _mali_osk_malloc( sizeof( ump_mem_allocation ) ); + if ( NULL==ret_allocation ) return MALI_MEM_ALLOC_INTERNAL_FAILURE; + + ump_mem = (ump_dd_handle)ctx; + + MALI_DEBUG_PRINT(4, ("In ump_memory_commit\n")); + + nr_blocks = ump_dd_phys_block_count_get(ump_mem); + + MALI_DEBUG_PRINT(4, ("Have %d blocks\n", nr_blocks)); + + if (nr_blocks == 0) + { + MALI_DEBUG_PRINT(1, ("No block count\n")); + _mali_osk_free( ret_allocation ); + return MALI_MEM_ALLOC_INTERNAL_FAILURE; + } + + ump_blocks = _mali_osk_malloc(sizeof(*ump_blocks)*nr_blocks ); + if ( NULL==ump_blocks ) + { + _mali_osk_free( ret_allocation ); + return MALI_MEM_ALLOC_INTERNAL_FAILURE; + } + + if (UMP_DD_INVALID == ump_dd_phys_blocks_get(ump_mem, ump_blocks, nr_blocks)) + { + _mali_osk_free(ump_blocks); + _mali_osk_free( ret_allocation ); + return MALI_MEM_ALLOC_INTERNAL_FAILURE; + } + + /* Store away the initial offset for unmapping purposes */ + ret_allocation->initial_offset = *offset; + + for(i=0; i<nr_blocks; ++i) + { + MALI_DEBUG_PRINT(4, ("Mapping in 0x%08x size %d\n", ump_blocks[i].addr , ump_blocks[i].size)); + if (_MALI_OSK_ERR_OK != mali_allocation_engine_map_physical(engine, descriptor, *offset, ump_blocks[i].addr , 0, ump_blocks[i].size )) + { + u32 size_allocated = *offset - ret_allocation->initial_offset; + MALI_DEBUG_PRINT(1, ("Mapping of external memory failed\n")); + + /* unmap all previous blocks (if any) */ + mali_allocation_engine_unmap_physical(engine, descriptor, ret_allocation->initial_offset, size_allocated, (_mali_osk_mem_mapregion_flags_t)0 ); + + _mali_osk_free(ump_blocks); + _mali_osk_free(ret_allocation); + return MALI_MEM_ALLOC_INTERNAL_FAILURE; + } + *offset += ump_blocks[i].size; + } + + if (descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_GUARD_PAGE) + { + /* Map in an extra virtual guard page at the end of the VMA */ + MALI_DEBUG_PRINT(4, ("Mapping in extra guard page\n")); + if (_MALI_OSK_ERR_OK != mali_allocation_engine_map_physical(engine, descriptor, *offset, ump_blocks[0].addr , 0, _MALI_OSK_MALI_PAGE_SIZE )) + { + u32 size_allocated = *offset - ret_allocation->initial_offset; + MALI_DEBUG_PRINT(1, ("Mapping of external memory (guard page) failed\n")); + + /* unmap all previous blocks (if any) */ + mali_allocation_engine_unmap_physical(engine, descriptor, ret_allocation->initial_offset, size_allocated, (_mali_osk_mem_mapregion_flags_t)0 ); + + _mali_osk_free(ump_blocks); + _mali_osk_free(ret_allocation); + return MALI_MEM_ALLOC_INTERNAL_FAILURE; + } + *offset += _MALI_OSK_MALI_PAGE_SIZE; + } + + _mali_osk_free( ump_blocks ); + + ret_allocation->engine = engine; + ret_allocation->descriptor = descriptor; + ret_allocation->ump_mem = ump_mem; + ret_allocation->size_allocated = *offset - ret_allocation->initial_offset; + + alloc_info->ctx = NULL; + alloc_info->handle = ret_allocation; + alloc_info->next = NULL; + alloc_info->release = ump_memory_release; + + return MALI_MEM_ALLOC_FINISHED; +} + +static void ump_memory_release(void * ctx, void * handle) +{ + ump_dd_handle ump_mem; + ump_mem_allocation *allocation; + + allocation = (ump_mem_allocation *)handle; + + MALI_DEBUG_ASSERT_POINTER( allocation ); + + ump_mem = allocation->ump_mem; + + MALI_DEBUG_ASSERT(UMP_DD_HANDLE_INVALID!=ump_mem); + + /* At present, this is a no-op. But, it allows the mali_address_manager to + * do unmapping of a subrange in future. */ + mali_allocation_engine_unmap_physical( allocation->engine, + allocation->descriptor, + allocation->initial_offset, + allocation->size_allocated, + (_mali_osk_mem_mapregion_flags_t)0 + ); + _mali_osk_free( allocation ); + + + ump_dd_reference_release(ump_mem) ; + return; +} + +_mali_osk_errcode_t _mali_ukk_attach_ump_mem( _mali_uk_attach_ump_mem_s *args ) +{ + ump_dd_handle ump_mem; + mali_physical_memory_allocator external_memory_allocator; + memory_session * session_data; + mali_memory_allocation * descriptor; + int md; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = (memory_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_memory_id); + MALI_CHECK_NON_NULL(session_data, _MALI_OSK_ERR_INVALID_ARGS); + + /* check arguments */ + /* NULL might be a valid Mali address */ + if ( ! args->size) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + + /* size must be a multiple of the system page size */ + if ( args->size % _MALI_OSK_MALI_PAGE_SIZE ) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + + MALI_DEBUG_PRINT(3, + ("Requested to map ump memory with secure id %d into virtual memory 0x%08X, size 0x%08X\n", + args->secure_id, args->mali_address, args->size)); + + ump_mem = ump_dd_handle_create_from_secure_id( (int)args->secure_id ) ; + + if ( UMP_DD_HANDLE_INVALID==ump_mem ) MALI_ERROR(_MALI_OSK_ERR_FAULT); + + descriptor = _mali_osk_calloc(1, sizeof(mali_memory_allocation)); + if (NULL == descriptor) + { + ump_dd_reference_release(ump_mem); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + descriptor->size = args->size; + descriptor->mapping = NULL; + descriptor->mali_address = args->mali_address; + descriptor->mali_addr_mapping_info = (void*)session_data; + descriptor->process_addr_mapping_info = NULL; /* do not map to process address space */ + descriptor->lock = session_data->lock; + if (args->flags & _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE) + { + descriptor->flags = MALI_MEMORY_ALLOCATION_FLAG_MAP_GUARD_PAGE; + } + _mali_osk_list_init( &descriptor->list ); + + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_allocate_mapping(session_data->descriptor_mapping, descriptor, &md)) + { + ump_dd_reference_release(ump_mem); + _mali_osk_free(descriptor); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + external_memory_allocator.allocate = ump_memory_commit; + external_memory_allocator.allocate_page_table_block = NULL; + external_memory_allocator.ctx = ump_mem; + external_memory_allocator.name = "UMP Memory"; + external_memory_allocator.next = NULL; + + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + if (_MALI_OSK_ERR_OK != mali_allocation_engine_allocate_memory(memory_engine, descriptor, &external_memory_allocator, NULL)) + { + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + mali_descriptor_mapping_free(session_data->descriptor_mapping, md); + ump_dd_reference_release(ump_mem); + _mali_osk_free(descriptor); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + args->cookie = md; + + MALI_DEBUG_PRINT(5,("Returning from UMP attach\n")); + + /* All OK */ + MALI_SUCCESS; +} + + +_mali_osk_errcode_t _mali_ukk_release_ump_mem( _mali_uk_release_ump_mem_s *args ) +{ + mali_memory_allocation * descriptor; + memory_session * session_data; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = (memory_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_memory_id); + MALI_CHECK_NON_NULL(session_data, _MALI_OSK_ERR_INVALID_ARGS); + + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_get(session_data->descriptor_mapping, args->cookie, (void**)&descriptor)) + { + MALI_DEBUG_PRINT(1, ("Invalid memory descriptor %d used to release ump memory\n", args->cookie)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + mali_descriptor_mapping_free(session_data->descriptor_mapping, args->cookie); + + _mali_osk_lock_wait( session_data->lock, _MALI_OSK_LOCKMODE_RW ); + + mali_allocation_engine_release_memory(memory_engine, descriptor); + + _mali_osk_lock_signal( session_data->lock, _MALI_OSK_LOCKMODE_RW ); + + _mali_osk_free(descriptor); + + MALI_SUCCESS; + +} +#endif /* MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 */ + + +static mali_physical_memory_allocation_result external_memory_commit(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info) +{ + u32 * data; + external_mem_allocation * ret_allocation; + + MALI_DEBUG_ASSERT_POINTER(ctx); + MALI_DEBUG_ASSERT_POINTER(engine); + MALI_DEBUG_ASSERT_POINTER(descriptor); + MALI_DEBUG_ASSERT_POINTER(alloc_info); + + ret_allocation = _mali_osk_malloc( sizeof(external_mem_allocation) ); + + if ( NULL == ret_allocation ) + { + return MALI_MEM_ALLOC_INTERNAL_FAILURE; + } + + data = (u32*)ctx; + + ret_allocation->engine = engine; + ret_allocation->descriptor = descriptor; + ret_allocation->initial_offset = *offset; + + alloc_info->ctx = NULL; + alloc_info->handle = ret_allocation; + alloc_info->next = NULL; + alloc_info->release = external_memory_release; + + MALI_DEBUG_PRINT(3, ("External map: mapping phys 0x%08X at mali virtual address 0x%08X staring at offset 0x%08X length 0x%08X\n", data[0], descriptor->mali_address, *offset, data[1])); + + if (_MALI_OSK_ERR_OK != mali_allocation_engine_map_physical(engine, descriptor, *offset, data[0], 0, data[1])) + { + MALI_DEBUG_PRINT(1, ("Mapping of external memory failed\n")); + _mali_osk_free(ret_allocation); + return MALI_MEM_ALLOC_INTERNAL_FAILURE; + } + *offset += data[1]; + + if (descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_GUARD_PAGE) + { + /* Map in an extra virtual guard page at the end of the VMA */ + MALI_DEBUG_PRINT(4, ("Mapping in extra guard page\n")); + if (_MALI_OSK_ERR_OK != mali_allocation_engine_map_physical(engine, descriptor, *offset, data[0], 0, _MALI_OSK_MALI_PAGE_SIZE)) + { + u32 size_allocated = *offset - ret_allocation->initial_offset; + MALI_DEBUG_PRINT(1, ("Mapping of external memory (guard page) failed\n")); + + /* unmap what we previously mapped */ + mali_allocation_engine_unmap_physical(engine, descriptor, ret_allocation->initial_offset, size_allocated, (_mali_osk_mem_mapregion_flags_t)0 ); + _mali_osk_free(ret_allocation); + return MALI_MEM_ALLOC_INTERNAL_FAILURE; + } + *offset += _MALI_OSK_MALI_PAGE_SIZE; + } + + ret_allocation->size = *offset - ret_allocation->initial_offset; + + return MALI_MEM_ALLOC_FINISHED; +} + +static void external_memory_release(void * ctx, void * handle) +{ + external_mem_allocation * allocation; + + allocation = (external_mem_allocation *) handle; + MALI_DEBUG_ASSERT_POINTER( allocation ); + + /* At present, this is a no-op. But, it allows the mali_address_manager to + * do unmapping of a subrange in future. */ + + mali_allocation_engine_unmap_physical( allocation->engine, + allocation->descriptor, + allocation->initial_offset, + allocation->size, + (_mali_osk_mem_mapregion_flags_t)0 + ); + + _mali_osk_free( allocation ); + + return; +} + +_mali_osk_errcode_t _mali_ukk_map_external_mem( _mali_uk_map_external_mem_s *args ) +{ + mali_physical_memory_allocator external_memory_allocator; + memory_session * session_data; + u32 info[2]; + mali_memory_allocation * descriptor; + int md; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = (memory_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_memory_id); + MALI_CHECK_NON_NULL(session_data, _MALI_OSK_ERR_INVALID_ARGS); + + external_memory_allocator.allocate = external_memory_commit; + external_memory_allocator.allocate_page_table_block = NULL; + external_memory_allocator.ctx = &info[0]; + external_memory_allocator.name = "External Memory"; + external_memory_allocator.next = NULL; + + /* check arguments */ + /* NULL might be a valid Mali address */ + if ( ! args->size) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + + /* size must be a multiple of the system page size */ + if ( args->size % _MALI_OSK_MALI_PAGE_SIZE ) MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + + MALI_DEBUG_PRINT(3, + ("Requested to map physical memory 0x%x-0x%x into virtual memory 0x%x\n", + (void*)args->phys_addr, + (void*)(args->phys_addr + args->size -1), + (void*)args->mali_address) + ); + + /* Validate the mali physical range */ + MALI_CHECK_NO_ERROR( mali_kernel_core_validate_mali_phys_range( args->phys_addr, args->size ) ); + + info[0] = args->phys_addr; + info[1] = args->size; + + descriptor = _mali_osk_calloc(1, sizeof(mali_memory_allocation)); + if (NULL == descriptor) MALI_ERROR(_MALI_OSK_ERR_NOMEM); + + descriptor->size = args->size; + descriptor->mapping = NULL; + descriptor->mali_address = args->mali_address; + descriptor->mali_addr_mapping_info = (void*)session_data; + descriptor->process_addr_mapping_info = NULL; /* do not map to process address space */ + descriptor->lock = session_data->lock; + if (args->flags & _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE) + { + descriptor->flags = MALI_MEMORY_ALLOCATION_FLAG_MAP_GUARD_PAGE; + } + _mali_osk_list_init( &descriptor->list ); + + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_allocate_mapping(session_data->descriptor_mapping, descriptor, &md)) + { + _mali_osk_free(descriptor); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + if (_MALI_OSK_ERR_OK != mali_allocation_engine_allocate_memory(memory_engine, descriptor, &external_memory_allocator, NULL)) + { + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + mali_descriptor_mapping_free(session_data->descriptor_mapping, md); + _mali_osk_free(descriptor); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + args->cookie = md; + + MALI_DEBUG_PRINT(5,("Returning from range_map_external_memory\n")); + + /* All OK */ + MALI_SUCCESS; +} + + +_mali_osk_errcode_t _mali_ukk_unmap_external_mem( _mali_uk_unmap_external_mem_s *args ) +{ + mali_memory_allocation * descriptor; + memory_session * session_data; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = (memory_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_memory_id); + MALI_CHECK_NON_NULL(session_data, _MALI_OSK_ERR_INVALID_ARGS); + + if (_MALI_OSK_ERR_OK != mali_descriptor_mapping_get(session_data->descriptor_mapping, args->cookie, (void**)&descriptor)) + { + MALI_DEBUG_PRINT(1, ("Invalid memory descriptor %d used to unmap external memory\n", args->cookie)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + mali_descriptor_mapping_free(session_data->descriptor_mapping, args->cookie); + + _mali_osk_lock_wait( session_data->lock, _MALI_OSK_LOCKMODE_RW ); + + mali_allocation_engine_release_memory(memory_engine, descriptor); + + _mali_osk_lock_signal( session_data->lock, _MALI_OSK_LOCKMODE_RW ); + + _mali_osk_free(descriptor); + + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_init_mem( _mali_uk_init_mem_s *args ) +{ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + args->memory_size = 2 * 1024 * 1024 * 1024UL; /* 2GB address space */ + args->mali_address_base = 1 * 1024 * 1024 * 1024UL; /* staring at 1GB, causing this layout: (0-1GB unused)(1GB-3G usage by Mali)(3G-4G unused) */ + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_term_mem( _mali_uk_term_mem_s *args ) +{ + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_mmu_page_table_cache_create(void) +{ + page_table_cache.lock = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_ORDERED | _MALI_OSK_LOCKFLAG_ONELOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, 110); + MALI_CHECK_NON_NULL( page_table_cache.lock, _MALI_OSK_ERR_FAULT ); + _MALI_OSK_INIT_LIST_HEAD(&page_table_cache.partial); + _MALI_OSK_INIT_LIST_HEAD(&page_table_cache.full); + MALI_SUCCESS; +} + +void mali_mmu_page_table_cache_destroy(void) +{ + mali_mmu_page_table_allocation * alloc, *temp; + + _MALI_OSK_LIST_FOREACHENTRY(alloc, temp, &page_table_cache.partial, mali_mmu_page_table_allocation, list) + { + MALI_DEBUG_PRINT_IF(1, 0 != alloc->usage_count, ("Destroying page table cache while pages are tagged as in use. %d allocations still marked as in use.\n", alloc->usage_count)); + _mali_osk_list_del(&alloc->list); + alloc->pages.release(&alloc->pages); + _mali_osk_free(alloc->usage_map); + _mali_osk_free(alloc); + } + + MALI_DEBUG_PRINT_IF(1, 0 == _mali_osk_list_empty(&page_table_cache.full), ("Page table cache full list contains one or more elements \n")); + + _MALI_OSK_LIST_FOREACHENTRY(alloc, temp, &page_table_cache.full, mali_mmu_page_table_allocation, list) + { + MALI_DEBUG_PRINT(1, ("Destroy alloc 0x%08X with usage count %d\n", (u32)alloc, alloc->usage_count)); + _mali_osk_list_del(&alloc->list); + alloc->pages.release(&alloc->pages); + _mali_osk_free(alloc->usage_map); + _mali_osk_free(alloc); + } + + _mali_osk_lock_term(page_table_cache.lock); +} + +_mali_osk_errcode_t mali_mmu_get_table_page(u32 *table_page, mali_io_address *mapping) +{ + _mali_osk_lock_wait(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + + if (0 == _mali_osk_list_empty(&page_table_cache.partial)) + { + mali_mmu_page_table_allocation * alloc = _MALI_OSK_LIST_ENTRY(page_table_cache.partial.next, mali_mmu_page_table_allocation, list); + int page_number = _mali_osk_find_first_zero_bit(alloc->usage_map, alloc->num_pages); + MALI_DEBUG_PRINT(6, ("Partial page table allocation found, using page offset %d\n", page_number)); + _mali_osk_set_nonatomic_bit(page_number, alloc->usage_map); + alloc->usage_count++; + if (alloc->num_pages == alloc->usage_count) + { + /* full, move alloc to full list*/ + _mali_osk_list_move(&alloc->list, &page_table_cache.full); + } + _mali_osk_lock_signal(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + + *table_page = (MALI_MMU_PAGE_SIZE * page_number) + alloc->pages.phys_base; + *mapping = (mali_io_address)((MALI_MMU_PAGE_SIZE * page_number) + (u32)alloc->pages.mapping); + MALI_DEBUG_PRINT(4, ("Page table allocated for VA=0x%08X, MaliPA=0x%08X\n", *mapping, *table_page )); + MALI_SUCCESS; + } + else + { + mali_mmu_page_table_allocation * alloc; + /* no free pages, allocate a new one */ + + alloc = (mali_mmu_page_table_allocation *)_mali_osk_calloc(1, sizeof(mali_mmu_page_table_allocation)); + if (NULL == alloc) + { + _mali_osk_lock_signal(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + *table_page = MALI_INVALID_PAGE; + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + _MALI_OSK_INIT_LIST_HEAD(&alloc->list); + + if (_MALI_OSK_ERR_OK != mali_allocation_engine_allocate_page_tables(memory_engine, &alloc->pages, physical_memory_allocators)) + { + MALI_DEBUG_PRINT(1, ("No more memory for page tables\n")); + _mali_osk_free(alloc); + _mali_osk_lock_signal(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + *table_page = MALI_INVALID_PAGE; + *mapping = NULL; + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + /* create the usage map */ + alloc->num_pages = alloc->pages.size / MALI_MMU_PAGE_SIZE; + alloc->usage_count = 1; + MALI_DEBUG_PRINT(3, ("New page table cache expansion, %d pages in new cache allocation\n", alloc->num_pages)); + alloc->usage_map = _mali_osk_calloc(1, ((alloc->num_pages + BITS_PER_LONG - 1) & ~(BITS_PER_LONG-1) / BITS_PER_LONG) * sizeof(unsigned long)); + if (NULL == alloc->usage_map) + { + MALI_DEBUG_PRINT(1, ("Failed to allocate memory to describe MMU page table cache usage\n")); + alloc->pages.release(&alloc->pages); + _mali_osk_free(alloc); + _mali_osk_lock_signal(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + *table_page = MALI_INVALID_PAGE; + *mapping = NULL; + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + + /* clear memory allocation */ + fill_page(alloc->pages.mapping, 0); + + _mali_osk_set_nonatomic_bit(0, alloc->usage_map); + + if (alloc->num_pages > 1) + { + _mali_osk_list_add(&alloc->list, &page_table_cache.partial); + } + else + { + _mali_osk_list_add(&alloc->list, &page_table_cache.full); + } + + _mali_osk_lock_signal(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + *table_page = alloc->pages.phys_base; /* return the first page */ + *mapping = alloc->pages.mapping; /* Mapping for first page */ + MALI_DEBUG_PRINT(4, ("Page table allocated for VA=0x%08X, MaliPA=0x%08X\n", *mapping, *table_page )); + MALI_SUCCESS; + } +} + +void mali_mmu_release_table_page(u32 pa) +{ + mali_mmu_page_table_allocation * alloc, * temp_alloc; + + MALI_DEBUG_PRINT_IF(1, pa & 4095, ("Bad page address 0x%x given to mali_mmu_release_table_page\n", (void*)pa)); + + MALI_DEBUG_PRINT(4, ("Releasing table page 0x%08X to the cache\n", pa)); + + _mali_osk_lock_wait(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + + /* find the entry this address belongs to */ + /* first check the partial list */ + _MALI_OSK_LIST_FOREACHENTRY(alloc, temp_alloc, &page_table_cache.partial, mali_mmu_page_table_allocation, list) + { + u32 start = alloc->pages.phys_base; + u32 last = start + (alloc->num_pages - 1) * MALI_MMU_PAGE_SIZE; + if (pa >= start && pa <= last) + { + MALI_DEBUG_ASSERT(0 != _mali_osk_test_bit((pa - start)/MALI_MMU_PAGE_SIZE, alloc->usage_map)); + _mali_osk_clear_nonatomic_bit((pa - start)/MALI_MMU_PAGE_SIZE, alloc->usage_map); + alloc->usage_count--; + + _mali_osk_memset((void*)( ((u32)alloc->pages.mapping) + (pa - start) ), 0, MALI_MMU_PAGE_SIZE); + + if (0 == alloc->usage_count) + { + /* empty, release whole page alloc */ + _mali_osk_list_del(&alloc->list); + alloc->pages.release(&alloc->pages); + _mali_osk_free(alloc->usage_map); + _mali_osk_free(alloc); + } + _mali_osk_lock_signal(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + MALI_DEBUG_PRINT(4, ("(partial list)Released table page 0x%08X to the cache\n", pa)); + return; + } + } + + /* the check the full list */ + _MALI_OSK_LIST_FOREACHENTRY(alloc, temp_alloc, &page_table_cache.full, mali_mmu_page_table_allocation, list) + { + u32 start = alloc->pages.phys_base; + u32 last = start + (alloc->num_pages - 1) * MALI_MMU_PAGE_SIZE; + if (pa >= start && pa <= last) + { + _mali_osk_clear_nonatomic_bit((pa - start)/MALI_MMU_PAGE_SIZE, alloc->usage_map); + alloc->usage_count--; + + _mali_osk_memset((void*)( ((u32)alloc->pages.mapping) + (pa - start) ), 0, MALI_MMU_PAGE_SIZE); + + + if (0 == alloc->usage_count) + { + /* empty, release whole page alloc */ + _mali_osk_list_del(&alloc->list); + alloc->pages.release(&alloc->pages); + _mali_osk_free(alloc->usage_map); + _mali_osk_free(alloc); + } + else + { + /* transfer to partial list */ + _mali_osk_list_move(&alloc->list, &page_table_cache.partial); + } + + _mali_osk_lock_signal(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); + MALI_DEBUG_PRINT(4, ("(full list)Released table page 0x%08X to the cache\n", pa)); + return; + } + } + + MALI_DEBUG_PRINT(1, ("pa 0x%x not found in the page table cache\n", (void*)pa)); + + _mali_osk_lock_signal(page_table_cache.lock, _MALI_OSK_LOCKMODE_RW); +} + +void* mali_memory_core_mmu_lookup(u32 id) +{ + mali_kernel_memory_mmu * mmu, * temp_mmu; + + /* find an MMU with a matching id */ + _MALI_OSK_LIST_FOREACHENTRY(mmu, temp_mmu, &mmu_head, mali_kernel_memory_mmu, list) + { + if (id == mmu->id) return mmu; + } + + /* not found */ + return NULL; +} + +void mali_memory_core_mmu_owner(void *core, void *mmu_ptr) +{ + mali_kernel_memory_mmu *mmu; + + MALI_DEBUG_ASSERT_POINTER(mmu_ptr); + MALI_DEBUG_ASSERT_POINTER(core); + + mmu = (mali_kernel_memory_mmu *)mmu_ptr; + mmu->core = core; +} + +void mali_mmu_activate_address_space(mali_kernel_memory_mmu * mmu, u32 page_directory) +{ + mali_mmu_enable_stall(mmu); /* this might fail, but changing the DTE address and ZAP should work anyway... */ + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_DTE_ADDR, page_directory); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ZAP_CACHE); + mali_mmu_disable_stall(mmu); +} + +_mali_osk_errcode_t mali_memory_core_mmu_activate_page_table(void* mmu_ptr, struct mali_session_data * mali_session_data, void(*callback)(void*), void * callback_argument) +{ + memory_session * requested_memory_session; + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; + mali_kernel_memory_mmu * mmu; + + MALI_DEBUG_ASSERT_POINTER(mmu_ptr); + MALI_DEBUG_ASSERT_POINTER(mali_session_data); + + mmu = (mali_kernel_memory_mmu *)mmu_ptr; + + MALI_DEBUG_PRINT(4, ("Asked to activate page table for session 0x%x on MMU %s\n", mali_session_data, mmu->description)); + requested_memory_session = mali_kernel_session_manager_slot_get(mali_session_data, mali_subsystem_memory_id); + MALI_DEBUG_PRINT(5, ("Session 0x%x looked up as using memory session 0x%x\n", mali_session_data, requested_memory_session)); + + MALI_DEBUG_ASSERT_POINTER(requested_memory_session); + + MALI_DEBUG_PRINT(7, ("Taking locks\n")); + + _mali_osk_lock_wait(requested_memory_session->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + if (0 == mmu->usage_count) + { + /* no session currently active, activate the requested session */ + MALI_DEBUG_ASSERT(NULL == mmu->active_session); + mmu->active_session = requested_memory_session; + mmu->usage_count = 1; + MALI_DEBUG_PRINT(4, ("MMU idle, activating page directory 0x%08X on MMU %s\n", requested_memory_session->page_directory, mmu->description)); + mali_mmu_activate_address_space(mmu, requested_memory_session->page_directory); + { + /* Insert mmu into the right place in the active_mmus list so that + * it is still sorted. The list must be sorted by ID so we can get + * the mutexes in the right order in + * _mali_ukk_mem_munmap_internal(). + */ + _mali_osk_list_t *entry; + for (entry = requested_memory_session->active_mmus.next; + entry != &requested_memory_session->active_mmus; + entry = entry->next) + { + mali_kernel_memory_mmu *temp = _MALI_OSK_LIST_ENTRY(entry, mali_kernel_memory_mmu, session_link); + if (mmu->id < temp->id) + break; + } + /* If we broke out, then 'entry' points to the list node of the + * first mmu with a greater ID; otherwise, it points to + * active_mmus. We want to add *before* this node. + */ + _mali_osk_list_addtail(&mmu->session_link, entry); + } + err = _MALI_OSK_ERR_OK; + } + + /* Allow two cores to run in parallel if they come from the same session */ + else if ( + (mmu->in_page_fault_handler == 0) && + (requested_memory_session == mmu->active_session ) && + (0==(MALI_MMU_DISALLOW_PARALLELL_WORK_OF_MALI_CORES & mmu->flags)) + ) + { + /* nested activation detected, just update the reference count */ + MALI_DEBUG_PRINT(4, ("Nested activation detected, %d previous activations found\n", mmu->usage_count)); + mmu->usage_count++; + err = _MALI_OSK_ERR_OK; + } + + else if (NULL != callback) + { + /* can't activate right now, notify caller on idle via callback */ + mali_kernel_memory_mmu_idle_callback * callback_object, * temp_callback_object; + int found = 0; + + MALI_DEBUG_PRINT(3, ("The MMU is busy and is using a different address space, callback given\n")); + /* check for existing registration */ + _MALI_OSK_LIST_FOREACHENTRY(callback_object, temp_callback_object, &mmu->callbacks, mali_kernel_memory_mmu_idle_callback, link) + { + if (callback_object->callback == callback) + { + found = 1; + break; + } + } + + if (found) + { + MALI_DEBUG_PRINT(5, ("Duplicate callback registration found, ignoring\n")); + /* callback already registered */ + err = _MALI_OSK_ERR_BUSY; + } + else + { + MALI_DEBUG_PRINT(5,("New callback, registering\n")); + /* register the new callback */ + callback_object = _mali_osk_malloc(sizeof(mali_kernel_memory_mmu_idle_callback)); + if (NULL != callback_object) + { + MALI_DEBUG_PRINT(7,("Callback struct setup\n")); + callback_object->callback = callback; + callback_object->callback_argument = callback_argument; + _mali_osk_list_addtail(&callback_object->link, &mmu->callbacks); + err = _MALI_OSK_ERR_BUSY; + } + } + } + + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_lock_signal(requested_memory_session->lock, _MALI_OSK_LOCKMODE_RW); + + MALI_ERROR(err); +} + +void mali_memory_core_mmu_release_address_space_reference(void* mmu_ptr) +{ + mali_kernel_memory_mmu_idle_callback * callback_object, * temp; + mali_kernel_memory_mmu * mmu; + memory_session * session; + + _MALI_OSK_LIST_HEAD(callbacks); + + MALI_DEBUG_ASSERT_POINTER(mmu_ptr); + mmu = (mali_kernel_memory_mmu *)mmu_ptr; + + session = mmu->active_session; + + /* support that we handle spurious page faults */ + if (NULL != session) + { + _mali_osk_lock_wait(session->lock, _MALI_OSK_LOCKMODE_RW); + } + + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + MALI_DEBUG_PRINT(4, ("Deactivation of address space on MMU %s, %d references exists\n", mmu->description, mmu->usage_count)); + MALI_DEBUG_ASSERT(0 != mmu->usage_count); + mmu->usage_count--; + if (0 != mmu->usage_count) + { + MALI_DEBUG_PRINT(4, ("MMU still in use by this address space, %d references still exists\n", mmu->usage_count)); + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + /* support that we handle spurious page faults */ + if (NULL != session) + { + _mali_osk_lock_signal(session->lock, _MALI_OSK_LOCKMODE_RW); + } + return; + } + + MALI_DEBUG_PRINT(4, ("Activating the empty page directory on %s\n", mmu->description)); + + /* last reference gone, deactivate current address space */ + mali_mmu_activate_address_space(mmu, mali_empty_page_directory); + + /* unlink from session */ + _mali_osk_list_delinit(&mmu->session_link); + /* remove the active session pointer */ + mmu->active_session = NULL; + + /* Notify all registered callbacks. + * We have to be clever here: + * We must call the callbacks with the spinlock unlocked and + * the callback list emptied to allow them to re-register. + * So we make a copy of the list, clears the list and then later call the callbacks on the local copy + */ + /* copy list */ + _MALI_OSK_INIT_LIST_HEAD(&callbacks); + _mali_osk_list_splice(&mmu->callbacks, &callbacks); + /* clear the original, allowing new registrations during the callback */ + _MALI_OSK_INIT_LIST_HEAD(&mmu->callbacks); + + /* end of mmu manipulation, so safe to unlock */ + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + + /* then finally remove the (possible) session lock, supporting that no session was active (spurious page fault handling) */ + if (NULL != session) + { + _mali_osk_lock_signal(session->lock, _MALI_OSK_LOCKMODE_RW); + } + + _MALI_OSK_LIST_FOREACHENTRY(callback_object, temp, &callbacks, mali_kernel_memory_mmu_idle_callback, link) + { + MALI_DEBUG_ASSERT_POINTER(callback_object->callback); + (callback_object->callback)(callback_object->callback_argument); + _mali_osk_list_del(&callback_object->link); + _mali_osk_free(callback_object); + } +} + +void mali_memory_core_mmu_unregister_callback(void* mmu_ptr, void(*callback)(void*)) +{ + mali_kernel_memory_mmu_idle_callback * callback_object, * temp_callback_object; + mali_kernel_memory_mmu * mmu; + MALI_DEBUG_ASSERT_POINTER(mmu_ptr); + + MALI_DEBUG_ASSERT_POINTER(callback); + MALI_DEBUG_ASSERT_POINTER(mmu_ptr); + + mmu = (mali_kernel_memory_mmu *)mmu_ptr; + + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + _MALI_OSK_LIST_FOREACHENTRY(callback_object, temp_callback_object, &mmu->callbacks, mali_kernel_memory_mmu_idle_callback, link) + { + MALI_DEBUG_ASSERT_POINTER(callback_object->callback); + if (callback_object->callback == callback) + { + _mali_osk_list_del(&callback_object->link); + _mali_osk_free(callback_object); + break; + } + } + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); +} + +static _mali_osk_errcode_t mali_address_manager_allocate(mali_memory_allocation * descriptor) +{ + /* allocate page tables, if needed */ + int i; + const int first_pde_idx = MALI_MMU_PDE_ENTRY(descriptor->mali_address); + int last_pde_idx; + memory_session * session_data; +#if defined USING_MALI400_L2_CACHE + int has_active_mmus = 0; + int page_dir_updated = 0; +#endif + + + if (descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_GUARD_PAGE) + { + last_pde_idx = MALI_MMU_PDE_ENTRY(descriptor->mali_address + _MALI_OSK_MALI_PAGE_SIZE + descriptor->size - 1); + } + else + { + last_pde_idx = MALI_MMU_PDE_ENTRY(descriptor->mali_address + descriptor->size - 1); + } + + session_data = (memory_session*)descriptor->mali_addr_mapping_info; + MALI_DEBUG_ASSERT_POINTER(session_data); + + MALI_DEBUG_PRINT(4, ("allocating page tables for Mali virtual address space 0x%08X to 0x%08X\n", descriptor->mali_address, descriptor->mali_address + descriptor->size - 1)); + +#if defined USING_MALI400_L2_CACHE + if (0 == _mali_osk_list_empty(&session_data->active_mmus)) + { + /* + * We have active MMUs, so we are probably in the process of alocating more memory for a suspended GP job (PLBU heap) + * From Mali-400 MP r1p0, MMU page directory/tables are also cached by the Mali L2 cache, thus we need to invalidate the page directory + * from the L2 cache if we add new page directory entries (PDEs) to the page directory. + * We only need to do this when we have an active MMU, because we otherwise invalidate the entire Mali L2 cache before at job start + */ + has_active_mmus = 1; + } +#endif + + for (i = first_pde_idx; i <= last_pde_idx; i++) + { + if ( 0 == (_mali_osk_mem_ioread32(session_data->page_directory_mapped, i * sizeof(u32)) & MALI_MMU_FLAGS_PRESENT) ) + { + u32 pte_phys; + mali_io_address pte_mapped; + _mali_osk_errcode_t err; + + /* allocate a new page table */ + MALI_DEBUG_ASSERT(0 == session_data->page_entries_usage_count[i]); + MALI_DEBUG_ASSERT(NULL == session_data->page_entries_mapped[i]); + + err = mali_mmu_get_table_page(&pte_phys, &pte_mapped); + if (_MALI_OSK_ERR_OK == err) + { + session_data->page_entries_mapped[i] = pte_mapped; + MALI_DEBUG_ASSERT_POINTER( session_data->page_entries_mapped[i] ); + + _mali_osk_mem_iowrite32(session_data->page_directory_mapped, i * sizeof(u32), pte_phys | MALI_MMU_FLAGS_PRESENT); /* mark page table as present */ + + /* update usage count */ + session_data->page_entries_usage_count[i]++; +#if defined USING_MALI400_L2_CACHE + page_dir_updated = 1; +#endif + continue; /* continue loop */ + } + + MALI_DEBUG_PRINT(1, ("Page table alloc failed\n")); + break; /* abort loop, failed to allocate one or more page tables */ + } + else + { + session_data->page_entries_usage_count[i]++; + } + } + + if (i <= last_pde_idx) + { + /* one or more pages could not be allocated, release reference count for the ones we added one for */ + /* adjust for the one which caused the for loop to be aborted */ + i--; + + while (i >= first_pde_idx) + { + MALI_DEBUG_ASSERT(0 != session_data->page_entries_usage_count[i]); + session_data->page_entries_usage_count[i]--; + if (0 == session_data->page_entries_usage_count[i]) + { + /* last reference removed */ + mali_mmu_release_table_page(MALI_MMU_ENTRY_ADDRESS(_mali_osk_mem_ioread32(session_data->page_directory_mapped, i * sizeof(u32)))); + session_data->page_entries_mapped[i] = NULL; + _mali_osk_mem_iowrite32(session_data->page_directory_mapped, i * sizeof(u32), 0); /* mark as not present in the page directory */ + } + i--; + } + + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + +#if defined USING_MALI400_L2_CACHE + if (1 == has_active_mmus && 1 == page_dir_updated) + { + /* + * We have updated the page directory and have an active MMU using it, so invalidate it in the Mali L2 cache. + */ + mali_kernel_l2_cache_invalidate_page(session_data->page_directory); + } +#endif + + /* all OK */ + MALI_SUCCESS; +} + +static void mali_address_manager_release(mali_memory_allocation * descriptor) +{ + int first_pde_idx; + int last_pde_idx; + memory_session * session_data; + u32 mali_address; + u32 mali_address_end; + u32 left; + int i; +#if defined USING_MALI400_L2_CACHE + int has_active_mmus = 0; + int page_dir_updated = 0; +#endif + + MALI_DEBUG_ASSERT_POINTER(descriptor); + session_data = (memory_session*)descriptor->mali_addr_mapping_info; + MALI_DEBUG_ASSERT_POINTER(session_data); + MALI_DEBUG_ASSERT_POINTER(session_data->page_directory_mapped); + + mali_address = descriptor->mali_address; + mali_address_end = descriptor->mali_address + descriptor->size; + left = descriptor->size; + + first_pde_idx = MALI_MMU_PDE_ENTRY(mali_address); + last_pde_idx = MALI_MMU_PDE_ENTRY(mali_address_end - 1); + + MALI_DEBUG_PRINT(3, ("Zapping Mali MMU table for address 0x%08X size 0x%08X\n", mali_address, left)); + MALI_DEBUG_PRINT(4, ("Zapping PDE %d through %d\n", first_pde_idx, last_pde_idx)); + +#if defined USING_MALI400_L2_CACHE + if (0 == _mali_osk_list_empty(&session_data->active_mmus)) + { + /* + * From Mali-400 MP r1p0, MMU page directory/tables are also cached by the Mali L2 cache, thus we need to invalidate the page tables + * from the L2 cache to ensure that the memory is unmapped. + * We only need to do this when we have an active MMU, because we otherwise invalidate the entire Mali L2 cache before at job start + */ + has_active_mmus = 1; + } +#endif + + + for (i = first_pde_idx; i <= last_pde_idx; i++) + { + int size_inside_pte = left < 0x400000 ? left : 0x400000; + const int first_pte_idx = MALI_MMU_PTE_ENTRY(mali_address); + int last_pte_idx = MALI_MMU_PTE_ENTRY(mali_address + size_inside_pte - 1); + + if (last_pte_idx < first_pte_idx) + { + /* The last_pte_idx is into the next PTE, crop it to fit into this */ + last_pte_idx = 1023; /* 1024 PTE entries, so 1023 is the last one */ + size_inside_pte = MALI_MMU_ADDRESS(i + 1, 0) - mali_address; + } + + MALI_DEBUG_ASSERT_POINTER(session_data->page_entries_mapped[i]); + MALI_DEBUG_ASSERT(0 != session_data->page_entries_usage_count[i]); + MALI_DEBUG_PRINT(4, ("PDE %d: zapping entries %d through %d, address 0x%08X, size 0x%08X, left 0x%08X (page table at 0x%08X)\n", + i, first_pte_idx, last_pte_idx, mali_address, size_inside_pte, left, + MALI_MMU_ENTRY_ADDRESS(_mali_osk_mem_ioread32(session_data->page_directory_mapped, i * sizeof(u32))))); + + session_data->page_entries_usage_count[i]--; + + if (0 == session_data->page_entries_usage_count[i]) + { + MALI_DEBUG_PRINT(4, ("Releasing page table as this is the last reference\n")); + /* last reference removed, no need to zero out each PTE */ + mali_mmu_release_table_page(MALI_MMU_ENTRY_ADDRESS(_mali_osk_mem_ioread32(session_data->page_directory_mapped, i * sizeof(u32)))); + session_data->page_entries_mapped[i] = NULL; + _mali_osk_mem_iowrite32(session_data->page_directory_mapped, i * sizeof(u32), 0); /* mark as not present in the page directory */ +#if defined USING_MALI400_L2_CACHE + page_dir_updated = 1; +#endif + } + else + { + int j; + + for (j = first_pte_idx; j <= last_pte_idx; j++) + { + _mali_osk_mem_iowrite32(session_data->page_entries_mapped[i], j * sizeof(u32), 0); + } + +#if defined USING_MALI400_L2_CACHE + if (1 == has_active_mmus) + { + /* Invalidate the page we've just modified */ + mali_kernel_l2_cache_invalidate_page( _mali_osk_mem_ioread32(session_data->page_directory_mapped, i*sizeof(u32)) & ~MALI_MMU_FLAGS_MASK); + } +#endif + } + left -= size_inside_pte; + mali_address += size_inside_pte; + } + +#if defined USING_MALI400_L2_CACHE + if ((1 == page_dir_updated) && (1== has_active_mmus)) + { + /* The page directory was also updated */ + mali_kernel_l2_cache_invalidate_page(session_data->page_directory); + } +#endif +} + +static _mali_osk_errcode_t mali_address_manager_map(mali_memory_allocation * descriptor, u32 offset, u32 *phys_addr, u32 size) +{ + memory_session * session_data; + u32 mali_address; + u32 mali_address_end; + u32 current_phys_addr; +#if defined USING_MALI400_L2_CACHE + int has_active_mmus = 0; +#endif + + MALI_DEBUG_ASSERT_POINTER(descriptor); + + MALI_DEBUG_ASSERT_POINTER( phys_addr ); + + current_phys_addr = *phys_addr; + + session_data = (memory_session*)descriptor->mali_addr_mapping_info; + MALI_DEBUG_ASSERT_POINTER(session_data); + + mali_address = descriptor->mali_address + offset; + mali_address_end = descriptor->mali_address + offset + size; + +#if defined USING_MALI400_L2_CACHE + if (0 == _mali_osk_list_empty(&session_data->active_mmus)) + { + /* + * We have active MMUs, so we are probably in the process of alocating more memory for a suspended GP job (PLBU heap) + * From Mali-400 MP r1p0, MMU page directory/tables are also cached by the Mali L2 cache, thus we need to invalidate the page tables + * from the L2 cache when we have allocated more heap memory. + * We only need to do this when we have an active MMU, because we otherwise invalidate the entire Mali L2 cache before at job start + */ + has_active_mmus = 1; + } +#endif + + MALI_DEBUG_PRINT(6, ("Mali map: mapping 0x%08X to Mali address 0x%08X length 0x%08X\n", current_phys_addr, mali_address, size)); + + MALI_DEBUG_ASSERT_POINTER(session_data->page_entries_mapped); + + for ( ; mali_address < mali_address_end; mali_address += MALI_MMU_PAGE_SIZE, current_phys_addr += MALI_MMU_PAGE_SIZE) + { + MALI_DEBUG_ASSERT_POINTER(session_data->page_entries_mapped[MALI_MMU_PDE_ENTRY(mali_address)]); + _mali_osk_mem_iowrite32_relaxed(session_data->page_entries_mapped[MALI_MMU_PDE_ENTRY(mali_address)], MALI_MMU_PTE_ENTRY(mali_address) * sizeof(u32), current_phys_addr | MALI_MMU_FLAGS_WRITE_PERMISSION | MALI_MMU_FLAGS_READ_PERMISSION | MALI_MMU_FLAGS_PRESENT); + } + _mali_osk_write_mem_barrier(); + +#if defined USING_MALI400_L2_CACHE + if (1 == has_active_mmus) + { + int i; + const int first_pde_idx = MALI_MMU_PDE_ENTRY(mali_address); + const int last_pde_idx = MALI_MMU_PDE_ENTRY(mali_address_end - 1); + + /* + * Invalidate the updated page table(s), incase they have been used for something + * else since last job start (invalidation of entire Mali L2 cache) + */ + for (i = first_pde_idx; i <= last_pde_idx; i++) + { + mali_kernel_l2_cache_invalidate_page( _mali_osk_mem_ioread32(session_data->page_directory_mapped, i*sizeof(u32)) & ~MALI_MMU_FLAGS_MASK); + } + } +#endif + + MALI_SUCCESS; +} + +/* This handler registered to mali_mmap for MMU builds */ +_mali_osk_errcode_t _mali_ukk_mem_mmap( _mali_uk_mem_mmap_s *args ) +{ + struct mali_session_data * mali_session_data; + mali_memory_allocation * descriptor; + memory_session * session_data; + + /* validate input */ + if (NULL == args) { MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: args was NULL\n")); MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); } + + /* Unpack arguments */ + mali_session_data = (struct mali_session_data *)args->ctx; + + if (NULL == mali_session_data) { MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: mali_session data was NULL\n")); MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); } + + MALI_DEBUG_ASSERT( mali_subsystem_memory_id >= 0 ); + + session_data = mali_kernel_session_manager_slot_get(mali_session_data, mali_subsystem_memory_id); + /* validate input */ + if (NULL == session_data) { MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: session data was NULL\n")); MALI_ERROR(_MALI_OSK_ERR_FAULT); } + + descriptor = (mali_memory_allocation*) _mali_osk_calloc( 1, sizeof(mali_memory_allocation) ); + if (NULL == descriptor) { MALI_DEBUG_PRINT(3,("mali_ukk_mem_mmap: descriptor was NULL\n")); MALI_ERROR(_MALI_OSK_ERR_NOMEM); } + + descriptor->size = args->size; + descriptor->mali_address = args->phys_addr; + descriptor->mali_addr_mapping_info = (void*)session_data; + + descriptor->process_addr_mapping_info = args->ukk_private; /* save to be used during physical manager callback */ + descriptor->flags = MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE; + descriptor->lock = session_data->lock; + _mali_osk_list_init( &descriptor->list ); + + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + if (0 == mali_allocation_engine_allocate_memory(memory_engine, descriptor, physical_memory_allocators, &session_data->memory_head)) + { + mali_kernel_memory_mmu * mmu, * temp_mmu; + + _MALI_OSK_LIST_FOREACHENTRY(mmu, temp_mmu, &session_data->active_mmus, mali_kernel_memory_mmu, session_link) + { + /* no need to lock the MMU as we own it already */ + MALI_DEBUG_PRINT(5, ("Zapping the cache of mmu %s as it's using the page table we have updated\n", mmu->description)); + + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + + mali_mmu_enable_stall(mmu); /* this might fail, but ZAP should work anyway... */ + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ZAP_CACHE); + mali_mmu_disable_stall(mmu); + + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + } + + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + /* All ok, write out any information generated from this call */ + args->mapping = descriptor->mapping; + args->cookie = (u32)descriptor; + + MALI_DEBUG_PRINT(7, ("MMAP OK\n")); + /* All done */ + MALI_SUCCESS; + } + else + { + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + /* OOM, but not a fatal error */ + MALI_DEBUG_PRINT(4, ("Memory allocation failure, OOM\n")); + _mali_osk_free(descriptor); + /* Linux will free the CPU address allocation, userspace client the Mali address allocation */ + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } +} + +static _mali_osk_errcode_t _mali_ukk_mem_munmap_internal( _mali_uk_mem_munmap_s *args ) +{ + memory_session * session_data; + mali_kernel_memory_mmu * mmu, * temp_mmu; + mali_memory_allocation * descriptor; + + descriptor = (mali_memory_allocation *)args->cookie; + MALI_DEBUG_ASSERT_POINTER(descriptor); + + /** @note args->context unused; we use the memory_session from the cookie */ + /* args->mapping and args->size are also discarded. They are only necessary + for certain do_munmap implementations. However, they could be used to check the + descriptor at this point. */ + + session_data = (memory_session*)descriptor->mali_addr_mapping_info; + MALI_DEBUG_ASSERT_POINTER(session_data); + + /* Stall the MMU(s) which is using the address space we're operating on. + * Note that active_mmus must be sorted in order of ID to avoid a mutex + * ordering violation. + */ + _MALI_OSK_LIST_FOREACHENTRY(mmu, temp_mmu, &session_data->active_mmus, mali_kernel_memory_mmu, session_link) + { + u32 status; + status = mali_mmu_register_read(mmu, MALI_MMU_REGISTER_STATUS); + if ( MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE == (status & MALI_MMU_STATUS_BIT_PAGE_FAULT_ACTIVE) ) { + MALI_DEBUG_PRINT(2, ("Stopped stall attempt for mmu with id %d since it is in page fault mode.\n", mmu->id)); + continue; + } + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + + /* + * If we're unable to stall, then make sure we tell our caller that, + * the caller should then release the session lock for a while, + * then this function again. + * This function will fail if we're in page fault mode, and to get + * out of page fault mode, the page fault handler must be able to + * take the session lock. + */ + if (!mali_mmu_enable_stall(mmu)) + { + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + return _MALI_OSK_ERR_BUSY; + } + + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + } + + _MALI_OSK_LIST_FOREACHENTRY(mmu, temp_mmu, &session_data->active_mmus, mali_kernel_memory_mmu, session_link) + { + _mali_osk_lock_wait(mmu->lock, _MALI_OSK_LOCKMODE_RW); + } + + /* This function also removes the memory from the session's memory list */ + mali_allocation_engine_release_memory(memory_engine, descriptor); + _mali_osk_free(descriptor); + + /* any L2 maintenance was done during mali_allocation_engine_release_memory */ + /* the session is locked, so the active mmu list should be the same */ + /* zap the TLB and resume operation */ + _MALI_OSK_LIST_FOREACHENTRY(mmu, temp_mmu, &session_data->active_mmus, mali_kernel_memory_mmu, session_link) + { + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_COMMAND, MALI_MMU_COMMAND_ZAP_CACHE); + mali_mmu_disable_stall(mmu); + + _mali_osk_lock_signal(mmu->lock, _MALI_OSK_LOCKMODE_RW); + } + + return _MALI_OSK_ERR_OK; +} + +/* Handler for unmapping memory for MMU builds */ +_mali_osk_errcode_t _mali_ukk_mem_munmap( _mali_uk_mem_munmap_s *args ) +{ + mali_memory_allocation * descriptor; + _mali_osk_lock_t *descriptor_lock; + _mali_osk_errcode_t err; + + descriptor = (mali_memory_allocation *)args->cookie; + MALI_DEBUG_ASSERT_POINTER(descriptor); + + /** @note args->context unused; we use the memory_session from the cookie */ + /* args->mapping and args->size are also discarded. They are only necessary + for certain do_munmap implementations. However, they could be used to check the + descriptor at this point. */ + + MALI_DEBUG_ASSERT_POINTER((memory_session*)descriptor->mali_addr_mapping_info); + + descriptor_lock = descriptor->lock; /* should point to the session data lock... */ + + err = _MALI_OSK_ERR_BUSY; + while (err == _MALI_OSK_ERR_BUSY) + { + if (descriptor_lock) + { + _mali_osk_lock_wait( descriptor_lock, _MALI_OSK_LOCKMODE_RW ); + } + + err = _mali_ukk_mem_munmap_internal( args ); + + if (descriptor_lock) + { + _mali_osk_lock_signal( descriptor_lock, _MALI_OSK_LOCKMODE_RW ); + } + + if (err == _MALI_OSK_ERR_BUSY) + { + /* + * Reason for this; + * We where unable to stall the MMU, probably because we are in page fault handling. + * Sleep for a while with the session lock released, then try again. + * Abnormal termination of programs with running Mali jobs is a normal reason for this. + */ + _mali_osk_time_ubusydelay(10); + } + } + + return err; +} + +/* Is called when the rendercore wants the mmu to give an interrupt */ +static void mali_mmu_probe_irq_trigger(mali_kernel_memory_mmu * mmu) +{ + MALI_DEBUG_PRINT(2, ("mali_mmu_probe_irq_trigger\n")); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_RAWSTAT, MALI_MMU_INTERRUPT_PAGE_FAULT|MALI_MMU_INTERRUPT_READ_BUS_ERROR); +} + +/* Is called when the irq probe wants the mmu to acknowledge an interrupt from the hw */ +static _mali_osk_errcode_t mali_mmu_probe_irq_acknowledge(mali_kernel_memory_mmu * mmu) +{ + u32 int_stat; + + int_stat = mali_mmu_register_read(mmu, MALI_MMU_REGISTER_INT_STATUS); + + MALI_DEBUG_PRINT(2, ("mali_mmu_probe_irq_acknowledge: intstat 0x%x\n", int_stat)); + if (int_stat & MALI_MMU_INTERRUPT_PAGE_FAULT) + { + MALI_DEBUG_PRINT(2, ("Probe: Page fault detect: PASSED\n")); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_CLEAR, MALI_MMU_INTERRUPT_PAGE_FAULT); + } + else MALI_DEBUG_PRINT(1, ("Probe: Page fault detect: FAILED\n")); + + if (int_stat & MALI_MMU_INTERRUPT_READ_BUS_ERROR) + { + MALI_DEBUG_PRINT(2, ("Probe: Bus read error detect: PASSED\n")); + mali_mmu_register_write(mmu, MALI_MMU_REGISTER_INT_CLEAR, MALI_MMU_INTERRUPT_READ_BUS_ERROR); + } + else MALI_DEBUG_PRINT(1, ("Probe: Bus read error detect: FAILED\n")); + + if ( (int_stat & (MALI_MMU_INTERRUPT_PAGE_FAULT|MALI_MMU_INTERRUPT_READ_BUS_ERROR)) == + (MALI_MMU_INTERRUPT_PAGE_FAULT|MALI_MMU_INTERRUPT_READ_BUS_ERROR)) + { + MALI_SUCCESS; + } + + MALI_ERROR(_MALI_OSK_ERR_FAULT); +} + +struct dump_info +{ + u32 buffer_left; + u32 register_writes_size; + u32 page_table_dump_size; + u32 *buffer; +}; + +static _mali_osk_errcode_t writereg(u32 where, u32 what, const char * comment, struct dump_info * info, int dump_to_serial) +{ + if (dump_to_serial) MALI_DEBUG_PRINT(1, ("writereg %08X %08X # %s\n", where, what, comment)); + + if (NULL != info) + { + info->register_writes_size += sizeof(u32)*2; /* two 32-bit words */ + + if (NULL != info->buffer) + { + /* check that we have enough space */ + if (info->buffer_left < sizeof(u32)*2) MALI_ERROR(_MALI_OSK_ERR_NOMEM); + + *info->buffer = where; + info->buffer++; + + *info->buffer = what; + info->buffer++; + + info->buffer_left -= sizeof(u32)*2; + } + } + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t dump_page(mali_io_address page, u32 phys_addr, struct dump_info * info, int dump_to_serial) +{ + if (dump_to_serial) + { + int i; + for (i = 0; i < 256; i++) + { + MALI_DEBUG_PRINT(1, ("%08X: %08X %08X %08X %08X\n", phys_addr + 16*i, _mali_osk_mem_ioread32(page, (i*4 + 0) * sizeof(u32)), + _mali_osk_mem_ioread32(page, (i*4 + 1) * sizeof(u32)), + _mali_osk_mem_ioread32(page, (i*4 + 2) * sizeof(u32)), + _mali_osk_mem_ioread32(page, (i*4 + 3) * sizeof(u32)))); + + } + } + + if (NULL != info) + { + /* 4096 for the page and 4 bytes for the address */ + const u32 page_size_in_elements = MALI_MMU_PAGE_SIZE / 4; + const u32 page_size_in_bytes = MALI_MMU_PAGE_SIZE; + const u32 dump_size_in_bytes = MALI_MMU_PAGE_SIZE + 4; + + info->page_table_dump_size += dump_size_in_bytes; + + if (NULL != info->buffer) + { + if (info->buffer_left < dump_size_in_bytes) MALI_ERROR(_MALI_OSK_ERR_NOMEM); + + *info->buffer = phys_addr; + info->buffer++; + + _mali_osk_memcpy(info->buffer, page, page_size_in_bytes); + info->buffer += page_size_in_elements; + + info->buffer_left -= dump_size_in_bytes; + } + } + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t dump_mmu_page_table(memory_session * session_data, struct dump_info * info) +{ + MALI_DEBUG_ASSERT_POINTER(session_data); + MALI_DEBUG_ASSERT_POINTER(info); + + if (NULL != session_data->page_directory_mapped) + { + int i; + + MALI_CHECK_NO_ERROR( + dump_page(session_data->page_directory_mapped, session_data->page_directory, info, 0) + ); + + for (i = 0; i < 1024; i++) + { + if (NULL != session_data->page_entries_mapped[i]) + { + MALI_CHECK_NO_ERROR( + dump_page(session_data->page_entries_mapped[i], _mali_osk_mem_ioread32(session_data->page_directory_mapped, i * sizeof(u32)) & ~MALI_MMU_FLAGS_MASK, info, 0) + ); + } + } + } + + MALI_SUCCESS; +} + +static _mali_osk_errcode_t dump_mmu_registers(memory_session * session_data, struct dump_info * info) +{ + MALI_CHECK_NO_ERROR(writereg(0x00000000, session_data->page_directory, "set the page directory address", info, 0)); + MALI_CHECK_NO_ERROR(writereg(0x00000008, 4, "zap???", info, 0)); + MALI_CHECK_NO_ERROR(writereg(0x00000008, 0, "enable paging", info, 0)); + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_query_mmu_page_table_dump_size( _mali_uk_query_mmu_page_table_dump_size_s *args ) +{ + struct dump_info info = { 0, 0, 0, NULL }; + memory_session * session_data; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = (memory_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_memory_id); + + MALI_CHECK_NO_ERROR(dump_mmu_registers(session_data, &info)); + MALI_CHECK_NO_ERROR(dump_mmu_page_table(session_data, &info)); + args->size = info.register_writes_size + info.page_table_dump_size; + MALI_SUCCESS; +} + +_mali_osk_errcode_t _mali_ukk_dump_mmu_page_table( _mali_uk_dump_mmu_page_table_s * args ) +{ + struct dump_info info = { 0, 0, 0, NULL }; + memory_session * session_data; + + MALI_DEBUG_ASSERT_POINTER(args); + MALI_CHECK_NON_NULL(args->ctx, _MALI_OSK_ERR_INVALID_ARGS); + MALI_CHECK_NON_NULL(args->buffer, _MALI_OSK_ERR_INVALID_ARGS); + + session_data = (memory_session *)mali_kernel_session_manager_slot_get(args->ctx, mali_subsystem_memory_id); + + info.buffer_left = args->size; + info.buffer = args->buffer; + + args->register_writes = info.buffer; + MALI_CHECK_NO_ERROR(dump_mmu_registers(session_data, &info)); + + args->page_table_dump = info.buffer; + MALI_CHECK_NO_ERROR(dump_mmu_page_table(session_data, &info)); + + args->register_writes_size = info.register_writes_size; + args->page_table_dump_size = info.page_table_dump_size; + + MALI_SUCCESS; +} + +/** + * Stub function to satisfy UDD interface exclusion requirement. + * This is because the Base code compiles in \b both MMU and non-MMU calls, + * so both sets must be declared (but the 'unused' set may be stub) + */ +_mali_osk_errcode_t _mali_ukk_get_big_block( _mali_uk_get_big_block_s *args ) +{ + MALI_IGNORE( args ); + return _MALI_OSK_ERR_FAULT; +} + +/** + * Stub function to satisfy UDD interface exclusion requirement. + * This is because the Base code compiles in \b both MMU and non-MMU calls, + * so both sets must be declared (but the 'unused' set may be stub) + */ +_mali_osk_errcode_t _mali_ukk_free_big_block( _mali_uk_free_big_block_s *args ) +{ + MALI_IGNORE( args ); + return _MALI_OSK_ERR_FAULT; +} + +u32 _mali_ukk_report_memory_usage(void) +{ + return mali_allocation_engine_memory_usage(physical_memory_allocators); +} diff --git a/drivers/gpu/arm/mali/common/mali_kernel_mem_mmu.h b/drivers/gpu/arm/mali/common/mali_kernel_mem_mmu.h new file mode 100644 index 000000000000..0462f6603855 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_mem_mmu.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_MEM_MMU_H__ +#define __MALI_KERNEL_MEM_MMU_H__ + +#include "mali_kernel_session_manager.h" + +/** + * Lookup a MMU core by ID. + * @param id ID of the MMU to find + * @return NULL if ID not found or valid, non-NULL if a core was found. + */ +void* mali_memory_core_mmu_lookup(u32 id); + +/** + * Set the core pointer of MMU to core owner of MMU + * + * @param core Core holding this MMU + * @param mmu_ptr The MMU whose core pointer needs set to core holding the MMU + * + */ +void mali_memory_core_mmu_owner(void *core, void *mmu_ptr); + +/** + * Activate a user session with its address space on the given MMU. + * If the session can't be activated due to that the MMU is busy and + * a callback pointer is given, the callback will be called once the MMU becomes idle. + * If the same callback pointer is registered multiple time it will only be called once. + * Nested activations are supported. + * Each call must be matched by a call to @see mali_memory_core_mmu_release_address_space_reference + * + * @param mmu The MMU to activate the address space on + * @param mali_session_data The user session object which address space to activate + * @param callback Pointer to the function to call when the MMU becomes idle + * @param callback_arg Argument given to the callback + * @return 0 if the address space was activated, -EBUSY if the MMU was busy, -EFAULT in all other cases. + */ +int mali_memory_core_mmu_activate_page_table(void* mmu_ptr, struct mali_session_data * mali_session_data, void(*callback)(void*), void * callback_argument); + +/** + * Release a reference to the current active address space. + * Once the last reference is released any callback(s) registered will be called before the function returns + * + * @note Caution must be shown calling this function with locks held due to that callback can be called + * @param mmu The mmu to release a reference to the active address space of + */ +void mali_memory_core_mmu_release_address_space_reference(void* mmu); + +/** + * Soft reset of MMU - needed after power up + * + * @param mmu_ptr The MMU pointer registered with the relevant core + */ +void mali_kernel_mmu_reset(void * mmu_ptr); + +void mali_kernel_mmu_force_bus_reset(void * mmu_ptr); + +/** + * Unregister a previously registered callback. + * @param mmu The MMU to unregister the callback on + * @param callback The function to unregister + */ +void mali_memory_core_mmu_unregister_callback(void* mmu, void(*callback)(void*)); + + + +#endif /* __MALI_KERNEL_MEM_MMU_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_mem_os.c b/drivers/gpu/arm/mali/common/mali_kernel_mem_os.c new file mode 100644 index 000000000000..9ac122d05b89 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_mem_os.c @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_kernel_core.h" +#include "mali_kernel_memory_engine.h" +#include "mali_osk.h" + +typedef struct os_allocation +{ + u32 num_pages; + u32 offset_start; + mali_allocation_engine * engine; + mali_memory_allocation * descriptor; +} os_allocation; + +typedef struct os_allocator +{ + _mali_osk_lock_t *mutex; + + /** + * Maximum number of pages to allocate from the OS + */ + u32 num_pages_max; + + /** + * Number of pages allocated from the OS + */ + u32 num_pages_allocated; + + /** CPU Usage adjustment (add to mali physical address to get cpu physical address) */ + u32 cpu_usage_adjust; +} os_allocator; + +static mali_physical_memory_allocation_result os_allocator_allocate(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info); +static mali_physical_memory_allocation_result os_allocator_allocate_page_table_block(void * ctx, mali_page_table_block * block); +static void os_allocator_release(void * ctx, void * handle); +static void os_allocator_page_table_block_release( mali_page_table_block *page_table_block ); +static void os_allocator_destroy(mali_physical_memory_allocator * allocator); +static u32 os_allocator_stat(mali_physical_memory_allocator * allocator); + +mali_physical_memory_allocator * mali_os_allocator_create(u32 max_allocation, u32 cpu_usage_adjust, const char *name) +{ + mali_physical_memory_allocator * allocator; + os_allocator * info; + + max_allocation = (max_allocation + _MALI_OSK_CPU_PAGE_SIZE-1) & ~(_MALI_OSK_CPU_PAGE_SIZE-1); + + MALI_DEBUG_PRINT(2, ("Mali OS memory allocator created with max allocation size of 0x%X bytes, cpu_usage_adjust 0x%08X\n", max_allocation, cpu_usage_adjust)); + + allocator = _mali_osk_malloc(sizeof(mali_physical_memory_allocator)); + if (NULL != allocator) + { + info = _mali_osk_malloc(sizeof(os_allocator)); + if (NULL != info) + { + info->num_pages_max = max_allocation / _MALI_OSK_CPU_PAGE_SIZE; + info->num_pages_allocated = 0; + info->cpu_usage_adjust = cpu_usage_adjust; + + info->mutex = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE | _MALI_OSK_LOCKFLAG_ORDERED, 0, 106); + if (NULL != info->mutex) + { + allocator->allocate = os_allocator_allocate; + allocator->allocate_page_table_block = os_allocator_allocate_page_table_block; + allocator->destroy = os_allocator_destroy; + allocator->stat = os_allocator_stat; + allocator->ctx = info; + allocator->name = name; + + return allocator; + } + _mali_osk_free(info); + } + _mali_osk_free(allocator); + } + + return NULL; +} + +static u32 os_allocator_stat(mali_physical_memory_allocator * allocator) +{ + os_allocator * info; + info = (os_allocator*)allocator->ctx; + return info->num_pages_allocated * _MALI_OSK_MALI_PAGE_SIZE; +} + +static void os_allocator_destroy(mali_physical_memory_allocator * allocator) +{ + os_allocator * info; + MALI_DEBUG_ASSERT_POINTER(allocator); + MALI_DEBUG_ASSERT_POINTER(allocator->ctx); + info = (os_allocator*)allocator->ctx; + _mali_osk_lock_term(info->mutex); + _mali_osk_free(info); + _mali_osk_free(allocator); +} + +static mali_physical_memory_allocation_result os_allocator_allocate(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info) +{ + mali_physical_memory_allocation_result result = MALI_MEM_ALLOC_NONE; + u32 left; + os_allocator * info; + os_allocation * allocation; + int pages_allocated = 0; + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + + MALI_DEBUG_ASSERT_POINTER(ctx); + MALI_DEBUG_ASSERT_POINTER(engine); + MALI_DEBUG_ASSERT_POINTER(descriptor); + MALI_DEBUG_ASSERT_POINTER(offset); + MALI_DEBUG_ASSERT_POINTER(alloc_info); + + info = (os_allocator*)ctx; + left = descriptor->size - *offset; + + if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) return MALI_MEM_ALLOC_INTERNAL_FAILURE; + + /** @note this code may not work on Linux, or may require a more complex Linux implementation */ + allocation = _mali_osk_malloc(sizeof(os_allocation)); + if (NULL != allocation) + { + u32 os_mem_max_usage = info->num_pages_max * _MALI_OSK_CPU_PAGE_SIZE; + allocation->offset_start = *offset; + allocation->num_pages = ((left + _MALI_OSK_CPU_PAGE_SIZE - 1) & ~(_MALI_OSK_CPU_PAGE_SIZE - 1)) >> _MALI_OSK_CPU_PAGE_ORDER; + MALI_DEBUG_PRINT(6, ("Allocating page array of size %d bytes\n", allocation->num_pages * sizeof(struct page*))); + + while (left > 0 && ((info->num_pages_allocated + pages_allocated) < info->num_pages_max) && _mali_osk_mem_check_allocated(os_mem_max_usage)) + { + err = mali_allocation_engine_map_physical(engine, descriptor, *offset, MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC, info->cpu_usage_adjust, _MALI_OSK_CPU_PAGE_SIZE); + if ( _MALI_OSK_ERR_OK != err) + { + if ( _MALI_OSK_ERR_NOMEM == err) + { + /* 'Partial' allocation (or, out-of-memory on first page) */ + break; + } + + MALI_DEBUG_PRINT(1, ("Mapping of physical memory failed\n")); + + /* Fatal error, cleanup any previous pages allocated. */ + if ( pages_allocated > 0 ) + { + mali_allocation_engine_unmap_physical( engine, descriptor, allocation->offset_start, _MALI_OSK_CPU_PAGE_SIZE*pages_allocated, _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR ); + /* (*offset) doesn't need to be restored; it will not be used by the caller on failure */ + } + + pages_allocated = 0; + + result = MALI_MEM_ALLOC_INTERNAL_FAILURE; + break; + } + + /* Loop iteration */ + if (left < _MALI_OSK_CPU_PAGE_SIZE) left = 0; + else left -= _MALI_OSK_CPU_PAGE_SIZE; + + pages_allocated++; + + *offset += _MALI_OSK_CPU_PAGE_SIZE; + } + + if (left) MALI_PRINT(("Out of memory. Mali memory allocated: %d kB Configured maximum OS memory usage: %d kB\n", + (info->num_pages_allocated * _MALI_OSK_CPU_PAGE_SIZE)/1024, (info->num_pages_max* _MALI_OSK_CPU_PAGE_SIZE)/1024)); + + /* Loop termination; decide on result */ + if (pages_allocated) + { + MALI_DEBUG_PRINT(6, ("Allocated %d pages\n", pages_allocated)); + if (left) result = MALI_MEM_ALLOC_PARTIAL; + else result = MALI_MEM_ALLOC_FINISHED; + + /* Some OS do not perform a full cache flush (including all outer caches) for uncached mapped memory. + * They zero the memory through a cached mapping, then flush the inner caches but not the outer caches. + * This is required for MALI to have the correct view of the memory. + */ + _mali_osk_cache_ensure_uncached_range_flushed( (void *)descriptor, allocation->offset_start, pages_allocated *_MALI_OSK_CPU_PAGE_SIZE ); + allocation->num_pages = pages_allocated; + allocation->engine = engine; /* Necessary to make the engine's unmap call */ + allocation->descriptor = descriptor; /* Necessary to make the engine's unmap call */ + info->num_pages_allocated += pages_allocated; + + MALI_DEBUG_PRINT(6, ("%d out of %d pages now allocated\n", info->num_pages_allocated, info->num_pages_max)); + + alloc_info->ctx = info; + alloc_info->handle = allocation; + alloc_info->release = os_allocator_release; + } + else + { + MALI_DEBUG_PRINT(6, ("Releasing pages array due to no pages allocated\n")); + _mali_osk_free( allocation ); + } + } + + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + + return result; +} + +static void os_allocator_release(void * ctx, void * handle) +{ + os_allocator * info; + os_allocation * allocation; + mali_allocation_engine * engine; + mali_memory_allocation * descriptor; + + MALI_DEBUG_ASSERT_POINTER(ctx); + MALI_DEBUG_ASSERT_POINTER(handle); + + info = (os_allocator*)ctx; + allocation = (os_allocation*)handle; + engine = allocation->engine; + descriptor = allocation->descriptor; + + MALI_DEBUG_ASSERT_POINTER( engine ); + MALI_DEBUG_ASSERT_POINTER( descriptor ); + + if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) + { + MALI_DEBUG_PRINT(1, ("allocator release: Failed to get mutex\n")); + return; + } + + MALI_DEBUG_PRINT(6, ("Releasing %d os pages\n", allocation->num_pages)); + + MALI_DEBUG_ASSERT( allocation->num_pages <= info->num_pages_allocated); + info->num_pages_allocated -= allocation->num_pages; + + mali_allocation_engine_unmap_physical( engine, descriptor, allocation->offset_start, _MALI_OSK_CPU_PAGE_SIZE*allocation->num_pages, _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR ); + + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + + _mali_osk_free(allocation); +} + +static mali_physical_memory_allocation_result os_allocator_allocate_page_table_block(void * ctx, mali_page_table_block * block) +{ + int allocation_order = 6; /* _MALI_OSK_CPU_PAGE_SIZE << 6 */ + void *virt = NULL; + u32 size = _MALI_OSK_CPU_PAGE_SIZE << allocation_order; + os_allocator * info; + + u32 cpu_phys_base; + + MALI_DEBUG_ASSERT_POINTER(ctx); + info = (os_allocator*)ctx; + + /* Ensure we don't allocate more than we're supposed to from the ctx */ + if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) return MALI_MEM_ALLOC_INTERNAL_FAILURE; + + /* if the number of pages to be requested lead to exceeding the memory + * limit in info->num_pages_max, reduce the size that is to be requested. */ + while ( (info->num_pages_allocated + (1 << allocation_order) > info->num_pages_max) + && _mali_osk_mem_check_allocated(info->num_pages_max * _MALI_OSK_CPU_PAGE_SIZE) ) + { + if ( allocation_order > 0 ) { + --allocation_order; + } else { + /* return OOM */ + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + return MALI_MEM_ALLOC_NONE; + } + } + + /* try to allocate 2^(allocation_order) pages, if that fails, try + * allocation_order-1 to allocation_order 0 (inclusive) */ + while ( allocation_order >= 0 ) + { + size = _MALI_OSK_CPU_PAGE_SIZE << allocation_order; + virt = _mali_osk_mem_allocioregion( &cpu_phys_base, size ); + + if (NULL != virt) break; + + --allocation_order; + } + + if ( NULL == virt ) + { + MALI_DEBUG_PRINT(1, ("Failed to allocate consistent memory. Is CONSISTENT_DMA_SIZE set too low?\n")); + /* return OOM */ + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + return MALI_MEM_ALLOC_NONE; + } + + MALI_DEBUG_PRINT(5, ("os_allocator_allocate_page_table_block: Allocation of order %i succeeded\n", + allocation_order)); + + /* we now know the size of the allocation since we know for what + * allocation_order the allocation succeeded */ + size = _MALI_OSK_CPU_PAGE_SIZE << allocation_order; + + + block->release = os_allocator_page_table_block_release; + block->ctx = ctx; + block->handle = (void*)allocation_order; + block->size = size; + block->phys_base = cpu_phys_base - info->cpu_usage_adjust; + block->mapping = virt; + + info->num_pages_allocated += (1 << allocation_order); + + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); + + return MALI_MEM_ALLOC_FINISHED; +} + +static void os_allocator_page_table_block_release( mali_page_table_block *page_table_block ) +{ + os_allocator * info; + u32 allocation_order; + u32 pages_allocated; + + MALI_DEBUG_ASSERT_POINTER( page_table_block ); + + info = (os_allocator*)page_table_block->ctx; + + MALI_DEBUG_ASSERT_POINTER( info ); + + allocation_order = (u32)page_table_block->handle; + + pages_allocated = 1 << allocation_order; + + MALI_DEBUG_ASSERT( pages_allocated * _MALI_OSK_CPU_PAGE_SIZE == page_table_block->size ); + + if (_MALI_OSK_ERR_OK != _mali_osk_lock_wait(info->mutex, _MALI_OSK_LOCKMODE_RW)) + { + MALI_DEBUG_PRINT(1, ("allocator release: Failed to get mutex\n")); + return; + } + + MALI_DEBUG_ASSERT( pages_allocated <= info->num_pages_allocated); + info->num_pages_allocated -= pages_allocated; + + /* Adjust phys_base from mali physical address to CPU physical address */ + _mali_osk_mem_freeioregion( page_table_block->phys_base + info->cpu_usage_adjust, page_table_block->size, page_table_block->mapping ); + + _mali_osk_lock_signal(info->mutex, _MALI_OSK_LOCKMODE_RW); +} diff --git a/drivers/gpu/arm/mali/common/mali_kernel_mem_os.h b/drivers/gpu/arm/mali/common/mali_kernel_mem_os.h new file mode 100644 index 000000000000..59e6494c5eb6 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_mem_os.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_MEM_OS_H__ +#define __MALI_KERNEL_MEM_OS_H__ + +/** + * @brief Creates an object that manages allocating OS memory + * + * Creates an object that provides an interface to allocate OS memory and + * have it mapped into the Mali virtual memory space. + * + * The object exposes pointers to + * - allocate OS memory + * - allocate Mali page tables in OS memory + * - destroy the object + * + * Allocations from OS memory are of type mali_physical_memory_allocation + * which provides a function to release the allocation. + * + * @param max_allocation max. number of bytes that can be allocated from OS memory + * @param cpu_usage_adjust value to add to mali physical addresses to obtain CPU physical addresses + * @param name description of the allocator + * @return pointer to mali_physical_memory_allocator object. NULL on failure. + **/ +mali_physical_memory_allocator * mali_os_allocator_create(u32 max_allocation, u32 cpu_usage_adjust, const char *name); + +#endif /* __MALI_KERNEL_MEM_OS_H__ */ + + diff --git a/drivers/gpu/arm/mali/common/mali_kernel_memory_engine.c b/drivers/gpu/arm/mali/common/mali_kernel_memory_engine.c new file mode 100644 index 000000000000..8bb80e63cbf2 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_memory_engine.c @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_kernel_core.h" +#include "mali_kernel_memory_engine.h" +#include "mali_osk.h" +#include "mali_osk_list.h" + +typedef struct memory_engine +{ + mali_kernel_mem_address_manager * mali_address; + mali_kernel_mem_address_manager * process_address; +} memory_engine; + +mali_allocation_engine mali_allocation_engine_create(mali_kernel_mem_address_manager * mali_address_manager, mali_kernel_mem_address_manager * process_address_manager) +{ + memory_engine * engine; + + /* Mali Address Manager need not support unmap_physical */ + MALI_DEBUG_ASSERT_POINTER(mali_address_manager); + MALI_DEBUG_ASSERT_POINTER(mali_address_manager->allocate); + MALI_DEBUG_ASSERT_POINTER(mali_address_manager->release); + MALI_DEBUG_ASSERT_POINTER(mali_address_manager->map_physical); + + /* Process Address Manager must support unmap_physical for OS allocation + * error path handling */ + MALI_DEBUG_ASSERT_POINTER(process_address_manager); + MALI_DEBUG_ASSERT_POINTER(process_address_manager->allocate); + MALI_DEBUG_ASSERT_POINTER(process_address_manager->release); + MALI_DEBUG_ASSERT_POINTER(process_address_manager->map_physical); + MALI_DEBUG_ASSERT_POINTER(process_address_manager->unmap_physical); + + + engine = (memory_engine*)_mali_osk_malloc(sizeof(memory_engine)); + if (NULL == engine) return NULL; + + engine->mali_address = mali_address_manager; + engine->process_address = process_address_manager; + + return (mali_allocation_engine)engine; +} + +void mali_allocation_engine_destroy(mali_allocation_engine engine) +{ + MALI_DEBUG_ASSERT_POINTER(engine); + _mali_osk_free(engine); +} + +_mali_osk_errcode_t mali_allocation_engine_allocate_memory(mali_allocation_engine mem_engine, mali_memory_allocation * descriptor, mali_physical_memory_allocator * physical_allocators, _mali_osk_list_t *tracking_list ) +{ + memory_engine * engine = (memory_engine*)mem_engine; + + MALI_DEBUG_ASSERT_POINTER(engine); + MALI_DEBUG_ASSERT_POINTER(descriptor); + MALI_DEBUG_ASSERT_POINTER(physical_allocators); + /* ASSERT that the list member has been initialized, even if it won't be + * used for tracking. We need it to be initialized to see if we need to + * delete it from a list in the release function. */ + MALI_DEBUG_ASSERT( NULL != descriptor->list.next && NULL != descriptor->list.prev ); + + if (_MALI_OSK_ERR_OK == engine->mali_address->allocate(descriptor)) + { + _mali_osk_errcode_t res = _MALI_OSK_ERR_OK; + if ( descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE ) + { + res = engine->process_address->allocate(descriptor); + } + if ( _MALI_OSK_ERR_OK == res ) + { + /* address space setup OK, commit physical memory to the allocation */ + mali_physical_memory_allocator * active_allocator = physical_allocators; + struct mali_physical_memory_allocation * active_allocation_tracker = &descriptor->physical_allocation; + u32 offset = 0; + + while ( NULL != active_allocator ) + { + switch (active_allocator->allocate(active_allocator->ctx, mem_engine, descriptor, &offset, active_allocation_tracker)) + { + case MALI_MEM_ALLOC_FINISHED: + if ( NULL != tracking_list ) + { + /* Insert into the memory session list */ + /* ASSERT that it is not already part of a list */ + MALI_DEBUG_ASSERT( _mali_osk_list_empty( &descriptor->list ) ); + _mali_osk_list_add( &descriptor->list, tracking_list ); + } + + MALI_SUCCESS; /* all done */ + case MALI_MEM_ALLOC_NONE: + /* reuse current active_allocation_tracker */ + MALI_DEBUG_PRINT( 4, ("Memory Engine Allocate: No allocation on %s, resorting to %s\n", + ( active_allocator->name ) ? active_allocator->name : "UNNAMED", + ( active_allocator->next ) ? (( active_allocator->next->name )? active_allocator->next->name : "UNNAMED") : "NONE") ); + active_allocator = active_allocator->next; + break; + case MALI_MEM_ALLOC_PARTIAL: + if (NULL != active_allocator->next) + { + /* need a new allocation tracker */ + active_allocation_tracker->next = _mali_osk_calloc(1, sizeof(mali_physical_memory_allocation)); + if (NULL != active_allocation_tracker->next) + { + active_allocation_tracker = active_allocation_tracker->next; + MALI_DEBUG_PRINT( 2, ("Memory Engine Allocate: Partial allocation on %s, resorting to %s\n", + ( active_allocator->name ) ? active_allocator->name : "UNNAMED", + ( active_allocator->next ) ? (( active_allocator->next->name )? active_allocator->next->name : "UNNAMED") : "NONE") ); + active_allocator = active_allocator->next; + break; + } + } + /* FALL THROUGH */ + case MALI_MEM_ALLOC_INTERNAL_FAILURE: + active_allocator = NULL; /* end the while loop */ + break; + } + } + + MALI_PRINT(("Memory allocate failed, could not allocate size %d kB.\n", descriptor->size/1024)); + + /* allocation failure, start cleanup */ + /* loop over any potential partial allocations */ + active_allocation_tracker = &descriptor->physical_allocation; + while (NULL != active_allocation_tracker) + { + /* handle blank trackers which will show up during failure */ + if (NULL != active_allocation_tracker->release) + { + active_allocation_tracker->release(active_allocation_tracker->ctx, active_allocation_tracker->handle); + } + active_allocation_tracker = active_allocation_tracker->next; + } + + /* free the allocation tracker objects themselves, skipping the tracker stored inside the descriptor itself */ + for ( active_allocation_tracker = descriptor->physical_allocation.next; active_allocation_tracker != NULL; ) + { + void * buf = active_allocation_tracker; + active_allocation_tracker = active_allocation_tracker->next; + _mali_osk_free(buf); + } + + /* release the address spaces */ + + if ( descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE ) + { + engine->process_address->release(descriptor); + } + } + engine->mali_address->release(descriptor); + } + + MALI_ERROR(_MALI_OSK_ERR_FAULT); +} + +void mali_allocation_engine_release_memory(mali_allocation_engine mem_engine, mali_memory_allocation * descriptor) +{ + memory_engine * engine = (memory_engine*)mem_engine; + mali_physical_memory_allocation * active_allocation_tracker; + + MALI_DEBUG_ASSERT_POINTER(engine); + MALI_DEBUG_ASSERT_POINTER(descriptor); + + /* Determine whether we need to remove this from a tracking list */ + if ( ! _mali_osk_list_empty( &descriptor->list ) ) + { + _mali_osk_list_del( &descriptor->list ); + /* Clear the list for debug mode, catch use-after-free */ + MALI_DEBUG_CODE( descriptor->list.next = descriptor->list.prev = NULL; ) + } + + engine->mali_address->release(descriptor); + + active_allocation_tracker = &descriptor->physical_allocation; + while (NULL != active_allocation_tracker) + { + MALI_DEBUG_ASSERT_POINTER(active_allocation_tracker->release); + active_allocation_tracker->release(active_allocation_tracker->ctx, active_allocation_tracker->handle); + active_allocation_tracker = active_allocation_tracker->next; + } + + /* free the allocation tracker objects themselves, skipping the tracker stored inside the descriptor itself */ + for ( active_allocation_tracker = descriptor->physical_allocation.next; active_allocation_tracker != NULL; ) + { + void * buf = active_allocation_tracker; + active_allocation_tracker = active_allocation_tracker->next; + _mali_osk_free(buf); + } + + if ( descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE ) + { + engine->process_address->release(descriptor); + } +} + + +_mali_osk_errcode_t mali_allocation_engine_map_physical(mali_allocation_engine mem_engine, mali_memory_allocation * descriptor, u32 offset, u32 phys, u32 cpu_usage_adjust, u32 size) +{ + _mali_osk_errcode_t err; + memory_engine * engine = (memory_engine*)mem_engine; + _mali_osk_mem_mapregion_flags_t unmap_flags = (_mali_osk_mem_mapregion_flags_t)0; + + MALI_DEBUG_ASSERT_POINTER(engine); + MALI_DEBUG_ASSERT_POINTER(descriptor); + + MALI_DEBUG_PRINT(7, ("Mapping phys 0x%08X length 0x%08X at offset 0x%08X\n", phys, size, offset)); + + MALI_DEBUG_ASSERT_POINTER(engine->mali_address); + MALI_DEBUG_ASSERT_POINTER(engine->mali_address->map_physical); + + /* Handle process address manager first, because we may need them to + * allocate the physical page */ + if ( descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE ) + { + /* Handle OS-allocated specially, since an adjustment may be required */ + if ( MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC == phys ) + { + MALI_DEBUG_ASSERT( _MALI_OSK_CPU_PAGE_SIZE == size ); + + /* Set flags to use on error path */ + unmap_flags |= _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR; + + err = engine->process_address->map_physical(descriptor, offset, &phys, size); + /* Adjust for cpu physical address to mali physical address */ + phys -= cpu_usage_adjust; + } + else + { + u32 cpu_phys; + /* Adjust mali physical address to cpu physical address */ + cpu_phys = phys + cpu_usage_adjust; + err = engine->process_address->map_physical(descriptor, offset, &cpu_phys, size); + } + + if ( _MALI_OSK_ERR_OK != err ) + { + MALI_ERROR( err ); + } + } + + MALI_DEBUG_PRINT(4, ("Mapping phys 0x%08X length 0x%08X at offset 0x%08X to CPUVA 0x%08X\n", phys, size, offset, (u32)(descriptor->mapping) + offset)); + + /* Mali address manager must use the physical address - no point in asking + * it to allocate another one for us */ + MALI_DEBUG_ASSERT( MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC != phys ); + + err = engine->mali_address->map_physical(descriptor, offset, &phys, size); + + if ( _MALI_OSK_ERR_OK != err ) + { + if ( descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE ) + { + MALI_DEBUG_PRINT( 2, ("Process address manager succeeded, but Mali Address manager failed for phys=0x%08X size=0x%08X, offset=0x%08X. Will unmap.\n", phys, size, offset)); + engine->process_address->unmap_physical(descriptor, offset, size, unmap_flags); + } + + MALI_ERROR( err ); + } + + MALI_SUCCESS; +} + +void mali_allocation_engine_unmap_physical(mali_allocation_engine mem_engine, mali_memory_allocation * descriptor, u32 offset, u32 size, _mali_osk_mem_mapregion_flags_t unmap_flags ) +{ + memory_engine * engine = (memory_engine*)mem_engine; + + MALI_DEBUG_ASSERT_POINTER(engine); + MALI_DEBUG_ASSERT_POINTER(descriptor); + + MALI_DEBUG_PRINT(7, ("UnMapping length 0x%08X at offset 0x%08X\n", size, offset)); + + MALI_DEBUG_ASSERT_POINTER(engine->mali_address); + MALI_DEBUG_ASSERT_POINTER(engine->process_address); + + if ( descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE ) + { + /* Mandetory for process_address manager to have an unmap function*/ + engine->process_address->unmap_physical( descriptor, offset, size, unmap_flags ); + } + + /* Optional for mali_address manager to have an unmap function*/ + if ( NULL != engine->mali_address->unmap_physical ) + { + engine->mali_address->unmap_physical( descriptor, offset, size, unmap_flags ); + } +} + + +_mali_osk_errcode_t mali_allocation_engine_allocate_page_tables(mali_allocation_engine engine, mali_page_table_block * descriptor, mali_physical_memory_allocator * physical_provider) +{ + mali_physical_memory_allocator * active_allocator = physical_provider; + + MALI_DEBUG_ASSERT_POINTER(descriptor); + MALI_DEBUG_ASSERT_POINTER(physical_provider); + + while ( NULL != active_allocator ) + { + switch (active_allocator->allocate_page_table_block(active_allocator->ctx, descriptor)) + { + case MALI_MEM_ALLOC_FINISHED: + MALI_SUCCESS; /* all done */ + case MALI_MEM_ALLOC_NONE: + /* try next */ + MALI_DEBUG_PRINT( 2, ("Memory Engine Allocate PageTables: No allocation on %s, resorting to %s\n", + ( active_allocator->name ) ? active_allocator->name : "UNNAMED", + ( active_allocator->next ) ? (( active_allocator->next->name )? active_allocator->next->name : "UNNAMED") : "NONE") ); + active_allocator = active_allocator->next; + break; + case MALI_MEM_ALLOC_PARTIAL: + MALI_DEBUG_PRINT(1, ("Invalid return value from allocate_page_table_block call: MALI_MEM_ALLOC_PARTIAL\n")); + /* FALL THROUGH */ + case MALI_MEM_ALLOC_INTERNAL_FAILURE: + MALI_DEBUG_PRINT(1, ("Aborting due to allocation failure\n")); + active_allocator = NULL; /* end the while loop */ + break; + } + } + + MALI_ERROR(_MALI_OSK_ERR_FAULT); +} + + +void mali_allocation_engine_report_allocators( mali_physical_memory_allocator * physical_provider ) +{ + mali_physical_memory_allocator * active_allocator = physical_provider; + MALI_DEBUG_ASSERT_POINTER(physical_provider); + + MALI_DEBUG_PRINT( 1, ("Mali memory allocators will be used in this order of preference (lowest numbered first) :\n")); + while ( NULL != active_allocator ) + { + if ( NULL != active_allocator->name ) + { + MALI_DEBUG_PRINT( 1, ("\t%d: %s\n", active_allocator->alloc_order, active_allocator->name) ); + } + else + { + MALI_DEBUG_PRINT( 1, ("\t%d: (UNNAMED ALLOCATOR)\n", active_allocator->alloc_order) ); + } + active_allocator = active_allocator->next; + } + +} + +u32 mali_allocation_engine_memory_usage(mali_physical_memory_allocator *allocator) +{ + u32 sum = 0; + while(NULL != allocator) + { + /* Only count allocators that have set up a stat function. */ + if(allocator->stat) + sum += allocator->stat(allocator); + + allocator = allocator->next; + } + + return sum; +} diff --git a/drivers/gpu/arm/mali/common/mali_kernel_memory_engine.h b/drivers/gpu/arm/mali/common/mali_kernel_memory_engine.h new file mode 100644 index 000000000000..367219f191d6 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_memory_engine.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_MEMORY_ENGINE_H__ +#define __MALI_KERNEL_MEMORY_ENGINE_H__ + +typedef void * mali_allocation_engine; + +typedef enum { MALI_MEM_ALLOC_FINISHED, MALI_MEM_ALLOC_PARTIAL, MALI_MEM_ALLOC_NONE, MALI_MEM_ALLOC_INTERNAL_FAILURE } mali_physical_memory_allocation_result; + +typedef struct mali_physical_memory_allocation +{ + void (*release)(void * ctx, void * handle); /**< Function to call on to release the physical memory */ + void * ctx; + void * handle; + struct mali_physical_memory_allocation * next; +} mali_physical_memory_allocation; + +struct mali_page_table_block; + +typedef struct mali_page_table_block +{ + void (*release)(struct mali_page_table_block *page_table_block); + void * ctx; + void * handle; + u32 size; /**< In bytes, should be a multiple of MALI_MMU_PAGE_SIZE to avoid internal fragementation */ + u32 phys_base; /**< Mali physical address */ + mali_io_address mapping; +} mali_page_table_block; + + +/** @addtogroup _mali_osk_low_level_memory + * @{ */ + +typedef enum +{ + MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE = 0x1, + MALI_MEMORY_ALLOCATION_FLAG_MAP_GUARD_PAGE = 0x2, +} mali_memory_allocation_flag; + +/** + * Supplying this 'magic' physical address requests that the OS allocate the + * physical address at page commit time, rather than committing a specific page + */ +#define MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC ((u32)(-1)) + +typedef struct mali_memory_allocation +{ + /* Information about the allocation */ + void * mapping; /**< CPU virtual address where the memory is mapped at */ + u32 mali_address; /**< The Mali seen address of the memory allocation */ + u32 size; /**< Size of the allocation */ + u32 permission; /**< Permission settings */ + mali_memory_allocation_flag flags; + + _mali_osk_lock_t * lock; + + /* Manager specific information pointers */ + void * mali_addr_mapping_info; /**< Mali address allocation specific info */ + void * process_addr_mapping_info; /**< Mapping manager specific info */ + + mali_physical_memory_allocation physical_allocation; + + _mali_osk_list_t list; /**< List for linking together memory allocations into the session's memory head */ +} mali_memory_allocation; +/** @} */ /* end group _mali_osk_low_level_memory */ + + +typedef struct mali_physical_memory_allocator +{ + mali_physical_memory_allocation_result (*allocate)(void* ctx, mali_allocation_engine * engine, mali_memory_allocation * descriptor, u32* offset, mali_physical_memory_allocation * alloc_info); + mali_physical_memory_allocation_result (*allocate_page_table_block)(void * ctx, mali_page_table_block * block); /* MALI_MEM_ALLOC_PARTIAL not allowed */ + void (*destroy)(struct mali_physical_memory_allocator * allocator); + u32 (*stat)(struct mali_physical_memory_allocator * allocator); + void * ctx; + const char * name; /**< Descriptive name for use in mali_allocation_engine_report_allocators, or NULL */ + u32 alloc_order; /**< Order in which the allocations should happen */ + struct mali_physical_memory_allocator * next; +} mali_physical_memory_allocator; + +typedef struct mali_kernel_mem_address_manager +{ + _mali_osk_errcode_t (*allocate)(mali_memory_allocation *); /**< Function to call to reserve an address */ + void (*release)(mali_memory_allocation *); /**< Function to call to free the address allocated */ + + /** + * Function called for each physical sub allocation. + * Called for each physical block allocated by the physical memory manager. + * @param[in] descriptor The memory descriptor in question + * @param[in] off Offset from the start of range + * @param[in,out] phys_addr A pointer to the physical address of the start of the + * physical block. When *phys_addr == MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC + * is used, this requests the function to allocate the physical page + * itself, and return it through the pointer provided. + * @param[in] size Length in bytes of the physical block + * @return _MALI_OSK_ERR_OK on success. + * A value of type _mali_osk_errcode_t other than _MALI_OSK_ERR_OK indicates failure. + * Specifically, _MALI_OSK_ERR_UNSUPPORTED indicates that the function + * does not support allocating physical pages itself. + */ + _mali_osk_errcode_t (*map_physical)(mali_memory_allocation * descriptor, u32 offset, u32 *phys_addr, u32 size); + + /** + * Function called to remove a physical sub allocation. + * Called on error paths where one of the address managers fails. + * + * @note this is optional. For address managers where this is not + * implemented, the value of this member is NULL. The memory engine + * currently does not require the mali address manager to be able to + * unmap individual pages, but the process address manager must have this + * capability. + * + * @param[in] descriptor The memory descriptor in question + * @param[in] off Offset from the start of range + * @param[in] size Length in bytes of the physical block + * @param[in] flags flags to use on a per-page basis. For OS-allocated + * physical pages, this must include _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR. + * @return _MALI_OSK_ERR_OK on success. + * A value of type _mali_osk_errcode_t other than _MALI_OSK_ERR_OK indicates failure. + */ + void (*unmap_physical)(mali_memory_allocation * descriptor, u32 offset, u32 size, _mali_osk_mem_mapregion_flags_t flags); + +} mali_kernel_mem_address_manager; + +mali_allocation_engine mali_allocation_engine_create(mali_kernel_mem_address_manager * mali_address_manager, mali_kernel_mem_address_manager * process_address_manager); + +void mali_allocation_engine_destroy(mali_allocation_engine engine); + +int mali_allocation_engine_allocate_memory(mali_allocation_engine engine, mali_memory_allocation * descriptor, mali_physical_memory_allocator * physical_provider, _mali_osk_list_t *tracking_list ); +void mali_allocation_engine_release_memory(mali_allocation_engine engine, mali_memory_allocation * descriptor); + +int mali_allocation_engine_map_physical(mali_allocation_engine engine, mali_memory_allocation * descriptor, u32 offset, u32 phys, u32 cpu_usage_adjust, u32 size); +void mali_allocation_engine_unmap_physical(mali_allocation_engine engine, mali_memory_allocation * descriptor, u32 offset, u32 size, _mali_osk_mem_mapregion_flags_t unmap_flags); + +int mali_allocation_engine_allocate_page_tables(mali_allocation_engine, mali_page_table_block * descriptor, mali_physical_memory_allocator * physical_provider); + +void mali_allocation_engine_report_allocators(mali_physical_memory_allocator * physical_provider); + +u32 mali_allocation_engine_memory_usage(mali_physical_memory_allocator *allocator); + +#endif /* __MALI_KERNEL_MEMORY_ENGINE_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_pp.h b/drivers/gpu/arm/mali/common/mali_kernel_pp.h new file mode 100644 index 000000000000..61721e786fce --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_pp.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_PP_H__ +#define __MALI_KERNEL_PP_H__ + +extern struct mali_kernel_subsystem mali_subsystem_mali200; + +#if USING_MALI_PMM +_mali_osk_errcode_t malipp_signal_power_up( u32 core_num, mali_bool queue_only ); +_mali_osk_errcode_t malipp_signal_power_down( u32 core_num, mali_bool immediate_only ); +#endif + +#endif /* __MALI_KERNEL_PP_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_profiling.h b/drivers/gpu/arm/mali/common/mali_kernel_profiling.h new file mode 100644 index 000000000000..074b2b3149b5 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_profiling.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_PROFILING_H__ +#define __MALI_KERNEL_PROFILING_H__ + +#if MALI_TIMELINE_PROFILING_ENABLED + +#include <../../../include/cinstr/mali_cinstr_profiling_events_m200.h> + +#define MALI_PROFILING_MAX_BUFFER_ENTRIES 1048576 + +/** + * Initialize the profiling module. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_profiling_init(mali_bool auto_start); + +/* + * Terminate the profiling module. + */ +void _mali_profiling_term(void); + +/** + * Start recording profiling data + * + * The specified limit will determine how large the capture buffer is. + * MALI_PROFILING_MAX_BUFFER_ENTRIES determines the maximum size allowed by the device driver. + * + * @param limit The desired maximum number of events to record on input, the actual maximum on output. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_profiling_start(u32 * limit); + +/** + * Add an profiling event + * + * @param event_id The event identificator. + * @param data0 First data parameter, depending on event_id specified. + * @param data1 Second data parameter, depending on event_id specified. + * @param data2 Third data parameter, depending on event_id specified. + * @param data3 Fourth data parameter, depending on event_id specified. + * @param data4 Fifth data parameter, depending on event_id specified. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_profiling_add_event(u32 event_id, u32 data0, u32 data1, u32 data2, u32 data3, u32 data4); + +/** + * Stop recording profiling data + * + * @param count Returns the number of recorded events. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_profiling_stop(u32 * count); + +/** + * Retrieves the number of events that can be retrieved + * + * @return The number of recorded events that can be retrieved. + */ +u32 _mali_profiling_get_count(void); + +/** + * Retrieve an event + * + * @param index Event index (start with 0 and continue until this function fails to retrieve all events) + * @param timestamp The timestamp for the retrieved event will be stored here. + * @param event_id The event ID for the retrieved event will be stored here. + * @param data The 5 data values for the retrieved event will be stored here. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */_mali_osk_errcode_t _mali_profiling_get_event(u32 index, u64* timestamp, u32* event_id, u32 data[5]); + +/** + * Clear the recorded buffer. + * + * This is needed in order to start another recording. + * + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_profiling_clear(void); + +/** + * Checks if a recording of profiling data is in progress + * + * @return MALI_TRUE if recording of profiling data is in progress, MALI_FALSE if not + */ +mali_bool _mali_profiling_is_recording(void); + +/** + * Checks if profiling data is available for retrival + * + * @return MALI_TRUE if profiling data is avaiable, MALI_FALSE if not + */ +mali_bool _mali_profiling_have_recording(void); + +/** + * Enable or disable profiling events as default for new sessions (applications) + * + * @param enable MALI_TRUE if profiling events should be turned on, otherwise MALI_FALSE + */ +void _mali_profiling_set_default_enable_state(mali_bool enable); + +/** + * Get current default enable state for new sessions (applications) + * + * @return MALI_TRUE if profiling events should be turned on, otherwise MALI_FALSE + */ +mali_bool _mali_profiling_get_default_enable_state(void); + +#endif /* MALI_TIMELINE_PROFILING_ENABLED */ + +#endif /* __MALI_KERNEL_PROFILING_H__ */ + + diff --git a/drivers/gpu/arm/mali/common/mali_kernel_rendercore.c b/drivers/gpu/arm/mali/common/mali_kernel_rendercore.c new file mode 100644 index 000000000000..db5dd5aa35f1 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_rendercore.c @@ -0,0 +1,2021 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_kernel_core.h" +#include "mali_osk.h" +#include "mali_kernel_subsystem.h" +#include "mali_kernel_rendercore.h" +#include "mali_osk_list.h" +#if MALI_GPU_UTILIZATION +#include "mali_kernel_utilization.h" +#endif +#if MALI_TIMELINE_PROFILING_ENABLED +#include "mali_osk_profiling.h" +#endif +#if USING_MMU +#include "mali_kernel_mem_mmu.h" +#endif /* USING_MMU */ +#if defined USING_MALI400_L2_CACHE +#include "mali_kernel_l2_cache.h" +#endif /* USING_MALI400_L2_CACHE */ + +#define HANG_CHECK_MSECS_MIN 100 +#define HANG_CHECK_MSECS_MAX 2000 /* 2 secs */ +#define HANG_CHECK_MSECS_DEFAULT 500 /* 500 ms */ + +#define WATCHDOG_MSECS_MIN (2*HANG_CHECK_MSECS_MIN) +#define WATCHDOG_MSECS_MAX 3600000 /* 1 hour */ +#define WATCHDOG_MSECS_DEFAULT 900000 /* 15 mins */ + +/* max value that will be converted from jiffies to micro seconds and written to job->render_time_usecs */ +#define JOB_MAX_JIFFIES 100000 + +int mali_hang_check_interval = HANG_CHECK_MSECS_DEFAULT; +int mali_max_job_runtime = WATCHDOG_MSECS_DEFAULT; + +#if MALI_TIMELINE_PROFILING_ENABLED +int mali_boot_profiling = 0; +#endif + +/* Subsystem entrypoints: */ +static _mali_osk_errcode_t rendercore_subsystem_startup(mali_kernel_subsystem_identifier id); +static void rendercore_subsystem_terminate(mali_kernel_subsystem_identifier id); +#if USING_MMU +static void rendercore_subsystem_broadcast_notification(mali_core_notification_message message, u32 data); +#endif + + +static void mali_core_subsystem_cleanup_all_renderunits(struct mali_core_subsystem* subsys); +static void mali_core_subsystem_move_core_set_idle(struct mali_core_renderunit *core); + +static mali_core_session * mali_core_subsystem_get_waiting_session(mali_core_subsystem *subsystem); +static mali_core_job * mali_core_subsystem_release_session_get_job(mali_core_subsystem *subsystem, mali_core_session * session); + +static void find_and_abort(mali_core_session* session, u32 abort_id); + +static void mali_core_job_start_on_core(mali_core_job *job, mali_core_renderunit *core); +#if USING_MMU +static void mali_core_subsystem_callback_schedule_wrapper(void* sub); +#endif +static void mali_core_subsystem_schedule(mali_core_subsystem*subsystem); +static void mali_core_renderunit_detach_job_from_core(mali_core_renderunit* core, mali_subsystem_reschedule_option reschedule, mali_subsystem_job_end_code end_status); + +static void mali_core_renderunit_irq_handler_remove(struct mali_core_renderunit *core); + +static _mali_osk_errcode_t mali_core_irq_handler_upper_half (void * data); +static void mali_core_irq_handler_bottom_half ( void *data ); + +#if USING_MMU +static void lock_subsystem(struct mali_core_subsystem * subsys); +static void unlock_subsystem(struct mali_core_subsystem * subsys); +#endif + + +/** + * This will be one of the subsystems in the array of subsystems: + * static struct mali_kernel_subsystem * subsystems[]; + * found in file: mali_kernel_core.c + * + * This subsystem is necessary for operations common to all rendercore + * subsystems. For example, mali_subsystem_mali200 and mali_subsystem_gp2 may + * share a mutex when RENDERCORES_USE_GLOBAL_MUTEX is non-zero. + */ +struct mali_kernel_subsystem mali_subsystem_rendercore= +{ + rendercore_subsystem_startup, /* startup */ + rendercore_subsystem_terminate, /* shutdown */ + NULL, /* load_complete */ + NULL, /* system_info_fill */ + NULL, /* session_begin */ + NULL, /* session_end */ +#if USING_MMU + rendercore_subsystem_broadcast_notification, /* broadcast_notification */ +#else + NULL, +#endif +#if MALI_STATE_TRACKING + NULL, /* dump_state */ +#endif +} ; + +static _mali_osk_lock_t *rendercores_global_mutex = NULL; +static u32 rendercores_global_mutex_is_held = 0; +static u32 rendercores_global_mutex_owner = 0; + +/** The 'dummy' rendercore subsystem to allow global subsystem mutex to be + * locked for all subsystems that extend the ''rendercore'' */ +static mali_core_subsystem rendercore_dummy_subsystem = {0,}; + +/* + * Rendercore Subsystem functions. + * + * These are exposed by mali_subsystem_rendercore + */ + +/** + * @brief Initialize the Rendercore subsystem. + * + * This must be called before any other subsystem that extends the + * ''rendercore'' may be initialized. For example, this must be called before + * the following functions: + * - mali200_subsystem_startup(), from mali_subsystem_mali200 + * - maligp_subsystem_startup(), from mali_subsystem_gp2 + * + * @note This function is separate from mali_core_subsystem_init(). They + * are related, in that mali_core_subsystem_init() may use the structures + * initialized by rendercore_subsystem_startup() + */ +static _mali_osk_errcode_t rendercore_subsystem_startup(mali_kernel_subsystem_identifier id) +{ + rendercores_global_mutex_is_held = 0; + rendercores_global_mutex = _mali_osk_lock_init( + (_mali_osk_lock_flags_t)(_MALI_OSK_LOCKFLAG_NONINTERRUPTABLE | _MALI_OSK_LOCKFLAG_ORDERED), + 0, 129); + + if (NULL == rendercores_global_mutex) + { + MALI_PRINT_ERROR(("Failed: _mali_osk_lock_init\n")) ; + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + rendercore_dummy_subsystem.name = "Rendercore Global Subsystem"; /* On the constant pool, do not free */ + rendercore_dummy_subsystem.magic_nr = SUBSYSTEM_MAGIC_NR; /* To please the Subsystem Mutex code */ + +#if MALI_GPU_UTILIZATION + if (mali_utilization_init() != _MALI_OSK_ERR_OK) + { + _mali_osk_lock_term(rendercores_global_mutex); + rendercores_global_mutex = NULL; + MALI_PRINT_ERROR(("Failed: mali_utilization_init\n")) ; + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } +#endif + +#if MALI_TIMELINE_PROFILING_ENABLED + if (_mali_osk_profiling_init(mali_boot_profiling ? MALI_TRUE : MALI_FALSE) != _MALI_OSK_ERR_OK) + { + /* No biggie if we wheren't able to initialize the profiling */ + MALI_PRINT_ERROR(("Rendercore: Failed to initialize profiling, feature will be unavailable\n")) ; + } +#endif + + MALI_DEBUG_PRINT(2, ("Rendercore: subsystem global mutex initialized\n")) ; + MALI_SUCCESS; +} + +/** + * @brief Terminate the Rendercore subsystem. + * + * This must only be called \b after any other subsystem that extends the + * ''rendercore'' has been terminated. For example, this must be called \b after + * the following functions: + * - mali200_subsystem_terminate(), from mali_subsystem_mali200 + * - maligp_subsystem_terminate(), from mali_subsystem_gp2 + * + * @note This function is separate from mali_core_subsystem_cleanup(), though, + * the subsystems that extend ''rendercore'' must still call + * mali_core_subsystem_cleanup() when they terminate. + */ +static void rendercore_subsystem_terminate(mali_kernel_subsystem_identifier id) +{ + /* Catch double-terminate */ + MALI_DEBUG_ASSERT_POINTER( rendercores_global_mutex ); + +#if MALI_TIMELINE_PROFILING_ENABLED + _mali_osk_profiling_term(); +#endif + +#if MALI_GPU_UTILIZATION + mali_utilization_term(); +#endif + + rendercore_dummy_subsystem.name = NULL; /* The original string was on the constant pool, do not free */ + rendercore_dummy_subsystem.magic_nr = 0; + + /* ASSERT that no-one's holding this */ + MALI_DEBUG_PRINT_ASSERT( 0 == rendercores_global_mutex_is_held, + ("Rendercores' Global Mutex was held at termination time. Have the subsystems that extend ''rendercore'' been terminated?\n") ); + + _mali_osk_lock_term( rendercores_global_mutex ); + rendercores_global_mutex = NULL; + + MALI_DEBUG_PRINT(2, ("Rendercore: subsystem global mutex terminated\n")) ; +} + + +#if USING_MMU +/** + * @brief Handle certain Rendercore subsystem broadcast notifications + * + * When RENDERCORES_USE_GLOBAL_MUTEX is non-zero, this handles the following messages: + * - MMU_KILL_STEP0_LOCK_SUBSYSTEM + * - MMU_KILL_STEP4_UNLOCK_SUBSYSTEM + * + * The purpose is to manage the Rendercode Global Mutex, which cannot be + * managed by any system that extends the ''rendercore''. + * + * All other messages must be handled by mali_core_subsystem_broadcast_notification() + * + * + * When RENDERCORES_USE_GLOBAL_MUTEX is 0, this function does nothing. + * Instead, the subsystem that extends the ''rendercore' \b must handle its + * own mutexes - refer to mali_core_subsystem_broadcast_notification(). + * + * Used currently only for signalling when MMU has a pagefault + */ +static void rendercore_subsystem_broadcast_notification(mali_core_notification_message message, u32 data) +{ + switch(message) + { + case MMU_KILL_STEP0_LOCK_SUBSYSTEM: + lock_subsystem( &rendercore_dummy_subsystem ); + break; + case MMU_KILL_STEP4_UNLOCK_SUBSYSTEM: + unlock_subsystem( &rendercore_dummy_subsystem ); + break; + + case MMU_KILL_STEP1_STOP_BUS_FOR_ALL_CORES: + /** FALLTHROUGH */ + case MMU_KILL_STEP2_RESET_ALL_CORES_AND_ABORT_THEIR_JOBS: + /** FALLTHROUGH */ + case MMU_KILL_STEP3_CONTINUE_JOB_HANDLING: + break; + + default: + MALI_PRINT_ERROR(("Illegal message: 0x%x, data: 0x%x\n", (u32)message, data)); + break; + } + +} +#endif + +/* + * Functions inherited by the subsystems that extend the ''rendercore''. + */ + +void mali_core_renderunit_timeout_function_hang_detection(void *arg) +{ + mali_bool action = MALI_FALSE; + mali_core_renderunit * core; + + core = (mali_core_renderunit *) arg; + if( !core ) return; + + /* if NOT idle OR NOT powered off OR has TIMED_OUT */ + if ( !((CORE_WATCHDOG_TIMEOUT == core->state ) || (CORE_IDLE== core->state) || (CORE_OFF == core->state)) ) + { + core->state = CORE_HANG_CHECK_TIMEOUT; + action = MALI_TRUE; + } + + if(action) _mali_osk_irq_schedulework(core->irq); +} + + +void mali_core_renderunit_timeout_function(void *arg) +{ + mali_core_renderunit * core; + mali_bool is_watchdog; + + core = (mali_core_renderunit *)arg; + if( !core ) return; + + is_watchdog = MALI_TRUE; + if (mali_benchmark) + { + /* poll based core */ + mali_core_job *job; + job = core->current_job; + if ( (NULL != job) && + (0 != _mali_osk_time_after(job->watchdog_jiffies,_mali_osk_time_tickcount())) + ) + { + core->state = CORE_POLL; + is_watchdog = MALI_FALSE; + } + } + + if (is_watchdog) + { + MALI_DEBUG_PRINT(3, ("SW-Watchdog timeout: Core:%s\n", core->description)); + core->state = CORE_WATCHDOG_TIMEOUT; + } + + _mali_osk_irq_schedulework(core->irq); +} + +/* Used by external renderunit_create<> function */ +_mali_osk_errcode_t mali_core_renderunit_init(mali_core_renderunit * core) +{ + MALI_DEBUG_PRINT(5, ("Core: renderunit_init: Core:%s\n", core->description)); + + _MALI_OSK_INIT_LIST_HEAD(&core->list) ; + core->timer = _mali_osk_timer_init(); + if (NULL == core->timer) + { + MALI_PRINT_ERROR(("Core: renderunit_init: Core:%s -- cannot init timer\n", core->description)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + _mali_osk_timer_setcallback(core->timer, mali_core_renderunit_timeout_function, (void *)core); + + core->timer_hang_detection = _mali_osk_timer_init(); + if (NULL == core->timer_hang_detection) + { + _mali_osk_timer_term(core->timer); + MALI_PRINT_ERROR(("Core: renderunit_init: Core:%s -- cannot init hang detection timer\n", core->description)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + _mali_osk_timer_setcallback(core->timer_hang_detection, mali_core_renderunit_timeout_function_hang_detection, (void *)core); + +#if USING_MALI_PMM + /* Init no pending power downs */ + core->pend_power_down = MALI_FALSE; + + /* Register the core with the PMM - which powers it up */ + if (_MALI_OSK_ERR_OK != malipmm_core_register( core->pmm_id )) + { + _mali_osk_timer_term(core->timer); + _mali_osk_timer_term(core->timer_hang_detection); + MALI_PRINT_ERROR(("Core: renderunit_init: Core:%s -- cannot register with PMM\n", core->description)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } +#endif /* USING_MALI_PMM */ + + core->error_recovery = MALI_FALSE; + core->in_detach_function = MALI_FALSE; + core->state = CORE_IDLE; + core->current_job = NULL; + core->magic_nr = CORE_MAGIC_NR; +#if USING_MMU + core->mmu = NULL; +#endif /* USING_MMU */ + + MALI_SUCCESS; +} + +void mali_core_renderunit_term(mali_core_renderunit * core) +{ + MALI_DEBUG_PRINT(5, ("Core: renderunit_term: Core:%s\n", core->description)); + + if (NULL != core->timer) + { + _mali_osk_timer_term(core->timer); + core->timer = NULL; + } + if (NULL != core->timer_hang_detection) + { + _mali_osk_timer_term(core->timer_hang_detection); + core->timer_hang_detection = NULL; + } + +#if USING_MALI_PMM + /* Unregister the core with the PMM */ + malipmm_core_unregister( core->pmm_id ); +#endif +} + +/* Used by external renderunit_create<> function */ +_mali_osk_errcode_t mali_core_renderunit_map_registers(mali_core_renderunit *core) +{ + MALI_DEBUG_PRINT(3, ("Core: renderunit_map_registers: Core:%s\n", core->description)) ; + if( (0 == core->registers_base_addr) || + (0 == core->size) || + (NULL == core->description) + ) + { + MALI_PRINT_ERROR(("Missing fields in the core structure %u %u 0x%x;\n", core->registers_base_addr, core->size, core->description)); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + if (_MALI_OSK_ERR_OK != _mali_osk_mem_reqregion(core->registers_base_addr, core->size, core->description)) + { + MALI_PRINT_ERROR(("Could not request register region (0x%08X - 0x%08X) to core: %s\n", + core->registers_base_addr, core->registers_base_addr + core->size - 1, core->description)); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + else + { + MALI_DEBUG_PRINT(6, ("Success: request_mem_region: (0x%08X - 0x%08X) Core:%s\n", + core->registers_base_addr, core->registers_base_addr + core->size - 1, core->description)); + } + + core->registers_mapped = _mali_osk_mem_mapioregion( core->registers_base_addr, core->size, core->description ); + + if ( 0 == core->registers_mapped ) + { + MALI_PRINT_ERROR(("Could not ioremap registers for %s .\n", core->description)); + _mali_osk_mem_unreqregion(core->registers_base_addr, core->size); + MALI_ERROR(_MALI_OSK_ERR_NOMEM); + } + else + { + MALI_DEBUG_PRINT(6, ("Success: ioremap_nocache: Internal ptr: (0x%08X - 0x%08X) Core:%s\n", + (u32) core->registers_mapped, + ((u32)core->registers_mapped)+ core->size - 1, + core->description)); + } + + MALI_DEBUG_PRINT(4, ("Success: Mapping registers to core: %s\n",core->description)); + + MALI_SUCCESS; +} + +/* Used by external renderunit_create<> function + other places */ +void mali_core_renderunit_unmap_registers(mali_core_renderunit *core) +{ + MALI_DEBUG_PRINT(3, ("Core: renderunit_unmap_registers: Core:%s\n", core->description)); + if (0 == core->registers_mapped) + { + MALI_PRINT_ERROR(("Trying to unmap register-mapping with NULL from core: %s\n", core->description)); + return; + } + _mali_osk_mem_unmapioregion(core->registers_base_addr, core->size, core->registers_mapped); + core->registers_mapped = 0; + _mali_osk_mem_unreqregion(core->registers_base_addr, core->size); +} + +static void mali_core_renderunit_irq_handler_remove(mali_core_renderunit *core) +{ + MALI_DEBUG_PRINT(3, ("Core: renderunit_irq_handler_remove: Core:%s\n", core->description)); + _mali_osk_irq_term(core->irq); +} + +mali_core_renderunit * mali_core_renderunit_get_mali_core_nr(mali_core_subsystem *subsys, u32 mali_core_nr) +{ + mali_core_renderunit * core; + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); + if (subsys->number_of_cores <= mali_core_nr) + { + MALI_PRINT_ERROR(("Trying to get illegal mali_core_nr: 0x%x for %s", mali_core_nr, subsys->name)); + return NULL; + } + core = (subsys->mali_core_array)[mali_core_nr]; + MALI_DEBUG_PRINT(6, ("Core: renderunit_get_mali_core_nr: Core:%s\n", core->description)); + MALI_CHECK_CORE(core); + return core; +} + +/* Is used by external function: + subsystem_startup<> */ +_mali_osk_errcode_t mali_core_subsystem_init(mali_core_subsystem* new_subsys) +{ + int i; + + /* These function pointers must have been set on before calling this function */ + if ( + ( NULL == new_subsys->name ) || + ( NULL == new_subsys->start_job ) || + ( NULL == new_subsys->irq_handler_upper_half ) || + ( NULL == new_subsys->irq_handler_bottom_half ) || + ( NULL == new_subsys->get_new_job_from_user ) || + ( NULL == new_subsys->return_job_to_user ) + ) + { + MALI_PRINT_ERROR(("Missing functions in subsystem.")); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + MALI_DEBUG_PRINT(2, ("Core: subsystem_init: %s\n", new_subsys->name)) ; + + /* Catch use-before-initialize/use-after-terminate */ + MALI_DEBUG_ASSERT_POINTER( rendercores_global_mutex ); + + new_subsys->magic_nr = SUBSYSTEM_MAGIC_NR; + + _MALI_OSK_INIT_LIST_HEAD(&new_subsys->renderunit_idle_head); /* Idle cores of this type */ + _MALI_OSK_INIT_LIST_HEAD(&new_subsys->renderunit_off_head); /* Powered off cores of this type */ + + /* Linked list for each priority of sessions with a job ready for scheduleing */ + for(i=0; i<PRIORITY_LEVELS; ++i) + { + _MALI_OSK_INIT_LIST_HEAD(&new_subsys->awaiting_sessions_head[i]); + } + + /* Linked list of all sessions connected to this coretype */ + _MALI_OSK_INIT_LIST_HEAD(&new_subsys->all_sessions_head); + + MALI_SUCCESS; +} + +#if USING_MMU +void mali_core_subsystem_attach_mmu(mali_core_subsystem* subsys) +{ + u32 i; + mali_core_renderunit * core; + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys); + + for(i=0 ; i < subsys->number_of_cores ; ++i) + { + core = mali_core_renderunit_get_mali_core_nr(subsys,i); + if ( NULL==core ) break; + core->mmu = mali_memory_core_mmu_lookup(core->mmu_id); + mali_memory_core_mmu_owner(core,core->mmu); + MALI_DEBUG_PRINT(2, ("Attach mmu: 0x%x to core: %s in subsystem: %s\n", core->mmu, core->description, subsys->name)); + } + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); +} +#endif + +/* This will register an IRQ handler, and add the core to the list of available cores for this subsystem. */ +_mali_osk_errcode_t mali_core_subsystem_register_renderunit(mali_core_subsystem* subsys, mali_core_renderunit * core) +{ + mali_core_renderunit ** mali_core_array; + u32 previous_nr; + u32 previous_size; + u32 new_nr; + u32 new_size; + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; + + /* If any of these are 0 there is an error */ + if(0 == core->subsystem || + 0 == core->registers_base_addr || + 0 == core->size || + 0 == core->description) + { + MALI_PRINT_ERROR(("Missing fields in the core structure 0x%x 0x%x 0x%x;\n", + core->registers_base_addr, core->size, core->description)); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + MALI_DEBUG_PRINT(3, ("Core: subsystem_register_renderunit: %s\n", core->description)); + + MALI_CHECK_NON_NULL( + core->irq = _mali_osk_irq_init( + core->irq_nr, + mali_core_irq_handler_upper_half, + mali_core_irq_handler_bottom_half, + (_mali_osk_irq_trigger_t)subsys->probe_core_irq_trigger, + (_mali_osk_irq_ack_t)subsys->probe_core_irq_acknowledge, + core, + "mali_core_irq_handlers" + ), + _MALI_OSK_ERR_FAULT + ); + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys); + + /* Update which core number this is */ + core->core_number = subsys->number_of_cores; + + /* Update the array of cores in the subsystem. */ + previous_nr = subsys->number_of_cores; + previous_size = sizeof(mali_core_renderunit*)*previous_nr; + new_nr = previous_nr + 1; + new_size = sizeof(mali_core_renderunit*)*new_nr; + + if (0 != previous_nr) + { + if (NULL == subsys->mali_core_array) + { + MALI_PRINT_ERROR(("Internal error")); + goto exit_function; + } + + mali_core_array = (mali_core_renderunit **) _mali_osk_malloc( new_size ); + if (NULL == mali_core_array ) + { + MALI_PRINT_ERROR(("Out of mem")); + err = _MALI_OSK_ERR_NOMEM; + goto exit_function; + } + _mali_osk_memcpy(mali_core_array, subsys->mali_core_array, previous_size); + _mali_osk_free( subsys->mali_core_array); + MALI_DEBUG_PRINT(5, ("Success: adding a new core to subsystem array %s\n", core->description) ) ; + } + else + { + mali_core_array = (mali_core_renderunit **) _mali_osk_malloc( new_size ); + if (NULL == mali_core_array ) + { + MALI_PRINT_ERROR(("Out of mem")); + err = _MALI_OSK_ERR_NOMEM; + goto exit_function; + } + MALI_DEBUG_PRINT(6, ("Success: adding first core to subsystem array %s\n", core->description) ) ; + } + subsys->mali_core_array = mali_core_array; + mali_core_array[previous_nr] = core; + + /* Add the core to the list of available cores on the system */ + _mali_osk_list_add(&(core->list), &(subsys->renderunit_idle_head)); + + /* Update total number of cores */ + subsys->number_of_cores = new_nr; + MALI_DEBUG_PRINT(6, ("Success: mali_core_subsystem_register_renderunit %s\n", core->description)); + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + MALI_SUCCESS; + +exit_function: + mali_core_renderunit_irq_handler_remove(core); + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + MALI_ERROR(err); +} + + +/** + * Called by the core when a system info update is needed + * We fill in info about all the core types available + * @param subsys Pointer to the core's @a mali_core_subsystem data structure + * @param info Pointer to system info struct to update + * @return _MALI_OSK_ERR_OK on success, or another _mali_osk_errcode_t error code on failure + */ +_mali_osk_errcode_t mali_core_subsystem_system_info_fill(mali_core_subsystem* subsys, _mali_system_info* info) +{ + u32 i; + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; /* OK if no cores to update info for */ + mali_core_renderunit * core; + _mali_core_info **core_info_nextp; + _mali_core_info * cinfo; + + MALI_DEBUG_PRINT(4, ("mali_core_subsystem_system_info_fill: %s\n", subsys->name) ) ; + + /* check input */ + MALI_CHECK_NON_NULL(info, _MALI_OSK_ERR_INVALID_ARGS); + + core_info_nextp = &(info->core_info); + cinfo = info->core_info; + + while(NULL!=cinfo) + { + core_info_nextp = &(cinfo->next); + cinfo = cinfo->next; + } + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys); + for(i=0 ; i < subsys->number_of_cores ; ++i) + { + core = mali_core_renderunit_get_mali_core_nr(subsys,i); + if ( NULL==core ) + { + err = _MALI_OSK_ERR_FAULT; + goto early_exit; + } + cinfo = (_mali_core_info *)_mali_osk_calloc(1, sizeof(_mali_core_info)); + if ( NULL==cinfo ) + { + err = _MALI_OSK_ERR_NOMEM; + goto early_exit; + } + cinfo->version = core->core_version; + cinfo->type =subsys->core_type; + cinfo->reg_address = core->registers_base_addr; + cinfo->core_nr = i; + cinfo->next = NULL; + /* Writing this address to the previous' *(&next) ptr */ + *core_info_nextp = cinfo; + /* Setting the next_ptr to point to &this->next_ptr */ + core_info_nextp = &(cinfo->next); + } +early_exit: + if ( _MALI_OSK_ERR_OK != err) MALI_PRINT_ERROR(("Error: In mali_core_subsystem_system_info_fill %d\n", err)); + MALI_DEBUG_CODE( + cinfo = info->core_info; + + MALI_DEBUG_PRINT(3, ("Current list of cores\n")); + while( NULL != cinfo ) + { + MALI_DEBUG_PRINT(3, ("Type: 0x%x\n", cinfo->type)); + MALI_DEBUG_PRINT(3, ("Version: 0x%x\n", cinfo->version)); + MALI_DEBUG_PRINT(3, ("Reg_addr: 0x%x\n", cinfo->reg_address)); + MALI_DEBUG_PRINT(3, ("Core_nr: 0x%x\n", cinfo->core_nr)); + MALI_DEBUG_PRINT(3, ("Flags: 0x%x\n", cinfo->flags)); + MALI_DEBUG_PRINT(3, ("*****\n")); + cinfo = cinfo->next; + } + ); + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + MALI_ERROR(err); +} + + +/* Is used by external function: + subsystem_terminate<> */ +void mali_core_subsystem_cleanup(mali_core_subsystem* subsys) +{ + u32 i; + mali_core_renderunit * core; + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys); + MALI_DEBUG_PRINT(2, ("Core: subsystem_cleanup: %s\n", subsys->name )) ; + + for(i=0 ; i < subsys->number_of_cores ; ++i) + { + core = mali_core_renderunit_get_mali_core_nr(subsys,i); + +#if USING_MMU + if (NULL != core->mmu) + { + /* the MMU is attached in the load_complete callback, which will never be called if the module fails to load, handle that case */ + mali_memory_core_mmu_unregister_callback(core->mmu, mali_core_subsystem_callback_schedule_wrapper); + } +#endif + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + + mali_core_renderunit_irq_handler_remove(core); + + /* When a process terminates, all cores running jobs from that process is reset and put to idle. + That means that when the module is unloading (this code) we are guaranteed that all cores are idle. + However: if something (we can't think of) is really wrong, a core may give an interrupt during this + unloading, and we may now in the code have a bottom-half-processing pending from the interrupts + we deregistered above. To be sure that the bottom halves do not access the structures after they + are deallocated we flush the bottom-halves processing here, before the deallocation. */ + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys); + +#if USING_MALI_PMM + /* Only reset when we are using PMM and the core is not off */ +#if MALI_PMM_NO_PMU + /* We need to reset when there is no PMU - but this will + * cause the register read/write functions to report an + * error (hence the if to check for CORE_OFF below) we + * change state to allow the reset to happen. + */ + core->state = CORE_IDLE; +#endif + if( core->state != CORE_OFF ) + { + subsys->reset_core( core, MALI_CORE_RESET_STYLE_DISABLE ); + } +#else + /* Always reset the core */ + subsys->reset_core( core, MALI_CORE_RESET_STYLE_DISABLE ); +#endif + + mali_core_renderunit_unmap_registers(core); + + _mali_osk_list_delinit(&core->list); + + mali_core_renderunit_term(core); + + subsys->renderunit_delete(core); + } + + mali_core_subsystem_cleanup_all_renderunits(subsys); + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + MALI_DEBUG_PRINT(6, ("SUCCESS: mali_core_subsystem_cleanup: %s\n", subsys->name )) ; +} + +_mali_osk_errcode_t mali_core_subsystem_ioctl_number_of_cores_get(mali_core_session * session, u32 *number_of_cores) +{ + mali_core_subsystem * subsystem; + + subsystem = session->subsystem; + if ( NULL != number_of_cores ) + { + *number_of_cores = subsystem->number_of_cores; + + MALI_DEBUG_PRINT(4, ("Core: ioctl_number_of_cores_get: %s: %u\n", subsystem->name, *number_of_cores) ) ; + } + + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_core_subsystem_ioctl_start_job(mali_core_session * session, void *job_data) +{ + mali_core_subsystem * subsystem; + _mali_osk_errcode_t err; + + /* need the subsystem to run callback function */ + subsystem = session->subsystem; + MALI_CHECK_NON_NULL(subsystem, _MALI_OSK_ERR_FAULT); + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsystem); + err = subsystem->get_new_job_from_user(session, job_data); + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsystem); + + MALI_ERROR(err); +} + + +/* We return the version number to the first core in this subsystem */ +_mali_osk_errcode_t mali_core_subsystem_ioctl_core_version_get(mali_core_session * session, _mali_core_version *version) +{ + mali_core_subsystem * subsystem; + mali_core_renderunit * core0; + u32 nr_return; + + subsystem = session->subsystem; + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsystem); + + core0 = mali_core_renderunit_get_mali_core_nr(subsystem, 0); + + if( NULL == core0 ) + { + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsystem); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + nr_return = core0->core_version; + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsystem); + + MALI_DEBUG_PRINT(4, ("Core: ioctl_core_version_get: %s: %u\n", subsystem->name, nr_return )) ; + + *version = nr_return; + + MALI_SUCCESS; +} + +void mali_core_subsystem_ioctl_abort_job(mali_core_session * session, u32 id) +{ + find_and_abort(session, id); +} + +static mali_bool job_should_be_aborted(mali_core_job *job, u32 abort_id) +{ + if ( job->abort_id == abort_id ) return MALI_TRUE; + else return MALI_FALSE; +} + +static void find_and_abort(mali_core_session* session, u32 abort_id) +{ + mali_core_subsystem * subsystem; + mali_core_renderunit *core; + mali_core_renderunit *tmp; + mali_core_job *job; + + subsystem = session->subsystem; + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB( subsystem ); + + job = mali_job_queue_abort_job(session, abort_id); + if (NULL != job) + { + MALI_DEBUG_PRINT(3, ("Core: Aborting %s job, with id nr: %u, from the waiting_to_run slot.\n", subsystem->name, abort_id )); + if (mali_job_queue_empty(session)) + { + _mali_osk_list_delinit(&(session->awaiting_sessions_list)); + } + subsystem->awaiting_sessions_sum_all_priorities--; + subsystem->return_job_to_user(job , JOB_STATUS_END_ABORT); + } + + _MALI_OSK_LIST_FOREACHENTRY( core, tmp, &session->renderunits_working_head, mali_core_renderunit, list ) + { + job = core->current_job; + if ( (job!=NULL) && (job_should_be_aborted (job, abort_id) ) ) + { + MALI_DEBUG_PRINT(3, ("Core: Aborting %s job, with id nr: %u, which is currently running on mali.\n", subsystem->name, abort_id )); + if ( core->state==CORE_IDLE ) + { + MALI_PRINT_ERROR(("Aborting core with running job which is idle. Must be something very wrong.")); + goto end_bug; + } + mali_core_renderunit_detach_job_from_core(core, SUBSYSTEM_RESCHEDULE, JOB_STATUS_END_ABORT); + } + } +end_bug: + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE( subsystem ); +} + + +_mali_osk_errcode_t mali_core_subsystem_ioctl_suspend_response(mali_core_session * session, void *argument) +{ + mali_core_subsystem * subsystem; + _mali_osk_errcode_t err = _MALI_OSK_ERR_FAULT; + + /* need the subsystem to run callback function */ + subsystem = session->subsystem; + MALI_CHECK_NON_NULL(subsystem, _MALI_OSK_ERR_FAULT); + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsystem); + if ( NULL != subsystem->suspend_response) + { + MALI_DEBUG_PRINT(4, ("MALI_IOC_CORE_CMD_SUSPEND_RESPONSE start\n")); + err = subsystem->suspend_response(session, argument); + MALI_DEBUG_PRINT(4, ("MALI_IOC_CORE_CMD_SUSPEND_RESPONSE end\n")); + } + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsystem); + + return err; +} + + +/* Is used by internal function: + mali_core_subsystem_cleanup<>s */ +/* All cores should be removed before calling this function +Must hold subsystem_mutex before entering this function */ +static void mali_core_subsystem_cleanup_all_renderunits(mali_core_subsystem* subsys) +{ + int i; + _mali_osk_free(subsys->mali_core_array); + subsys->number_of_cores = 0; + + MALI_DEBUG_PRINT(5, ("Core: subsystem_cleanup_all_renderunits: %s\n", subsys->name) ) ; + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); + + if ( ! _mali_osk_list_empty(&(subsys->renderunit_idle_head))) + { + MALI_PRINT_ERROR(("List renderunit_list_idle should be empty.")); + _MALI_OSK_INIT_LIST_HEAD(&(subsys->renderunit_idle_head)) ; + } + + if ( ! _mali_osk_list_empty(&(subsys->renderunit_off_head))) + { + MALI_PRINT_ERROR(("List renderunit_list_off should be empty.")); + _MALI_OSK_INIT_LIST_HEAD(&(subsys->renderunit_off_head)) ; + } + + for(i=0; i<PRIORITY_LEVELS; ++i) + { + if ( ! _mali_osk_list_empty(&(subsys->awaiting_sessions_head[i]))) + { + MALI_PRINT_ERROR(("List awaiting_sessions_linkedlist should be empty.")); + _MALI_OSK_INIT_LIST_HEAD(&(subsys->awaiting_sessions_head[i])) ; + subsys->awaiting_sessions_sum_all_priorities = 0; + } + } + + if ( ! _mali_osk_list_empty(&(subsys->all_sessions_head))) + { + MALI_PRINT_ERROR(("List all_sessions_linkedlist should be empty.")); + _MALI_OSK_INIT_LIST_HEAD(&(subsys->all_sessions_head)) ; + } +} + +/* Is used by internal functions: + mali_core_irq_handler_bottom_half<>; + mali_core_subsystem_schedule<>; */ +/* Will release the core.*/ +/* Must hold subsystem_mutex before entering this function */ +static void mali_core_subsystem_move_core_set_idle(mali_core_renderunit *core) +{ + mali_core_subsystem *subsystem; +#if USING_MALI_PMM + mali_core_status oldstatus; +#endif + subsystem = core->subsystem; + MALI_ASSERT_MUTEX_IS_GRABBED(subsystem); + MALI_CHECK_CORE(core); + MALI_CHECK_SUBSYSTEM(subsystem); + + _mali_osk_timer_del(core->timer); + _mali_osk_timer_del(core->timer_hang_detection); + + MALI_DEBUG_PRINT(5, ("Core: subsystem_move_core_set_idle: %s\n", core->description) ) ; + + core->current_job = NULL ; + +#if USING_MALI_PMM + + oldstatus = core->state; + + if ( !core->pend_power_down ) + { + core->state = CORE_IDLE ; + _mali_osk_list_move( &core->list, &subsystem->renderunit_idle_head ); + } + + if( CORE_OFF != oldstatus ) + { + /* Message that this core is now idle or in fact off */ + _mali_uk_pmm_message_s event = { + NULL, + MALI_PMM_EVENT_JOB_FINISHED, + 0 }; + event.data = core->pmm_id; + _mali_ukk_pmm_event_message( &event ); +#if USING_MMU + /* Only free the reference when entering idle state from + * anything other than power off + */ + mali_memory_core_mmu_release_address_space_reference(core->mmu); +#endif /* USING_MMU */ + } + + if( core->pend_power_down ) + { + core->state = CORE_OFF ; + _mali_osk_list_move( &core->list, &subsystem->renderunit_off_head ); + + /* Done the move from the active queues, so the pending power down can be done */ + core->pend_power_down = MALI_FALSE; + malipmm_core_power_down_okay( core->pmm_id ); + } + +#else /* !USING_MALI_PMM */ + + core->state = CORE_IDLE ; + _mali_osk_list_move( &core->list, &subsystem->renderunit_idle_head ); + +#if USING_MMU + mali_memory_core_mmu_release_address_space_reference(core->mmu); +#endif + +#endif /* USING_MALI_PMM */ +} + +/* Must hold subsystem_mutex before entering this function */ +static void mali_core_subsystem_move_set_working(mali_core_renderunit *core, mali_core_job *job) +{ + mali_core_subsystem *subsystem; + mali_core_session *session; + u64 time_now; + + session = job->session; + subsystem = core->subsystem; + + MALI_CHECK_CORE(core); + MALI_CHECK_JOB(job); + MALI_CHECK_SUBSYSTEM(subsystem); + + MALI_ASSERT_MUTEX_IS_GRABBED(subsystem); + + MALI_DEBUG_PRINT(5, ("Core: subsystem_move_set_working: %s\n", core->description) ) ; + + time_now = _mali_osk_time_get_ns(); + job->start_time = time_now; +#if MALI_GPU_UTILIZATION + mali_utilization_core_start(time_now); +#endif + + core->current_job = job ; + core->state = CORE_WORKING ; + _mali_osk_list_move( &core->list, &session->renderunits_working_head ); + +} + +#if USING_MALI_PMM + +/* Must hold subsystem_mutex before entering this function */ +static void mali_core_subsystem_move_core_set_off(mali_core_renderunit *core) +{ + mali_core_subsystem *subsystem; + subsystem = core->subsystem; + MALI_ASSERT_MUTEX_IS_GRABBED(subsystem); + MALI_CHECK_CORE(core); + MALI_CHECK_SUBSYSTEM(subsystem); + + /* Cores must be idle before powering off */ + MALI_DEBUG_ASSERT(core->state == CORE_IDLE); + + MALI_DEBUG_PRINT(5, ("Core: subsystem_move_core_set_off: %s\n", core->description) ) ; + + core->current_job = NULL ; + core->state = CORE_OFF ; + _mali_osk_list_move( &core->list, &subsystem->renderunit_off_head ); +} + +#endif /* USING_MALI_PMM */ + +/* Is used by internal function: + mali_core_subsystem_schedule<>; */ +/* Returns the job with the highest priority for the subsystem. NULL if none*/ +/* Must hold subsystem_mutex before entering this function */ +static mali_core_session * mali_core_subsystem_get_waiting_session(mali_core_subsystem *subsystem) +{ + int i; + + MALI_CHECK_SUBSYSTEM(subsystem); + MALI_ASSERT_MUTEX_IS_GRABBED(subsystem); + + if ( 0 == subsystem->awaiting_sessions_sum_all_priorities ) + { + MALI_DEBUG_PRINT(5, ("Core: subsystem_get_waiting_job: No awaiting session found\n")); + return NULL; + } + + for( i=0; i<PRIORITY_LEVELS ; ++i) + { + if (!_mali_osk_list_empty(&subsystem->awaiting_sessions_head[i])) + { + return _MALI_OSK_LIST_ENTRY(subsystem->awaiting_sessions_head[i].next, mali_core_session, awaiting_sessions_list); + } + } + + return NULL; +} + +static mali_core_job * mali_core_subsystem_release_session_get_job(mali_core_subsystem *subsystem, mali_core_session * session) +{ + mali_core_job *job; + MALI_CHECK_SUBSYSTEM(subsystem); + MALI_ASSERT_MUTEX_IS_GRABBED(subsystem); + + job = mali_job_queue_get_job(session); + subsystem->awaiting_sessions_sum_all_priorities--; + + if(mali_job_queue_empty(session)) + { + /* This is the last job, so remove it from the list */ + _mali_osk_list_delinit(&session->awaiting_sessions_list); + } + else + { + if (0 == (job->flags & MALI_UK_START_JOB_FLAG_MORE_JOBS_FOLLOW)) + { + /* There are more jobs, but the follow flag is not set, so let other sessions run their jobs first */ + _mali_osk_list_del(&(session->awaiting_sessions_list)); + _mali_osk_list_addtail(&(session->awaiting_sessions_list), &(subsystem->awaiting_sessions_head[ + session->queue[session->queue_head]->priority])); + } + /* else; keep on list, follow flag is set and there are more jobs in queue for this session */ + } + + MALI_CHECK_JOB(job); + return job; +} + +/* Is used by internal functions: + mali_core_subsystem_schedule<> */ +/* This will start the job on the core. It will also release the core if it did not start.*/ +/* Must hold subsystem_mutex before entering this function */ +static void mali_core_job_start_on_core(mali_core_job *job, mali_core_renderunit *core) +{ + mali_core_session *session; + mali_core_subsystem *subsystem; + _mali_osk_errcode_t err; + session = job->session; + subsystem = core->subsystem; + + MALI_CHECK_CORE(core); + MALI_CHECK_JOB(job); + MALI_CHECK_SUBSYSTEM(subsystem); + MALI_CHECK_SESSION(session); + MALI_ASSERT_MUTEX_IS_GRABBED(subsystem); + + MALI_DEBUG_PRINT(4, ("Core: job_start_on_core: job=0x%x, session=0x%x, core=%s\n", job, session, core->description)); + + MALI_DEBUG_ASSERT(NULL == core->current_job) ; + MALI_DEBUG_ASSERT(CORE_IDLE == core->state ); + + mali_core_subsystem_move_set_working(core, job); + +#if defined USING_MALI400_L2_CACHE + if (0 == (job->flags & MALI_UK_START_JOB_FLAG_NO_FLUSH)) + { + /* Invalidate the L2 cache */ + if (_MALI_OSK_ERR_OK != mali_kernel_l2_cache_invalidate_all() ) + { + MALI_DEBUG_PRINT(4, ("Core: Clear of L2 failed, return job. System may not be usable for some reason.\n")); + mali_core_subsystem_move_core_set_idle(core); + subsystem->return_job_to_user(job,JOB_STATUS_END_SYSTEM_UNUSABLE ); + return; + } + } +#endif + + /* Tries to start job on the core. Returns MALI_FALSE if the job could not be started */ + err = subsystem->start_job(job, core); + + if ( _MALI_OSK_ERR_OK != err ) + { + /* This will happen only if there is something in the job object + which make it inpossible to start. Like if it require illegal memory.*/ + MALI_DEBUG_PRINT(4, ("Core: start_job failed, return job and putting core back into idle list\n")); + mali_core_subsystem_move_core_set_idle(core); + subsystem->return_job_to_user(job,JOB_STATUS_END_ILLEGAL_JOB ); + } + else + { + u32 delay = _mali_osk_time_mstoticks(job->watchdog_msecs)+1; + job->watchdog_jiffies = _mali_osk_time_tickcount() + delay; + if (mali_benchmark) + { + _mali_osk_timer_add(core->timer, 1); + } + else + { + _mali_osk_timer_add(core->timer, delay); + } + } +} + +#if USING_MMU +static void mali_core_subsystem_callback_schedule_wrapper(void* sub) +{ + mali_core_subsystem * subsystem; + subsystem = (mali_core_subsystem *)sub; + MALI_DEBUG_PRINT(3, ("MMU: Is schedulling subsystem: %s\n", subsystem->name)); + mali_core_subsystem_schedule(subsystem); +} +#endif + +/* Is used by internal function: + mali_core_irq_handler_bottom_half + mali_core_session_add_job +*/ +/* Must hold subsystem_mutex before entering this function */ +static void mali_core_subsystem_schedule(mali_core_subsystem * subsystem) +{ + mali_core_renderunit *core, *tmp; + mali_core_session *session; + mali_core_job *job; + + MALI_DEBUG_PRINT(5, ("Core: subsystem_schedule: %s\n", subsystem->name )) ; + + MALI_ASSERT_MUTEX_IS_GRABBED(subsystem); + + /* First check that there are sessions with jobs waiting to run */ + if ( 0 == subsystem->awaiting_sessions_sum_all_priorities) + { + MALI_DEBUG_PRINT(6, ("Core: No jobs available for %s\n", subsystem->name) ) ; + return; + } + + /* Returns the session with the highest priority job for the subsystem. NULL if none*/ + session = mali_core_subsystem_get_waiting_session(subsystem); + + if (NULL == session) + { + MALI_DEBUG_PRINT(6, ("Core: Schedule: No runnable job found\n")); + return; + } + + _MALI_OSK_LIST_FOREACHENTRY(core, tmp, &subsystem->renderunit_idle_head, mali_core_renderunit, list) + { +#if USING_MMU + int err = mali_memory_core_mmu_activate_page_table(core->mmu, session->mmu_session, mali_core_subsystem_callback_schedule_wrapper, subsystem); + if (0 == err) + { + /* core points to a core where the MMU page table activation succeeded */ +#endif + /* This will remove the job from queue system */ + job = mali_core_subsystem_release_session_get_job(subsystem, session); + MALI_DEBUG_ASSERT_POINTER(job); + + MALI_DEBUG_PRINT(6, ("Core: Schedule: Got a job 0x%x\n", job)); + +#if USING_MALI_PMM + { + /* Message that there is a job scheduled to run + * NOTE: mali_core_job_start_on_core() can fail to start + * the job for several reasons, but it will move the core + * back to idle which will create the FINISHED message + * so we can still say that the job is SCHEDULED + */ + _mali_uk_pmm_message_s event = { + NULL, + MALI_PMM_EVENT_JOB_SCHEDULED, + 0 }; + event.data = core->pmm_id; + _mali_ukk_pmm_event_message( &event ); + } +#endif + /* This will {remove core from freelist AND start the job on the core}*/ + mali_core_job_start_on_core(job, core); + + MALI_DEBUG_PRINT(6, ("Core: Schedule: Job started, done\n")); + return; +#if USING_MMU + } +#endif + } + MALI_DEBUG_PRINT(6, ("Core: Schedule: Could not activate MMU. Scheduelling postponed to MMU, checking next.\n")); + +#if USING_MALI_PMM + { + /* Message that there are jobs to run */ + _mali_uk_pmm_message_s event = { + NULL, + MALI_PMM_EVENT_JOB_QUEUED, + 0 }; + if( subsystem->core_type == _MALI_GP2 || subsystem->core_type == _MALI_400_GP ) + { + event.data = MALI_PMM_CORE_GP; + } + else + { + /* Check the PP is supported by the PMM */ + MALI_DEBUG_ASSERT( subsystem->core_type == _MALI_200 || subsystem->core_type == _MALI_400_PP ); + /* We state that all PP cores are scheduled to inform the PMM + * that it may need to power something up! + */ + event.data = MALI_PMM_CORE_PP_ALL; + } + _mali_ukk_pmm_event_message( &event ); + } +#endif /* USING_MALI_PMM */ + +} + +/* Is used by external function: + session_begin<> */ +void mali_core_session_begin(mali_core_session * session) +{ + mali_core_subsystem * subsystem; + int i; + + subsystem = session->subsystem; + if ( NULL == subsystem ) + { + MALI_PRINT_ERROR(("Missing data in struct\n")); + return; + } + MALI_DEBUG_PRINT(2, ("Core: session_begin: for %s\n", session->subsystem->name )) ; + + session->magic_nr = SESSION_MAGIC_NR; + + _MALI_OSK_INIT_LIST_HEAD(&session->renderunits_working_head); + + for (i = 0; i < MALI_JOB_QUEUE_SIZE; i++) + { + session->queue[i] = NULL; + } + session->queue_head = 0; + session->queue_tail = 0; + _MALI_OSK_INIT_LIST_HEAD(&session->awaiting_sessions_list); + _MALI_OSK_INIT_LIST_HEAD(&session->all_sessions_list); + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsystem); + _mali_osk_list_add(&session->all_sessions_list, &session->subsystem->all_sessions_head); + +#if MALI_STATE_TRACKING + _mali_osk_atomic_init(&session->jobs_received, 0); + _mali_osk_atomic_init(&session->jobs_returned, 0); + session->pid = _mali_osk_get_pid(); +#endif + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsystem); + + MALI_DEBUG_PRINT(5, ("Core: session_begin: for %s DONE\n", session->subsystem->name) ) ; +} + +#if USING_MMU +static void mali_core_renderunit_stop_bus(mali_core_renderunit* core) +{ + core->subsystem->stop_bus(core); +} +#endif + +void mali_core_session_close(mali_core_session * session) +{ + mali_core_subsystem * subsystem; + mali_core_renderunit *core; + + subsystem = session->subsystem; + MALI_DEBUG_ASSERT_POINTER(subsystem); + + MALI_DEBUG_PRINT(2, ("Core: session_close: for %s\n", session->subsystem->name) ) ; + + /* We must grab subsystem mutex since the list this session belongs to + is owned by the subsystem */ + MALI_CORE_SUBSYSTEM_MUTEX_GRAB( subsystem ); + + /* Remove this session from the global sessionlist */ + _mali_osk_list_delinit(&session->all_sessions_list); + + _mali_osk_list_delinit(&(session->awaiting_sessions_list)); + + /* Return the potensial waiting job to user */ + while ( !mali_job_queue_empty(session) ) + { + /* Queue not empty */ + mali_core_job *job = mali_job_queue_get_job(session); + subsystem->return_job_to_user( job, JOB_STATUS_END_SHUTDOWN ); + subsystem->awaiting_sessions_sum_all_priorities--; + } + + /* Kill active cores working for this session - freeing their jobs + Since the handling of one core also could stop jobs from another core, there is a while loop */ + while ( ! _mali_osk_list_empty(&session->renderunits_working_head) ) + { + core = _MALI_OSK_LIST_ENTRY(session->renderunits_working_head.next, mali_core_renderunit, list); + MALI_DEBUG_PRINT(3, ("Core: session_close: Core was working: %s\n", core->description )) ; + mali_core_renderunit_detach_job_from_core(core, SUBSYSTEM_RESCHEDULE, JOB_STATUS_END_SHUTDOWN ); + } + _MALI_OSK_INIT_LIST_HEAD(&session->renderunits_working_head); /* Not necessary - we will _mali_osk_free session*/ + + MALI_DEBUG_PRINT(5, ("Core: session_close: for %s FINISHED\n", session->subsystem->name )) ; + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE( subsystem ); +} + +/* Must hold subsystem_mutex before entering this function */ +_mali_osk_errcode_t mali_core_session_add_job(mali_core_session * session, mali_core_job *job, mali_core_job **job_return) +{ + mali_core_subsystem * subsystem; + + job->magic_nr = JOB_MAGIC_NR; + MALI_CHECK_SESSION(session); + + subsystem = session->subsystem; + MALI_CHECK_SUBSYSTEM(subsystem); + MALI_ASSERT_MUTEX_IS_GRABBED(subsystem); + + MALI_DEBUG_PRINT(5, ("Core: session_add_job: for %s\n", subsystem->name )) ; + + /* Setting the default value; No job to return */ + MALI_DEBUG_ASSERT_POINTER(job_return); + *job_return = NULL; + + if (mali_job_queue_empty(session)) + { + /* Add session to the wait list only if it didn't already have a job waiting. */ + _mali_osk_list_addtail( &(session->awaiting_sessions_list), &(subsystem->awaiting_sessions_head[job->priority])); + } + + + if (_MALI_OSK_ERR_OK != mali_job_queue_add_job(session, job)) + { + if (mali_job_queue_empty(session)) + { + _mali_osk_list_delinit(&(session->awaiting_sessions_list)); + } + MALI_DEBUG_PRINT(4, ("Core: session_add_job: %s queue is full\n", subsystem->name)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* Continue to add the new job as the next job from this session */ + MALI_DEBUG_PRINT(6, ("Core: session_add_job job=0x%x\n", job)); + + subsystem->awaiting_sessions_sum_all_priorities++; + + mali_core_subsystem_schedule(subsystem); + + MALI_DEBUG_PRINT(6, ("Core: session_add_job: for %s FINISHED\n", session->subsystem->name )) ; + + MALI_SUCCESS; +} + +static void mali_core_job_set_run_time(mali_core_job * job, u64 end_time) + { + u32 time_used_nano_seconds; + + time_used_nano_seconds = end_time - job->start_time; + job->render_time_usecs = time_used_nano_seconds / 1000; +} + +static void mali_core_renderunit_detach_job_from_core(mali_core_renderunit* core, mali_subsystem_reschedule_option reschedule, mali_subsystem_job_end_code end_status) +{ + mali_core_job * job; + mali_core_subsystem * subsystem; + mali_bool already_in_detach_function; + u64 time_now; + + MALI_DEBUG_ASSERT(CORE_IDLE != core->state); + time_now = _mali_osk_time_get_ns(); + job = core->current_job; + subsystem = core->subsystem; + + /* The reset_core() called some lines below might call this detach + * funtion again. To protect the core object from being modified by + * recursive calls, the in_detach_function would track if it is an recursive call + */ + already_in_detach_function = core->in_detach_function; + + + if ( MALI_FALSE == already_in_detach_function ) + { + core->in_detach_function = MALI_TRUE; + if ( NULL != job ) + { + mali_core_job_set_run_time(job, time_now); + core->current_job = NULL; + } + } + + if (JOB_STATUS_END_SEG_FAULT == end_status) + { + subsystem->reset_core( core, MALI_CORE_RESET_STYLE_HARD ); + } + else + { + subsystem->reset_core( core, MALI_CORE_RESET_STYLE_RUNABLE ); + } + + if ( MALI_FALSE == already_in_detach_function ) + { + if ( CORE_IDLE != core->state ) + { + #if MALI_GPU_UTILIZATION + mali_utilization_core_end(time_now); + #endif + mali_core_subsystem_move_core_set_idle(core); + } + + core->in_detach_function = MALI_FALSE; + + if ( SUBSYSTEM_RESCHEDULE == reschedule ) + { + mali_core_subsystem_schedule(subsystem); + } + if ( NULL != job ) + { + core->subsystem->return_job_to_user(job, end_status); + } + } +} + +#if USING_MMU +/* This function intentionally does not release the semaphore. You must run + stop_bus_for_all_cores(), reset_all_cores_on_mmu() and continue_job_handling() + after calling this function, and then call unlock_subsystem() to release the + semaphore. */ + +static void lock_subsystem(struct mali_core_subsystem * subsys) +{ + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys); + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); +} + +/* You must run lock_subsystem() before entering this function, to ensure that + the subsystem mutex is held. + Later, unlock_subsystem() can be called to release the mutex. + + This function only stops cores behind the given MMU, unless "mmu" is NULL, in + which case all cores are stopped. +*/ +static void stop_bus_for_all_cores_on_mmu(struct mali_core_subsystem * subsys, void* mmu) +{ + u32 i; + + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); + MALI_DEBUG_PRINT(2,("Handling: bus stop %s\n", subsys->name )); + for(i=0 ; i < subsys->number_of_cores ; ++i) + { + mali_core_renderunit * core; + core = mali_core_renderunit_get_mali_core_nr(subsys,i); + + /* We stop only cores behind the given MMU, unless MMU is NULL */ + if ( (NULL!=mmu) && (core->mmu != mmu) ) continue; + + if ( CORE_IDLE != core->state ) + { + MALI_DEBUG_PRINT(4, ("Stopping bus on core %s\n", core->description)); + mali_core_renderunit_stop_bus(core); + core->error_recovery = MALI_TRUE; + } + else + { + MALI_DEBUG_PRINT(4,("Core: not active %s\n", core->description )); + } + } + /* Mutex is still being held, to prevent things to happen while we do cleanup */ + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); +} + +/* You must run lock_subsystem() before entering this function, to ensure that + the subsystem mutex is held. + Later, unlock_subsystem() can be called to release the mutex. + + This function only resets cores behind the given MMU, unless "mmu" is NULL, in + which case all cores are reset. +*/ +static void reset_all_cores_on_mmu(struct mali_core_subsystem * subsys, void* mmu) +{ + u32 i; + + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); + MALI_DEBUG_PRINT(3, ("Handling: reset cores from mmu: 0x%x on %s\n", mmu, subsys->name )); + for(i=0 ; i < subsys->number_of_cores ; ++i) + { + mali_core_renderunit * core; + core = mali_core_renderunit_get_mali_core_nr(subsys,i); + + /* We reset only cores behind the given MMU, unless MMU is NULL */ + if ( (NULL!=mmu) && (core->mmu != mmu) ) continue; + + if ( CORE_IDLE != core->state ) + { + MALI_DEBUG_PRINT(4, ("Abort and reset core: %s\n", core->description )); + mali_core_renderunit_detach_job_from_core(core, SUBSYSTEM_WAIT, JOB_STATUS_END_SEG_FAULT); + } + else + { + MALI_DEBUG_PRINT(4, ("Core: not active %s\n", core->description )); + } + } + MALI_DEBUG_PRINT(4, ("Handling: done %s\n", subsys->name )); + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); +} + +/* You must run lock_subsystem() before entering this function, to ensure that + the subsystem mutex is held. + Later, unlock_subsystem() can be called to release the mutex. */ +static void continue_job_handling(struct mali_core_subsystem * subsys) +{ + u32 i, j; + + MALI_DEBUG_PRINT(3, ("Handling: Continue: %s\n", subsys->name )); + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); + + + for(i=0 ; i < subsys->number_of_cores ; ++i) + { + mali_core_renderunit * core; + core = mali_core_renderunit_get_mali_core_nr(subsys,i); + core->error_recovery = MALI_FALSE; + } + + i = subsys->number_of_cores; + j = subsys->awaiting_sessions_sum_all_priorities; + + /* Schedule MIN(nr_waiting_jobs , number of cores) times */ + while( i-- && j--) + { + mali_core_subsystem_schedule(subsys); + } + MALI_DEBUG_PRINT(4, ("Handling: done %s\n", subsys->name )); + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); +} + +/* Unlock the subsystem. */ +static void unlock_subsystem(struct mali_core_subsystem * subsys) +{ + MALI_ASSERT_MUTEX_IS_GRABBED(subsys); + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); +} + +void mali_core_subsystem_broadcast_notification(struct mali_core_subsystem * subsys, mali_core_notification_message message, u32 data) +{ + void * mmu; + mmu = (void*) data; + + switch(message) + { + case MMU_KILL_STEP0_LOCK_SUBSYSTEM: + break; + case MMU_KILL_STEP1_STOP_BUS_FOR_ALL_CORES: + stop_bus_for_all_cores_on_mmu(subsys, mmu); + break; + case MMU_KILL_STEP2_RESET_ALL_CORES_AND_ABORT_THEIR_JOBS: + reset_all_cores_on_mmu(subsys, mmu ); + break; + case MMU_KILL_STEP3_CONTINUE_JOB_HANDLING: + continue_job_handling(subsys); + break; + case MMU_KILL_STEP4_UNLOCK_SUBSYSTEM: + break; + + default: + MALI_PRINT_ERROR(("Illegal message: 0x%x, data: 0x%x\n", (u32)message, data)); + break; + } +} +#endif /* USING_MMU */ + +void job_watchdog_set(mali_core_job * job, u32 watchdog_msecs) +{ + if (watchdog_msecs == 0) job->watchdog_msecs = mali_max_job_runtime; /* use the default */ + else if (watchdog_msecs > WATCHDOG_MSECS_MAX) job->watchdog_msecs = WATCHDOG_MSECS_MAX; /* no larger than max */ + else if (watchdog_msecs < WATCHDOG_MSECS_MIN) job->watchdog_msecs = WATCHDOG_MSECS_MIN; /* not below min */ + else job->watchdog_msecs = watchdog_msecs; +} + +u32 mali_core_hang_check_timeout_get(void) +{ + /* check the value. The user might have set the value outside the allowed range */ + if (mali_hang_check_interval > HANG_CHECK_MSECS_MAX) mali_hang_check_interval = HANG_CHECK_MSECS_MAX; /* cap to max */ + else if (mali_hang_check_interval < HANG_CHECK_MSECS_MIN) mali_hang_check_interval = HANG_CHECK_MSECS_MIN; /* cap to min */ + + /* return the active value */ + return mali_hang_check_interval; +} + +static _mali_osk_errcode_t mali_core_irq_handler_upper_half (void * data) +{ + mali_core_renderunit *core; + u32 has_pending_irq; + + core = (mali_core_renderunit * )data; + + if(core && (CORE_OFF == core->state)) + { + MALI_SUCCESS; + } + + if ( (NULL == core) || + (NULL == core->subsystem) || + (NULL == core->subsystem->irq_handler_upper_half) ) + { + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + MALI_CHECK_CORE(core); + MALI_CHECK_SUBSYSTEM(core->subsystem); + + has_pending_irq = core->subsystem->irq_handler_upper_half(core); + + if ( has_pending_irq ) + { + _mali_osk_irq_schedulework( core->irq ) ; + MALI_SUCCESS; + } + + if (mali_benchmark) MALI_SUCCESS; + + MALI_ERROR(_MALI_OSK_ERR_FAULT); +} + +static void mali_core_irq_handler_bottom_half ( void *data ) +{ + mali_core_renderunit *core; + mali_core_subsystem* subsystem; + + mali_subsystem_job_end_code job_status; + + core = (mali_core_renderunit * )data; + + MALI_CHECK_CORE(core); + subsystem = core->subsystem; + MALI_CHECK_SUBSYSTEM(subsystem); + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB( subsystem ); + if ( CORE_IDLE == core->state || CORE_OFF == core->state ) goto end_function; + + MALI_DEBUG_PRINT(5, ("IRQ: handling irq from core %s\n", core->description )) ; + + _mali_osk_cache_flushall(); + + /* This function must also update the job status flag */ + job_status = subsystem->irq_handler_bottom_half( core ); + + /* Retval is nonzero if the job is finished. */ + if ( JOB_STATUS_CONTINUE_RUN != job_status ) + { + mali_core_renderunit_detach_job_from_core(core, SUBSYSTEM_RESCHEDULE, job_status); + } + else + { + switch ( core->state ) + { + case CORE_WATCHDOG_TIMEOUT: + MALI_DEBUG_PRINT(2, ("Watchdog SW Timeout of job from core: %s\n", core->description )); + mali_core_renderunit_detach_job_from_core(core, SUBSYSTEM_RESCHEDULE, JOB_STATUS_END_TIMEOUT_SW ); + break; + + case CORE_POLL: + MALI_DEBUG_PRINT(5, ("Poll core: %s\n", core->description )) ; + core->state = CORE_WORKING; + _mali_osk_timer_add( core->timer, 1); + break; + + default: + MALI_DEBUG_PRINT(4, ("IRQ: The job on the core continue to run: %s\n", core->description )) ; + break; + } + } +end_function: + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsystem); +} + +void subsystem_flush_mapped_mem_cache(void) +{ + _mali_osk_cache_flushall(); + _mali_osk_mem_barrier(); +} + +#if USING_MALI_PMM + +_mali_osk_errcode_t mali_core_subsystem_signal_power_down(mali_core_subsystem *subsys, u32 mali_core_nr, mali_bool immediate_only) +{ + mali_core_renderunit * core = NULL; + + MALI_CHECK_SUBSYSTEM(subsys); + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys); + + /* It is possible that this signal funciton can be called during a driver exit, + * and so the requested core may now be destroyed. (This is due to us not having + * the subsys lock before signalling power down). + * mali_core_renderunit_get_mali_core_nr() will report a Mali ERR because + * the core number is out of range (which is a valid error in other cases). + * So instead we check here (now that we have the subsys lock) and let the + * caller cope with the core get failure and check that the core has + * been unregistered in the PMM as part of its destruction. + */ + if ( subsys->number_of_cores > mali_core_nr ) + { + core = mali_core_renderunit_get_mali_core_nr(subsys, mali_core_nr); + } + + if ( NULL == core ) + { + /* Couldn't find the core */ + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + MALI_DEBUG_PRINT( 1, ("Core: Failed to find core to power down\n") ); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + else if ( core->state != CORE_IDLE ) + { + /* When powering down we either set a pending power down flag here so we + * can power down cleanly after the job completes or we don't set the + * flag if we have been asked to only do a power down right now + * In either case, return that the core is busy + */ + if ( !immediate_only ) core->pend_power_down = MALI_TRUE; + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + MALI_DEBUG_PRINT( 5, ("Core: No idle core to power down\n") ); + MALI_ERROR(_MALI_OSK_ERR_BUSY); + } + + /* Shouldn't have a pending power down flag set */ + MALI_DEBUG_ASSERT( !core->pend_power_down ); + + /* Move core to off queue */ + mali_core_subsystem_move_core_set_off(core); + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_core_subsystem_signal_power_up(mali_core_subsystem *subsys, u32 mali_core_nr, mali_bool queue_only) +{ + mali_core_renderunit * core; + + MALI_CHECK_SUBSYSTEM(subsys); + MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys); + + core = mali_core_renderunit_get_mali_core_nr(subsys, mali_core_nr); + + if( core == NULL ) + { + /* Couldn't find the core */ + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + MALI_DEBUG_PRINT( 1, ("Core: Failed to find core to power up\n") ); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + else if( core->state != CORE_OFF ) + { + /* This will usually happen because we are trying to cancel a pending power down */ + core->pend_power_down = MALI_FALSE; + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + MALI_DEBUG_PRINT( 1, ("Core: No powered off core to power up (cancelled power down?)\n") ); + MALI_ERROR(_MALI_OSK_ERR_BUSY); + } + + /* Shouldn't have a pending power down set */ + MALI_DEBUG_ASSERT( !core->pend_power_down ); + + /* Move core to idle queue */ + mali_core_subsystem_move_core_set_idle(core); + + if( !queue_only ) + { + /* Reset MMU & core - core must be idle to allow this */ +#if USING_MMU + if ( NULL!=core->mmu ) + { +#if defined(USING_MALI200) + if (core->pmm_id != MALI_PMM_CORE_PP0) + { +#endif + mali_kernel_mmu_reset(core->mmu); +#if defined(USING_MALI200) + } +#endif + + } +#endif /* USING_MMU */ + subsys->reset_core( core, MALI_CORE_RESET_STYLE_RUNABLE ); + } + + /* Need to schedule work to start on this core */ + mali_core_subsystem_schedule(subsys); + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys); + + MALI_SUCCESS; +} + +#endif /* USING_MALI_PMM */ + +#if MALI_STATE_TRACKING +u32 mali_core_renderunit_dump_state(mali_core_subsystem* subsystem, char *buf, u32 size) +{ + u32 i, len = 0; + mali_core_renderunit *core; + mali_core_renderunit *tmp_core; + + mali_core_session* session; + mali_core_session* tmp_session; + + if (0 >= size) + { + return 0; + } + + MALI_CORE_SUBSYSTEM_MUTEX_GRAB( subsystem ); + + len += _mali_osk_snprintf(buf + len, size - len, "Subsystem:\n"); + len += _mali_osk_snprintf(buf + len, size - len, " Name: %s\n", subsystem->name); + + for (i = 0; i < subsystem->number_of_cores; i++) + { + len += _mali_osk_snprintf(buf + len, size - len, " Core: #%u\n", + subsystem->mali_core_array[i]->core_number); + len += _mali_osk_snprintf(buf + len, size - len, " Description: %s\n", + subsystem->mali_core_array[i]->description); + switch(subsystem->mali_core_array[i]->state) + { + case CORE_IDLE: + len += _mali_osk_snprintf(buf + len, size - len, " State: CORE_IDLE\n"); + break; + case CORE_WORKING: + len += _mali_osk_snprintf(buf + len, size - len, " State: CORE_WORKING\n"); + break; + case CORE_WATCHDOG_TIMEOUT: + len += _mali_osk_snprintf(buf + len, size - len, " State: CORE_WATCHDOG_TIMEOUT\n"); + break; + case CORE_POLL: + len += _mali_osk_snprintf(buf + len, size - len, " State: CORE_POLL\n"); + break; + case CORE_HANG_CHECK_TIMEOUT: + len += _mali_osk_snprintf(buf + len, size - len, " State: CORE_HANG_CHECK_TIMEOUT\n"); + break; + case CORE_OFF: + len += _mali_osk_snprintf(buf + len, size - len, " State: CORE_OFF\n"); + break; + default: + len += _mali_osk_snprintf(buf + len, size - len, " State: Unknown (0x%X)\n", + subsystem->mali_core_array[i]->state); + break; + } + len += _mali_osk_snprintf(buf + len, size - len, " Current job: 0x%X\n", + (u32)(subsystem->mali_core_array[i]->current_job)); + if (subsystem->mali_core_array[i]->current_job) + { + u64 time_used_nano_seconds; + u32 time_used_micro_seconds; + u64 time_now = _mali_osk_time_get_ns(); + + time_used_nano_seconds = time_now - subsystem->mali_core_array[i]->current_job->start_time; + time_used_micro_seconds = ((u32)(time_used_nano_seconds)) / 1000; + + len += _mali_osk_snprintf(buf + len, size - len, " Current job session: 0x%X\n", + subsystem->mali_core_array[i]->current_job->session); + len += _mali_osk_snprintf(buf + len, size - len, " Current job number: %d\n", + subsystem->mali_core_array[i]->current_job->job_nr); + len += _mali_osk_snprintf(buf + len, size - len, " Current job render_time micro seconds: %d\n", + time_used_micro_seconds ); + len += _mali_osk_snprintf(buf + len, size - len, " Current job start time micro seconds: %d\n", + (u32) (subsystem->mali_core_array[i]->current_job->start_time >>10) ); + } + len += _mali_osk_snprintf(buf + len, size - len, " Core version: 0x%X\n", + subsystem->mali_core_array[i]->core_version); +#if USING_MALI_PMM + len += _mali_osk_snprintf(buf + len, size - len, " PMM id: 0x%X\n", + subsystem->mali_core_array[i]->pmm_id); + len += _mali_osk_snprintf(buf + len, size - len, " Power down requested: %s\n", + subsystem->mali_core_array[i]->pend_power_down ? "TRUE" : "FALSE"); +#endif + } + + len += _mali_osk_snprintf(buf + len, size - len, " Cores on idle list:\n"); + _MALI_OSK_LIST_FOREACHENTRY(core, tmp_core, &subsystem->renderunit_idle_head, mali_core_renderunit, list) + { + len += _mali_osk_snprintf(buf + len, size - len, " Core #%u\n", core->core_number); + } + + len += _mali_osk_snprintf(buf + len, size - len, " Cores on off list:\n"); + _MALI_OSK_LIST_FOREACHENTRY(core, tmp_core, &subsystem->renderunit_off_head, mali_core_renderunit, list) + { + len += _mali_osk_snprintf(buf + len, size - len, " Core #%u\n", core->core_number); + } + + len += _mali_osk_snprintf(buf + len, size - len, " Connected sessions:\n"); + _MALI_OSK_LIST_FOREACHENTRY(session, tmp_session, &subsystem->all_sessions_head, mali_core_session, all_sessions_list) + { + len += _mali_osk_snprintf(buf + len, size - len, + " Session 0x%X:\n", (u32)session); + len += _mali_osk_snprintf(buf + len, size - len, + " Queue depth: %u\n", mali_job_queue_size(session)); + len += _mali_osk_snprintf(buf + len, size - len, + " First waiting job: 0x%p\n", session->queue[session->queue_head]); + len += _mali_osk_snprintf(buf + len, size - len, " Notification queue: %s\n", + _mali_osk_notification_queue_is_empty(session->notification_queue) ? "EMPTY" : "NON-EMPTY"); + len += _mali_osk_snprintf(buf + len, size - len, + " Jobs received:%4d\n", _mali_osk_atomic_read(&session->jobs_received)); + len += _mali_osk_snprintf(buf + len, size - len, + " Jobs started :%4d\n", _mali_osk_atomic_read(&session->jobs_started)); + len += _mali_osk_snprintf(buf + len, size - len, + " Jobs ended :%4d\n", _mali_osk_atomic_read(&session->jobs_ended)); + len += _mali_osk_snprintf(buf + len, size - len, + " Jobs returned:%4d\n", _mali_osk_atomic_read(&session->jobs_returned)); + len += _mali_osk_snprintf(buf + len, size - len, " PID: %d\n", session->pid); + } + + len += _mali_osk_snprintf(buf + len, size - len, " Waiting sessions sum all priorities: %u\n", + subsystem->awaiting_sessions_sum_all_priorities); + for (i = 0; i < PRIORITY_LEVELS; i++) + { + len += _mali_osk_snprintf(buf + len, size - len, " Waiting sessions with priority %u:\n", i); + _MALI_OSK_LIST_FOREACHENTRY(session, tmp_session, &subsystem->awaiting_sessions_head[i], + mali_core_session, awaiting_sessions_list) + { + len += _mali_osk_snprintf(buf + len, size - len, " Session 0x%X:\n", (u32)session); + len += _mali_osk_snprintf(buf + len, size - len, " Waiting job: 0x%X\n", + (u32)session->queue[session->queue_head]); + len += _mali_osk_snprintf(buf + len, size - len, " Notification queue: %s\n", + _mali_osk_notification_queue_is_empty(session->notification_queue) ? "EMPTY" : "NON-EMPTY"); + } + } + + MALI_CORE_SUBSYSTEM_MUTEX_RELEASE( subsystem ); + return len; +} +#endif diff --git a/drivers/gpu/arm/mali/common/mali_kernel_rendercore.h b/drivers/gpu/arm/mali/common/mali_kernel_rendercore.h new file mode 100644 index 000000000000..6d0c11dd1885 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_rendercore.h @@ -0,0 +1,565 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_RENDERCORE_H__ +#define __MALI_RENDERCORE_H__ + +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_subsystem.h" + +#define PRIORITY_LEVELS 3 +#define PRIORITY_MAX 0 +#define PRIORITY_MIN (PRIORITY_MAX+PRIORITY_LEVELS-1) + +/* This file contains what we need in kernel for all core types. */ + +typedef enum +{ + CORE_IDLE, /**< Core is ready for a new job */ + CORE_WORKING, /**< Core is working on a job */ + CORE_WATCHDOG_TIMEOUT, /**< Core is working but it has timed out */ + CORE_POLL, /**< Poll timer triggered, pending handling */ + CORE_HANG_CHECK_TIMEOUT,/**< Timeout for hang detection */ + CORE_OFF /**< Core is powered off */ +} mali_core_status; + +typedef enum +{ + SUBSYSTEM_RESCHEDULE, + SUBSYSTEM_WAIT +} mali_subsystem_reschedule_option; + +typedef enum +{ + MALI_CORE_RESET_STYLE_RUNABLE, + MALI_CORE_RESET_STYLE_DISABLE, + MALI_CORE_RESET_STYLE_HARD +} mali_core_reset_style; + +typedef enum +{ + JOB_STATUS_CONTINUE_RUN = 0x01, + JOB_STATUS_END_SUCCESS = 1<<(16+0), + JOB_STATUS_END_OOM = 1<<(16+1), + JOB_STATUS_END_ABORT = 1<<(16+2), + JOB_STATUS_END_TIMEOUT_SW = 1<<(16+3), + JOB_STATUS_END_HANG = 1<<(16+4), + JOB_STATUS_END_SEG_FAULT = 1<<(16+5), + JOB_STATUS_END_ILLEGAL_JOB = 1<<(16+6), + JOB_STATUS_END_UNKNOWN_ERR = 1<<(16+7), + JOB_STATUS_END_SHUTDOWN = 1<<(16+8), + JOB_STATUS_END_SYSTEM_UNUSABLE = 1<<(16+9) +} mali_subsystem_job_end_code; + + +struct mali_core_job; +struct mali_core_subsystem; +struct mali_core_renderunit; +struct mali_core_session; + +/* We have one of these subsystems for each core type */ +typedef struct mali_core_subsystem +{ + struct mali_core_renderunit ** mali_core_array; /* An array of all cores of this type */ + u32 number_of_cores; /* Number of cores in this list */ + + _mali_core_type core_type; + + u32 magic_nr; + + _mali_osk_list_t renderunit_idle_head; /* Idle cores of this type */ + _mali_osk_list_t renderunit_off_head; /* Powered off cores of this type */ + + /* Linked list for each priority of sessions with a job ready for scheduelling */ + _mali_osk_list_t awaiting_sessions_head[PRIORITY_LEVELS]; + u32 awaiting_sessions_sum_all_priorities; + + /* Linked list of all sessions connected to this coretype */ + _mali_osk_list_t all_sessions_head; + + /* Linked list of all sessions connected to this coretype */ + struct _mali_osk_notification_queue_t * notification_queue; + + const char * name; + mali_kernel_subsystem_identifier id; + + /**** Functions registered for this core type. Set during mali_core_init ******/ + /* Start this job on this core. Return MALI_TRUE if the job was started. */ + _mali_osk_errcode_t (*start_job)(struct mali_core_job * job, struct mali_core_renderunit * core); + + /* Check if given core has an interrupt pending. Return MALI_TRUE and set mask to 0 if pending */ + u32 (*irq_handler_upper_half)(struct mali_core_renderunit * core); + + /* This function should check if the interrupt indicates that job was finished. + If so it should update the job-struct, reset the core registers, and return MALI_TRUE, . + If the job is still working after this function it should return MALI_FALSE. + The function must also enable the bits in the interrupt mask for the core. + Called by the bottom half interrupt function. */ + int (*irq_handler_bottom_half)(struct mali_core_renderunit* core); + + /* This function is called from the ioctl function and should return a mali_core_job pointer + to a created mali_core_job object with the data given from userspace */ + _mali_osk_errcode_t (*get_new_job_from_user)(struct mali_core_session * session, void * argument); + + _mali_osk_errcode_t (*suspend_response)(struct mali_core_session * session, void * argument); + + /* This function is called from the ioctl function and should write the necessary data + to userspace telling which job was finished and the status and debuginfo for this job. + The function must also free and cleanup the input job object. */ + void (*return_job_to_user)(struct mali_core_job * job, mali_subsystem_job_end_code end_status); + + /* Is called when a subsystem shuts down. This function needs to + release internal pointers in the core struct, and free the + core struct before returning. + It is not allowed to write to any registers, since this + unmapping is already done. */ + void (*renderunit_delete)(struct mali_core_renderunit * core); + + /* Is called when we want to abort a job that is running on the core. + This is done if program exits while core is running */ + void (*reset_core)(struct mali_core_renderunit * core, mali_core_reset_style style); + + /* Is called when the rendercore wants the core to give an interrupt */ + void (*probe_core_irq_trigger)(struct mali_core_renderunit* core); + + /* Is called when the irq probe wants the core to acknowledge an interrupt from the hw */ + _mali_osk_errcode_t (*probe_core_irq_acknowledge)(struct mali_core_renderunit* core); + + /* Called when the rendercore want to issue a bus stop request to a core */ + void (*stop_bus)(struct mali_core_renderunit* core); +} mali_core_subsystem; + + +/* Per core data. This must be embedded into each core type internal core info. */ +typedef struct mali_core_renderunit +{ + struct mali_core_subsystem * subsystem; /* The core belongs to this subsystem */ + _mali_osk_list_t list; /* Is always in subsystem->idle_list OR session->renderunits_working */ + mali_core_status state; + mali_bool error_recovery; /* Indicates if the core is waiting for external help to recover (typically the MMU) */ + mali_bool in_detach_function; + struct mali_core_job * current_job; /* Current job being processed on this core ||NULL */ + u32 magic_nr; + _mali_osk_timer_t * timer; + _mali_osk_timer_t * timer_hang_detection; + + mali_io_address registers_mapped; /* IO-mapped pointer to registers */ + u32 registers_base_addr; /* Base addres of the registers */ + u32 size; /* The size of registers_mapped */ + const char * description; /* Description of this core. */ + u32 irq_nr; /* The IRQ nr for this core */ + u32 core_version; +#if USING_MMU + u32 mmu_id; + void * mmu; /* The MMU this rendercore is behind.*/ +#endif +#if USING_MALI_PMM + mali_pmm_core_id pmm_id; /* The PMM core id */ + mali_bool pend_power_down; /* Power down is requested */ +#endif + + u32 core_number; /* 0 for first detected core of this type, 1 for second and so on */ + + _mali_osk_irq_t *irq; +} mali_core_renderunit; + + +#define MALI_JOB_QUEUE_SIZE 8 +/* Per open FILE data. */ +/* You must held subsystem->mutex before any transactions to this datatype. */ +typedef struct mali_core_session +{ + struct mali_core_subsystem * subsystem; /* The session belongs to this subsystem */ + _mali_osk_list_t renderunits_working_head; /* List of renderunits working for this session */ + struct mali_core_job *queue[MALI_JOB_QUEUE_SIZE]; /* The next job from this session to run */ + int queue_head; + int queue_tail; + int queue_size; + + _mali_osk_list_t awaiting_sessions_list; /* Linked list of sessions with jobs, for each priority */ + _mali_osk_list_t all_sessions_list; /* Linked list of all sessions on the system. */ + + _mali_osk_notification_queue_t * notification_queue; /* Messages back to Base in userspace*/ +#if USING_MMU + struct mali_session_data * mmu_session; /* The session associated with the MMU page tables for this core */ +#endif + u32 magic_nr; +#if MALI_STATE_TRACKING + _mali_osk_atomic_t jobs_received; + _mali_osk_atomic_t jobs_started; + _mali_osk_atomic_t jobs_ended; + _mali_osk_atomic_t jobs_returned; + u32 pid; +#endif +} mali_core_session; + +/* This must be embedded into a specific mali_core_job struct */ +/* use this macro to get spesific mali_core_job: container_of(ptr, type, member)*/ +typedef struct mali_core_job +{ + _mali_osk_list_t list; /* Linked list of jobs. Used by struct mali_core_session */ + struct mali_core_session *session; + u32 magic_nr; + u32 priority; + u32 watchdog_msecs; + u32 render_time_usecs ; + u64 start_time; + unsigned long watchdog_jiffies; + u32 abort_id; + u32 job_nr; + _mali_uk_start_job_flags flags; +} mali_core_job; + +MALI_STATIC_INLINE mali_bool mali_job_queue_empty(mali_core_session *session) +{ + if (0 == session->queue_size) + { + return MALI_TRUE; + } + return MALI_FALSE; +} + +MALI_STATIC_INLINE mali_bool mali_job_queue_full(mali_core_session *session) +{ + if (MALI_JOB_QUEUE_SIZE == session->queue_size) + { + return MALI_TRUE; + } + return MALI_FALSE; +} + + +MALI_STATIC_INLINE _mali_osk_errcode_t mali_job_queue_add_job(mali_core_session *session, struct mali_core_job *job) +{ + if (mali_job_queue_full(session)) + { + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + session->queue[session->queue_tail] = job; + session->queue_tail = (session->queue_tail + 1) % MALI_JOB_QUEUE_SIZE; + session->queue_size++; + + MALI_SUCCESS; +} + +MALI_STATIC_INLINE struct mali_core_job *mali_job_queue_get_job(mali_core_session *session) +{ + struct mali_core_job *job; + MALI_DEBUG_ASSERT(!mali_job_queue_empty(session)); + + job = session->queue[session->queue_head]; + + MALI_DEBUG_ASSERT_POINTER(job); + + session->queue[session->queue_head] = NULL; + session->queue_head = (session->queue_head + 1) % MALI_JOB_QUEUE_SIZE; + session->queue_size--; + + return job; +} + +MALI_STATIC_INLINE u32 mali_job_queue_size(mali_core_session *session) +{ + return (u32)(session->queue_size); +} + +MALI_STATIC_INLINE struct mali_core_job *mali_job_queue_abort_job(mali_core_session *session, u32 abort_id) +{ + int i; + int n; + struct mali_core_job *job = NULL; + + for (i = session->queue_head, n = session->queue_size; n > 0; n--, i = (i+1)%MALI_JOB_QUEUE_SIZE) + { + if (session->queue[i]->abort_id == abort_id) + { + /* Remove job from queue */ + job = session->queue[i]; + session->queue[i] = NULL; + + session->queue_size -= 1; + n--; + break; + } + } + if (NULL == job) + { + return NULL; + } + + /* Rearrange queue */ + while (n > 0) + { + int next = (i + 1) % MALI_JOB_QUEUE_SIZE; + session->queue[i] = session->queue[next]; + i = next; + n--; + } + session->queue_tail = i; + + return job; +} + + +/* + * The rendercode subsystem is included in the subsystems[] array. + */ +extern struct mali_kernel_subsystem mali_subsystem_rendercore; + +void subsystem_flush_mapped_mem_cache(void); + + +#define SUBSYSTEM_MAGIC_NR 0xdeadbeef +#define CORE_MAGIC_NR 0xcafebabe +#define SESSION_MAGIC_NR 0xbabe1234 +#define JOB_MAGIC_NR 0x0123abcd + + +#define MALI_CHECK_SUBSYSTEM(subsystem)\ + do { \ + if ( SUBSYSTEM_MAGIC_NR != subsystem->magic_nr) MALI_PRINT_ERROR(("Wrong magic number"));\ + } while (0) + +#define MALI_CHECK_CORE(CORE)\ + do { \ + if ( CORE_MAGIC_NR != CORE->magic_nr) MALI_PRINT_ERROR(("Wrong magic number"));\ +} while (0) + +#define MALI_CHECK_SESSION(SESSION)\ + do { \ + if ( SESSION_MAGIC_NR != SESSION->magic_nr) MALI_PRINT_ERROR(("Wrong magic number"));\ +} while (0) + +#define MALI_CHECK_JOB(JOB)\ + do { \ + if ( JOB_MAGIC_NR != JOB->magic_nr) MALI_PRINT_ERROR(("Wrong magic number"));\ +} while (0) + + +/* Check if job_a has higher priority than job_b */ +MALI_STATIC_INLINE int job_has_higher_priority(mali_core_job * job_a, mali_core_job * job_b) +{ + /* The lowest number has the highest priority */ + return (int) (job_a->priority < job_b->priority); +} + +MALI_STATIC_INLINE void job_priority_set(mali_core_job * job, u32 priority) +{ + if (priority > PRIORITY_MIN) job->priority = PRIORITY_MIN; + else job->priority = priority; +} + +void job_watchdog_set(mali_core_job * job, u32 watchdog_msecs); + +/* For use by const default register settings (e.g. set these after reset) */ +typedef struct register_address_and_value +{ + u32 address; + u32 value; +} register_address_and_value ; + + +/* For use by dynamic default register settings (e.g. set these after reset) */ +typedef struct register_address_and_value_list +{ + _mali_osk_list_t list; + register_address_and_value item; +} register_address_and_value_list ; + +/* Used if the user wants to set a continious block of registers */ +typedef struct register_array_user +{ + u32 entries_in_array; + u32 start_address; + void __user * reg_array; +}register_array_user; + + +#define MALI_CORE_SUBSYSTEM_MUTEX_GRAB(subsys) \ + do { \ + MALI_DEBUG_PRINT(5, ("MUTEX: GRAB %s() %d on %s\n",__FUNCTION__, __LINE__, subsys->name)); \ + _mali_osk_lock_wait( rendercores_global_mutex, _MALI_OSK_LOCKMODE_RW); \ + MALI_DEBUG_PRINT(5, ("MUTEX: GRABBED %s() %d on %s\n",__FUNCTION__, __LINE__, subsys->name)); \ + if ( SUBSYSTEM_MAGIC_NR != subsys->magic_nr ) MALI_PRINT_ERROR(("Wrong magic number"));\ + rendercores_global_mutex_is_held = 1; \ + rendercores_global_mutex_owner = _mali_osk_get_tid(); \ + } while (0) ; + +#define MALI_CORE_SUBSYSTEM_MUTEX_RELEASE(subsys) \ + do { \ + MALI_DEBUG_PRINT(5, ("MUTEX: RELEASE %s() %d on %s\n",__FUNCTION__, __LINE__, subsys->name)); \ + rendercores_global_mutex_is_held = 0; \ + rendercores_global_mutex_owner = 0; \ + if ( SUBSYSTEM_MAGIC_NR != subsys->magic_nr ) MALI_PRINT_ERROR(("Wrong magic number"));\ + _mali_osk_lock_signal( rendercores_global_mutex, _MALI_OSK_LOCKMODE_RW); \ + MALI_DEBUG_PRINT(5, ("MUTEX: RELEASED %s() %d on %s\n",__FUNCTION__, __LINE__, subsys->name)); \ + if ( SUBSYSTEM_MAGIC_NR != subsys->magic_nr ) MALI_PRINT_ERROR(("Wrong magic number"));\ + } while (0) ; + + +#define MALI_ASSERT_MUTEX_IS_GRABBED(input_pointer)\ + do { \ + if ( 0 == rendercores_global_mutex_is_held ) MALI_PRINT_ERROR(("ASSERT MUTEX SHOULD BE GRABBED"));\ + if ( SUBSYSTEM_MAGIC_NR != input_pointer->magic_nr ) MALI_PRINT_ERROR(("Wrong magic number"));\ + if ( rendercores_global_mutex_owner != _mali_osk_get_tid() ) MALI_PRINT_ERROR(("Owner mismatch"));\ + } while (0) + +MALI_STATIC_INLINE _mali_osk_errcode_t mali_core_renderunit_register_rw_check(mali_core_renderunit *core, + u32 relative_address) +{ +#if USING_MALI_PMM + if( core->state == CORE_OFF ) + { + MALI_PRINT_ERROR(("Core is OFF during access: Core: %s Addr: 0x%04X\n", + core->description,relative_address)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } +#endif + + MALI_DEBUG_ASSERT((relative_address & 0x03) == 0); + + if (mali_benchmark) MALI_ERROR(_MALI_OSK_ERR_FAULT); + + MALI_DEBUG_CODE(if (relative_address >= core->size) + { + MALI_PRINT_ERROR(("Trying to access illegal register: 0x%04x in core: %s", + relative_address, core->description)); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + }) + + MALI_SUCCESS; +} + + +MALI_STATIC_INLINE u32 mali_core_renderunit_register_read(struct mali_core_renderunit *core, u32 relative_address) +{ + u32 read_val; + + if(_MALI_OSK_ERR_FAULT == mali_core_renderunit_register_rw_check(core, relative_address)) + return 0xDEADBEEF; + + read_val = _mali_osk_mem_ioread32(core->registers_mapped, relative_address); + + MALI_DEBUG_PRINT(6, ("Core: renderunit_register_read: Core:%s Addr:0x%04X Val:0x%08x\n", + core->description,relative_address, read_val)); + + return read_val; +} + +MALI_STATIC_INLINE void mali_core_renderunit_register_read_array(struct mali_core_renderunit *core, + u32 relative_address, + u32 * result_array, + u32 nr_of_regs) +{ + /* NOTE Do not use burst reads against the registers */ + u32 i; + + MALI_DEBUG_PRINT(6, ("Core: renderunit_register_read_array: Core:%s Addr:0x%04X Nr_regs: %u\n", + core->description,relative_address, nr_of_regs)); + + for(i=0; i<nr_of_regs; ++i) + { + result_array[i] = mali_core_renderunit_register_read(core, relative_address + i*4); + } +} + +/* + * Write to a core register, and bypass implied memory barriers. + * + * On some systems, _mali_osk_mem_iowrite32() implies a memory barrier. This + * can be a performance problem when doing many writes in sequence. + * + * When using this function, ensure proper barriers are put in palce. Most + * likely a _mali_osk_mem_barrier() is needed after all related writes are + * completed. + * + */ +MALI_STATIC_INLINE void mali_core_renderunit_register_write_relaxed(mali_core_renderunit *core, + u32 relative_address, + u32 new_val) +{ + if(_MALI_OSK_ERR_FAULT == mali_core_renderunit_register_rw_check(core, relative_address)) + return; + + MALI_DEBUG_PRINT(6, ("mali_core_renderunit_register_write_relaxed: Core:%s Addr:0x%04X Val:0x%08x\n", + core->description,relative_address, new_val)); + + _mali_osk_mem_iowrite32_relaxed(core->registers_mapped, relative_address, new_val); +} + +MALI_STATIC_INLINE void mali_core_renderunit_register_write(struct mali_core_renderunit *core, + u32 relative_address, + u32 new_val) +{ + MALI_DEBUG_PRINT(6, ("mali_core_renderunit_register_write: Core:%s Addr:0x%04X Val:0x%08x\n", + core->description,relative_address, new_val)); + + if(_MALI_OSK_ERR_FAULT == mali_core_renderunit_register_rw_check(core, relative_address)) + return; + + _mali_osk_mem_iowrite32(core->registers_mapped, relative_address, new_val); +} + +MALI_STATIC_INLINE void mali_core_renderunit_register_write_array(struct mali_core_renderunit *core, + u32 relative_address, + u32 * write_array, + u32 nr_of_regs) +{ + u32 i; + MALI_DEBUG_PRINT(6, ("Core: renderunit_register_write_array: Core:%s Addr:0x%04X Nr_regs: %u\n", + core->description,relative_address, nr_of_regs)); + + /* Do not use burst writes against the registers */ + for( i = 0; i< nr_of_regs; i++) + { + mali_core_renderunit_register_write_relaxed(core, relative_address + i*4, write_array[i]); + } +} + +_mali_osk_errcode_t mali_core_renderunit_init(struct mali_core_renderunit * core); +void mali_core_renderunit_term(struct mali_core_renderunit * core); +int mali_core_renderunit_map_registers(struct mali_core_renderunit *core); +void mali_core_renderunit_unmap_registers(struct mali_core_renderunit *core); +int mali_core_renderunit_irq_handler_add(struct mali_core_renderunit *core); +mali_core_renderunit * mali_core_renderunit_get_mali_core_nr(mali_core_subsystem *subsys, u32 mali_core_nr); + +int mali_core_subsystem_init(struct mali_core_subsystem * new_subsys); +#if USING_MMU +void mali_core_subsystem_attach_mmu(mali_core_subsystem* subsys); +#endif +int mali_core_subsystem_register_renderunit(struct mali_core_subsystem * subsys, struct mali_core_renderunit * core); +int mali_core_subsystem_system_info_fill(mali_core_subsystem* subsys, _mali_system_info* info); +void mali_core_subsystem_cleanup(struct mali_core_subsystem * subsys); +#if USING_MMU +void mali_core_subsystem_broadcast_notification(struct mali_core_subsystem * subsys, mali_core_notification_message message, u32 data); +#endif +void mali_core_session_begin(mali_core_session *session); +void mali_core_session_close(mali_core_session * session); +int mali_core_session_add_job(mali_core_session * session, mali_core_job *job, mali_core_job **job_return); +u32 mali_core_hang_check_timeout_get(void); + +_mali_osk_errcode_t mali_core_subsystem_ioctl_start_job(mali_core_session * session, void *job_data); +_mali_osk_errcode_t mali_core_subsystem_ioctl_number_of_cores_get(mali_core_session * session, u32 *number_of_cores); +_mali_osk_errcode_t mali_core_subsystem_ioctl_core_version_get(mali_core_session * session, _mali_core_version *version); +_mali_osk_errcode_t mali_core_subsystem_ioctl_suspend_response(mali_core_session * session, void* argument); +void mali_core_subsystem_ioctl_abort_job(mali_core_session * session, u32 id); + +#if USING_MALI_PMM +_mali_osk_errcode_t mali_core_subsystem_signal_power_down(mali_core_subsystem *subsys, u32 mali_core_nr, mali_bool immediate_only); +_mali_osk_errcode_t mali_core_subsystem_signal_power_up(mali_core_subsystem *subsys, u32 mali_core_nr, mali_bool queue_only); +#endif + +#if MALI_STATE_TRACKING +u32 mali_core_renderunit_dump_state(mali_core_subsystem* subsystem, char *buf, u32 size); +#endif + +#endif /* __MALI_RENDERCORE_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_session_manager.h b/drivers/gpu/arm/mali/common/mali_kernel_session_manager.h new file mode 100644 index 000000000000..821f0fd906b0 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_session_manager.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_SESSION_MANAGER_H__ +#define __MALI_KERNEL_SESSION_MANAGER_H__ + +/* Incomplete struct to pass around pointers to it */ +struct mali_session_data; + +void * mali_kernel_session_manager_slot_get(struct mali_session_data * session, int id); + +#endif /* __MALI_KERNEL_SESSION_MANAGER_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_subsystem.h b/drivers/gpu/arm/mali/common/mali_kernel_subsystem.h new file mode 100644 index 000000000000..1828913a9d80 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_subsystem.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_kernel_subsystem.h + */ + +#ifndef __MALI_KERNEL_SUBSYSTEM_H__ +#define __MALI_KERNEL_SUBSYSTEM_H__ + +#include "mali_osk.h" +#include "mali_uk_types.h" +#include "mali_kernel_common.h" +#include "mali_kernel_session_manager.h" + +/* typedefs of the datatypes used in the hook functions */ +typedef void * mali_kernel_subsystem_session_slot; +typedef int mali_kernel_subsystem_identifier; +typedef _mali_osk_errcode_t (*mali_kernel_resource_registrator)(_mali_osk_resource_t *); + +/** + * Broadcast notification messages + */ +typedef enum mali_core_notification_message +{ + MMU_KILL_STEP0_LOCK_SUBSYSTEM, /**< Request to lock subsystem */ + MMU_KILL_STEP1_STOP_BUS_FOR_ALL_CORES, /**< Request to stop all buses */ + MMU_KILL_STEP2_RESET_ALL_CORES_AND_ABORT_THEIR_JOBS, /**< Request kill all jobs, and not start more jobs */ + MMU_KILL_STEP3_CONTINUE_JOB_HANDLING, /**< Request to continue with new jobs on all cores */ + MMU_KILL_STEP4_UNLOCK_SUBSYSTEM /**< Request to unlock subsystem */ +} mali_core_notification_message; + +/** + * A function pointer can be NULL if the subsystem isn't interested in the event. + */ +typedef struct mali_kernel_subsystem +{ + /* subsystem control */ + _mali_osk_errcode_t (*startup)(mali_kernel_subsystem_identifier id); /**< Called during module load or system startup*/ + void (*shutdown)(mali_kernel_subsystem_identifier id); /**< Called during module unload or system shutdown */ + + /** + * Called during module load or system startup. + * Called when all subsystems have reported startup OK and all resources where successfully initialized + */ + _mali_osk_errcode_t (*load_complete)(mali_kernel_subsystem_identifier id); + + /* per subsystem handlers */ + _mali_osk_errcode_t (*system_info_fill)(_mali_system_info* info); /**< Fill info into info struct. MUST allocate memory with kmalloc, since it's kfree'd */ + + /* per session handlers */ + /** + * Informs about a new session. + * slot can be used to track per-session per-subsystem data. + * queue can be used to send events to user space. + * _mali_osk_errcode_t error return value. + */ + _mali_osk_errcode_t (*session_begin)(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot, _mali_osk_notification_queue_t * queue); + /** + * Informs that a session is ending + * slot was the same as given during session_begin + */ + void (*session_end)(struct mali_session_data * mali_session_data, mali_kernel_subsystem_session_slot * slot); + + /* Used by subsystems to send messages to each other. This is the receiving end */ + void (*broadcast_notification)(mali_core_notification_message message, u32 data); + +#if MALI_STATE_TRACKING + /** Dump the current state of the subsystem */ + u32 (*dump_state)(char *buf, u32 size); +#endif +} mali_kernel_subsystem; + +/* functions used by the subsystems to interact with the core */ +/** + * Register a resouce handler + * @param type The resoruce type to register a handler for + * @param handler Pointer to the function handling this resource + * @return _MALI_OSK_ERR_OK on success. Otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t _mali_kernel_core_register_resource_handler(_mali_osk_resource_type_t type, mali_kernel_resource_registrator handler); + +/* function used to interact with other subsystems */ +/** + * Broadcast a message + * Sends a message to all subsystems which have registered a broadcast notification handler + * @param message The message to send + * @param data Message specific extra data + */ +void _mali_kernel_core_broadcast_subsystem_message(mali_core_notification_message message, u32 data); + +#if MALI_STATE_TRACKING +/** + * Tell all subsystems to dump their current state + */ +u32 _mali_kernel_core_dump_state(char *buf, u32 size); +#endif + + +#endif /* __MALI_KERNEL_SUBSYSTEM_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_utilization.c b/drivers/gpu/arm/mali/common/mali_kernel_utilization.c new file mode 100644 index 000000000000..72eb4b0c6090 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_utilization.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_utilization.h" +#include "mali_osk.h" +#include "mali_platform.h" + +/* Define how often to calculate and report GPU utilization, in milliseconds */ +#define MALI_GPU_UTILIZATION_TIMEOUT 1000 + +static _mali_osk_lock_t *time_data_lock; + +static _mali_osk_atomic_t num_running_cores; + +static u64 period_start_time = 0; +static u64 work_start_time = 0; +static u64 accumulated_work_time = 0; + +static _mali_osk_timer_t *utilization_timer = NULL; +static mali_bool timer_running = MALI_FALSE; + + +static void calculate_gpu_utilization(void* arg) +{ + u64 time_now; + u64 time_period; + u32 leading_zeroes; + u32 shift_val; + u32 work_normalized; + u32 period_normalized; + u32 utilization; + + _mali_osk_lock_wait(time_data_lock, _MALI_OSK_LOCKMODE_RW); + + if (accumulated_work_time == 0 && work_start_time == 0) + { + /* Don't reschedule timer, this will be started if new work arrives */ + timer_running = MALI_FALSE; + + _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW); + + /* No work done for this period, report zero usage */ + mali_gpu_utilization_handler(0); + + return; + } + + time_now = _mali_osk_time_get_ns(); + time_period = time_now - period_start_time; + + /* If we are currently busy, update working period up to now */ + if (work_start_time != 0) + { + accumulated_work_time += (time_now - work_start_time); + work_start_time = time_now; + } + + /* + * We have two 64-bit values, a dividend and a divisor. + * To avoid dependencies to a 64-bit divider, we shift down the two values + * equally first. + * We shift the dividend up and possibly the divisor down, making the result X in 256. + */ + + /* Shift the 64-bit values down so they fit inside a 32-bit integer */ + leading_zeroes = _mali_osk_clz((u32)(time_period >> 32)); + shift_val = 32 - leading_zeroes; + work_normalized = (u32)(accumulated_work_time >> shift_val); + period_normalized = (u32)(time_period >> shift_val); + + /* + * Now, we should report the usage in parts of 256 + * this means we must shift up the dividend or down the divisor by 8 + * (we could do a combination, but we just use one for simplicity, + * but the end result should be good enough anyway) + */ + if (period_normalized > 0x00FFFFFF) + { + /* The divisor is so big that it is safe to shift it down */ + period_normalized >>= 8; + } + else + { + /* + * The divisor is so small that we can shift up the dividend, without loosing any data. + * (dividend is always smaller than the divisor) + */ + work_normalized <<= 8; + } + + utilization = work_normalized / period_normalized; + + accumulated_work_time = 0; + period_start_time = time_now; /* starting a new period */ + + _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW); + + _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(MALI_GPU_UTILIZATION_TIMEOUT)); + + mali_gpu_utilization_handler(utilization); +} + + + +_mali_osk_errcode_t mali_utilization_init(void) +{ + time_data_lock = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ|_MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, 0 ); + if (NULL == time_data_lock) + { + return _MALI_OSK_ERR_FAULT; + } + + _mali_osk_atomic_init(&num_running_cores, 0); + + utilization_timer = _mali_osk_timer_init(); + if (NULL == utilization_timer) + { + _mali_osk_lock_term(time_data_lock); + return _MALI_OSK_ERR_FAULT; + } + _mali_osk_timer_setcallback(utilization_timer, calculate_gpu_utilization, NULL); + + return _MALI_OSK_ERR_OK; +} + +void mali_utilization_suspend(void) +{ + if (NULL != utilization_timer) + { + _mali_osk_timer_del(utilization_timer); + timer_running = MALI_FALSE; + } +} + +void mali_utilization_term(void) +{ + if (NULL != utilization_timer) + { + _mali_osk_timer_del(utilization_timer); + timer_running = MALI_FALSE; + _mali_osk_timer_term(utilization_timer); + utilization_timer = NULL; + } + + _mali_osk_atomic_term(&num_running_cores); + + _mali_osk_lock_term(time_data_lock); +} + + + +void mali_utilization_core_start(u64 time_now) +{ + if (_mali_osk_atomic_inc_return(&num_running_cores) == 1) + { + /* + * We went from zero cores working, to one core working, + * we now consider the entire GPU for being busy + */ + + _mali_osk_lock_wait(time_data_lock, _MALI_OSK_LOCKMODE_RW); + + if (time_now < period_start_time) + { + /* + * This might happen if the calculate_gpu_utilization() was able + * to run between the sampling of time_now and us grabbing the lock above + */ + time_now = period_start_time; + } + + work_start_time = time_now; + if (timer_running != MALI_TRUE) + { + timer_running = MALI_TRUE; + period_start_time = work_start_time; /* starting a new period */ + + _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW); + + _mali_osk_timer_add(utilization_timer, _mali_osk_time_mstoticks(MALI_GPU_UTILIZATION_TIMEOUT)); + } + else + { + _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW); + } + } +} + + + +void mali_utilization_core_end(u64 time_now) +{ + if (_mali_osk_atomic_dec_return(&num_running_cores) == 0) + { + /* + * No more cores are working, so accumulate the time we was busy. + */ + _mali_osk_lock_wait(time_data_lock, _MALI_OSK_LOCKMODE_RW); + + if (time_now < work_start_time) + { + /* + * This might happen if the calculate_gpu_utilization() was able + * to run between the sampling of time_now and us grabbing the lock above + */ + time_now = work_start_time; + } + + accumulated_work_time += (time_now - work_start_time); + work_start_time = 0; + + _mali_osk_lock_signal(time_data_lock, _MALI_OSK_LOCKMODE_RW); + } +} diff --git a/drivers/gpu/arm/mali/common/mali_kernel_utilization.h b/drivers/gpu/arm/mali/common/mali_kernel_utilization.h new file mode 100644 index 000000000000..1f605179d8fc --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_utilization.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_UTILIZATION_H__ +#define __MALI_KERNEL_UTILIZATION_H__ + +#include "mali_osk.h" + +/** + * Initialize/start the Mali GPU utilization metrics reporting. + * + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t mali_utilization_init(void); + +/** + * Terminate the Mali GPU utilization metrics reporting + */ +void mali_utilization_term(void); + +/** + * Should be called when a job is about to execute a job + */ +void mali_utilization_core_start(u64 time_now); + +/** + * Should be called to stop the utilization timer during system suspend + */ +void mali_utilization_suspend(void); + +/** + * Should be called when a job has completed executing a job + */ +void mali_utilization_core_end(u64 time_now); + + +#endif /* __MALI_KERNEL_UTILIZATION_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_kernel_vsync.c b/drivers/gpu/arm/mali/common/mali_kernel_vsync.c new file mode 100644 index 000000000000..686f84c7933d --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_kernel_vsync.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2011-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_mali.h" +#include "mali_ukk.h" +/*#include "mali_timestamp.h"*/ + +#if MALI_TIMELINE_PROFILING_ENABLED +#include "mali_kernel_profiling.h" +#endif + +_mali_osk_errcode_t _mali_ukk_vsync_event_report(_mali_uk_vsync_event_report_s *args) +{ + _mali_uk_vsync_event event = (_mali_uk_vsync_event)args->event; + MALI_IGNORE(event); /* event is not used for release code, and that is OK */ +/* u64 ts = _mali_timestamp_get(); + */ + +#if MALI_TIMELINE_PROFILING_ENABLED + if ( event==_MALI_UK_VSYNC_EVENT_BEGIN_WAIT) + { + _mali_profiling_add_event( MALI_PROFILING_EVENT_TYPE_SUSPEND | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VSYNC, + 0, 0, 0, 0, 0); + } + + if ( event==_MALI_UK_VSYNC_EVENT_END_WAIT) + { + + _mali_profiling_add_event( MALI_PROFILING_EVENT_TYPE_RESUME | + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE | + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VSYNC, + 0, 0, 0, 0, 0); + } +#endif + MALI_DEBUG_PRINT(4, ("Received VSYNC event: %d\n", event)); + MALI_SUCCESS; +} + diff --git a/drivers/gpu/arm/mali/common/mali_osk.h b/drivers/gpu/arm/mali/common/mali_osk.h new file mode 100644 index 000000000000..6a6c3e0b76d9 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_osk.h @@ -0,0 +1,1715 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk.h + * Defines the OS abstraction layer for the kernel device driver (OSK) + */ + +#ifndef __MALI_OSK_H__ +#define __MALI_OSK_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup uddapi Unified Device Driver (UDD) APIs + * + * @{ + */ + +/** + * @addtogroup oskapi UDD OS Abstraction for Kernel-side (OSK) APIs + * + * @{ + */ + +/** @defgroup _mali_osk_miscellaneous OSK Miscellaneous functions, constants and types + * @{ */ + +/* Define integer types used by OSK. Note: these currently clash with Linux so we only define them if not defined already */ +#ifndef __KERNEL__ + typedef unsigned char u8; + typedef signed char s8; + typedef unsigned short u16; + typedef signed short s16; + typedef unsigned int u32; + typedef signed int s32; + typedef unsigned long long u64; + #define BITS_PER_LONG (sizeof(long)*8) +#else + /* Ensure Linux types u32, etc. are defined */ + #include <linux/types.h> +#endif + +/** @brief Mali Boolean type which uses MALI_TRUE and MALI_FALSE + */ + typedef unsigned long mali_bool; + +#ifndef MALI_TRUE + #define MALI_TRUE ((mali_bool)1) +#endif + +#ifndef MALI_FALSE + #define MALI_FALSE ((mali_bool)0) +#endif + +/** + * @brief OSK Error codes + * + * Each OS may use its own set of error codes, and may require that the + * User/Kernel interface take certain error code. This means that the common + * error codes need to be sufficiently rich to pass the correct error code + * thorugh from the OSK to U/K layer, across all OSs. + * + * The result is that some error codes will appear redundant on some OSs. + * Under all OSs, the OSK layer must translate native OS error codes to + * _mali_osk_errcode_t codes. Similarly, the U/K layer must translate from + * _mali_osk_errcode_t codes to native OS error codes. + */ +typedef enum +{ + _MALI_OSK_ERR_OK = 0, /**< Success. */ + _MALI_OSK_ERR_FAULT = -1, /**< General non-success */ + _MALI_OSK_ERR_INVALID_FUNC = -2, /**< Invalid function requested through User/Kernel interface (e.g. bad IOCTL number) */ + _MALI_OSK_ERR_INVALID_ARGS = -3, /**< Invalid arguments passed through User/Kernel interface */ + _MALI_OSK_ERR_NOMEM = -4, /**< Insufficient memory */ + _MALI_OSK_ERR_TIMEOUT = -5, /**< Timeout occurred */ + _MALI_OSK_ERR_RESTARTSYSCALL = -6, /**< Special: On certain OSs, must report when an interruptable mutex is interrupted. Ignore otherwise. */ + _MALI_OSK_ERR_ITEM_NOT_FOUND = -7, /**< Table Lookup failed */ + _MALI_OSK_ERR_BUSY = -8, /**< Device/operation is busy. Try again later */ + _MALI_OSK_ERR_UNSUPPORTED = -9, /**< Optional part of the interface used, and is unsupported */ +} _mali_osk_errcode_t; + +/** @} */ /* end group _mali_osk_miscellaneous */ + + +/** @defgroup _mali_osk_irq OSK IRQ handling + * @{ */ + +/** @brief Private type for IRQ handling objects */ +typedef struct _mali_osk_irq_t_struct _mali_osk_irq_t; + +/** @brief Optional function to trigger an irq from a resource + * + * This function is implemented by the common layer to allow probing of a resource's IRQ. + * @param arg resource-specific data */ +typedef void (*_mali_osk_irq_trigger_t)( void * arg ); + +/** @brief Optional function to acknowledge an irq from a resource + * + * This function is implemented by the common layer to allow probing of a resource's IRQ. + * @param arg resource-specific data + * @return _MALI_OSK_ERR_OK if the IRQ was successful, or a suitable _mali_osk_errcode_t on failure. */ +typedef _mali_osk_errcode_t (*_mali_osk_irq_ack_t)( void * arg ); + +/** @brief IRQ 'upper-half' handler callback. + * + * This function is implemented by the common layer to do the initial handling of a + * resource's IRQ. This maps on to the concept of an ISR that does the minimum + * work necessary before handing off to an IST. + * + * The communication of the resource-specific data from the ISR to the IST is + * handled by the OSK implementation. + * + * On most systems, the IRQ upper-half handler executes in IRQ context. + * Therefore, the system may have restrictions about what can be done in this + * context + * + * If an IRQ upper-half handler requires more work to be done than can be + * acheived in an IRQ context, then it may defer the work with + * _mali_osk_irq_schedulework(). Refer to \ref _mali_osk_irq_schedulework() for + * more information. + * + * @param arg resource-specific data + * @return _MALI_OSK_ERR_OK if the IRQ was correctly handled, or a suitable + * _mali_osk_errcode_t otherwise. + */ +typedef _mali_osk_errcode_t (*_mali_osk_irq_uhandler_t)( void * arg ); + +/** @brief IRQ 'bottom-half' handler callback. + * + * This function is implemented by the common layer to do the deferred handling + * of a resource's IRQ. Usually, this work cannot be carried out in IRQ context + * by the IRQ upper-half handler. + * + * The IRQ bottom-half handler maps on to the concept of an IST that may + * execute some time after the actual IRQ has fired. + * + * All OSK-registered IRQ bottom-half handlers will be serialized, across all + * CPU-cores in the system. + * + * Refer to \ref _mali_osk_irq_schedulework() for more information on the + * IRQ work-queue, and the calling of the IRQ bottom-half handler. + * + * @param arg resource-specific data + */ +typedef void (*_mali_osk_irq_bhandler_t)( void * arg ); +/** @} */ /* end group _mali_osk_irq */ + + +/** @defgroup _mali_osk_atomic OSK Atomic counters + * @{ */ + +/** @brief Public type of atomic counters + * + * This is public for allocation on stack. On systems that support it, this is just a single 32-bit value. + * On others, it could be encapsulating an object stored elsewhere. + * + * Even though the structure has space for a u32, the counters will only + * represent signed 24-bit integers. + * + * Regardless of implementation, the \ref _mali_osk_atomic functions \b must be used + * for all accesses to the variable's value, even if atomicity is not required. + * Do not access u.val or u.obj directly. + */ +typedef struct +{ + union + { + u32 val; + void *obj; + } u; +} _mali_osk_atomic_t; +/** @} */ /* end group _mali_osk_atomic */ + + +/** @defgroup _mali_osk_lock OSK Mutual Exclusion Locks + * @{ */ + +/** @brief OSK Mutual Exclusion Lock flags type + * + * Flags are supplied at the point where the Lock is initialized. Each flag can + * be combined with others using bitwise OR, '|'. + * + * The flags must be sufficiently rich to cope with all our OSs. This means + * that on some OSs, certain flags can be completely ignored. We define a + * number of terms that are significant across all OSs: + * + * - Sleeping/non-sleeping mutexs. Sleeping mutexs can block on waiting, and so + * schedule out the current thread. This is significant on OSs where there are + * situations in which the current thread must not be put to sleep. On OSs + * without this restriction, sleeping and non-sleeping mutexes can be treated + * as the same (if that is required). + * - Interruptable/non-interruptable mutexes. For sleeping mutexes, it may be + * possible for the sleep to be interrupted for a reason other than the thread + * being able to obtain the lock. OSs behaving in this way may provide a + * mechanism to control whether sleeping mutexes can be interrupted. On OSs + * that do not support the concept of interruption, \b or they do not support + * control of mutex interruption, then interruptable mutexes may be treated + * as non-interruptable. + * + * Some constrains apply to the lock type flags: + * + * - Spinlocks are by nature, non-interruptable. Hence, they must always be + * combined with the NONINTERRUPTABLE flag, because it is meaningless to ask + * for a spinlock that is interruptable (and this highlights its + * non-interruptable-ness). For example, on certain OSs they should be used when + * you must not sleep. + * - Reader/writer is an optimization hint, and any type of lock can be + * reader/writer. Since this is an optimization hint, the implementation need + * not respect this for any/all types of lock. For example, on certain OSs, + * there's no interruptable reader/writer mutex. If such a thing were requested + * on that OS, the fact that interruptable was requested takes priority over the + * reader/writer-ness, because reader/writer-ness is not necessary for correct + * operation. + * - Any lock can use the order parameter. + * - A onelock is an optimization hint specific to certain OSs. It can be + * specified when it is known that only one lock will be held by the thread, + * and so can provide faster mutual exclusion. This can be safely ignored if + * such optimization is not required/present. + * + * The absence of any flags (the value 0) results in a sleeping-mutex, which is interruptable. + */ +typedef enum +{ + _MALI_OSK_LOCKFLAG_SPINLOCK = 0x1, /**< Specifically, don't sleep on those architectures that require it */ + _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE = 0x2, /**< The mutex cannot be interrupted, e.g. delivery of signals on those architectures where this is required */ + _MALI_OSK_LOCKFLAG_READERWRITER = 0x4, /**< Optimise for readers/writers */ + _MALI_OSK_LOCKFLAG_ORDERED = 0x8, /**< Use the order parameter; otherwise use automatic ordering */ + _MALI_OSK_LOCKFLAG_ONELOCK = 0x10, /**< Each thread can only hold one lock at a time */ + _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ = 0x20, /**< IRQ version of spinlock */ + /** @enum _mali_osk_lock_flags_t + * + * Flags from 0x10000--0x80000000 are RESERVED for User-mode */ + +} _mali_osk_lock_flags_t; + +/** @brief Mutual Exclusion Lock Mode Optimization hint + * + * The lock mode is used to implement the read/write locking of locks specified + * as _MALI_OSK_LOCKFLAG_READERWRITER. In this case, the RO mode can be used + * to allow multiple concurrent readers, but no writers. The RW mode is used for + * writers, and so will wait for all readers to release the lock (if any present). + * Further readers and writers will wait until the writer releases the lock. + * + * The mode is purely an optimization hint: for example, it is permissible for + * all locks to behave in RW mode, regardless of that supplied. + * + * It is an error to attempt to use locks in anything other that RW mode when + * _MALI_OSK_LOCKFLAG_READERWRITER is not supplied. + * + */ +typedef enum +{ + _MALI_OSK_LOCKMODE_UNDEF = -1, /**< Undefined lock mode. For internal use only */ + _MALI_OSK_LOCKMODE_RW = 0x0, /**< Read-write mode, default. All readers and writers are mutually-exclusive */ + _MALI_OSK_LOCKMODE_RO, /**< Read-only mode, to support multiple concurrent readers, but mutual exclusion in the presence of writers. */ + /** @enum _mali_osk_lock_mode_t + * + * Lock modes 0x40--0x7F are RESERVED for User-mode */ +} _mali_osk_lock_mode_t; + +/** @brief Private type for Mutual Exclusion lock objects */ +typedef struct _mali_osk_lock_t_struct _mali_osk_lock_t; +/** @} */ /* end group _mali_osk_lock */ + +/** @defgroup _mali_osk_low_level_memory OSK Low-level Memory Operations + * @{ */ + +/** + * @brief Private data type for use in IO accesses to/from devices. + * + * This represents some range that is accessible from the device. Examples + * include: + * - Device Registers, which could be readable and/or writeable. + * - Memory that the device has access to, for storing configuration structures. + * + * Access to this range must be made through the _mali_osk_mem_ioread32() and + * _mali_osk_mem_iowrite32() functions. + */ +typedef struct _mali_io_address * mali_io_address; + +/** @defgroup _MALI_OSK_CPU_PAGE CPU Physical page size macros. + * + * The order of the page size is supplied for + * ease of use by algorithms that might require it, since it is easier to know + * it ahead of time rather than calculating it. + * + * The Mali Page Mask macro masks off the lower bits of a physical address to + * give the start address of the page for that physical address. + * + * @note The Mali device driver code is designed for systems with 4KB page size. + * Changing these macros will not make the entire Mali device driver work with + * page sizes other than 4KB. + * + * @note The CPU Physical Page Size has been assumed to be the same as the Mali + * Physical Page Size. + * + * @{ + */ + +/** CPU Page Order, as log to base 2 of the Page size. @see _MALI_OSK_CPU_PAGE_SIZE */ +#define _MALI_OSK_CPU_PAGE_ORDER ((u32)12) +/** CPU Page Size, in bytes. */ +#define _MALI_OSK_CPU_PAGE_SIZE (((u32)1) << (_MALI_OSK_CPU_PAGE_ORDER)) +/** CPU Page Mask, which masks off the offset within a page */ +#define _MALI_OSK_CPU_PAGE_MASK (~((((u32)1) << (_MALI_OSK_CPU_PAGE_ORDER)) - ((u32)1))) +/** @} */ /* end of group _MALI_OSK_CPU_PAGE */ + +/** @defgroup _MALI_OSK_MALI_PAGE Mali Physical Page size macros + * + * Mali Physical page size macros. The order of the page size is supplied for + * ease of use by algorithms that might require it, since it is easier to know + * it ahead of time rather than calculating it. + * + * The Mali Page Mask macro masks off the lower bits of a physical address to + * give the start address of the page for that physical address. + * + * @note The Mali device driver code is designed for systems with 4KB page size. + * Changing these macros will not make the entire Mali device driver work with + * page sizes other than 4KB. + * + * @note The Mali Physical Page Size has been assumed to be the same as the CPU + * Physical Page Size. + * + * @{ + */ + +/** Mali Page Order, as log to base 2 of the Page size. @see _MALI_OSK_MALI_PAGE_SIZE */ +#define _MALI_OSK_MALI_PAGE_ORDER ((u32)12) +/** Mali Page Size, in bytes. */ +#define _MALI_OSK_MALI_PAGE_SIZE (((u32)1) << (_MALI_OSK_MALI_PAGE_ORDER)) +/** Mali Page Mask, which masks off the offset within a page */ +#define _MALI_OSK_MALI_PAGE_MASK (~((((u32)1) << (_MALI_OSK_MALI_PAGE_ORDER)) - ((u32)1))) +/** @} */ /* end of group _MALI_OSK_MALI_PAGE*/ + +/** @brief flags for mapping a user-accessible memory range + * + * Where a function with prefix '_mali_osk_mem_mapregion' accepts flags as one + * of the function parameters, it will use one of these. These allow per-page + * control over mappings. Compare with the mali_memory_allocation_flag type, + * which acts over an entire range + * + * These may be OR'd together with bitwise OR (|), but must be cast back into + * the type after OR'ing. + */ +typedef enum +{ + _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR = 0x1, /**< Physical address is OS Allocated */ +} _mali_osk_mem_mapregion_flags_t; +/** @} */ /* end group _mali_osk_low_level_memory */ + +/** @defgroup _mali_osk_notification OSK Notification Queues + * @{ */ + +/** @brief Private type for notification queue objects */ +typedef struct _mali_osk_notification_queue_t_struct _mali_osk_notification_queue_t; + +/** @brief Public notification data object type */ +typedef struct _mali_osk_notification_t_struct +{ + u32 notification_type; /**< The notification type */ + u32 result_buffer_size; /**< Size of the result buffer to copy to user space */ + void * result_buffer; /**< Buffer containing any type specific data */ +} _mali_osk_notification_t; + +/** @} */ /* end group _mali_osk_notification */ + + +/** @defgroup _mali_osk_timer OSK Timer Callbacks + * @{ */ + +/** @brief Function to call when a timer expires + * + * When a timer expires, this function is called. Note that on many systems, + * a timer callback will be executed in IRQ context. Therefore, restrictions + * may apply on what can be done inside the timer callback. + * + * If a timer requires more work to be done than can be acheived in an IRQ + * context, then it may defer the work with a work-queue. For example, it may + * use \ref _mali_osk_irq_schedulework() to make use of the IRQ bottom-half handler + * to carry out the remaining work. + * + * Stopping the timer with \ref _mali_osk_timer_del() blocks on compeletion of + * the callback. Therefore, the callback may not obtain any mutexes also held + * by any callers of _mali_osk_timer_del(). Otherwise, a deadlock may occur. + * + * @param arg Function-specific data */ +typedef void (*_mali_osk_timer_callback_t)(void * arg ); + +/** @brief Private type for Timer Callback Objects */ +typedef struct _mali_osk_timer_t_struct _mali_osk_timer_t; +/** @} */ /* end group _mali_osk_timer */ + + +/** @addtogroup _mali_osk_list OSK Doubly-Linked Circular Lists + * @{ */ + +/** @brief Public List objects. + * + * To use, add a _mali_osk_list_t member to the structure that may become part + * of a list. When traversing the _mali_osk_list_t objects, use the + * _MALI_OSK_CONTAINER_OF() macro to recover the structure from its + *_mali_osk_list_t member + * + * Each structure may have multiple _mali_osk_list_t members, so that the + * structure is part of multiple lists. When traversing lists, ensure that the + * correct _mali_osk_list_t member is used, because type-checking will be + * lost by the compiler. + */ +typedef struct _mali_osk_list_s +{ + struct _mali_osk_list_s *next; + struct _mali_osk_list_s *prev; +} _mali_osk_list_t; + +/** @brief Initialize a list to be a head of an empty list + * @param exp the list to initialize. */ +#define _MALI_OSK_INIT_LIST_HEAD(exp) _mali_osk_list_init(exp) + +/** @brief Define a list variable, which is uninitialized. + * @param exp the name of the variable that the list will be defined as. */ +#define _MALI_OSK_LIST_HEAD(exp) _mali_osk_list_t exp + +/** @brief Find the containing structure of another structure + * + * This is the reverse of the operation 'offsetof'. This means that the + * following condition is satisfied: + * + * ptr == _MALI_OSK_CONTAINER_OF( &ptr->member, type, member ) + * + * When ptr is of type 'type'. + * + * Its purpose it to recover a larger structure that has wrapped a smaller one. + * + * @note no type or memory checking occurs to ensure that a wrapper structure + * does in fact exist, and that it is being recovered with respect to the + * correct member. + * + * @param ptr the pointer to the member that is contained within the larger + * structure + * @param type the type of the structure that contains the member + * @param member the name of the member in the structure that ptr points to. + * @return a pointer to a \a type object which contains \a member, as pointed + * to by \a ptr. + */ +#define _MALI_OSK_CONTAINER_OF(ptr, type, member) \ + ((type *)( ((char *)ptr) - offsetof(type,member) )) + +/** @brief Find the containing structure of a list + * + * When traversing a list, this is used to recover the containing structure, + * given that is contains a _mali_osk_list_t member. + * + * Each list must be of structures of one type, and must link the same members + * together, otherwise it will not be possible to correctly recover the + * sturctures that the lists link. + * + * @note no type or memory checking occurs to ensure that a structure does in + * fact exist for the list entry, and that it is being recovered with respect + * to the correct list member. + * + * @param ptr the pointer to the _mali_osk_list_t member in this structure + * @param type the type of the structure that contains the member + * @param member the member of the structure that ptr points to. + * @return a pointer to a \a type object which contains the _mali_osk_list_t + * \a member, as pointed to by the _mali_osk_list_t \a *ptr. + */ +#define _MALI_OSK_LIST_ENTRY(ptr, type, member) \ + _MALI_OSK_CONTAINER_OF(ptr, type, member) + +/** @brief Enumerate a list safely + * + * With this macro, lists can be enumerated in a 'safe' manner. That is, + * entries can be deleted from the list without causing an error during + * enumeration. To achieve this, a 'temporary' pointer is required, which must + * be provided to the macro. + * + * Use it like a 'for()', 'while()' or 'do()' construct, and so it must be + * followed by a statement or compound-statement which will be executed for + * each list entry. + * + * Upon loop completion, providing that an early out was not taken in the + * loop body, then it is guaranteed that ptr->member == list, even if the loop + * body never executed. + * + * @param ptr a pointer to an object of type 'type', which points to the + * structure that contains the currently enumerated list entry. + * @param tmp a pointer to an object of type 'type', which must not be used + * inside the list-execution statement. + * @param list a pointer to a _mali_osk_list_t, from which enumeration will + * begin + * @param type the type of the structure that contains the _mali_osk_list_t + * member that is part of the list to be enumerated. + * @param member the _mali_osk_list_t member of the structure that is part of + * the list to be enumerated. + */ +#define _MALI_OSK_LIST_FOREACHENTRY(ptr, tmp, list, type, member) \ + for (ptr = _MALI_OSK_LIST_ENTRY((list)->next, type, member), \ + tmp = _MALI_OSK_LIST_ENTRY(ptr->member.next, type, member); \ + &ptr->member != (list); \ + ptr = tmp, tmp = _MALI_OSK_LIST_ENTRY(tmp->member.next, type, member)) +/** @} */ /* end group _mali_osk_list */ + + +/** @addtogroup _mali_osk_miscellaneous + * @{ */ + +/** @brief The known resource types + * + * @note \b IMPORTANT: these must remain fixed, and only be extended. This is + * because not all systems use a header file for reading in their resources. + * The resources may instead come from a data file where these resources are + * 'hard-coded' in, because there's no easy way of transferring the enum values + * into such data files. E.g. the C-Pre-processor does \em not process enums. + */ +typedef enum _mali_osk_resource_type +{ + RESOURCE_TYPE_FIRST =0, /**< Duplicate resource marker for the first resource*/ + MEMORY =0, /**< Physically contiguous memory block, not managed by the OS */ + OS_MEMORY =1, /**< Memory managed by and shared with the OS */ + MALI200 =3, /**< Mali200 Programmable Fragment Shader */ + MALIGP2 =4, /**< MaliGP2 Programmable Vertex Shader */ + MMU =5, /**< Mali MMU (Memory Management Unit) */ + FPGA_FRAMEWORK =6, /**< Mali registers specific to FPGA implementations */ + MALI400L2 =7, /**< Mali400 L2 Cache */ + MALI300L2 =7, /**< Mali300 L2 Cache */ + MALI400GP =8, /**< Mali400 Programmable Vertex Shader Core */ + MALI300GP =8, /**< Mali300 Programmable Vertex Shader Core */ + MALI400PP =9, /**< Mali400 Programmable Fragment Shader Core */ + MALI300PP =9, /**< Mali300 Programmable Fragment Shader Core */ + MEM_VALIDATION =10, /**< External Memory Validator */ + PMU =11, /**< Power Manangement Unit */ + RESOURCE_TYPE_COUNT /**< The total number of known resources */ +} _mali_osk_resource_type_t; + +/** @brief resource description struct + * + * _mali_osk_resources_init() will enumerate objects of this type. Not all + * members have a valid meaning across all types. + * + * The mmu_id is used to group resources to a certain MMU, since there may be + * more than one MMU in the system, and each resource may be using a different + * MMU: + * - For MMU resources, the setting of mmu_id is a uniquely identifying number. + * - For Other resources, the setting of mmu_id determines which MMU the + * resource uses. + */ +typedef struct _mali_osk_resource +{ + _mali_osk_resource_type_t type; /**< type of the resource */ + const char * description; /**< short description of the resource */ + u32 base; /**< Physical base address of the resource, as seen by Mali resources. */ + s32 cpu_usage_adjust; /**< Offset added to the base address of the resource to arrive at the CPU physical address of the resource (if different from the Mali physical address) */ + u32 size; /**< Size in bytes of the resource - either the size of its register range, or the size of the memory block. */ + u32 irq; /**< IRQ number delivered to the CPU, or -1 to tell the driver to probe for it (if possible) */ + u32 flags; /**< Resources-specific flags. */ + u32 mmu_id; /**< Identifier for Mali MMU resources. */ + u32 alloc_order; /**< Order in which MEMORY/OS_MEMORY resources are used */ +} _mali_osk_resource_t; +/** @} */ /* end group _mali_osk_miscellaneous */ + + +#include "mali_kernel_memory_engine.h" /* include for mali_memory_allocation and mali_physical_memory_allocation type */ + +/** @addtogroup _mali_osk_irq + * @{ */ + +/** @brief Fake IRQ number for testing purposes + */ +#define _MALI_OSK_IRQ_NUMBER_FAKE ((u32)0xFFFFFFF1) + +/** @addtogroup _mali_osk_irq + * @{ */ + +/** @brief PMM Virtual IRQ number + */ +#define _MALI_OSK_IRQ_NUMBER_PMM ((u32)0xFFFFFFF2) + + +/** @brief Initialize IRQ handling for a resource + * + * The _mali_osk_irq_t returned must be written into the resource-specific data + * pointed to by data. This is so that the upper and lower handlers can call + * _mali_osk_irq_schedulework(). + * + * @note The caller must ensure that the resource does not generate an + * interrupt after _mali_osk_irq_init() finishes, and before the + * _mali_osk_irq_t is written into the resource-specific data. Otherwise, + * the upper-half handler will fail to call _mali_osk_irq_schedulework(). + * + * @param irqnum The IRQ number that the resource uses, as seen by the CPU. + * The value -1 has a special meaning which indicates the use of probing, and trigger_func and ack_func must be + * non-NULL. + * @param uhandler The upper-half handler, corresponding to a ISR handler for + * the resource + * @param bhandler The lower-half handler, corresponding to an IST handler for + * the resource + * @param trigger_func Optional: a function to trigger the resource's irq, to + * probe for the interrupt. Use NULL if irqnum != -1. + * @param ack_func Optional: a function to acknowledge the resource's irq, to + * probe for the interrupt. Use NULL if irqnum != -1. + * @param data resource-specific data, which will be passed to uhandler, + * bhandler and (if present) trigger_func and ack_funnc + * @param description textual description of the IRQ resource. + * @return on success, a pointer to a _mali_osk_irq_t object, which represents + * the IRQ handling on this resource. NULL on failure. + */ +_mali_osk_irq_t *_mali_osk_irq_init( u32 irqnum, _mali_osk_irq_uhandler_t uhandler, _mali_osk_irq_bhandler_t bhandler, _mali_osk_irq_trigger_t trigger_func, _mali_osk_irq_ack_t ack_func, void *data, const char *description ); + +/** @brief Cause a queued, deferred call of the IRQ bottom-half. + * + * _mali_osk_irq_schedulework provides a mechanism for enqueuing deferred calls + * to the IRQ bottom-half handler. The queue is known as the IRQ work-queue. + * After calling _mali_osk_irq_schedulework(), the IRQ bottom-half handler will + * be scheduled to run at some point in the future. + * + * This is called by the IRQ upper-half to defer further processing of + * IRQ-related work to the IRQ bottom-half handler. This is necessary for work + * that cannot be done in an IRQ context by the IRQ upper-half handler. Timer + * callbacks also use this mechanism, because they are treated as though they + * operate in an IRQ context. Refer to \ref _mali_osk_timer_t for more + * information. + * + * Code that operates in a kernel-process context (with no IRQ context + * restrictions) may also enqueue deferred calls to the IRQ bottom-half. The + * advantage over direct calling is that deferred calling allows the caller and + * IRQ bottom half to hold the same mutex, with a guarantee that they will not + * deadlock just by using this mechanism. + * + * _mali_osk_irq_schedulework() places deferred call requests on a queue, to + * allow for more than one thread to make a deferred call. Therfore, if it is + * called 'K' times, then the IRQ bottom-half will be scheduled 'K' times too. + * 'K' is a number that is implementation-specific. + * + * _mali_osk_irq_schedulework() is guaranteed to not block on: + * - enqueuing a deferred call request. + * - the completion of the IRQ bottom-half handler. + * + * This is to prevent deadlock. For example, if _mali_osk_irq_schedulework() + * blocked, then it would cause a deadlock when the following two conditions + * hold: + * - The IRQ bottom-half callback (of type _mali_osk_irq_bhandler_t) locks + * a mutex + * - And, at the same time, the caller of _mali_osk_irq_schedulework() also + * holds the same mutex + * + * @note care must be taken to not overflow the queue that + * _mali_osk_irq_schedulework() operates on. Code must be structured to + * ensure that the number of requests made to the queue is bounded. Otherwise, + * IRQs will be lost. + * + * The queue that _mali_osk_irq_schedulework implements is a FIFO of N-writer, + * 1-reader type. The writers are the callers of _mali_osk_irq_schedulework + * (all OSK-registered IRQ upper-half handlers in the system, watchdog timers, + * callers from a Kernel-process context). The reader is a single thread that + * handles all OSK-registered IRQs. + * + * The consequence of the queue being a 1-reader type is that calling + * _mali_osk_irq_schedulework() on different _mali_osk_irq_t objects causes + * their IRQ bottom-halves to be serialized, across all CPU-cores in the + * system. + * + * @param irq a pointer to the _mali_osk_irq_t object corresponding to the + * resource whose IRQ bottom-half must begin processing. + */ +void _mali_osk_irq_schedulework( _mali_osk_irq_t *irq ); + +/** @brief Terminate IRQ handling on a resource. + * + * This will disable the interrupt from the device, and then waits for the + * IRQ work-queue to finish the work that is currently in the queue. That is, + * for every deferred call currently in the IRQ work-queue, it waits for each + * of those to be processed by their respective IRQ bottom-half handler. + * + * This function is used to ensure that the bottom-half handler of the supplied + * IRQ object will not be running at the completion of this function call. + * However, the caller must ensure that no other sources could call the + * _mali_osk_irq_schedulework() on the same IRQ object. For example, the + * relevant timers must be stopped. + * + * @note While this function is being called, other OSK-registered IRQs in the + * system may enqueue work for their respective bottom-half handlers. This + * function will not wait for those entries in the work-queue to be flushed. + * + * Since this blocks on the completion of work in the IRQ work-queue, the + * caller of this function \b must \b not hold any mutexes that are taken by + * any OSK-registered IRQ bottom-half handler. To do so may cause a deadlock. + * + * @param irq a pointer to the _mali_osk_irq_t object corresponding to the + * resource whose IRQ handling is to be terminated. + */ +void _mali_osk_irq_term( _mali_osk_irq_t *irq ); + +/** @brief flushing workqueue. + * + * This will flush the workqueue. + * + * @param irq a pointer to the _mali_osk_irq_t object corresponding to the + * resource whose IRQ handling is to be terminated. + */ +void _mali_osk_flush_workqueue( _mali_osk_irq_t *irq ); + +/** @} */ /* end group _mali_osk_irq */ + + +/** @addtogroup _mali_osk_atomic + * @{ */ + +/** @brief Decrement an atomic counter + * + * @note It is an error to decrement the counter beyond -(1<<23) + * + * @param atom pointer to an atomic counter */ +void _mali_osk_atomic_dec( _mali_osk_atomic_t *atom ); + +/** @brief Decrement an atomic counter, return new value + * + * Although the value returned is a u32, only numbers with signed 24-bit + * precision (sign extended to u32) are returned. + * + * @note It is an error to decrement the counter beyond -(1<<23) + * + * @param atom pointer to an atomic counter + * @return The new value, after decrement */ +u32 _mali_osk_atomic_dec_return( _mali_osk_atomic_t *atom ); + +/** @brief Increment an atomic counter + * + * @note It is an error to increment the counter beyond (1<<23)-1 + * + * @param atom pointer to an atomic counter */ +void _mali_osk_atomic_inc( _mali_osk_atomic_t *atom ); + +/** @brief Increment an atomic counter, return new value + * + * Although the value returned is a u32, only numbers with signed 24-bit + * precision (sign extended to u32) are returned. + * + * @note It is an error to increment the counter beyond (1<<23)-1 + * + * @param atom pointer to an atomic counter */ +u32 _mali_osk_atomic_inc_return( _mali_osk_atomic_t *atom ); + +/** @brief Initialize an atomic counter + * + * The counters have storage for signed 24-bit integers. Initializing to signed + * values requiring more than 24-bits storage will fail. + * + * @note the parameter required is a u32, and so signed integers should be + * cast to u32. + * + * @param atom pointer to an atomic counter + * @param val the value to initialize the atomic counter. + * @return _MALI_OSK_ERR_OK on success, otherwise, a suitable + * _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_osk_atomic_init( _mali_osk_atomic_t *atom, u32 val ); + +/** @brief Read a value from an atomic counter + * + * Although the value returned is a u32, only numbers with signed 24-bit + * precision (sign extended to u32) are returned. + * + * This can only be safely used to determine the value of the counter when it + * is guaranteed that other threads will not be modifying the counter. This + * makes its usefulness limited. + * + * @param atom pointer to an atomic counter + */ +u32 _mali_osk_atomic_read( _mali_osk_atomic_t *atom ); + +/** @brief Terminate an atomic counter + * + * @param atom pointer to an atomic counter + */ +void _mali_osk_atomic_term( _mali_osk_atomic_t *atom ); +/** @} */ /* end group _mali_osk_atomic */ + + +/** @defgroup _mali_osk_memory OSK Memory Allocation + * @{ */ + +/** @brief Allocate zero-initialized memory. + * + * Returns a buffer capable of containing at least \a n elements of \a size + * bytes each. The buffer is initialized to zero. + * + * If there is a need for a bigger block of memory (16KB or bigger), then + * consider to use _mali_osk_vmalloc() instead, as this function might + * map down to a OS function with size limitations. + * + * The buffer is suitably aligned for storage and subsequent access of every + * type that the compiler supports. Therefore, the pointer to the start of the + * buffer may be cast into any pointer type, and be subsequently accessed from + * such a pointer, without loss of information. + * + * When the buffer is no longer in use, it must be freed with _mali_osk_free(). + * Failure to do so will cause a memory leak. + * + * @note Most toolchains supply memory allocation functions that meet the + * compiler's alignment requirements. + * + * @param n Number of elements to allocate + * @param size Size of each element + * @return On success, the zero-initialized buffer allocated. NULL on failure + */ +void *_mali_osk_calloc( u32 n, u32 size ); + +/** @brief Allocate memory. + * + * Returns a buffer capable of containing at least \a size bytes. The + * contents of the buffer are undefined. + * + * If there is a need for a bigger block of memory (16KB or bigger), then + * consider to use _mali_osk_vmalloc() instead, as this function might + * map down to a OS function with size limitations. + * + * The buffer is suitably aligned for storage and subsequent access of every + * type that the compiler supports. Therefore, the pointer to the start of the + * buffer may be cast into any pointer type, and be subsequently accessed from + * such a pointer, without loss of information. + * + * When the buffer is no longer in use, it must be freed with _mali_osk_free(). + * Failure to do so will cause a memory leak. + * + * @note Most toolchains supply memory allocation functions that meet the + * compiler's alignment requirements. + * + * Remember to free memory using _mali_osk_free(). + * @param size Number of bytes to allocate + * @return On success, the buffer allocated. NULL on failure. + */ +void *_mali_osk_malloc( u32 size ); + +/** @brief Free memory. + * + * Reclaims the buffer pointed to by the parameter \a ptr for the system. + * All memory returned from _mali_osk_malloc() and _mali_osk_calloc() + * must be freed before the application exits. Otherwise, + * a memory leak will occur. + * + * Memory must be freed once. It is an error to free the same non-NULL pointer + * more than once. + * + * It is legal to free the NULL pointer. + * + * @param ptr Pointer to buffer to free + */ +void _mali_osk_free( void *ptr ); + +/** @brief Allocate memory. + * + * Returns a buffer capable of containing at least \a size bytes. The + * contents of the buffer are undefined. + * + * This function is potentially slower than _mali_osk_malloc() and _mali_osk_calloc(), + * but do support bigger sizes. + * + * The buffer is suitably aligned for storage and subsequent access of every + * type that the compiler supports. Therefore, the pointer to the start of the + * buffer may be cast into any pointer type, and be subsequently accessed from + * such a pointer, without loss of information. + * + * When the buffer is no longer in use, it must be freed with _mali_osk_free(). + * Failure to do so will cause a memory leak. + * + * @note Most toolchains supply memory allocation functions that meet the + * compiler's alignment requirements. + * + * Remember to free memory using _mali_osk_free(). + * @param size Number of bytes to allocate + * @return On success, the buffer allocated. NULL on failure. + */ +void *_mali_osk_valloc( u32 size ); + +/** @brief Free memory. + * + * Reclaims the buffer pointed to by the parameter \a ptr for the system. + * All memory returned from _mali_osk_valloc() must be freed before the + * application exits. Otherwise a memory leak will occur. + * + * Memory must be freed once. It is an error to free the same non-NULL pointer + * more than once. + * + * It is legal to free the NULL pointer. + * + * @param ptr Pointer to buffer to free + */ +void _mali_osk_vfree( void *ptr ); + +/** @brief Copies memory. + * + * Copies the \a len bytes from the buffer pointed by the parameter \a src + * directly to the buffer pointed by \a dst. + * + * It is an error for \a src to overlap \a dst anywhere in \a len bytes. + * + * @param dst Pointer to the destination array where the content is to be + * copied. + * @param src Pointer to the source of data to be copied. + * @param len Number of bytes to copy. + * @return \a dst is always passed through unmodified. + */ +void *_mali_osk_memcpy( void *dst, const void *src, u32 len ); + +/** @brief Fills memory. + * + * Sets the first \a n bytes of the block of memory pointed to by \a s to + * the specified value + * @param s Pointer to the block of memory to fill. + * @param c Value to be set, passed as u32. Only the 8 Least Significant Bits (LSB) + * are used. + * @param n Number of bytes to be set to the value. + * @return \a s is always passed through unmodified + */ +void *_mali_osk_memset( void *s, u32 c, u32 n ); +/** @} */ /* end group _mali_osk_memory */ + + +/** @brief Checks the amount of memory allocated + * + * Checks that not more than \a max_allocated bytes are allocated. + * + * Some OS bring up an interactive out of memory dialogue when the + * system runs out of memory. This can stall non-interactive + * apps (e.g. automated test runs). This function can be used to + * not trigger the OOM dialogue by keeping allocations + * within a certain limit. + * + * @return MALI_TRUE when \a max_allocated bytes are not in use yet. MALI_FALSE + * when at least \a max_allocated bytes are in use. + */ +mali_bool _mali_osk_mem_check_allocated( u32 max_allocated ); + +/** @addtogroup _mali_osk_lock + * @{ */ + +/** @brief Initialize a Mutual Exclusion Lock + * + * Locks are created in the signalled (unlocked) state. + * + * initial must be zero, since there is currently no means of expressing + * whether a reader/writer lock should be initially locked as a reader or + * writer. This would require some encoding to be used. + * + * 'Automatic' ordering means that locks must be obtained in the order that + * they were created. For all locks that can be held at the same time, they must + * either all provide the order parameter, or they all must use 'automatic' + * ordering - because there is no way of mixing 'automatic' and 'manual' + * ordering. + * + * @param flags flags combined with bitwise OR ('|'), or zero. There are + * restrictions on which flags can be combined, @see _mali_osk_lock_flags_t. + * @param initial For future expansion into semaphores. SBZ. + * @param order The locking order of the mutex. That is, locks obtained by the + * same thread must have been created with an increasing order parameter, for + * deadlock prevention. Setting to zero causes 'automatic' ordering to be used. + * @return On success, a pointer to a _mali_osk_lock_t object. NULL on failure. + */ +_mali_osk_lock_t *_mali_osk_lock_init( _mali_osk_lock_flags_t flags, u32 initial, u32 order ); + +/** @brief Wait for a lock to be signalled (obtained) + + * After a thread has successfully waited on the lock, the lock is obtained by + * the thread, and is marked as unsignalled. The thread releases the lock by + * signalling it. + * + * In the case of Reader/Writer locks, multiple readers can obtain a lock in + * the absence of writers, which is a performance optimization (providing that + * the readers never write to the protected resource). + * + * To prevent deadlock, locks must always be obtained in the same order. + * + * For locks marked as _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, it is a + * programming error for the function to exit without obtaining the lock. This + * means that the error code must only be checked for interruptible locks. + * + * @param lock the lock to wait upon (obtain). + * @param mode the mode in which the lock should be obtained. Unless the lock + * was created with _MALI_OSK_LOCKFLAG_READERWRITER, this must be + * _MALI_OSK_LOCKMODE_RW. + * @return On success, _MALI_OSK_ERR_OK. For interruptible locks, a suitable + * _mali_osk_errcode_t will be returned on failure, and the lock will not be + * obtained. In this case, the error code must be propagated up to the U/K + * interface. + */ +_mali_osk_errcode_t _mali_osk_lock_wait( _mali_osk_lock_t *lock, _mali_osk_lock_mode_t mode); + + +/** @brief Signal (release) a lock + * + * Locks may only be signalled by the thread that originally waited upon the + * lock. + * + * @note In the OSU, a flag exists to allow any thread to signal a + * lock. Such functionality is not present in the OSK. + * + * @param lock the lock to signal (release). + * @param mode the mode in which the lock should be obtained. This must match + * the mode in which the lock was waited upon. + */ +void _mali_osk_lock_signal( _mali_osk_lock_t *lock, _mali_osk_lock_mode_t mode ); + +/** @brief Terminate a lock + * + * This terminates a lock and frees all associated resources. + * + * It is a programming error to terminate the lock when it is held (unsignalled) + * by a thread. + * + * @param lock the lock to terminate. + */ +void _mali_osk_lock_term( _mali_osk_lock_t *lock ); +/** @} */ /* end group _mali_osk_lock */ + + +/** @addtogroup _mali_osk_low_level_memory + * @{ */ + +/** @brief Issue a memory barrier + * + * This defines an arbitrary memory barrier operation, which forces an ordering constraint + * on memory read and write operations. + */ +void _mali_osk_mem_barrier( void ); + +/** @brief Issue a write memory barrier + * + * This defines an write memory barrier operation which forces an ordering constraint + * on memory write operations. + */ +void _mali_osk_write_mem_barrier( void ); + +/** @brief Map a physically contiguous region into kernel space + * + * This is primarily used for mapping in registers from resources, and Mali-MMU + * page tables. The mapping is only visable from kernel-space. + * + * Access has to go through _mali_osk_mem_ioread32 and _mali_osk_mem_iowrite32 + * + * @param phys CPU-physical base address of the memory to map in. This must + * be aligned to the system's page size, which is assumed to be 4K. + * @param size the number of bytes of physically contiguous address space to + * map in + * @param description A textual description of the memory being mapped in. + * @return On success, a Mali IO address through which the mapped-in + * memory/registers can be accessed. NULL on failure. + */ +mali_io_address _mali_osk_mem_mapioregion( u32 phys, u32 size, const char *description ); + +/** @brief Unmap a physically contiguous address range from kernel space. + * + * The address range should be one previously mapped in through + * _mali_osk_mem_mapioregion. + * + * It is a programming error to do (but not limited to) the following: + * - attempt an unmap twice + * - unmap only part of a range obtained through _mali_osk_mem_mapioregion + * - unmap more than the range obtained through _mali_osk_mem_mapioregion + * - unmap an address range that was not successfully mapped using + * _mali_osk_mem_mapioregion + * - provide a mapping that does not map to phys. + * + * @param phys CPU-physical base address of the memory that was originally + * mapped in. This must be aligned to the system's page size, which is assumed + * to be 4K + * @param size The number of bytes that were originally mapped in. + * @param mapping The Mali IO address through which the mapping is + * accessed. + */ +void _mali_osk_mem_unmapioregion( u32 phys, u32 size, mali_io_address mapping ); + +/** @brief Allocate and Map a physically contiguous region into kernel space + * + * This is used for allocating physically contiguous regions (such as Mali-MMU + * page tables) and mapping them into kernel space. The mapping is only + * visible from kernel-space. + * + * The alignment of the returned memory is guaranteed to be at least + * _MALI_OSK_CPU_PAGE_SIZE. + * + * Access must go through _mali_osk_mem_ioread32 and _mali_osk_mem_iowrite32 + * + * @note This function is primarily to provide support for OSs that are + * incapable of separating the tasks 'allocate physically contiguous memory' + * and 'map it into kernel space' + * + * @param[out] phys CPU-physical base address of memory that was allocated. + * (*phys) will be guaranteed to be aligned to at least + * _MALI_OSK_CPU_PAGE_SIZE on success. + * + * @param[in] size the number of bytes of physically contiguous memory to + * allocate. This must be a multiple of _MALI_OSK_CPU_PAGE_SIZE. + * + * @return On success, a Mali IO address through which the mapped-in + * memory/registers can be accessed. NULL on failure, and (*phys) is unmodified. + */ +mali_io_address _mali_osk_mem_allocioregion( u32 *phys, u32 size ); + +/** @brief Free a physically contiguous address range from kernel space. + * + * The address range should be one previously mapped in through + * _mali_osk_mem_allocioregion. + * + * It is a programming error to do (but not limited to) the following: + * - attempt a free twice on the same ioregion + * - free only part of a range obtained through _mali_osk_mem_allocioregion + * - free more than the range obtained through _mali_osk_mem_allocioregion + * - free an address range that was not successfully mapped using + * _mali_osk_mem_allocioregion + * - provide a mapping that does not map to phys. + * + * @param phys CPU-physical base address of the memory that was originally + * mapped in, which was aligned to _MALI_OSK_CPU_PAGE_SIZE. + * @param size The number of bytes that were originally mapped in, which was + * a multiple of _MALI_OSK_CPU_PAGE_SIZE. + * @param mapping The Mali IO address through which the mapping is + * accessed. + */ +void _mali_osk_mem_freeioregion( u32 phys, u32 size, mali_io_address mapping ); + +/** @brief Request a region of physically contiguous memory + * + * This is used to ensure exclusive access to a region of physically contigous + * memory. + * + * It is acceptable to implement this as a stub. However, it is then the job + * of the System Integrator to ensure that no other device driver will be using + * the physical address ranges used by Mali, while the Mali device driver is + * loaded. + * + * @param phys CPU-physical base address of the memory to request. This must + * be aligned to the system's page size, which is assumed to be 4K. + * @param size the number of bytes of physically contiguous address space to + * request. + * @param description A textual description of the memory being requested. + * @return _MALI_OSK_ERR_OK on success. Otherwise, a suitable + * _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_osk_mem_reqregion( u32 phys, u32 size, const char *description ); + +/** @brief Un-request a region of physically contiguous memory + * + * This is used to release a regious of physically contiguous memory previously + * requested through _mali_osk_mem_reqregion, so that other device drivers may + * use it. This will be called at time of Mali device driver termination. + * + * It is a programming error to attempt to: + * - unrequest a region twice + * - unrequest only part of a range obtained through _mali_osk_mem_reqregion + * - unrequest more than the range obtained through _mali_osk_mem_reqregion + * - unrequest an address range that was not successfully requested using + * _mali_osk_mem_reqregion + * + * @param phys CPU-physical base address of the memory to un-request. This must + * be aligned to the system's page size, which is assumed to be 4K + * @param size the number of bytes of physically contiguous address space to + * un-request. + */ +void _mali_osk_mem_unreqregion( u32 phys, u32 size ); + +/** @brief Read from a location currently mapped in through + * _mali_osk_mem_mapioregion + * + * This reads a 32-bit word from a 32-bit aligned location. It is a programming + * error to provide unaligned locations, or to read from memory that is not + * mapped in, or not mapped through either _mali_osk_mem_mapioregion() or + * _mali_osk_mem_allocioregion(). + * + * @param mapping Mali IO address to read from + * @param offset Byte offset from the given IO address to operate on, must be a multiple of 4 + * @return the 32-bit word from the specified location. + */ +u32 _mali_osk_mem_ioread32( volatile mali_io_address mapping, u32 offset ); + +/** @brief Write to a location currently mapped in through + * _mali_osk_mem_mapioregion without memory barriers + * + * This write a 32-bit word to a 32-bit aligned location without using memory barrier. + * It is a programming error to provide unaligned locations, or to write to memory that is not + * mapped in, or not mapped through either _mali_osk_mem_mapioregion() or + * _mali_osk_mem_allocioregion(). + * + * @param mapping Mali IO address to write to + * @param offset Byte offset from the given IO address to operate on, must be a multiple of 4 + * @param val the 32-bit word to write. + */ +void _mali_osk_mem_iowrite32_relaxed( volatile mali_io_address addr, u32 offset, u32 val ); + +/** @brief Write to a location currently mapped in through + * _mali_osk_mem_mapioregion with write memory barrier + * + * This write a 32-bit word to a 32-bit aligned location. It is a programming + * error to provide unaligned locations, or to write to memory that is not + * mapped in, or not mapped through either _mali_osk_mem_mapioregion() or + * _mali_osk_mem_allocioregion(). + * + * @param mapping Mali IO address to write to + * @param offset Byte offset from the given IO address to operate on, must be a multiple of 4 + * @param val the 32-bit word to write. + */ +void _mali_osk_mem_iowrite32( volatile mali_io_address mapping, u32 offset, u32 val ); + +/** @brief Flush all CPU caches + * + * This should only be implemented if flushing of the cache is required for + * memory mapped in through _mali_osk_mem_mapregion. + */ +void _mali_osk_cache_flushall( void ); + +/** @brief Flush any caches necessary for the CPU and MALI to have the same view of a range of uncached mapped memory + * + * This should only be implemented if your OS doesn't do a full cache flush (inner & outer) + * after allocating uncached mapped memory. + * + * Some OS do not perform a full cache flush (including all outer caches) for uncached mapped memory. + * They zero the memory through a cached mapping, then flush the inner caches but not the outer caches. + * This is required for MALI to have the correct view of the memory. + */ +void _mali_osk_cache_ensure_uncached_range_flushed( void *uncached_mapping, u32 offset, u32 size ); + +/** @} */ /* end group _mali_osk_low_level_memory */ + + +/** @addtogroup _mali_osk_notification + * + * User space notification framework + * + * Communication with user space of asynchronous events is performed through a + * synchronous call to the \ref u_k_api. + * + * Since the events are asynchronous, the events have to be queued until a + * synchronous U/K API call can be made by user-space. A U/K API call might also + * be received before any event has happened. Therefore the notifications the + * different subsystems wants to send to user space has to be queued for later + * reception, or a U/K API call has to be blocked until an event has occured. + * + * Typical uses of notifications are after running of jobs on the hardware or + * when changes to the system is detected that needs to be relayed to user + * space. + * + * After an event has occured user space has to be notified using some kind of + * message. The notification framework supports sending messages to waiting + * threads or queueing of messages until a U/K API call is made. + * + * The notification queue is a FIFO. There are no restrictions on the numbers + * of readers or writers in the queue. + * + * A message contains what user space needs to identifiy how to handle an + * event. This includes a type field and a possible type specific payload. + * + * A notification to user space is represented by a + * \ref _mali_osk_notification_t object. A sender gets hold of such an object + * using _mali_osk_notification_create(). The buffer given by the + * _mali_osk_notification_t::result_buffer field in the object is used to store + * any type specific data. The other fields are internal to the queue system + * and should not be touched. + * + * @{ */ + +/** @brief Create a notification object + * + * Returns a notification object which can be added to the queue of + * notifications pending for user space transfer. + * + * The implementation will initialize all members of the + * \ref _mali_osk_notification_t object. In particular, the + * _mali_osk_notification_t::result_buffer member will be initialized to point + * to \a size bytes of storage, and that storage will be suitably aligned for + * storage of any structure. That is, the created buffer meets the same + * requirements as _mali_osk_malloc(). + * + * The notification object must be deleted when not in use. Use + * _mali_osk_notification_delete() for deleting it. + * + * @note You \b must \b not call _mali_osk_free() on a \ref _mali_osk_notification_t, + * object, or on a _mali_osk_notification_t::result_buffer. You must only use + * _mali_osk_notification_delete() to free the resources assocaited with a + * \ref _mali_osk_notification_t object. + * + * @param type The notification type + * @param size The size of the type specific buffer to send + * @return Pointer to a notification object with a suitable buffer, or NULL on error. + */ +_mali_osk_notification_t *_mali_osk_notification_create( u32 type, u32 size ); + +/** @brief Delete a notification object + * + * This must be called to reclaim the resources of a notification object. This + * includes: + * - The _mali_osk_notification_t::result_buffer + * - The \ref _mali_osk_notification_t itself. + * + * A notification object \b must \b not be used after it has been deleted by + * _mali_osk_notification_delete(). + * + * In addition, the notification object may not be deleted while it is in a + * queue. That is, if it has been placed on a queue with + * _mali_osk_notification_queue_send(), then it must not be deleted until + * it has been received by a call to _mali_osk_notification_queue_receive(). + * Otherwise, the queue may be corrupted. + * + * @param object the notification object to delete. + */ +void _mali_osk_notification_delete( _mali_osk_notification_t *object ); + +/** @brief Create a notification queue + * + * Creates a notification queue which can be used to queue messages for user + * delivery and get queued messages from + * + * The queue is a FIFO, and has no restrictions on the numbers of readers or + * writers. + * + * When the queue is no longer in use, it must be terminated with + * \ref _mali_osk_notification_queue_term(). Failure to do so will result in a + * memory leak. + * + * @return Pointer to a new notification queue or NULL on error. + */ +_mali_osk_notification_queue_t *_mali_osk_notification_queue_init( void ); + +/** @brief Destroy a notification queue + * + * Destroys a notification queue and frees associated resources from the queue. + * + * A notification queue \b must \b not be destroyed in the following cases: + * - while there are \ref _mali_osk_notification_t objects in the queue. + * - while there are writers currently acting upon the queue. That is, while + * a thread is currently calling \ref _mali_osk_notification_queue_send() on + * the queue, or while a thread may call + * \ref _mali_osk_notification_queue_send() on the queue in the future. + * - while there are readers currently waiting upon the queue. That is, while + * a thread is currently calling \ref _mali_osk_notification_queue_receive() on + * the queue, or while a thread may call + * \ref _mali_osk_notification_queue_receive() on the queue in the future. + * + * Therefore, all \ref _mali_osk_notification_t objects must be flushed and + * deleted by the code that makes use of the notification queues, since only + * they know the structure of the _mali_osk_notification_t::result_buffer + * (even if it may only be a flat sturcture). + * + * @note Since the queue is a FIFO, the code using notification queues may + * create its own 'flush' type of notification, to assist in flushing the + * queue. + * + * Once the queue has been destroyed, it must not be used again. + * + * @param queue The queue to destroy + */ +void _mali_osk_notification_queue_term( _mali_osk_notification_queue_t *queue ); + +/** @brief Schedule notification for delivery + * + * When a \ref _mali_osk_notification_t object has been created successfully + * and set up, it may be added to the queue of objects waiting for user space + * transfer. + * + * The sending will not block if the queue is full. + * + * A \ref _mali_osk_notification_t object \b must \b not be put on two different + * queues at the same time, or enqueued twice onto a single queue before + * reception. However, it is acceptable for it to be requeued \em after reception + * from a call to _mali_osk_notification_queue_receive(), even onto the same queue. + * + * Again, requeuing must also not enqueue onto two different queues at the same + * time, or enqueue onto the same queue twice before reception. + * + * @param queue The notification queue to add this notification to + * @param object The entry to add + */ +void _mali_osk_notification_queue_send( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t *object ); + +#if MALI_STATE_TRACKING +/** @brief Receive a notification from a queue + * + * Check if a notification queue is empty. + * + * @param queue The queue to check. + * @return MALI_TRUE if queue is empty, otherwise MALI_FALSE. + */ +mali_bool _mali_osk_notification_queue_is_empty( _mali_osk_notification_queue_t *queue ); +#endif + +/** @brief Receive a notification from a queue + * + * Receives a single notification from the given queue. + * + * If no notifciations are ready the thread will sleep until one becomes ready. + * Therefore, notifications may not be received into an + * IRQ or 'atomic' context (that is, a context where sleeping is disallowed). + * + * @param queue The queue to receive from + * @param result Pointer to storage of a pointer of type + * \ref _mali_osk_notification_t*. \a result will be written to such that the + * expression \a (*result) will evaluate to a pointer to a valid + * \ref _mali_osk_notification_t object, or NULL if none were received. + * @return _MALI_OSK_ERR_OK on success. _MALI_OSK_ERR_RESTARTSYSCALL if the sleep was interrupted. + */ +_mali_osk_errcode_t _mali_osk_notification_queue_receive( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result ); + +/** @brief Dequeues a notification from a queue + * + * Receives a single notification from the given queue. + * + * If no notifciations are ready the function call will return an error code. + * + * @param queue The queue to receive from + * @param result Pointer to storage of a pointer of type + * \ref _mali_osk_notification_t*. \a result will be written to such that the + * expression \a (*result) will evaluate to a pointer to a valid + * \ref _mali_osk_notification_t object, or NULL if none were received. + * @return _MALI_OSK_ERR_OK on success, _MALI_OSK_ERR_ITEM_NOT_FOUND if queue was empty. + */ +_mali_osk_errcode_t _mali_osk_notification_queue_dequeue( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result ); + +/** @} */ /* end group _mali_osk_notification */ + + +/** @addtogroup _mali_osk_timer + * + * Timers use the OS's representation of time, which are 'ticks'. This is to + * prevent aliasing problems between the internal timer time, and the time + * asked for. + * + * @{ */ + +/** @brief Initialize a timer + * + * Allocates resources for a new timer, and initializes them. This does not + * start the timer. + * + * @return a pointer to the allocated timer object, or NULL on failure. + */ +_mali_osk_timer_t *_mali_osk_timer_init(void); + +/** @brief Start a timer + * + * It is an error to start a timer without setting the callback via + * _mali_osk_timer_setcallback(). + * + * It is an error to use this to start an already started timer. + * + * The timer will expire in \a ticks_to_expire ticks, at which point, the + * callback function will be invoked with the callback-specific data, + * as registered by _mali_osk_timer_setcallback(). + * + * @param tim the timer to start + * @param ticks_to_expire the amount of time in ticks for the timer to run + * before triggering. + */ +void _mali_osk_timer_add( _mali_osk_timer_t *tim, u32 ticks_to_expire ); + +/** @brief Modify a timer + * + * Set the absolute time at which a timer will expire, and start it if it is + * stopped. If \a expiry_tick is in the past (determined by + * _mali_osk_time_after() ), the timer fires immediately. + * + * It is an error to modify a timer without setting the callback via + * _mali_osk_timer_setcallback(). + * + * The timer will expire at absolute time \a expiry_tick, at which point, the + * callback function will be invoked with the callback-specific data, as set + * by _mali_osk_timer_setcallback(). + * + * @param tim the timer to modify, and start if necessary + * @param expiry_tick the \em absolute time in ticks at which this timer should + * trigger. + * + */ +void _mali_osk_timer_mod( _mali_osk_timer_t *tim, u32 expiry_tick); + +/** @brief Stop a timer, and block on its completion. + * + * Stop the timer. When the function returns, it is guaranteed that the timer's + * callback will not be running on any CPU core. + * + * Since stoping the timer blocks on compeletion of the callback, the callback + * may not obtain any mutexes that the caller holds. Otherwise, a deadlock will + * occur. + * + * @note While the callback itself is guaranteed to not be running, work + * enqueued on the IRQ work-queue by the timer (with + * \ref _mali_osk_irq_schedulework()) may still run. The timer callback and IRQ + * bottom-half handler must take this into account. + * + * It is legal to stop an already stopped timer. + * + * @param tim the timer to stop. + * + */ +void _mali_osk_timer_del( _mali_osk_timer_t *tim ); + +/** @brief Set a timer's callback parameters. + * + * This must be called at least once before a timer is started/modified. + * + * After a timer has been stopped or expires, the callback remains set. This + * means that restarting the timer will call the same function with the same + * parameters on expiry. + * + * @param tim the timer to set callback on. + * @param callback Function to call when timer expires + * @param data Function-specific data to supply to the function on expiry. + */ +void _mali_osk_timer_setcallback( _mali_osk_timer_t *tim, _mali_osk_timer_callback_t callback, void *data ); + +/** @brief Terminate a timer, and deallocate resources. + * + * The timer must first be stopped by calling _mali_osk_timer_del(). + * + * It is a programming error for _mali_osk_timer_term() to be called on: + * - timer that is currently running + * - a timer that is currently executing its callback. + * + * @param tim the timer to deallocate. + */ +void _mali_osk_timer_term( _mali_osk_timer_t *tim ); +/** @} */ /* end group _mali_osk_timer */ + + +/** @defgroup _mali_osk_time OSK Time functions + * + * \ref _mali_osk_time use the OS's representation of time, which are + * 'ticks'. This is to prevent aliasing problems between the internal timer + * time, and the time asked for. + * + * OS tick time is measured as a u32. The time stored in a u32 may either be + * an absolute time, or a time delta between two events. Whilst it is valid to + * use math opeartors to \em change the tick value represented as a u32, it + * is often only meaningful to do such operations on time deltas, rather than + * on absolute time. However, it is meaningful to add/subtract time deltas to + * absolute times. + * + * Conversion between tick time and milliseconds (ms) may not be loss-less, + * and are \em implementation \em depenedant. + * + * Code use OS time must take this into account, since: + * - a small OS time may (or may not) be rounded + * - a large time may (or may not) overflow + * + * @{ */ + +/** @brief Return whether ticka occurs after tickb + * + * Some OSs handle tick 'rollover' specially, and so can be more robust against + * tick counters rolling-over. This function must therefore be called to + * determine if a time (in ticks) really occurs after another time (in ticks). + * + * @param ticka ticka + * @param tickb tickb + * @return non-zero if ticka represents a time that occurs after tickb. + * Zero otherwise. + */ +int _mali_osk_time_after( u32 ticka, u32 tickb ); + +/** @brief Convert milliseconds to OS 'ticks' + * + * @param ms time interval in milliseconds + * @return the corresponding time interval in OS ticks. + */ +u32 _mali_osk_time_mstoticks( u32 ms ); + +/** @brief Convert OS 'ticks' to milliseconds + * + * @param ticks time interval in OS ticks. + * @return the corresponding time interval in milliseconds + */ +u32 _mali_osk_time_tickstoms( u32 ticks ); + + +/** @brief Get the current time in OS 'ticks'. + * @return the current time in OS 'ticks'. + */ +u32 _mali_osk_time_tickcount( void ); + +/** @brief Cause a microsecond delay + * + * The delay will have microsecond resolution, and is necessary for correct + * operation of the driver. At worst, the delay will be \b at least \a usecs + * microseconds, and so may be (significantly) more. + * + * This function may be implemented as a busy-wait, which is the most sensible + * implementation. On OSs where there are situations in which a thread must not + * sleep, this is definitely implemented as a busy-wait. + * + * @param usecs the number of microseconds to wait for. + */ +void _mali_osk_time_ubusydelay( u32 usecs ); + +/** @brief Return time in nano seconds, since any given reference. + * + * @return Time in nano seconds + */ +u64 _mali_osk_time_get_ns( void ); + + +/** @} */ /* end group _mali_osk_time */ + +/** @defgroup _mali_osk_math OSK Math + * @{ */ + +/** @brief Count Leading Zeros (Little-endian) + * + * @note This function must be implemented to support the reference + * implementation of _mali_osk_find_first_zero_bit, as defined in + * mali_osk_bitops.h. + * + * @param val 32-bit words to count leading zeros on + * @return the number of leading zeros. + */ +u32 _mali_osk_clz( u32 val ); +/** @} */ /* end group _mali_osk_math */ + + +/** @addtogroup _mali_osk_miscellaneous + * @{ */ + +/** @brief Output a device driver debug message. + * + * The interpretation of \a fmt is the same as the \c format parameter in + * _mali_osu_vsnprintf(). + * + * @param fmt a _mali_osu_vsnprintf() style format string + * @param ... a variable-number of parameters suitable for \a fmt + */ +void _mali_osk_dbgmsg( const char *fmt, ... ); + +/** @brief Print fmt into buf. + * + * The interpretation of \a fmt is the same as the \c format parameter in + * _mali_osu_vsnprintf(). + * + * @param buf a pointer to the result buffer + * @param size the total number of bytes allowed to write to \a buf + * @param fmt a _mali_osu_vsnprintf() style format string + * @param ... a variable-number of parameters suitable for \a fmt + */ +u32 _mali_osk_snprintf( char *buf, u32 size, const char *fmt, ... ); + +/** @brief Abnormal process abort. + * + * Terminates the caller-process if this function is called. + * + * This function will be called from Debug assert-macros in mali_kernel_common.h. + * + * This function will never return - because to continue from a Debug assert + * could cause even more problems, and hinder debugging of the initial problem. + * + * This function is only used in Debug builds, and is not used in Release builds. + */ +void _mali_osk_abort(void); + +/** @brief Sets breakpoint at point where function is called. + * + * This function will be called from Debug assert-macros in mali_kernel_common.h, + * to assist in debugging. If debugging at this level is not required, then this + * function may be implemented as a stub. + * + * This function is only used in Debug builds, and is not used in Release builds. + */ +void _mali_osk_break(void); + +/** @brief Return an identificator for calling process. + * + * @return Identificator for calling process. + */ +u32 _mali_osk_get_pid(void); + +/** @brief Return an identificator for calling thread. + * + * @return Identificator for calling thread. + */ +u32 _mali_osk_get_tid(void); + +/** @brief Return a handle to the current task. + * + * @return An OS-specific handle to the current task. + */ +void * _mali_osk_get_task(void); + +/** @} */ /* end group _mali_osk_miscellaneous */ + +/** @} */ /* end group osuapi */ + +/** @} */ /* end group uddapi */ + + +#ifdef __cplusplus +} +#endif + +#include "mali_osk_specific.h" /* include any per-os specifics */ + +/* Check standard inlines */ +#ifndef MALI_STATIC_INLINE + #error MALI_STATIC_INLINE not defined on your OS +#endif + +#ifndef MALI_NON_STATIC_INLINE + #error MALI_NON_STATIC_INLINE not defined on your OS +#endif + +#endif /* __MALI_OSK_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_osk_bitops.h b/drivers/gpu/arm/mali/common/mali_osk_bitops.h new file mode 100644 index 000000000000..ada1488eef67 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_osk_bitops.h @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_bitops.h + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#ifndef __MALI_OSK_BITOPS_H__ +#define __MALI_OSK_BITOPS_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +MALI_STATIC_INLINE void _mali_internal_clear_bit( u32 bit, u32 *addr ) +{ + MALI_DEBUG_ASSERT( bit < 32 ); + MALI_DEBUG_ASSERT( NULL != addr ); + + (*addr) &= ~(1 << bit); +} + +MALI_STATIC_INLINE void _mali_internal_set_bit( u32 bit, u32 *addr ) +{ + MALI_DEBUG_ASSERT( bit < 32 ); + MALI_DEBUG_ASSERT( NULL != addr ); + + (*addr) |= (1 << bit); +} + +MALI_STATIC_INLINE u32 _mali_internal_test_bit( u32 bit, u32 value ) +{ + MALI_DEBUG_ASSERT( bit < 32 ); + return value & (1 << bit); +} + +MALI_STATIC_INLINE int _mali_internal_find_first_zero_bit( u32 value ) +{ + u32 inverted; + u32 negated; + u32 isolated; + u32 leading_zeros; + + /* Begin with xxx...x0yyy...y, where ys are 1, number of ys is in range 0..31 */ + inverted = ~value; /* zzz...z1000...0 */ + /* Using count_trailing_zeros on inverted value - + * See ARM System Developers Guide for details of count_trailing_zeros */ + + /* Isolate the zero: it is preceeded by a run of 1s, so add 1 to it */ + negated = (u32)-inverted ; /* -a == ~a + 1 (mod 2^n) for n-bit numbers */ + /* negated = xxx...x1000...0 */ + + isolated = negated & inverted ; /* xxx...x1000...0 & zzz...z1000...0, zs are ~xs */ + /* And so the first zero bit is in the same position as the 1 == number of 1s that preceeded it + * Note that the output is zero if value was all 1s */ + + leading_zeros = _mali_osk_clz( isolated ); + + return 31 - leading_zeros; +} + + +/** @defgroup _mali_osk_bitops OSK Non-atomic Bit-operations + * @{ */ + +/** + * These bit-operations do not work atomically, and so locks must be used if + * atomicity is required. + * + * Reference implementations for Little Endian are provided, and so it should + * not normally be necessary to re-implement these. Efficient bit-twiddling + * techniques are used where possible, implemented in portable C. + * + * Note that these reference implementations rely on _mali_osk_clz() being + * implemented. + */ + +/** @brief Clear a bit in a sequence of 32-bit words + * @param nr bit number to clear, starting from the (Little-endian) least + * significant bit + * @param addr starting point for counting. + */ +MALI_STATIC_INLINE void _mali_osk_clear_nonatomic_bit( u32 nr, u32 *addr ) +{ + addr += nr >> 5; /* find the correct word */ + nr = nr & ((1 << 5)-1); /* The bit number within the word */ + + _mali_internal_clear_bit( nr, addr ); +} + +/** @brief Set a bit in a sequence of 32-bit words + * @param nr bit number to set, starting from the (Little-endian) least + * significant bit + * @param addr starting point for counting. + */ +MALI_STATIC_INLINE void _mali_osk_set_nonatomic_bit( u32 nr, u32 *addr ) +{ + addr += nr >> 5; /* find the correct word */ + nr = nr & ((1 << 5)-1); /* The bit number within the word */ + + _mali_internal_set_bit( nr, addr ); +} + +/** @brief Test a bit in a sequence of 32-bit words + * @param nr bit number to test, starting from the (Little-endian) least + * significant bit + * @param addr starting point for counting. + * @return zero if bit was clear, non-zero if set. Do not rely on the return + * value being related to the actual word under test. + */ +MALI_STATIC_INLINE u32 _mali_osk_test_bit( u32 nr, u32 *addr ) +{ + addr += nr >> 5; /* find the correct word */ + nr = nr & ((1 << 5)-1); /* The bit number within the word */ + + return _mali_internal_test_bit( nr, *addr ); +} + +/* Return maxbit if not found */ +/** @brief Find the first zero bit in a sequence of 32-bit words + * @param addr starting point for search. + * @param maxbit the maximum number of bits to search + * @return the number of the first zero bit found, or maxbit if none were found + * in the specified range. + */ +MALI_STATIC_INLINE u32 _mali_osk_find_first_zero_bit( const u32 *addr, u32 maxbit ) +{ + u32 total; + + for ( total = 0; total < maxbit; total += 32, ++addr ) + { + int result; + result = _mali_internal_find_first_zero_bit( *addr ); + + /* non-negative signifies the bit was found */ + if ( result >= 0 ) + { + total += (u32)result; + break; + } + } + + /* Now check if we reached maxbit or above */ + if ( total >= maxbit ) + { + total = maxbit; + } + + return total; /* either the found bit nr, or maxbit if not found */ +} +/** @} */ /* end group _mali_osk_bitops */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_OSK_BITOPS_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_osk_list.h b/drivers/gpu/arm/mali/common/mali_osk_list.h new file mode 100644 index 000000000000..5987b0a9149e --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_osk_list.h @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_list.h + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#ifndef __MALI_OSK_LIST_H__ +#define __MALI_OSK_LIST_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +MALI_STATIC_INLINE void __mali_osk_list_add(_mali_osk_list_t *new_entry, _mali_osk_list_t *prev, _mali_osk_list_t *next) +{ + next->prev = new_entry; + new_entry->next = next; + new_entry->prev = prev; + prev->next = new_entry; +} + +MALI_STATIC_INLINE void __mali_osk_list_del(_mali_osk_list_t *prev, _mali_osk_list_t *next) +{ + next->prev = prev; + prev->next = next; +} + +/** @addtogroup _mali_osk_list + * @{ */ + +/** Reference implementations of Doubly-linked Circular Lists are provided. + * There is often no need to re-implement these. + * + * @note The implementation may differ subtly from any lists the OS provides. + * For this reason, these lists should not be mixed with OS-specific lists + * inside the OSK/UKK implementation. */ + +/** @brief Initialize a list element. + * + * All list elements must be initialized before use. + * + * Do not use on any list element that is present in a list without using + * _mali_osk_list_del first, otherwise this will break the list. + * + * @param list the list element to initialize + */ +MALI_STATIC_INLINE void _mali_osk_list_init( _mali_osk_list_t *list ) +{ + list->next = list; + list->prev = list; +} + +/** @brief Insert a single list element after an entry in a list + * + * As an example, if this is inserted to the head of a list, then this becomes + * the first element of the list. + * + * Do not use to move list elements from one list to another, as it will break + * the originating list. + * + * + * @param newlist the list element to insert + * @param list the list in which to insert. The new element will be the next + * entry in this list + */ +MALI_STATIC_INLINE void _mali_osk_list_add( _mali_osk_list_t *new_entry, _mali_osk_list_t *list ) +{ + __mali_osk_list_add(new_entry, list, list->next); +} + +/** @brief Insert a single list element before an entry in a list + * + * As an example, if this is inserted to the head of a list, then this becomes + * the last element of the list. + * + * Do not use to move list elements from one list to another, as it will break + * the originating list. + * + * @param newlist the list element to insert + * @param list the list in which to insert. The new element will be the previous + * entry in this list + */ +MALI_STATIC_INLINE void _mali_osk_list_addtail( _mali_osk_list_t *new_entry, _mali_osk_list_t *list ) +{ + __mali_osk_list_add(new_entry, list->prev, list); +} + +/** @brief Remove a single element from a list + * + * The element will no longer be present in the list. The removed list element + * will be uninitialized, and so should not be traversed. It must be + * initialized before further use. + * + * @param list the list element to remove. + */ +MALI_STATIC_INLINE void _mali_osk_list_del( _mali_osk_list_t *list ) +{ + __mali_osk_list_del(list->prev, list->next); +} + +/** @brief Remove a single element from a list, and re-initialize it + * + * The element will no longer be present in the list. The removed list element + * will initialized, and so can be used as normal. + * + * @param list the list element to remove and initialize. + */ +MALI_STATIC_INLINE void _mali_osk_list_delinit( _mali_osk_list_t *list ) +{ + __mali_osk_list_del(list->prev, list->next); + _mali_osk_list_init(list); +} + +/** @brief Determine whether a list is empty. + * + * An empty list is one that contains a single element that points to itself. + * + * @param list the list to check. + * @return non-zero if the list is empty, and zero otherwise. + */ +MALI_STATIC_INLINE int _mali_osk_list_empty( _mali_osk_list_t *list ) +{ + return list->next == list; +} + +/** @brief Move a list element from one list to another. + * + * The list element must be initialized. + * + * As an example, moving a list item to the head of a new list causes this item + * to be the first element in the new list. + * + * @param move the list element to move + * @param list the new list into which the element will be inserted, as the next + * element in the list. + */ +MALI_STATIC_INLINE void _mali_osk_list_move( _mali_osk_list_t *move_entry, _mali_osk_list_t *list ) +{ + __mali_osk_list_del(move_entry->prev, move_entry->next); + _mali_osk_list_add(move_entry, list); +} + +/** @brief Join two lists + * + * The list element must be initialized. + * + * Allows you to join a list into another list at a specific location + * + * @param list the new list to add + * @param at the location in a list to add the new list into + */ +MALI_STATIC_INLINE void _mali_osk_list_splice( _mali_osk_list_t *list, _mali_osk_list_t *at ) +{ + if (!_mali_osk_list_empty(list)) + { + /* insert all items from 'list' after 'at' */ + _mali_osk_list_t *first = list->next; + _mali_osk_list_t *last = list->prev; + _mali_osk_list_t *split = at->next; + + first->prev = at; + at->next = first; + + last->next = split; + split->prev = last; + } +} +/** @} */ /* end group _mali_osk_list */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_OSK_LIST_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_osk_mali.h b/drivers/gpu/arm/mali/common/mali_osk_mali.h new file mode 100644 index 000000000000..704a436523bc --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_osk_mali.h @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_mali.h + * Defines the OS abstraction layer which is specific for the Mali kernel device driver (OSK) + */ + +#ifndef __MALI_OSK_MALI_H__ +#define __MALI_OSK_MALI_H__ + +#include <mali_osk.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** @addtogroup _mali_osk_miscellaneous + * @{ */ + +/** @brief Initialize the OSK layer + * + * This function is used to setup any initialization of OSK functionality, if + * required. + * + * This must be the first function called from the common code, specifically, + * from the common code entry-point, mali_kernel_constructor. + * + * The OS-integration into the OS's kernel must handle calling of + * mali_kernel_constructor when the device driver is loaded. + * + * @return _MALI_OSK_ERR_OK on success, or a suitable _mali_osk_errcode_t on + * failure. + */ +_mali_osk_errcode_t _mali_osk_init( void ); + +/** @brief Terminate the OSK layer + * + * This function is used to terminate any resources initialized by + * _mali_osk_init. + * + * This must be the last function called from the common code, specifically, + * from the common code closedown function, mali_kernel_destructor, and the + * error path in mali_kernel_constructor. + * + * The OS-integration into the OS's kernel must handle calling of + * mali_kernel_destructor when the device driver is terminated. + */ +void _mali_osk_term( void ); + +/** @brief Read the Mali Resource configuration + * + * Populates a _mali_arch_resource_t array from configuration settings, which + * are stored in an OS-specific way. + * + * For example, these may be compiled in to a static structure, or read from + * the filesystem at startup. + * + * On failure, do not call _mali_osk_resources_term. + * + * @param arch_config a pointer to the store the pointer to the resources + * @param num_resources the number of resources read + * @return _MALI_OSK_ERR_OK on success. _MALI_OSK_ERR_NOMEM on allocation + * error. For other failures, a suitable _mali_osk_errcode_t is returned. + */ +_mali_osk_errcode_t _mali_osk_resources_init( _mali_osk_resource_t **arch_config, u32 *num_resources ); + +/** @brief Free resources allocated by _mali_osk_resources_init. + * + * Frees the _mali_arch_resource_t array allocated by _mali_osk_resources_init + * + * @param arch_config a pointer to the stored the pointer to the resources + * @param num_resources the number of resources in the array + */ +void _mali_osk_resources_term( _mali_osk_resource_t **arch_config, u32 num_resources); +/** @} */ /* end group _mali_osk_miscellaneous */ + +/** @addtogroup _mali_osk_low_level_memory + * @{ */ + +/** @brief Initialize a user-space accessible memory range + * + * This initializes a virtual address range such that it is reserved for the + * current process, but does not map any physical pages into this range. + * + * This function may initialize or adjust any members of the + * mali_memory_allocation \a descriptor supplied, before the physical pages are + * mapped in with _mali_osk_mem_mapregion_map(). + * + * The function will always be called with MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE + * set in \a descriptor->flags. It is an error to call this function without + * setting this flag. Otherwise, \a descriptor->flags bits are reserved for + * future expansion + * + * The \a descriptor's process_addr_mapping_info member can be modified to + * allocate OS-specific information. Note that on input, this will be a + * ukk_private word from the U/K inteface, as inserted by _mali_ukk_mem_mmap(). + * This is used to pass information from the U/K interface to the OSK interface, + * if necessary. The precise usage of the process_addr_mapping_info member + * depends on the U/K implementation of _mali_ukk_mem_mmap(). + * + * Therefore, the U/K implementation of _mali_ukk_mem_mmap() and the OSK + * implementation of _mali_osk_mem_mapregion_init() must agree on the meaning and + * usage of the ukk_private word and process_addr_mapping_info member. + * + * Refer to \ref u_k_api for more information on the U/K interface. + * + * On successful return, \a descriptor's mapping member will be correct for + * use with _mali_osk_mem_mapregion_term() and _mali_osk_mem_mapregion_map(). + * + * @param descriptor the mali_memory_allocation to initialize. + */ +_mali_osk_errcode_t _mali_osk_mem_mapregion_init( mali_memory_allocation * descriptor ); + +/** @brief Terminate a user-space accessible memory range + * + * This terminates a virtual address range reserved in the current user process, + * where none, some or all of the virtual address ranges have mappings to + * physical pages. + * + * It will unmap any physical pages that had been mapped into a reserved + * virtual address range for the current process, and then releases the virtual + * address range. Any extra book-keeping information or resources allocated + * during _mali_osk_mem_mapregion_init() will also be released. + * + * The \a descriptor itself is not freed - this must be handled by the caller of + * _mali_osk_mem_mapregion_term(). + * + * The function will always be called with MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE + * set in descriptor->flags. It is an error to call this function without + * setting this flag. Otherwise, descriptor->flags bits are reserved for + * future expansion + * + * @param descriptor the mali_memory_allocation to terminate. + */ +void _mali_osk_mem_mapregion_term( mali_memory_allocation * descriptor ); + +/** @brief Map physical pages into a user process's virtual address range + * + * This is used to map a number of physically contigous pages into a + * user-process's virtual address range, which was previously reserved by a + * call to _mali_osk_mem_mapregion_init(). + * + * This need not provide a mapping for the entire virtual address range + * reserved for \a descriptor - it may be used to map single pages per call. + * + * The function will always be called with MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE + * set in \a descriptor->flags. It is an error to call this function without + * setting this flag. Otherwise, \a descriptor->flags bits are reserved for + * future expansion + * + * The function may supply \a *phys_addr == \ref MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC. + * In this case, \a size must be set to \ref _MALI_OSK_CPU_PAGE_SIZE, and the function + * will allocate the physical page itself. The physical address of the + * allocated page will be returned through \a phys_addr. + * + * It is an error to set \a size != \ref _MALI_OSK_CPU_PAGE_SIZE while + * \a *phys_addr == \ref MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC, + * since it is not always possible for OSs to support such a setting through this + * interface. + * + * @note \b IMPORTANT: This code must validate the input parameters. If the + * range defined by \a offset and \a size is outside the range allocated in + * \a descriptor, then this function \b MUST not attempt any mapping, and must + * instead return a suitable \ref _mali_osk_errcode_t \b failure code. + * + * @param[in,out] descriptor the mali_memory_allocation representing the + * user-process's virtual address range to map into. + * + * @param[in] offset the offset into the virtual address range. This is only added + * to the mapping member of the \a descriptor, and not the \a phys_addr parameter. + * It must be a multiple of \ref _MALI_OSK_CPU_PAGE_SIZE. + * + * @param[in,out] phys_addr a pointer to the physical base address to begin the + * mapping from. If \a size == \ref _MALI_OSK_CPU_PAGE_SIZE and + * \a *phys_addr == \ref MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC, then this + * function will allocate the physical page itself, and return the + * physical address of the page through \a phys_addr, which will be aligned to + * \ref _MALI_OSK_CPU_PAGE_SIZE. Otherwise, \a *phys_addr must be aligned to + * \ref _MALI_OSK_CPU_PAGE_SIZE, and is unmodified after the call. + * \a phys_addr is unaffected by the \a offset parameter. + * + * @param[in] size the number of bytes to map in. This must be a multiple of + * \ref _MALI_OSK_CPU_PAGE_SIZE. + * + * @return _MALI_OSK_ERR_OK on sucess, otherwise a _mali_osk_errcode_t value + * on failure + * + * @note could expand to use _mali_osk_mem_mapregion_flags_t instead of + * \ref MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC, but note that we must + * also modify the mali process address manager in the mmu/memory engine code. + */ +_mali_osk_errcode_t _mali_osk_mem_mapregion_map( mali_memory_allocation * descriptor, u32 offset, u32 *phys_addr, u32 size ); + + +/** @brief Unmap physical pages from a user process's virtual address range + * + * This is used to unmap a number of physically contigous pages from a + * user-process's virtual address range, which were previously mapped by a + * call to _mali_osk_mem_mapregion_map(). If the range specified was allocated + * from OS memory, then that memory will be returned to the OS. Whilst pages + * will be mapped out, the Virtual address range remains reserved, and at the + * same base address. + * + * When this function is used to unmap pages from OS memory + * (_mali_osk_mem_mapregion_map() was called with *phys_addr == + * \ref MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC), then the \a flags must + * include \ref _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR. This is because + * it is not always easy for an OS implementation to discover whether the + * memory was OS allocated or not (and so, how it should release the memory). + * + * For this reason, only a range of pages of the same allocation type (all OS + * allocated, or none OS allocacted) may be unmapped in one call. Multiple + * calls must be made if allocations of these different types exist across the + * entire region described by the \a descriptor. + * + * The function will always be called with MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE + * set in \a descriptor->flags. It is an error to call this function without + * setting this flag. Otherwise, \a descriptor->flags bits are reserved for + * future expansion + * + * @param[in,out] descriptor the mali_memory_allocation representing the + * user-process's virtual address range to map into. + * + * @param[in] offset the offset into the virtual address range. This is only added + * to the mapping member of the \a descriptor. \a offset must be a multiple of + * \ref _MALI_OSK_CPU_PAGE_SIZE. + * + * @param[in] size the number of bytes to unmap. This must be a multiple of + * \ref _MALI_OSK_CPU_PAGE_SIZE. + * + * @param[in] flags specifies how the memory should be unmapped. For a range + * of pages that were originally OS allocated, this must have + * \ref _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR set. + */ +void _mali_osk_mem_mapregion_unmap( mali_memory_allocation * descriptor, u32 offset, u32 size, _mali_osk_mem_mapregion_flags_t flags ); +/** @} */ /* end group _mali_osk_low_level_memory */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_OSK_MALI_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_osk_profiling.h b/drivers/gpu/arm/mali/common/mali_osk_profiling.h new file mode 100644 index 000000000000..8c41b0a9fa88 --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_osk_profiling.h @@ -0,0 +1,181 @@ +/** + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms of + * such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained + * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_OSK_PROFILING_H__ +#define __MALI_OSK_PROFILING_H__ + +#if MALI_TIMELINE_PROFILING_ENABLED + +#if defined (CONFIG_TRACEPOINTS) && !MALI_INTERNAL_TIMELINE_PROFILING_ENABLED +#include "mali_linux_trace.h" +#endif /* CONFIG_TRACEPOINTS && !MALI_INTERNAL_TIMELINE_PROFILING_ENABLED */ + +#include "mali_cinstr_profiling_events_m200.h" + +#define MALI_PROFILING_MAX_BUFFER_ENTRIES 1048576 + +#define MALI_PROFILING_PP_CORE_COUNTER0_OFFSET(core_number) (((core_number) * 2) + COUNTER_FP0_C0) +#define MALI_PROFILING_PP_CORE_COUNTER1_OFFSET(core_number) (((core_number) * 2) + COUNTER_FP0_C1) + +/** @defgroup _mali_osk_profiling External profiling connectivity + * @{ */ + +/** + * Initialize the profiling module. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_osk_profiling_init(mali_bool auto_start); + +/* + * Terminate the profiling module. + */ +void _mali_osk_profiling_term(void); + +/** + * Start recording profiling data + * + * The specified limit will determine how large the capture buffer is. + * MALI_PROFILING_MAX_BUFFER_ENTRIES determines the maximum size allowed by the device driver. + * + * @param limit The desired maximum number of events to record on input, the actual maximum on output. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_osk_profiling_start(u32 * limit); + +/** + * Add an profiling event + * + * @param event_id The event identificator. + * @param data0 First data parameter, depending on event_id specified. + * @param data1 Second data parameter, depending on event_id specified. + * @param data2 Third data parameter, depending on event_id specified. + * @param data3 Fourth data parameter, depending on event_id specified. + * @param data4 Fifth data parameter, depending on event_id specified. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +#if defined (CONFIG_TRACEPOINTS) && !MALI_INTERNAL_TIMELINE_PROFILING_ENABLED +/* + * On platforms where we are using Linux tracepoints and we aren't forcing + * internal profiling we can call through to the tracepoint directly and + * avoid the overhead of the function call. + */ +#define _mali_osk_profiling_add_event(event_id, data0, data1, data2, data3, data4) \ + trace_mali_timeline_event((event_id), (data0), (data1), (u32)_mali_osk_get_task(), (data3), (data4)) +#else +void _mali_osk_profiling_add_event(u32 event_id, u32 data0, u32 data1, u32 data2, u32 data3, u32 data4); +#endif /* CONFIG_TRACEPOINTS && !MALI_INTERNAL_TIMELINE_PROFILING_ENABLED */ + +/** + * Report a hardware counter event. + * + * @param counter_id The ID of the counter. + * @param value The value of the counter. + */ +#if defined (CONFIG_TRACEPOINTS) && !MALI_INTERNAL_TIMELINE_PROFILING_ENABLED +/* + * On platforms where we are using Linux tracepoints and we aren't forcing + * internal profiling we can call through to the tracepoint directly and + * avoid the overhead of the function call. + */ +#define _mali_osk_profiling_report_hw_counter trace_mali_hw_counter +#else +void _mali_osk_profiling_report_hw_counter(u32 counter_id, u32 value); +#endif /* CONFIG_TRACEPOINTS && !MALI_INTERNAL_TIMELINE_PROFILING_ENABLED */ + +/** + * Query a hardware counter. Given a counter ID, check which event the + * counter should report and update the given pointer with that event + * number before returning MALI_TRUE. If the counter has been disabled + * by the profiling tool, returns MALI_FALSE and does not update the + * pointer. + * + * MALI_FALSE is also returned if the counter is not a valid hardware + * counter ID. In this case the event value is not updated. + * + * @param counter_id The counter ID. + * @param event_id A pointer to a u32 value that will be updated with + * the event ID that should be counted, should the counter have been + * enabled by the profiling tool. + * + * @return MALI_TRUE if the counter should be enabled, MALI_FALSE otherwise. + */ +mali_bool _mali_osk_profiling_query_hw_counter(u32 counter_id, u32 *event_id); + +/** + * Stop recording profiling data + * + * @param count Returns the number of recorded events. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_osk_profiling_stop(u32 * count); + +/** + * Retrieves the number of events that can be retrieved + * + * @return The number of recorded events that can be retrieved. + */ +u32 _mali_osk_profiling_get_count(void); + +/** + * Retrieve an event + * + * @param index Event index (start with 0 and continue until this function fails to retrieve all events) + * @param timestamp The timestamp for the retrieved event will be stored here. + * @param event_id The event ID for the retrieved event will be stored here. + * @param data The 5 data values for the retrieved event will be stored here. + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_osk_profiling_get_event(u32 index, u64* timestamp, u32* event_id, u32 data[5]); + +/** + * Clear the recorded buffer. + * + * This is needed in order to start another recording. + * + * @return _MALI_OSK_ERR_OK on success, otherwise failure. + */ +_mali_osk_errcode_t _mali_osk_profiling_clear(void); + +/** + * Checks if a recording of profiling data is in progress + * + * @return MALI_TRUE if recording of profiling data is in progress, MALI_FALSE if not + */ +mali_bool _mali_osk_profiling_is_recording(void); + +/** + * Checks if profiling data is available for retrival + * + * @return MALI_TRUE if profiling data is avaiable, MALI_FALSE if not + */ +mali_bool _mali_osk_profiling_have_recording(void); + +/** + * Enable or disable profiling events as default for new sessions (applications) + * + * @param enable MALI_TRUE if profiling events should be turned on, otherwise MALI_FALSE + */ +void _mali_osk_profiling_set_default_enable_state(mali_bool enable); + +/** + * Get current default enable state for new sessions (applications) + * + * @return MALI_TRUE if profiling events should be turned on, otherwise MALI_FALSE + */ +mali_bool _mali_osk_profiling_get_default_enable_state(void); + +/** @} */ /* end group _mali_osk_profiling */ + +#endif /* MALI_TIMELINE_PROFILING_ENABLED */ + +#endif /* __MALI_OSK_PROFILING_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_uk_types.h b/drivers/gpu/arm/mali/common/mali_uk_types.h new file mode 100644 index 000000000000..85bb82d0a90e --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_uk_types.h @@ -0,0 +1,1167 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_uk_types.h + * Defines the types and constants used in the user-kernel interface + */ + +#ifndef __MALI_UK_TYPES_H__ +#define __MALI_UK_TYPES_H__ + +/* + * NOTE: Because this file can be included from user-side and kernel-side, + * it is up to the includee to ensure certain typedefs (e.g. u32) are already + * defined when #including this. + */ +#include "regs/mali_200_regs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup uddapi Unified Device Driver (UDD) APIs + * + * @{ + */ + +/** + * @addtogroup u_k_api UDD User/Kernel Interface (U/K) APIs + * + * @{ + */ + +/** @defgroup _mali_uk_core U/K Core + * @{ */ + +/** Definition of subsystem numbers, to assist in creating a unique identifier + * for each U/K call. + * + * @see _mali_uk_functions */ +typedef enum +{ + _MALI_UK_CORE_SUBSYSTEM, /**< Core Group of U/K calls */ + _MALI_UK_MEMORY_SUBSYSTEM, /**< Memory Group of U/K calls */ + _MALI_UK_PP_SUBSYSTEM, /**< Fragment Processor Group of U/K calls */ + _MALI_UK_GP_SUBSYSTEM, /**< Vertex Processor Group of U/K calls */ + _MALI_UK_PROFILING_SUBSYSTEM, /**< Profiling Group of U/K calls */ + _MALI_UK_PMM_SUBSYSTEM, /**< Power Management Module Group of U/K calls */ + _MALI_UK_VSYNC_SUBSYSTEM, /**< VSYNC Group of U/K calls */ +} _mali_uk_subsystem_t; + +/** Within a function group each function has its unique sequence number + * to assist in creating a unique identifier for each U/K call. + * + * An ordered pair of numbers selected from + * ( \ref _mali_uk_subsystem_t,\ref _mali_uk_functions) will uniquely identify the + * U/K call across all groups of functions, and all functions. */ +typedef enum +{ + /** Core functions */ + + _MALI_UK_OPEN = 0, /**< _mali_ukk_open() */ + _MALI_UK_CLOSE, /**< _mali_ukk_close() */ + _MALI_UK_GET_SYSTEM_INFO_SIZE, /**< _mali_ukk_get_system_info_size() */ + _MALI_UK_GET_SYSTEM_INFO, /**< _mali_ukk_get_system_info() */ + _MALI_UK_WAIT_FOR_NOTIFICATION, /**< _mali_ukk_wait_for_notification() */ + _MALI_UK_GET_API_VERSION, /**< _mali_ukk_get_api_version() */ + _MALI_UK_POST_NOTIFICATION, /**< _mali_ukk_post_notification() */ + + /** Memory functions */ + + _MALI_UK_INIT_MEM = 0, /**< _mali_ukk_init_mem() */ + _MALI_UK_TERM_MEM, /**< _mali_ukk_term_mem() */ + _MALI_UK_GET_BIG_BLOCK, /**< _mali_ukk_get_big_block() */ + _MALI_UK_FREE_BIG_BLOCK, /**< _mali_ukk_free_big_block() */ + _MALI_UK_MAP_MEM, /**< _mali_ukk_mem_mmap() */ + _MALI_UK_UNMAP_MEM, /**< _mali_ukk_mem_munmap() */ + _MALI_UK_QUERY_MMU_PAGE_TABLE_DUMP_SIZE, /**< _mali_ukk_mem_get_mmu_page_table_dump_size() */ + _MALI_UK_DUMP_MMU_PAGE_TABLE, /**< _mali_ukk_mem_dump_mmu_page_table() */ + _MALI_UK_ATTACH_UMP_MEM, /**< _mali_ukk_attach_ump_mem() */ + _MALI_UK_RELEASE_UMP_MEM, /**< _mali_ukk_release_ump_mem() */ + _MALI_UK_MAP_EXT_MEM, /**< _mali_uku_map_external_mem() */ + _MALI_UK_UNMAP_EXT_MEM, /**< _mali_uku_unmap_external_mem() */ + _MALI_UK_VA_TO_MALI_PA, /**< _mali_uku_va_to_mali_pa() */ + + /** Common functions for each core */ + + _MALI_UK_START_JOB = 0, /**< Start a Fragment/Vertex Processor Job on a core */ + _MALI_UK_ABORT_JOB, /**< Abort a job */ + _MALI_UK_GET_NUMBER_OF_CORES, /**< Get the number of Fragment/Vertex Processor cores */ + _MALI_UK_GET_CORE_VERSION, /**< Get the Fragment/Vertex Processor version compatible with all cores */ + + /** Fragment Processor Functions */ + + _MALI_UK_PP_START_JOB = _MALI_UK_START_JOB, /**< _mali_ukk_pp_start_job() */ + _MALI_UK_PP_ABORT_JOB = _MALI_UK_ABORT_JOB, /**< _mali_ukk_pp_abort_job() */ + _MALI_UK_GET_PP_NUMBER_OF_CORES = _MALI_UK_GET_NUMBER_OF_CORES, /**< _mali_ukk_get_pp_number_of_cores() */ + _MALI_UK_GET_PP_CORE_VERSION = _MALI_UK_GET_CORE_VERSION, /**< _mali_ukk_get_pp_core_version() */ + + /** Vertex Processor Functions */ + + _MALI_UK_GP_START_JOB = _MALI_UK_START_JOB, /**< _mali_ukk_gp_start_job() */ + _MALI_UK_GP_ABORT_JOB = _MALI_UK_ABORT_JOB, /**< _mali_ukk_gp_abort_job() */ + _MALI_UK_GET_GP_NUMBER_OF_CORES = _MALI_UK_GET_NUMBER_OF_CORES, /**< _mali_ukk_get_gp_number_of_cores() */ + _MALI_UK_GET_GP_CORE_VERSION = _MALI_UK_GET_CORE_VERSION, /**< _mali_ukk_get_gp_core_version() */ + _MALI_UK_GP_SUSPEND_RESPONSE, /**< _mali_ukk_gp_suspend_response() */ + + /** Profiling functions */ + + _MALI_UK_PROFILING_START = 0, /**< __mali_uku_profiling_start() */ + _MALI_UK_PROFILING_ADD_EVENT, /**< __mali_uku_profiling_add_event() */ + _MALI_UK_PROFILING_STOP, /**< __mali_uku_profiling_stop() */ + _MALI_UK_PROFILING_GET_EVENT, /**< __mali_uku_profiling_get_event() */ + _MALI_UK_PROFILING_CLEAR, /**< __mali_uku_profiling_clear() */ + _MALI_UK_PROFILING_GET_CONFIG, /**< __mali_uku_profiling_get_config() */ + +#if USING_MALI_PMM + /** Power Management Module Functions */ + _MALI_UK_PMM_EVENT_MESSAGE = 0, /**< Raise an event message */ +#endif + + /** VSYNC reporting fuctions */ + _MALI_UK_VSYNC_EVENT_REPORT = 0, /**< _mali_ukk_vsync_event_report() */ + +} _mali_uk_functions; + +/** @brief Get the size necessary for system info + * + * @see _mali_ukk_get_system_info_size() + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 size; /**< [out] size of buffer necessary to hold system information data, in bytes */ +} _mali_uk_get_system_info_size_s; + + +/** @defgroup _mali_uk_getsysteminfo U/K Get System Info + * @{ */ + +/** + * Type definition for the core version number. + * Used when returning the version number read from a core + * + * Its format is that of the 32-bit Version register for a particular core. + * Refer to the "Mali200 and MaliGP2 3D Graphics Processor Technical Reference + * Manual", ARM DDI 0415C, for more information. + */ +typedef u32 _mali_core_version; + +/** + * Enum values for the different modes the driver can be put in. + * Normal is the default mode. The driver then uses a job queue and takes job objects from the clients. + * Job completion is reported using the _mali_ukk_wait_for_notification call. + * The driver blocks this io command until a job has completed or failed or a timeout occurs. + * + * The 'raw' mode is reserved for future expansion. + */ +typedef enum _mali_driver_mode +{ + _MALI_DRIVER_MODE_RAW = 1, /**< Reserved for future expansion */ + _MALI_DRIVER_MODE_NORMAL = 2 /**< Normal mode of operation */ +} _mali_driver_mode; + +/** @brief List of possible cores + * + * add new entries to the end of this enum */ +typedef enum _mali_core_type +{ + _MALI_GP2 = 2, /**< MaliGP2 Programmable Vertex Processor */ + _MALI_200 = 5, /**< Mali200 Programmable Fragment Processor */ + _MALI_400_GP = 6, /**< Mali400 Programmable Vertex Processor */ + _MALI_400_PP = 7, /**< Mali400 Programmable Fragment Processor */ + /* insert new core here, do NOT alter the existing values */ +} _mali_core_type; + +/** @brief Information about each Mali Core + * + * Information is stored in a linked list, which is stored entirely in the + * buffer pointed to by the system_info member of the + * _mali_uk_get_system_info_s arguments provided to _mali_ukk_get_system_info() + * + * Both Fragment Processor (PP) and Vertex Processor (GP) cores are represented + * by this struct. + * + * The type is reported by the type field, _mali_core_info::_mali_core_type. + * + * Each core is given a unique Sequence number identifying it, the core_nr + * member. + * + * Flags are taken directly from the resource's flags, and are currently unused. + * + * Multiple mali_core_info structs are linked in a single linked list using the next field + */ +typedef struct _mali_core_info +{ + _mali_core_type type; /**< Type of core */ + _mali_core_version version; /**< Core Version, as reported by the Core's Version Register */ + u32 reg_address; /**< Address of Registers */ + u32 core_nr; /**< Sequence number */ + u32 flags; /**< Flags. Currently Unused. */ + struct _mali_core_info * next; /**< Next core in Linked List */ +} _mali_core_info; + +/** @brief Capabilities of Memory Banks + * + * These may be used to restrict memory banks for certain uses. They may be + * used when access is not possible (e.g. Bus does not support access to it) + * or when access is possible but not desired (e.g. Access is slow). + * + * In the case of 'possible but not desired', there is no way of specifying + * the flags as an optimization hint, so that the memory could be used as a + * last resort. + * + * @see _mali_mem_info + */ +typedef enum _mali_bus_usage +{ + + _MALI_PP_READABLE = (1<<0), /** Readable by the Fragment Processor */ + _MALI_PP_WRITEABLE = (1<<1), /** Writeable by the Fragment Processor */ + _MALI_GP_READABLE = (1<<2), /** Readable by the Vertex Processor */ + _MALI_GP_WRITEABLE = (1<<3), /** Writeable by the Vertex Processor */ + _MALI_CPU_READABLE = (1<<4), /** Readable by the CPU */ + _MALI_CPU_WRITEABLE = (1<<5), /** Writeable by the CPU */ + _MALI_MMU_READABLE = _MALI_PP_READABLE | _MALI_GP_READABLE, /** Readable by the MMU (including all cores behind it) */ + _MALI_MMU_WRITEABLE = _MALI_PP_WRITEABLE | _MALI_GP_WRITEABLE, /** Writeable by the MMU (including all cores behind it) */ +} _mali_bus_usage; + +/** @brief Information about the Mali Memory system + * + * Information is stored in a linked list, which is stored entirely in the + * buffer pointed to by the system_info member of the + * _mali_uk_get_system_info_s arguments provided to _mali_ukk_get_system_info() + * + * Each element of the linked list describes a single Mali Memory bank. + * Each allocation can only come from one bank, and will not cross multiple + * banks. + * + * Each bank is uniquely identified by its identifier member. On Mali-nonMMU + * systems, to allocate from this bank, the value of identifier must be passed + * as the type_id member of the _mali_uk_get_big_block_s arguments to + * _mali_ukk_get_big_block. + * + * On Mali-MMU systems, there is only one bank, which describes the maximum + * possible address range that could be allocated (which may be much less than + * the available physical memory) + * + * The flags member describes the capabilities of the memory. It is an error + * to attempt to build a job for a particular core (PP or GP) when the memory + * regions used do not have the capabilities for supporting that core. This + * would result in a job abort from the Device Driver. + * + * For example, it is correct to build a PP job where read-only data structures + * are taken from a memory with _MALI_PP_READABLE set and + * _MALI_PP_WRITEABLE clear, and a framebuffer with _MALI_PP_WRITEABLE set and + * _MALI_PP_READABLE clear. However, it would be incorrect to use a framebuffer + * where _MALI_PP_WRITEABLE is clear. + */ +typedef struct _mali_mem_info +{ + u32 size; /**< Size of the memory bank in bytes */ + _mali_bus_usage flags; /**< Capabilitiy flags of the memory */ + u32 maximum_order_supported; /**< log2 supported size */ + u32 identifier; /**< Unique identifier, to be used in allocate calls */ + struct _mali_mem_info * next; /**< Next List Link */ +} _mali_mem_info; + +/** @brief Info about the whole Mali system. + * + * This Contains a linked list of the cores and memory banks available. Each + * list pointer will remain inside the system_info buffer supplied in the + * _mali_uk_get_system_info_s arguments to a _mali_ukk_get_system_info call. + * + * The has_mmu member must be inspected to ensure the correct group of + * Memory function calls is obtained - that is, those for either Mali-MMU + * or Mali-nonMMU. @see _mali_uk_memory + */ +typedef struct _mali_system_info +{ + _mali_core_info * core_info; /**< List of _mali_core_info structures */ + _mali_mem_info * mem_info; /**< List of _mali_mem_info structures */ + u32 has_mmu; /**< Non-zero if Mali-MMU present. Zero otherwise. */ + _mali_driver_mode drivermode; /**< Reserved. Must always be _MALI_DRIVER_MODE_NORMAL */ +} _mali_system_info; + +/** @brief Arguments to _mali_ukk_get_system_info() + * + * A buffer of the size returned by _mali_ukk_get_system_info_size() must be + * allocated, and the pointer to this buffer must be written into the + * system_info member. The buffer must be suitably aligned for storage of + * the _mali_system_info structure - for example, one returned by + * _mali_osk_malloc(), which will be suitably aligned for any structure. + * + * The ukk_private member must be set to zero by the user-side. Under an OS + * implementation, the U/K interface must write in the user-side base address + * into the ukk_private member, so that the common code in + * _mali_ukk_get_system_info() can determine how to adjust the pointers such + * that they are sensible from user space. Leaving ukk_private as NULL implies + * that no pointer adjustment is necessary - which will be the case on a + * bare-metal/RTOS system. + * + * @see _mali_system_info + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 size; /**< [in] size of buffer provided to store system information data */ + _mali_system_info * system_info; /**< [in,out] pointer to buffer to store system information data. No initialisation of buffer required on input. */ + u32 ukk_private; /**< [in] Kernel-side private word inserted by certain U/K interface implementations. Caller must set to Zero. */ +} _mali_uk_get_system_info_s; +/** @} */ /* end group _mali_uk_getsysteminfo */ + +/** @} */ /* end group _mali_uk_core */ + + +/** @defgroup _mali_uk_gp U/K Vertex Processor + * @{ */ + +/** @defgroup _mali_uk_gp_suspend_response_s Vertex Processor Suspend Response + * @{ */ + +/** @brief Arguments for _mali_ukk_gp_suspend_response() + * + * When _mali_wait_for_notification() receives notification that a + * Vertex Processor job was suspended, you need to send a response to indicate + * what needs to happen with this job. You can either abort or resume the job. + * + * - set @c code to indicate response code. This is either @c _MALIGP_JOB_ABORT or + * @c _MALIGP_JOB_RESUME_WITH_NEW_HEAP to indicate you will provide a new heap + * for the job that will resolve the out of memory condition for the job. + * - copy the @c cookie value from the @c _mali_uk_gp_job_suspended_s notification; + * this is an identifier for the suspended job + * - set @c arguments[0] and @c arguments[1] to zero if you abort the job. If + * you resume it, @c argument[0] should specify the Mali start address for the new + * heap and @c argument[1] the Mali end address of the heap. + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * + */ +typedef enum _maligp_job_suspended_response_code +{ + _MALIGP_JOB_ABORT, /**< Abort the Vertex Processor job */ + _MALIGP_JOB_RESUME_WITH_NEW_HEAP /**< Resume the Vertex Processor job with a new heap */ +} _maligp_job_suspended_response_code; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 cookie; /**< [in] cookie from the _mali_uk_gp_job_suspended_s notification */ + _maligp_job_suspended_response_code code; /**< [in] abort or resume response code, see \ref _maligp_job_suspended_response_code */ + u32 arguments[2]; /**< [in] 0 when aborting a job. When resuming a job, the Mali start and end address for a new heap to resume the job with */ +} _mali_uk_gp_suspend_response_s; + +/** @} */ /* end group _mali_uk_gp_suspend_response_s */ + +/** @defgroup _mali_uk_gpstartjob_s Vertex Processor Start Job + * @{ */ + +/** @brief Status indicating the result of starting a Vertex or Fragment processor job */ +typedef enum +{ + _MALI_UK_START_JOB_STARTED, /**< Job started */ + _MALI_UK_START_JOB_STARTED_LOW_PRI_JOB_RETURNED, /**< Job started and bumped a lower priority job that was pending execution */ + _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE /**< Job could not be started at this time. Try starting the job again */ +} _mali_uk_start_job_status; + +/** @brief Status indicating the result of starting a Vertex or Fragment processor job */ +typedef enum +{ + MALI_UK_START_JOB_FLAG_DEFAULT = 0, /**< Default behaviour; Flush L2 caches before start, no following jobs */ + MALI_UK_START_JOB_FLAG_NO_FLUSH = 1, /**< No need to flush L2 caches before start */ + MALI_UK_START_JOB_FLAG_MORE_JOBS_FOLLOW = 2, /**< More related jobs follows, try to schedule them as soon as possible after this job */ +} _mali_uk_start_job_flags; + +/** @brief Status indicating the result of the execution of a Vertex or Fragment processor job */ + +typedef enum +{ + _MALI_UK_JOB_STATUS_END_SUCCESS = 1<<(16+0), + _MALI_UK_JOB_STATUS_END_OOM = 1<<(16+1), + _MALI_UK_JOB_STATUS_END_ABORT = 1<<(16+2), + _MALI_UK_JOB_STATUS_END_TIMEOUT_SW = 1<<(16+3), + _MALI_UK_JOB_STATUS_END_HANG = 1<<(16+4), + _MALI_UK_JOB_STATUS_END_SEG_FAULT = 1<<(16+5), + _MALI_UK_JOB_STATUS_END_ILLEGAL_JOB = 1<<(16+6), + _MALI_UK_JOB_STATUS_END_UNKNOWN_ERR = 1<<(16+7), + _MALI_UK_JOB_STATUS_END_SHUTDOWN = 1<<(16+8), + _MALI_UK_JOB_STATUS_END_SYSTEM_UNUSABLE = 1<<(16+9) +} _mali_uk_job_status; + +#define MALIGP2_NUM_REGS_FRAME (6) + +/** @brief Arguments for _mali_ukk_gp_start_job() + * + * To start a Vertex Processor job + * - associate the request with a reference to a @c mali_gp_job_info by setting + * user_job_ptr to the address of the @c mali_gp_job_info of the job. + * - set @c priority to the priority of the @c mali_gp_job_info + * - specify a timeout for the job by setting @c watchdog_msecs to the number of + * milliseconds the job is allowed to run. Specifying a value of 0 selects the + * default timeout in use by the device driver. + * - copy the frame registers from the @c mali_gp_job_info into @c frame_registers. + * - set the @c perf_counter_flag, @c perf_counter_src0 and @c perf_counter_src1 to zero + * for a non-instrumented build. For an instrumented build you can use up + * to two performance counters. Set the corresponding bit in @c perf_counter_flag + * to enable them. @c perf_counter_src0 and @c perf_counter_src1 specify + * the source of what needs to get counted (e.g. number of vertex loader + * cache hits). For source id values, see ARM DDI0415A, Table 3-60. + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * + * When @c _mali_ukk_gp_start_job() returns @c _MALI_OSK_ERR_OK, status contains the + * result of the request (see \ref _mali_uk_start_job_status). If the job could + * not get started (@c _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE) it should be + * tried again. If the job had a higher priority than the one currently pending + * execution (@c _MALI_UK_START_JOB_STARTED_LOW_PRI_JOB_RETURNED), it will bump + * the lower priority job and returns the address of the @c mali_gp_job_info + * for that job in @c returned_user_job_ptr. That job should get requeued. + * + * After the job has started, @c _mali_wait_for_notification() will be notified + * that the job finished or got suspended. It may get suspended due to + * resource shortage. If it finished (see _mali_ukk_wait_for_notification()) + * the notification will contain a @c _mali_uk_gp_job_finished_s result. If + * it got suspended the notification will contain a @c _mali_uk_gp_job_suspended_s + * result. + * + * The @c _mali_uk_gp_job_finished_s contains the job status (see \ref _mali_uk_job_status), + * the number of milliseconds the job took to render, and values of core registers + * when the job finished (irq status, performance counters, renderer list + * address). A job has finished succesfully when its status is + * @c _MALI_UK_JOB_STATUS_FINISHED. If the hardware detected a timeout while rendering + * the job, or software detected the job is taking more than watchdog_msecs to + * complete, the status will indicate @c _MALI_UK_JOB_STATUS_HANG. + * If the hardware detected a bus error while accessing memory associated with the + * job, status will indicate @c _MALI_UK_JOB_STATUS_SEG_FAULT. + * status will indicate @c _MALI_UK_JOB_STATUS_NOT_STARTED if the driver had to + * stop the job but the job didn't start on the hardware yet, e.g. when the + * driver shutdown. + * + * In case the job got suspended, @c _mali_uk_gp_job_suspended_s contains + * the @c user_job_ptr identifier used to start the job with, the @c reason + * why the job stalled (see \ref _maligp_job_suspended_reason) and a @c cookie + * to identify the core on which the job stalled. This @c cookie will be needed + * when responding to this nofication by means of _mali_ukk_gp_suspend_response(). + * (see _mali_ukk_gp_suspend_response()). The response is either to abort or + * resume the job. If the job got suspended due to an out of memory condition + * you may be able to resolve this by providing more memory and resuming the job. + * + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 user_job_ptr; /**< [in] identifier for the job in user space, a @c mali_gp_job_info* */ + u32 priority; /**< [in] job priority. A lower number means higher priority */ + u32 watchdog_msecs; /**< [in] maximum allowed runtime in milliseconds. The job gets killed if it runs longer than this. A value of 0 selects the default used by the device driver. */ + u32 frame_registers[MALIGP2_NUM_REGS_FRAME]; /**< [in] core specific registers associated with this job */ + u32 perf_counter_flag; /**< [in] bitmask indicating which performance counters to enable, see \ref _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE and related macro definitions */ + u32 perf_counter_src0; /**< [in] source id for performance counter 0 (see ARM DDI0415A, Table 3-60) */ + u32 perf_counter_src1; /**< [in] source id for performance counter 1 (see ARM DDI0415A, Table 3-60) */ + u32 returned_user_job_ptr; /**< [out] identifier for the returned job in user space, a @c mali_gp_job_info* */ + _mali_uk_start_job_status status; /**< [out] indicates job start status (success, previous job returned, requeue) */ + u32 abort_id; /**< [in] abort id of this job, used to identify this job for later abort requests */ + u32 perf_counter_l2_src0; /**< [in] soruce id for Mali-400 MP L2 cache performance counter 0 */ + u32 perf_counter_l2_src1; /**< [in] source id for Mali-400 MP L2 cache performance counter 1 */ + u32 frame_builder_id; /**< [in] id of the originating frame builder */ + u32 flush_id; /**< [in] flush id within the originating frame builder */ +} _mali_uk_gp_start_job_s; + +#define _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE (1<<0) /**< Enable performance counter SRC0 for a job */ +#define _MALI_PERFORMANCE_COUNTER_FLAG_SRC1_ENABLE (1<<1) /**< Enable performance counter SRC1 for a job */ +#define _MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC0_ENABLE (1<<2) /**< Enable performance counter L2_SRC0 for a job */ +#define _MALI_PERFORMANCE_COUNTER_FLAG_L2_SRC1_ENABLE (1<<3) /**< Enable performance counter L2_SRC1 for a job */ +#define _MALI_PERFORMANCE_COUNTER_FLAG_L2_RESET (1<<4) /**< Enable performance counter L2_RESET for a job */ + +/** @} */ /* end group _mali_uk_gpstartjob_s */ + +typedef struct +{ + u32 user_job_ptr; /**< [out] identifier for the job in user space */ + _mali_uk_job_status status; /**< [out] status of finished job */ + u32 irq_status; /**< [out] value of the GP interrupt rawstat register (see ARM DDI0415A) */ + u32 status_reg_on_stop; /**< [out] value of the GP control register */ + u32 vscl_stop_addr; /**< [out] value of the GP VLSCL start register */ + u32 plbcl_stop_addr; /**< [out] value of the GP PLBCL start register */ + u32 heap_current_addr; /**< [out] value of the GP PLB PL heap start address register */ + u32 perf_counter_src0; /**< [out] source id for performance counter 0 (see ARM DDI0415A, Table 3-60) */ + u32 perf_counter_src1; /**< [out] source id for performance counter 1 (see ARM DDI0415A, Table 3-60) */ + u32 perf_counter0; /**< [out] value of perfomance counter 0 (see ARM DDI0415A) */ + u32 perf_counter1; /**< [out] value of perfomance counter 1 (see ARM DDI0415A) */ + u32 render_time; /**< [out] number of microseconds it took for the job to render */ + u32 perf_counter_l2_src0; /**< [out] soruce id for Mali-400 MP L2 cache performance counter 0 */ + u32 perf_counter_l2_src1; /**< [out] soruce id for Mali-400 MP L2 cache performance counter 1 */ + u32 perf_counter_l2_val0; /**< [out] Value of the Mali-400 MP L2 cache performance counter 0 */ + u32 perf_counter_l2_val1; /**< [out] Value of the Mali-400 MP L2 cache performance counter 1 */ +} _mali_uk_gp_job_finished_s; + +typedef enum _maligp_job_suspended_reason +{ + _MALIGP_JOB_SUSPENDED_OUT_OF_MEMORY /**< Polygon list builder unit (PLBU) has run out of memory */ +} _maligp_job_suspended_reason; + +typedef struct +{ + u32 user_job_ptr; /**< [out] identifier for the job in user space */ + _maligp_job_suspended_reason reason; /**< [out] reason why the job stalled */ + u32 cookie; /**< [out] identifier for the core in kernel space on which the job stalled */ +} _mali_uk_gp_job_suspended_s; + +/** @} */ /* end group _mali_uk_gp */ + + +/** @defgroup _mali_uk_pp U/K Fragment Processor + * @{ */ + +/** @defgroup _mali_uk_ppstartjob_s Fragment Processor Start Job + * @{ */ + +/** @brief Arguments for _mali_ukk_pp_start_job() + * + * To start a Fragment Processor job + * - associate the request with a reference to a mali_pp_job by setting + * @c user_job_ptr to the address of the @c mali_pp_job of the job. + * - set @c priority to the priority of the mali_pp_job + * - specify a timeout for the job by setting @c watchdog_msecs to the number of + * milliseconds the job is allowed to run. Specifying a value of 0 selects the + * default timeout in use by the device driver. + * - copy the frame registers from the @c mali_pp_job into @c frame_registers. + * For MALI200 you also need to copy the write back 0,1 and 2 registers. + * - set the @c perf_counter_flag, @c perf_counter_src0 and @c perf_counter_src1 to zero + * for a non-instrumented build. For an instrumented build you can use up + * to two performance counters. Set the corresponding bit in @c perf_counter_flag + * to enable them. @c perf_counter_src0 and @c perf_counter_src1 specify + * the source of what needs to get counted (e.g. number of vertex loader + * cache hits). For source id values, see ARM DDI0415A, Table 3-60. + * - pass in the user-kernel context in @c ctx that was returned from _mali_ukk_open() + * + * When _mali_ukk_pp_start_job() returns @c _MALI_OSK_ERR_OK, @c status contains the + * result of the request (see \ref _mali_uk_start_job_status). If the job could + * not get started (@c _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE) it should be + * tried again. If the job had a higher priority than the one currently pending + * execution (@c _MALI_UK_START_JOB_STARTED_LOW_PRI_JOB_RETURNED), it will bump + * the lower priority job and returns the address of the @c mali_pp_job + * for that job in @c returned_user_job_ptr. That job should get requeued. + * + * After the job has started, _mali_wait_for_notification() will be notified + * when the job finished. The notification will contain a + * @c _mali_uk_pp_job_finished_s result. It contains the @c user_job_ptr + * identifier used to start the job with, the job @c status (see \ref _mali_uk_job_status), + * the number of milliseconds the job took to render, and values of core registers + * when the job finished (irq status, performance counters, renderer list + * address). A job has finished succesfully when its status is + * @c _MALI_UK_JOB_STATUS_FINISHED. If the hardware detected a timeout while rendering + * the job, or software detected the job is taking more than @c watchdog_msecs to + * complete, the status will indicate @c _MALI_UK_JOB_STATUS_HANG. + * If the hardware detected a bus error while accessing memory associated with the + * job, status will indicate @c _MALI_UK_JOB_STATUS_SEG_FAULT. + * status will indicate @c _MALI_UK_JOB_STATUS_NOT_STARTED if the driver had to + * stop the job but the job didn't start on the hardware yet, e.g. when the + * driver shutdown. + * + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 user_job_ptr; /**< [in] identifier for the job in user space */ + u32 priority; /**< [in] job priority. A lower number means higher priority */ + u32 watchdog_msecs; /**< [in] maximum allowed runtime in milliseconds. The job gets killed if it runs longer than this. A value of 0 selects the default used by the device driver. */ + u32 frame_registers[MALI200_NUM_REGS_FRAME]; /**< [in] core specific registers associated with this job, see ARM DDI0415A */ + u32 wb0_registers[MALI200_NUM_REGS_WBx]; + u32 wb1_registers[MALI200_NUM_REGS_WBx]; + u32 wb2_registers[MALI200_NUM_REGS_WBx]; + u32 perf_counter_flag; /**< [in] bitmask indicating which performance counters to enable, see \ref _MALI_PERFORMANCE_COUNTER_FLAG_SRC0_ENABLE and related macro definitions */ + u32 perf_counter_src0; /**< [in] source id for performance counter 0 (see ARM DDI0415A, Table 3-60) */ + u32 perf_counter_src1; /**< [in] source id for performance counter 1 (see ARM DDI0415A, Table 3-60) */ + u32 returned_user_job_ptr; /**< [out] identifier for the returned job in user space */ + _mali_uk_start_job_status status; /**< [out] indicates job start status (success, previous job returned, requeue) */ + u32 abort_id; /**< [in] abort id of this job, used to identify this job for later abort requests */ + u32 perf_counter_l2_src0; /**< [in] soruce id for Mali-400 MP L2 cache performance counter 0 */ + u32 perf_counter_l2_src1; /**< [in] source id for Mali-400 MP L2 cache performance counter 1 */ + u32 frame_builder_id; /**< [in] id of the originating frame builder */ + u32 flush_id; /**< [in] flush id within the originating frame builder */ + _mali_uk_start_job_flags flags; /**< [in] Flags for job, see _mali_uk_start_job_flags for more information */ +} _mali_uk_pp_start_job_s; +/** @} */ /* end group _mali_uk_ppstartjob_s */ + +typedef struct +{ + u32 user_job_ptr; /**< [out] identifier for the job in user space */ + _mali_uk_job_status status; /**< [out] status of finished job */ + u32 irq_status; /**< [out] value of interrupt rawstat register (see ARM DDI0415A) */ + u32 last_tile_list_addr; /**< [out] value of renderer list register (see ARM DDI0415A); necessary to restart a stopped job */ + u32 perf_counter_src0; /**< [out] source id for performance counter 0 (see ARM DDI0415A, Table 3-60) */ + u32 perf_counter_src1; /**< [out] source id for performance counter 1 (see ARM DDI0415A, Table 3-60) */ + u32 perf_counter0; /**< [out] value of perfomance counter 0 (see ARM DDI0415A) */ + u32 perf_counter1; /**< [out] value of perfomance counter 1 (see ARM DDI0415A) */ + u32 render_time; /**< [out] number of microseconds it took for the job to render */ + u32 perf_counter_l2_src0; /**< [out] soruce id for Mali-400 MP L2 cache performance counter 0 */ + u32 perf_counter_l2_src1; /**< [out] soruce id for Mali-400 MP L2 cache performance counter 1 */ + u32 perf_counter_l2_val0; /**< [out] Value of the Mali-400 MP L2 cache performance counter 0 */ + u32 perf_counter_l2_val1; /**< [out] Value of the Mali-400 MP L2 cache performance counter 1 */ + u32 perf_counter_l2_val0_raw; /**< [out] Raw value of the Mali-400 MP L2 cache performance counter 0 */ + u32 perf_counter_l2_val1_raw; /**< [out] Raw value of the Mali-400 MP L2 cache performance counter 1 */ +} _mali_uk_pp_job_finished_s; +/** @} */ /* end group _mali_uk_pp */ + + +/** @addtogroup _mali_uk_core U/K Core + * @{ */ + +/** @defgroup _mali_uk_waitfornotification_s Wait For Notification + * @{ */ + +/** @brief Notification type encodings + * + * Each Notification type is an ordered pair of (subsystem,id), and is unique. + * + * The encoding of subsystem,id into a 32-bit word is: + * encoding = (( subsystem << _MALI_NOTIFICATION_SUBSYSTEM_SHIFT ) & _MALI_NOTIFICATION_SUBSYSTEM_MASK) + * | (( id << _MALI_NOTIFICATION_ID_SHIFT ) & _MALI_NOTIFICATION_ID_MASK) + * + * @see _mali_uk_wait_for_notification_s + */ +typedef enum +{ + /** core notifications */ + + _MALI_NOTIFICATION_CORE_SHUTDOWN_IN_PROGRESS = (_MALI_UK_CORE_SUBSYSTEM << 16) | 0x20, + _MALI_NOTIFICATION_APPLICATION_QUIT = (_MALI_UK_CORE_SUBSYSTEM << 16) | 0x40, + + /** Fragment Processor notifications */ + + _MALI_NOTIFICATION_PP_FINISHED = (_MALI_UK_PP_SUBSYSTEM << 16) | 0x10, + + /** Vertex Processor notifications */ + + _MALI_NOTIFICATION_GP_FINISHED = (_MALI_UK_GP_SUBSYSTEM << 16) | 0x10, + _MALI_NOTIFICATION_GP_STALLED = (_MALI_UK_GP_SUBSYSTEM << 16) | 0x20, +} _mali_uk_notification_type; + +/** to assist in splitting up 32-bit notification value in subsystem and id value */ +#define _MALI_NOTIFICATION_SUBSYSTEM_MASK 0xFFFF0000 +#define _MALI_NOTIFICATION_SUBSYSTEM_SHIFT 16 +#define _MALI_NOTIFICATION_ID_MASK 0x0000FFFF +#define _MALI_NOTIFICATION_ID_SHIFT 0 + + +/** @brief Arguments for _mali_ukk_wait_for_notification() + * + * On successful return from _mali_ukk_wait_for_notification(), the members of + * this structure will indicate the reason for notification. + * + * Specifically, the source of the notification can be identified by the + * subsystem and id fields of the mali_uk_notification_type in the code.type + * member. The type member is encoded in a way to divide up the types into a + * subsystem field, and a per-subsystem ID field. See + * _mali_uk_notification_type for more information. + * + * Interpreting the data union member depends on the notification type: + * + * - type == _MALI_NOTIFICATION_CORE_SHUTDOWN_IN_PROGRESS + * - The kernel side is shutting down. No further + * _mali_uk_wait_for_notification() calls should be made. + * - In this case, the value of the data union member is undefined. + * - This is used to indicate to the user space client that it should close + * the connection to the Mali Device Driver. + * - type == _MALI_NOTIFICATION_PP_FINISHED + * - The notification data is of type _mali_uk_pp_job_finished_s. It contains the user_job_ptr + * identifier used to start the job with, the job status, the number of milliseconds the job took to render, + * and values of core registers when the job finished (irq status, performance counters, renderer list + * address). + * - A job has finished succesfully when its status member is _MALI_UK_JOB_STATUS_FINISHED. + * - If the hardware detected a timeout while rendering the job, or software detected the job is + * taking more than watchdog_msecs (see _mali_ukk_pp_start_job()) to complete, the status member will + * indicate _MALI_UK_JOB_STATUS_HANG. + * - If the hardware detected a bus error while accessing memory associated with the job, status will + * indicate _MALI_UK_JOB_STATUS_SEG_FAULT. + * - Status will indicate MALI_UK_JOB_STATUS_NOT_STARTED if the driver had to stop the job but the job + * didn't start the hardware yet, e.g. when the driver closes. + * - type == _MALI_NOTIFICATION_GP_FINISHED + * - The notification data is of type _mali_uk_gp_job_finished_s. The notification is similar to that of + * type == _MALI_NOTIFICATION_PP_FINISHED, except that several other GP core register values are returned. + * The status values have the same meaning for type == _MALI_NOTIFICATION_PP_FINISHED. + * - type == _MALI_NOTIFICATION_GP_STALLED + * - The nofication data is of type _mali_uk_gp_job_suspended_s. It contains the user_job_ptr + * identifier used to start the job with, the reason why the job stalled and a cookie to identify the core on + * which the job stalled. + * - The reason member of gp_job_suspended is set to _MALIGP_JOB_SUSPENDED_OUT_OF_MEMORY + * when the polygon list builder unit has run out of memory. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_uk_notification_type type; /**< [out] Type of notification available */ + union + { + _mali_uk_gp_job_suspended_s gp_job_suspended;/**< [out] Notification data for _MALI_NOTIFICATION_GP_STALLED notification type */ + _mali_uk_gp_job_finished_s gp_job_finished; /**< [out] Notification data for _MALI_NOTIFICATION_GP_FINISHED notification type */ + _mali_uk_pp_job_finished_s pp_job_finished; /**< [out] Notification data for _MALI_NOTIFICATION_PP_FINISHED notification type */ + } data; +} _mali_uk_wait_for_notification_s; + +/** @brief Arguments for _mali_ukk_post_notification() + * + * Posts the specified notification to the notification queue for this application. + * This is used to send a quit message to the callback thread. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_uk_notification_type type; /**< [in] Type of notification to post */ +} _mali_uk_post_notification_s; +/** @} */ /* end group _mali_uk_waitfornotification_s */ + +/** @defgroup _mali_uk_getapiversion_s Get API Version + * @{ */ + +/** helpers for Device Driver API version handling */ + +/** @brief Encode a version ID from a 16-bit input + * + * @note the input is assumed to be 16 bits. It must not exceed 16 bits. */ +#define _MAKE_VERSION_ID(x) (((x) << 16UL) | (x)) + +/** @brief Check whether a 32-bit value is likely to be Device Driver API + * version ID. */ +#define _IS_VERSION_ID(x) (((x) & 0xFFFF) == (((x) >> 16UL) & 0xFFFF)) + +/** @brief Decode a 16-bit version number from a 32-bit Device Driver API version + * ID */ +#define _GET_VERSION(x) (((x) >> 16UL) & 0xFFFF) + +/** @brief Determine whether two 32-bit encoded version IDs match */ +#define _IS_API_MATCH(x, y) (IS_VERSION_ID((x)) && IS_VERSION_ID((y)) && (GET_VERSION((x)) == GET_VERSION((y)))) + +/** + * API version define. + * Indicates the version of the kernel API + * The version is a 16bit integer incremented on each API change. + * The 16bit integer is stored twice in a 32bit integer + * For example, for version 1 the value would be 0x00010001 + */ +#define _MALI_API_VERSION 10 +#define _MALI_UK_API_VERSION _MAKE_VERSION_ID(_MALI_API_VERSION) + +/** + * The API version is a 16-bit integer stored in both the lower and upper 16-bits + * of a 32-bit value. The 16-bit API version value is incremented on each API + * change. Version 1 would be 0x00010001. Used in _mali_uk_get_api_version_s. + */ +typedef u32 _mali_uk_api_version; + +/** @brief Arguments for _mali_uk_get_api_version() + * + * The user-side interface version must be written into the version member, + * encoded using _MAKE_VERSION_ID(). It will be compared to the API version of + * the kernel-side interface. + * + * On successful return, the version member will be the API version of the + * kernel-side interface. _MALI_UK_API_VERSION macro defines the current version + * of the API. + * + * The compatible member must be checked to see if the version of the user-side + * interface is compatible with the kernel-side interface, since future versions + * of the interface may be backwards compatible. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_uk_api_version version; /**< [in,out] API version of user-side interface. */ + int compatible; /**< [out] @c 1 when @version is compatible, @c 0 otherwise */ +} _mali_uk_get_api_version_s; +/** @} */ /* end group _mali_uk_getapiversion_s */ + +/** @} */ /* end group _mali_uk_core */ + + +/** @defgroup _mali_uk_memory U/K Memory + * @{ */ + +/** @brief Arguments for _mali_ukk_init_mem(). */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 mali_address_base; /**< [out] start of MALI address space */ + u32 memory_size; /**< [out] total MALI address space available */ +} _mali_uk_init_mem_s; + +/** @brief Arguments for _mali_ukk_term_mem(). */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ +} _mali_uk_term_mem_s; + +/** @brief Arguments for _mali_ukk_get_big_block() + * + * - type_id should be set to the value of the identifier member of one of the + * _mali_mem_info structures returned through _mali_ukk_get_system_info() + * - ukk_private must be zero when calling from user-side. On Kernel-side, the + * OS implementation of the U/K interface can use it to communicate data to the + * OS implementation of the OSK layer. Specifically, ukk_private will be placed + * into the ukk_private member of the _mali_uk_mem_mmap_s structure. See + * _mali_ukk_mem_mmap() for more details. + * - minimum_size_requested will be updated if it is too small + * - block_size will always be >= minimum_size_requested, because the underlying + * allocation mechanism may only be able to divide up memory regions in certain + * ways. To avoid wasting memory, block_size should always be taken into account + * rather than assuming minimum_size_requested was really allocated. + * - to free the memory, the returned cookie member must be stored, and used to + * refer to it. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 type_id; /**< [in] the type id of the memory bank to allocate memory from */ + u32 minimum_size_requested; /**< [in,out] minimum size of the allocation */ + u32 ukk_private; /**< [in] Kernel-side private word inserted by certain U/K interface implementations. Caller must set to Zero. */ + u32 mali_address; /**< [out] address of the allocation in mali address space */ + void *cpuptr; /**< [out] address of the allocation in the current process address space */ + u32 block_size; /**< [out] size of the block that got allocated */ + u32 flags; /**< [out] flags associated with the allocated block, of type _mali_bus_usage */ + u32 cookie; /**< [out] identifier for the allocated block in kernel space */ +} _mali_uk_get_big_block_s; + +/** @brief Arguments for _mali_ukk_free_big_block() + * + * All that is required is that the cookie member must be set to the value of + * the cookie member returned through _mali_ukk_get_big_block() + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 cookie; /**< [in] identifier for mapped memory object in kernel space */ +} _mali_uk_free_big_block_s; + +/** @note Mali-MMU only */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 phys_addr; /**< [in] physical address */ + u32 size; /**< [in] size */ + u32 mali_address; /**< [in] mali address to map the physical memory to */ + u32 rights; /**< [in] rights necessary for accessing memory */ + u32 flags; /**< [in] flags, see \ref _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE */ + u32 cookie; /**< [out] identifier for mapped memory object in kernel space */ +} _mali_uk_map_external_mem_s; + +/** Flag for _mali_uk_map_external_mem_s and _mali_uk_attach_ump_mem_s */ +#define _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE (1<<0) + +/** @note Mali-MMU only */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 cookie; /**< [out] identifier for mapped memory object in kernel space */ +} _mali_uk_unmap_external_mem_s; + +/** @note This is identical to _mali_uk_map_external_mem_s above, however phys_addr is replaced by secure_id */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< [in] secure id */ + u32 size; /**< [in] size */ + u32 mali_address; /**< [in] mali address to map the physical memory to */ + u32 rights; /**< [in] rights necessary for accessing memory */ + u32 flags; /**< [in] flags, see \ref _MALI_MAP_EXTERNAL_MAP_GUARD_PAGE */ + u32 cookie; /**< [out] identifier for mapped memory object in kernel space */ +} _mali_uk_attach_ump_mem_s; + +/** @note Mali-MMU only; will be supported in future version */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 cookie; /**< [in] identifier for mapped memory object in kernel space */ +} _mali_uk_release_ump_mem_s; + +/** @brief Arguments for _mali_ukk_va_to_mali_pa() + * + * if size is zero or not a multiple of the system's page size, it will be + * rounded up to the next multiple of the page size. This will occur before + * any other use of the size parameter. + * + * if va is not PAGE_SIZE aligned, it will be rounded down to the next page + * boundary. + * + * The range (va) to ((u32)va)+(size-1) inclusive will be checked for physical + * contiguity. + * + * The implementor will check that the entire physical range is allowed to be mapped + * into user-space. + * + * Failure will occur if either of the above are not satisfied. + * + * Otherwise, the physical base address of the range is returned through pa, + * va is updated to be page aligned, and size is updated to be a non-zero + * multiple of the system's pagesize. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *va; /**< [in,out] Virtual address of the start of the range */ + u32 pa; /**< [out] Physical base address of the range */ + u32 size; /**< [in,out] Size of the range, in bytes. */ +} _mali_uk_va_to_mali_pa_s; + + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 size; /**< [out] size of MMU page table information (registers + page tables) */ +} _mali_uk_query_mmu_page_table_dump_size_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 size; /**< [in] size of buffer to receive mmu page table information */ + void *buffer; /**< [in,out] buffer to receive mmu page table information */ + u32 register_writes_size; /**< [out] size of MMU register dump */ + u32 *register_writes; /**< [out] pointer within buffer where MMU register dump is stored */ + u32 page_table_dump_size; /**< [out] size of MMU page table dump */ + u32 *page_table_dump; /**< [out] pointer within buffer where MMU page table dump is stored */ +} _mali_uk_dump_mmu_page_table_s; + +/** @} */ /* end group _mali_uk_memory */ + + +/** @addtogroup _mali_uk_pp U/K Fragment Processor + * @{ */ + +/** @brief Arguments for _mali_ukk_get_pp_number_of_cores() + * + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * - Upon successful return from _mali_ukk_get_pp_number_of_cores(), @c number_of_cores + * will contain the number of Fragment Processor cores in the system. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 number_of_cores; /**< [out] number of Fragment Processor cores in the system */ +} _mali_uk_get_pp_number_of_cores_s; + +/** @brief Arguments for _mali_ukk_get_pp_core_version() + * + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * - Upon successful return from _mali_ukk_get_pp_core_version(), @c version contains + * the version that all Fragment Processor cores are compatible with. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_core_version version; /**< [out] version returned from core, see \ref _mali_core_version */ +} _mali_uk_get_pp_core_version_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 abort_id; /**< [in] ID of job(s) to abort */ +} _mali_uk_pp_abort_job_s; + +/** @} */ /* end group _mali_uk_pp */ + + +/** @addtogroup _mali_uk_gp U/K Vertex Processor + * @{ */ + +/** @brief Arguments for _mali_ukk_get_gp_number_of_cores() + * + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * - Upon successful return from _mali_ukk_get_gp_number_of_cores(), @c number_of_cores + * will contain the number of Vertex Processor cores in the system. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 number_of_cores; /**< [out] number of Vertex Processor cores in the system */ +} _mali_uk_get_gp_number_of_cores_s; + +/** @brief Arguments for _mali_ukk_get_gp_core_version() + * + * - pass in the user-kernel context @c ctx that was returned from _mali_ukk_open() + * - Upon successful return from _mali_ukk_get_gp_core_version(), @c version contains + * the version that all Vertex Processor cores are compatible with. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_core_version version; /**< [out] version returned from core, see \ref _mali_core_version */ +} _mali_uk_get_gp_core_version_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 abort_id; /**< [in] ID of job(s) to abort */ +} _mali_uk_gp_abort_job_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 limit; /**< [in,out] The desired limit for number of events to record on input, actual limit on output */ +} _mali_uk_profiling_start_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 event_id; /**< [in] event id to register (see enum mali_profiling_events for values) */ + u32 data[5]; /**< [in] event specific data */ +} _mali_uk_profiling_add_event_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 count; /**< [out] The number of events sampled */ +} _mali_uk_profiling_stop_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 index; /**< [in] which index to get (starting at zero) */ + u64 timestamp; /**< [out] timestamp of event */ + u32 event_id; /**< [out] event id of event (see enum mali_profiling_events for values) */ + u32 data[5]; /**< [out] event specific data */ +} _mali_uk_profiling_get_event_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ +} _mali_uk_profiling_clear_s; + +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 enable_events; /**< [out] 1 if user space process should generate events, 0 if not */ +} _mali_uk_profiling_get_config_s; + + +/** @} */ /* end group _mali_uk_gp */ + + +/** @addtogroup _mali_uk_memory U/K Memory + * @{ */ + +/** @brief Arguments to _mali_ukk_mem_mmap() + * + * Use of the phys_addr member depends on whether the driver is compiled for + * Mali-MMU or nonMMU: + * - in the nonMMU case, this is the physical address of the memory as seen by + * the CPU (which may be a constant offset from that used by Mali) + * - in the MMU case, this is the Mali Virtual base address of the memory to + * allocate, and the particular physical pages used to back the memory are + * entirely determined by _mali_ukk_mem_mmap(). The details of the physical pages + * are not reported to user-space for security reasons. + * + * The cookie member must be stored for use later when freeing the memory by + * calling _mali_ukk_mem_munmap(). In the Mali-MMU case, the cookie is secure. + * + * The ukk_private word must be set to zero when calling from user-space. On + * Kernel-side, the OS implementation of the U/K interface can use it to + * communicate data to the OS implementation of the OSK layer. In particular, + * _mali_ukk_get_big_block() directly calls _mali_ukk_mem_mmap directly, and + * will communicate its own ukk_private word through the ukk_private member + * here. The common code itself will not inspect or modify the ukk_private + * word, and so it may be safely used for whatever purposes necessary to + * integrate Mali Memory handling into the OS. + * + * The uku_private member is currently reserved for use by the user-side + * implementation of the U/K interface. Its value must be zero. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; /**< [out] Returns user-space virtual address for the mapping */ + u32 size; /**< [in] Size of the requested mapping */ + u32 phys_addr; /**< [in] Physical address - could be offset, depending on caller+callee convention */ + u32 cookie; /**< [out] Returns a cookie for use in munmap calls */ + void *uku_private; /**< [in] User-side Private word used by U/K interface */ + void *ukk_private; /**< [in] Kernel-side Private word used by U/K interface */ +} _mali_uk_mem_mmap_s; + +/** @brief Arguments to _mali_ukk_mem_munmap() + * + * The cookie and mapping members must be that returned from the same previous + * call to _mali_ukk_mem_mmap(). The size member must correspond to cookie + * and mapping - that is, it must be the value originally supplied to a call to + * _mali_ukk_mem_mmap that returned the values of mapping and cookie. + * + * An error will be returned if an attempt is made to unmap only part of the + * originally obtained range, or to unmap more than was originally obtained. + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; /**< [in] The mapping returned from mmap call */ + u32 size; /**< [in] The size passed to mmap call */ + u32 cookie; /**< [in] Cookie from mmap call */ +} _mali_uk_mem_munmap_s; +/** @} */ /* end group _mali_uk_memory */ + +#if USING_MALI_PMM + +/** @defgroup _mali_uk_pmm U/K Power Management Module + * @{ */ + +/** @brief Power management event message identifiers. + * + * U/K events start after id 200, and can range up to 999 + * Adding new events will require updates to the PMM mali_pmm_event_id type + */ +#define _MALI_PMM_EVENT_UK_EXAMPLE 201 + +/** @brief Generic PMM message data type, that will be dependent on the event msg + */ +typedef u32 mali_pmm_message_data; + + +/** @brief Arguments to _mali_ukk_pmm_event_message() + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 id; /**< [in] event id */ + mali_pmm_message_data data; /**< [in] specific data associated with the event */ +} _mali_uk_pmm_message_s; + +/** @} */ /* end group _mali_uk_pmm */ +#endif /* USING_MALI_PMM */ + +/** @defgroup _mali_uk_vsync U/K VSYNC Wait Reporting Module + * @{ */ + +/** @brief VSYNC events + * + * These events are reported when DDK starts to wait for vsync and when the + * vsync has occured and the DDK can continue on the next frame. + */ +typedef enum _mali_uk_vsync_event +{ + _MALI_UK_VSYNC_EVENT_BEGIN_WAIT = 0, + _MALI_UK_VSYNC_EVENT_END_WAIT +} _mali_uk_vsync_event; + +/** @brief Arguments to _mali_ukk_vsync_event() + * + */ +typedef struct +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + _mali_uk_vsync_event event; /**< [in] VSYNCH event type */ +} _mali_uk_vsync_event_report_s; + +/** @} */ /* end group _mali_uk_vsync */ + +/** @} */ /* end group u_k_api */ + +/** @} */ /* end group uddapi */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_UK_TYPES_H__ */ diff --git a/drivers/gpu/arm/mali/common/mali_ukk.h b/drivers/gpu/arm/mali/common/mali_ukk.h new file mode 100644 index 000000000000..e95fa30123af --- /dev/null +++ b/drivers/gpu/arm/mali/common/mali_ukk.h @@ -0,0 +1,718 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_ukk.h + * Defines the kernel-side interface of the user-kernel interface + */ + +#ifndef __MALI_UKK_H__ +#define __MALI_UKK_H__ + +#include "mali_osk.h" +#include "mali_uk_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup uddapi Unified Device Driver (UDD) APIs + * + * @{ + */ + +/** + * @addtogroup u_k_api UDD User/Kernel Interface (U/K) APIs + * + * - The _mali_uk functions are an abstraction of the interface to the device + * driver. On certain OSs, this would be implemented via the IOCTL interface. + * On other OSs, it could be via extension of some Device Driver Class, or + * direct function call for Bare metal/RTOSs. + * - It is important to note that: + * - The Device Driver has implemented the _mali_ukk set of functions + * - The Base Driver calls the corresponding set of _mali_uku functions. + * - What requires porting is solely the calling mechanism from User-side to + * Kernel-side, and propagating back the results. + * - Each U/K function is associated with a (group, number) pair from + * \ref _mali_uk_functions to make it possible for a common function in the + * Base Driver and Device Driver to route User/Kernel calls from/to the + * correct _mali_uk function. For example, in an IOCTL system, the IOCTL number + * would be formed based on the group and number assigned to the _mali_uk + * function, as listed in \ref _mali_uk_functions. On the user-side, each + * _mali_uku function would just make an IOCTL with the IOCTL-code being an + * encoded form of the (group, number) pair. On the kernel-side, the Device + * Driver's IOCTL handler decodes the IOCTL-code back into a (group, number) + * pair, and uses this to determine which corresponding _mali_ukk should be + * called. + * - Refer to \ref _mali_uk_functions for more information about this + * (group, number) pairing. + * - In a system where there is no distinction between user and kernel-side, + * the U/K interface may be implemented as:@code + * MALI_STATIC_INLINE _mali_osk_errcode_t _mali_uku_examplefunction( _mali_uk_examplefunction_s *args ) + * { + * return mali_ukk_examplefunction( args ); + * } + * @endcode + * - Therefore, all U/K calls behave \em as \em though they were direct + * function calls (but the \b implementation \em need \em not be a direct + * function calls) + * + * @note Naming the _mali_uk functions the same on both User and Kernel sides + * on non-RTOS systems causes debugging issues when setting breakpoints. In + * this case, it is not clear which function the breakpoint is put on. + * Therefore the _mali_uk functions in user space are prefixed with \c _mali_uku + * and in kernel space with \c _mali_ukk. The naming for the argument + * structures is unaffected. + * + * - The _mali_uk functions are synchronous. + * - Arguments to the _mali_uk functions are passed in a structure. The only + * parameter passed to the _mali_uk functions is a pointer to this structure. + * This first member of this structure, ctx, is a pointer to a context returned + * by _mali_uku_open(). For example:@code + * typedef struct + * { + * void *ctx; + * u32 number_of_cores; + * } _mali_uk_get_gp_number_of_cores_s; + * @endcode + * + * - Each _mali_uk function has its own argument structure named after the + * function. The argument is distinguished by the _s suffix. + * - The argument types are defined by the base driver and user-kernel + * interface. + * - All _mali_uk functions return a standard \ref _mali_osk_errcode_t. + * - Only arguments of type input or input/output need be initialized before + * calling a _mali_uk function. + * - Arguments of type output and input/output are only valid when the + * _mali_uk function returns \ref _MALI_OSK_ERR_OK. + * - The \c ctx member is always invalid after it has been used by a + * _mali_uk function, except for the context management functions + * + * + * \b Interface \b restrictions + * + * The requirements of the interface mean that an implementation of the + * User-kernel interface may do no 'real' work. For example, the following are + * illegal in the User-kernel implementation: + * - Calling functions necessary for operation on all systems, which would + * not otherwise get called on RTOS systems. + * - For example, a U/K interface that calls multiple _mali_ukk functions + * during one particular U/K call. This could not be achieved by the same code + * which uses direct function calls for the U/K interface. + * - Writing in values to the args members, when otherwise these members would + * not hold a useful value for a direct function call U/K interface. + * - For example, U/K interface implementation that take NULL members in + * their arguments structure from the user side, but those members are + * replaced with non-NULL values in the kernel-side of the U/K interface + * implementation. A scratch area for writing data is one such example. In this + * case, a direct function call U/K interface would segfault, because no code + * would be present to replace the NULL pointer with a meaningful pointer. + * - Note that we discourage the case where the U/K implementation changes + * a NULL argument member to non-NULL, and then the Device Driver code (outside + * of the U/K layer) re-checks this member for NULL, and corrects it when + * necessary. Whilst such code works even on direct function call U/K + * intefaces, it reduces the testing coverage of the Device Driver code. This + * is because we have no way of testing the NULL == value path on an OS + * implementation. + * + * A number of allowable examples exist where U/K interfaces do 'real' work: + * - The 'pointer switching' technique for \ref _mali_ukk_get_system_info + * - In this case, without the pointer switching on direct function call + * U/K interface, the Device Driver code still sees the same thing: a pointer + * to which it can write memory. This is because such a system has no + * distinction between a user and kernel pointer. + * - Writing an OS-specific value into the ukk_private member for + * _mali_ukk_mem_mmap(). + * - In this case, this value is passed around by Device Driver code, but + * its actual value is never checked. Device Driver code simply passes it from + * the U/K layer to the OSK layer, where it can be acted upon. In this case, + * \em some OS implementations of the U/K (_mali_ukk_mem_mmap()) and OSK + * (_mali_osk_mem_mapregion_init()) functions will collaborate on the + * meaning of ukk_private member. On other OSs, it may be unused by both + * U/K and OSK layers + * - On OS systems (not including direct function call U/K interface + * implementations), _mali_ukk_get_big_block() may succeed, but the subsequent + * copying to user space may fail. + * - A problem scenario exists: some memory has been reserved by + * _mali_ukk_get_big_block(), but the user-mode will be unaware of it (it will + * never receive any information about this memory). In this case, the U/K + * implementation must do everything necessary to 'rollback' the \em atomic + * _mali_ukk_get_big_block() transaction. + * - Therefore, on error inside the U/K interface implementation itself, + * it will be as though the _mali_ukk function itself had failed, and cleaned + * up after itself. + * - Compare this to a direct function call U/K implementation, where all + * error cleanup is handled by the _mali_ukk function itself. The direct + * function call U/K interface implementation is automatically atomic. + * + * The last example highlights a consequence of all U/K interface + * implementations: they must be atomic with respect to the Device Driver code. + * And therefore, should Device Driver code succeed but the U/K implementation + * fail afterwards (but before return to user-space), then the U/K + * implementation must cause appropriate cleanup actions to preserve the + * atomicity of the interface. + * + * @{ + */ + + +/** @defgroup _mali_uk_context U/K Context management + * + * These functions allow for initialisation of the user-kernel interface once per process. + * + * Generally the context will store the OS specific object to communicate with the kernel device driver and further + * state information required by the specific implementation. The context is shareable among all threads in the caller process. + * + * On IOCTL systems, this is likely to be a file descriptor as a result of opening the kernel device driver. + * + * On a bare-metal/RTOS system with no distinction between kernel and + * user-space, the U/K interface simply calls the _mali_ukk variant of the + * function by direct function call. In this case, the context returned is the + * mali_session_data from _mali_ukk_open(). + * + * The kernel side implementations of the U/K interface expect the first member of the argument structure to + * be the context created by _mali_uku_open(). On some OS implementations, the meaning of this context + * will be different between user-side and kernel-side. In which case, the kernel-side will need to replace this context + * with the kernel-side equivalent, because user-side will not have access to kernel-side data. The context parameter + * in the argument structure therefore has to be of type input/output. + * + * It should be noted that the caller cannot reuse the \c ctx member of U/K + * argument structure after a U/K call, because it may be overwritten. Instead, + * the context handle must always be stored elsewhere, and copied into + * the appropriate U/K argument structure for each user-side call to + * the U/K interface. This is not usually a problem, since U/K argument + * structures are usually placed on the stack. + * + * @{ */ + +/** @brief Begin a new Mali Device Driver session + * + * This is used to obtain a per-process context handle for all future U/K calls. + * + * @param context pointer to storage to return a (void*)context handle. + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_open( void **context ); + +/** @brief End a Mali Device Driver session + * + * This should be called when the process no longer requires use of the Mali Device Driver. + * + * The context handle must not be used after it has been closed. + * + * @param context pointer to a stored (void*)context handle. + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_close( void **context ); + +/** @} */ /* end group _mali_uk_context */ + + +/** @addtogroup _mali_uk_core U/K Core + * + * The core functions provide the following functionality: + * - verify that the user and kernel API are compatible + * - retrieve information about the cores and memory banks in the system + * - wait for the result of jobs started on a core + * + * @{ */ + +/** @brief Returns the size of the buffer needed for a _mali_ukk_get_system_info call + * + * This function must be called before a call is made to + * _mali_ukk_get_system_info, so that memory of the correct size can be + * allocated, and a pointer to this memory written into the system_info member + * of _mali_uk_get_system_info_s. + * + * @param args see _mali_uk_get_system_info_size_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_system_info_size( _mali_uk_get_system_info_size_s *args ); + +/** @brief Returns information about the system (cores and memory banks) + * + * A buffer for this needs to be allocated by the caller. The size of the buffer required is returned by + * _mali_ukk_get_system_info_size(). The user is responsible for freeing the buffer. + * + * The _mali_system_info structure will be written to the start of this buffer, + * and the core_info and mem_info lists will be written to locations inside + * the buffer, and will be suitably aligned. + * + * Under OS implementations of the U/K interface we need to pack/unpack + * pointers across the user/kernel boundary. This has required that we malloc() + * an intermediate buffer inside the kernel-side U/K interface, and free it + * before returning to user-side. To avoid modifying common code, we do the + * following pseudo-code, which we shall call 'pointer switching': + * + * @code + * { + * Copy_From_User(kargs, args, ... ); + * void __user * local_ptr = kargs->system_info; + * kargs->system_info = _mali_osk_malloc( ... ); + * _mali_ukk_get_system_info( kargs ); + * Copy_To_User( local_ptr, kargs->system_info, ... ); + * _mali_osk_free( kargs->system_info ); + * } + * @endcode + * @note The user-side's args->system_info members was unmodified here. + * + * However, the current implementation requires an extra ukk_private word so that the common code can work out + * how to patch pointers to user-mode for an OS's U/K implementation, this should be set to the user-space + * destination address for pointer-patching to occur. When NULL, it is unused, an no pointer-patching occurs in the + * common code. + * + * @param args see _mali_uk_get_system_info_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_system_info( _mali_uk_get_system_info_s *args ); + +/** @brief Waits for a job notification. + * + * Sleeps until notified or a timeout occurs. Returns information about the notification. + * + * @param args see _mali_uk_wait_for_notification_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_wait_for_notification( _mali_uk_wait_for_notification_s *args ); + +/** @brief Post a notification to the notification queue of this application. + * + * @param args see _mali_uk_post_notification_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_post_notification( _mali_uk_post_notification_s *args ); + +/** @brief Verifies if the user and kernel side of this API are compatible. + * + * @param args see _mali_uk_get_api_version_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_api_version( _mali_uk_get_api_version_s *args ); +/** @} */ /* end group _mali_uk_core */ + + +/** @addtogroup _mali_uk_memory U/K Memory + * + * The memory functions provide functionality with and without a Mali-MMU present. + * + * For Mali-MMU based systems, the following functionality is provided: + * - Initialize and terminate MALI virtual address space + * - Allocate/deallocate physical memory to a MALI virtual address range and map into/unmap from the + * current process address space + * - Map/unmap external physical memory into the MALI virtual address range + * + * For Mali-nonMMU based systems: + * - Allocate/deallocate MALI memory + * + * @{ */ + +/** + * @brief Initialize the Mali-MMU Memory system + * + * For Mali-MMU builds of the drivers, this function must be called before any + * other functions in the \ref _mali_uk_memory group are called. + * + * @note This function is for Mali-MMU builds \b only. It should not be called + * when the drivers are built without Mali-MMU support. + * + * @param args see \ref _mali_uk_init_mem_s in mali_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable + * _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_init_mem( _mali_uk_init_mem_s *args ); + +/** + * @brief Terminate the MMU Memory system + * + * For Mali-MMU builds of the drivers, this function must be called when + * functions in the \ref _mali_uk_memory group will no longer be called. This + * function must be called before the application terminates. + * + * @note This function is for Mali-MMU builds \b only. It should not be called + * when the drivers are built without Mali-MMU support. + * + * @param args see \ref _mali_uk_term_mem_s in mali_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable + * _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_term_mem( _mali_uk_term_mem_s *args ); + +/** @brief Map a block of memory into the current user process + * + * Allocates a minimum of minimum_size_requested bytes of MALI memory and maps it into the current + * process space. The number of bytes allocated is returned in args->block_size. + * + * This is only used for Mali-nonMMU mode. + * + * @param args see _mali_uk_get_big_block_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_big_block( _mali_uk_get_big_block_s *args ); + +/** @brief Unmap a block of memory from the current user process + * + * Frees allocated MALI memory and unmaps it from the current process space. The previously allocated memory + * is indicated by the cookie as returned by _mali_ukk_get_big_block(). + * + * This is only used for Mali-nonMMU mode. + * + * @param args see _mali_uk_free_big_block_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_free_big_block( _mali_uk_free_big_block_s *args ); + +/** @brief Map Mali Memory into the current user process + * + * Maps Mali memory into the current user process in a generic way. + * + * This function is to be used for Mali-MMU mode. The function is available in both Mali-MMU and Mali-nonMMU modes, + * but should not be called by a user process in Mali-nonMMU mode. In Mali-nonMMU mode, the function is callable + * from the kernel side, and is used to implement _mali_ukk_get_big_block() in this case. + * + * The implementation and operation of _mali_ukk_mem_mmap() is dependant on whether the driver is built for Mali-MMU + * or Mali-nonMMU: + * - In the nonMMU case, _mali_ukk_mem_mmap() requires a physical address to be specified. For this reason, an OS U/K + * implementation should not allow this to be called from user-space. In any case, nonMMU implementations are + * inherently insecure, and so the overall impact is minimal. Mali-MMU mode should be used if security is desired. + * - In the MMU case, _mali_ukk_mem_mmap() the _mali_uk_mem_mmap_s::phys_addr + * member is used for the \em Mali-virtual address desired for the mapping. The + * implementation of _mali_ukk_mem_mmap() will allocate both the CPU-virtual + * and CPU-physical addresses, and can cope with mapping a contiguous virtual + * address range to a sequence of non-contiguous physical pages. In this case, + * the CPU-physical addresses are not communicated back to the user-side, as + * they are unnecsessary; the \em Mali-virtual address range must be used for + * programming Mali structures. + * + * This means that in the first (nonMMU) case, the caller must manage the physical address allocations. The caller + * in this case is _mali_ukk_get_big_block(), which does indeed manage the Mali physical address ranges. + * + * In the second (MMU) case, _mali_ukk_mem_mmap() handles management of + * CPU-virtual and CPU-physical ranges, but the \em caller must manage the + * \em Mali-virtual address range from the user-side. + * + * @note Mali-virtual address ranges are entirely separate between processes. + * It is not possible for a process to accidentally corrupt another process' + * \em Mali-virtual address space. + * + * @param args see _mali_uk_mem_mmap_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_mem_mmap( _mali_uk_mem_mmap_s *args ); + +/** @brief Unmap Mali Memory from the current user process + * + * Unmaps Mali memory from the current user process in a generic way. This only operates on Mali memory supplied + * from _mali_ukk_mem_mmap(). + * + * @param args see _mali_uk_mem_munmap_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_mem_munmap( _mali_uk_mem_munmap_s *args ); + +/** @brief Determine the buffer size necessary for an MMU page table dump. + * @param args see _mali_uk_query_mmu_page_table_dump_size_s in mali_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_query_mmu_page_table_dump_size( _mali_uk_query_mmu_page_table_dump_size_s *args ); +/** @brief Dump MMU Page tables. + * @param args see _mali_uk_dump_mmu_page_table_s in mali_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_dump_mmu_page_table( _mali_uk_dump_mmu_page_table_s * args ); + +/** @brief Map a physically contiguous range of memory into Mali + * @param args see _mali_uk_map_external_mem_s in mali_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_map_external_mem( _mali_uk_map_external_mem_s *args ); + +/** @brief Unmap a physically contiguous range of memory from Mali + * @param args see _mali_uk_unmap_external_mem_s in mali_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_unmap_external_mem( _mali_uk_unmap_external_mem_s *args ); + +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 +/** @brief Map UMP memory into Mali + * @param args see _mali_uk_attach_ump_mem_s in mali_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_attach_ump_mem( _mali_uk_attach_ump_mem_s *args ); +/** @brief Unmap UMP memory from Mali + * @param args see _mali_uk_release_ump_mem_s in mali_uk_types.h + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_release_ump_mem( _mali_uk_release_ump_mem_s *args ); +#endif /* MALI_USE_UNIFIED_MEMORY_PROVIDER */ + +/** @brief Determine virtual-to-physical mapping of a contiguous memory range + * (optional) + * + * This allows the user-side to do a virtual-to-physical address translation. + * In conjunction with _mali_uku_map_external_mem, this can be used to do + * direct rendering. + * + * This function will only succeed on a virtual range that is mapped into the + * current process, and that is contigious. + * + * If va is not page-aligned, then it is rounded down to the next page + * boundary. The remainer is added to size, such that ((u32)va)+size before + * rounding is equal to ((u32)va)+size after rounding. The rounded modified + * va and size will be written out into args on success. + * + * If the supplied size is zero, or not a multiple of the system's PAGE_SIZE, + * then size will be rounded up to the next multiple of PAGE_SIZE before + * translation occurs. The rounded up size will be written out into args on + * success. + * + * On most OSs, virtual-to-physical address translation is a priveledged + * function. Therefore, the implementer must validate the range supplied, to + * ensure they are not providing arbitrary virtual-to-physical address + * translations. While it is unlikely such a mechanism could be used to + * compromise the security of a system on its own, it is possible it could be + * combined with another small security risk to cause a much larger security + * risk. + * + * @note This is an optional part of the interface, and is only used by certain + * implementations of libEGL. If the platform layer in your libEGL + * implementation does not require Virtual-to-Physical address translation, + * then this function need not be implemented. A stub implementation should not + * be required either, as it would only be removed by the compiler's dead code + * elimination. + * + * @note if implemented, this function is entirely platform-dependant, and does + * not exist in common code. + * + * @param args see _mali_uk_va_to_mali_pa_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_va_to_mali_pa( _mali_uk_va_to_mali_pa_s * args ); + +/** @} */ /* end group _mali_uk_memory */ + + +/** @addtogroup _mali_uk_pp U/K Fragment Processor + * + * The Fragment Processor (aka PP (Pixel Processor)) functions provide the following functionality: + * - retrieving version of the fragment processors + * - determine number of fragment processors + * - starting a job on a fragment processor + * + * @{ */ + +/** @brief Issue a request to start a new job on a Fragment Processor. + * + * If the request fails args->status is set to _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE and you can + * try to start the job again. + * + * An existing job could be returned for requeueing if the new job has a higher priority than a previously started job + * which the hardware hasn't actually started processing yet. In this case the new job will be started instead and the + * existing one returned, otherwise the new job is started and the status field args->status is set to + * _MALI_UK_START_JOB_STARTED. + * + * If an existing lower priority job is returned, args->returned_user_job_ptr contains a + * pointer to the returned job and the status field args->status is set to + * _MALI_UK_START_JOB_STARTED_LOW_PRI_JOB_RETURNED. + * + * Job completion can be awaited with _mali_ukk_wait_for_notification(). + * + * @param args see _mali_uk_pp_start_job_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_pp_start_job( _mali_uk_pp_start_job_s *args ); + +/** @brief Returns the number of Fragment Processors in the system + * + * @param args see _mali_uk_get_pp_number_of_cores_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_pp_number_of_cores( _mali_uk_get_pp_number_of_cores_s *args ); + +/** @brief Returns the version that all Fragment Processor cores are compatible with. + * + * This function may only be called when _mali_ukk_get_pp_number_of_cores() indicated at least one Fragment + * Processor core is available. + * + * @param args see _mali_uk_get_pp_core_version_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_pp_core_version( _mali_uk_get_pp_core_version_s *args ); + +/** @brief Abort any PP jobs with the given ID. + * + * Jobs internally queued or currently running on the hardware is to be stopped/aborted. + * Jobs aborted are reported via the normal job completion system. + * Any jobs, running or internally queued should be aborted imediately. + * Normal notifiction procedures to report on the status of these jobs. + * + * + * @param args see _malu_uk_pp_abort_job_s in "mali_uk_types.h" + */ +void _mali_ukk_pp_abort_job( _mali_uk_pp_abort_job_s *args ); +/** @} */ /* end group _mali_uk_pp */ + + +/** @addtogroup _mali_uk_gp U/K Vertex Processor + * + * The Vertex Processor (aka GP (Geometry Processor)) functions provide the following functionality: + * - retrieving version of the Vertex Processors + * - determine number of Vertex Processors available + * - starting a job on a Vertex Processor + * + * @{ */ + +/** @brief Issue a request to start a new job on a Vertex Processor. + * + * If the request fails args->status is set to _MALI_UK_START_JOB_NOT_STARTED_DO_REQUEUE and you can + * try to start the job again. + * + * An existing job could be returned for requeueing if the new job has a higher priority than a previously started job + * which the hardware hasn't actually started processing yet. In this case the new job will be started and the + * existing one returned, otherwise the new job is started and the status field args->status is set to + * _MALI_UK_START_JOB_STARTED. + * + * If an existing lower priority job is returned, args->returned_user_job_ptr contains a pointer to + * the returned job and the status field args->status is set to + * _MALI_UK_START_JOB_STARTED_LOW_PRI_JOB_RETURNED. + * + * Job completion can be awaited with _mali_ukk_wait_for_notification(). + * + * @param args see _mali_uk_gp_start_job_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_gp_start_job( _mali_uk_gp_start_job_s *args ); + +/** @brief Returns the number of Vertex Processors in the system. + * + * @param args see _mali_uk_get_gp_number_of_cores_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_gp_number_of_cores( _mali_uk_get_gp_number_of_cores_s *args ); + +/** @brief Returns the version that all Vertex Processor cores are compatible with. + * + * This function may only be called when _mali_uk_get_gp_number_of_cores() indicated at least one Vertex + * Processor core is available. + * + * @param args see _mali_uk_get_gp_core_version_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_get_gp_core_version( _mali_uk_get_gp_core_version_s *args ); + +/** @brief Resume or abort suspended Vertex Processor jobs. + * + * After receiving notification that a Vertex Processor job was suspended from + * _mali_ukk_wait_for_notification() you can use this function to resume or abort the job. + * + * @param args see _mali_uk_gp_suspend_response_s in "mali_uk_types.h" + * @return _MALI_OSK_ERR_OK on success, otherwise a suitable _mali_osk_errcode_t on failure. + */ +_mali_osk_errcode_t _mali_ukk_gp_suspend_response( _mali_uk_gp_suspend_response_s *args ); + +/** @brief Abort any GP jobs with the given ID. + * + * Jobs internally queued or currently running on the hardware is to be stopped/aborted. + * Jobs aborted are reported via the normal job completion system. + * + * Any jobs, running or internally queued should be aborted imediately. + * Normal notifiction procedures to report on the status of these jobs. + * + * @param args see _mali_uk_gp_abort_job_s in "mali_uk_types.h" + */ +void _mali_ukk_gp_abort_job( _mali_uk_gp_abort_job_s *args ); +/** @} */ /* end group _mali_uk_gp */ + +#if USING_MALI_PMM +/** @addtogroup _mali_uk_pmm U/K Power Management Module + * @{ */ + +/* @brief Power Management Module event message + * + * @note The event message can fail to be sent due to OOM but this is + * stored in the PMM state machine to be handled later + * + * @param args see _mali_uk_pmm_event_message_s in "mali_uk_types.h" + */ +void _mali_ukk_pmm_event_message( _mali_uk_pmm_message_s *args ); +/** @} */ /* end group _mali_uk_pmm */ +#endif /* USING_MALI_PMM */ + +#if MALI_TIMELINE_PROFILING_ENABLED +/** @addtogroup _mali_uk_profiling U/K Timeline profiling module + * @{ */ + +/** @brief Start recording profiling events. + * + * @param args see _mali_uk_profiling_start_s in "mali_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_profiling_start(_mali_uk_profiling_start_s *args); + +/** @brief Add event to profiling buffer. + * + * @param args see _mali_uk_profiling_add_event_s in "mali_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_profiling_add_event(_mali_uk_profiling_add_event_s *args); + +/** @brief Stop recording profiling events. + * + * @param args see _mali_uk_profiling_stop_s in "mali_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_profiling_stop(_mali_uk_profiling_stop_s *args); + +/** @brief Retrieve a recorded profiling event. + * + * @param args see _mali_uk_profiling_get_event_s in "mali_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_profiling_get_event(_mali_uk_profiling_get_event_s *args); + +/** @brief Clear recorded profiling events. + * + * @param args see _mali_uk_profiling_clear_s in "mali_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_profiling_clear(_mali_uk_profiling_clear_s *args); + +/** @brief Get the profiling config applicable for calling process. + * + * @param args see _mali_uk_profiling_get_config_s in "mali_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_profiling_get_config(_mali_uk_profiling_get_config_s *args); + + +/** @} */ /* end group _mali_uk_profiling */ +#endif + +/** @addtogroup _mali_uk_vsync U/K VSYNC reporting module + * @{ */ + +/** @brief Report events related to vsync. + * + * @note Events should be reported when starting to wait for vsync and when the + * waiting is finished. This information can then be used in kernel space to + * complement the GPU utilization metric. + * + * @param args see _mali_uk_vsync_event_report_s in "mali_uk_types.h" + */ +_mali_osk_errcode_t _mali_ukk_vsync_event_report(_mali_uk_vsync_event_report_s *args); + +/** @} */ /* end group _mali_uk_vsync */ + +/** @} */ /* end group u_k_api */ + +/** @} */ /* end group uddapi */ + +u32 _mali_ukk_report_memory_usage(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_UKK_H__ */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm.c b/drivers/gpu/arm/mali/common/pmm/mali_pmm.c new file mode 100644 index 000000000000..3c015ba26fe4 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm.c @@ -0,0 +1,1006 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm.c + * Implementation of the power management module for the kernel device driver + */ + +#if USING_MALI_PMM + +#include "mali_ukk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_subsystem.h" + +#include "mali_pmm.h" +#include "mali_pmm_system.h" +#include "mali_pmm_state.h" +#include "mali_pmm_policy.h" +#include "mali_pmm_pmu.h" +#include "mali_platform.h" + +/* Internal PMM subsystem state */ +static _mali_pmm_internal_state_t *pmm_state = NULL; +/* Mali kernel subsystem id */ +static mali_kernel_subsystem_identifier mali_subsystem_pmm_id = -1; + +#define GET_PMM_STATE_PTR (pmm_state) + +/* Internal functions */ +static _mali_osk_errcode_t malipmm_create(_mali_osk_resource_t *resource); +static void pmm_event_process( void ); +_mali_osk_errcode_t malipmm_irq_uhandler(void *data); +void malipmm_irq_bhandler(void *data); + +/** @brief Start the PMM subsystem + * + * @param id Subsystem id to uniquely identify this subsystem + * @return _MALI_OSK_ERR_OK if the system started successfully, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t malipmm_kernel_subsystem_start( mali_kernel_subsystem_identifier id ); + +/** @brief Perform post start up of the PMM subsystem + * + * Post start up includes initializing the current policy, now that the system is + * completely started - to stop policies turning off hardware during the start up + * + * @param id the unique subsystem id + * @return _MALI_OSK_ERR_OK if the post startup was successful, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t malipmm_kernel_load_complete( mali_kernel_subsystem_identifier id ); + +/** @brief Terminate the PMM subsystem + * + * @param id the unique subsystem id + */ +void malipmm_kernel_subsystem_terminate( mali_kernel_subsystem_identifier id ); + +#if MALI_STATE_TRACKING +u32 malipmm_subsystem_dump_state( char *buf, u32 size ); +#endif + + +/* This will be one of the subsystems in the array of subsystems: + static struct mali_kernel_subsystem * subsystems[]; + found in file: mali_kernel_core.c +*/ +struct mali_kernel_subsystem mali_subsystem_pmm= +{ + malipmm_kernel_subsystem_start, /* startup */ + malipmm_kernel_subsystem_terminate, /* shutdown */ + malipmm_kernel_load_complete, /* loaded all subsystems */ + NULL, + NULL, + NULL, + NULL, +#if MALI_STATE_TRACKING + malipmm_subsystem_dump_state, /* dump_state */ +#endif +}; + +#if PMM_OS_TEST + +u32 power_test_event = 0; +mali_bool power_test_flag = MALI_FALSE; +_mali_osk_timer_t *power_test_timer = NULL; + +void _mali_osk_pmm_power_up_done(mali_pmm_message_data data) +{ + MALI_PRINT(("POWER TEST OS UP DONE\n")); +} + +void _mali_osk_pmm_power_down_done(mali_pmm_message_data data) +{ + MALI_PRINT(("POWER TEST OS DOWN DONE\n")); +} + +/** + * Symbian OS Power Up call to the driver + */ +void power_test_callback( void *arg ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + power_test_flag = MALI_TRUE; + _mali_osk_irq_schedulework( pmm->irq ); +} + +void power_test_start() +{ + power_test_timer = _mali_osk_timer_init(); + _mali_osk_timer_setcallback( power_test_timer, power_test_callback, NULL ); + + /* First event is power down */ + power_test_event = MALI_PMM_EVENT_OS_POWER_DOWN; + _mali_osk_timer_add( power_test_timer, 10000 ); +} + +mali_bool power_test_check() +{ + if( power_test_flag ) + { + _mali_uk_pmm_message_s event = { + NULL, + 0, + 1 }; + event.id = power_test_event; + + power_test_flag = MALI_FALSE; + + /* Send event */ + _mali_ukk_pmm_event_message( &event ); + + /* Switch to next event to test */ + if( power_test_event == MALI_PMM_EVENT_OS_POWER_DOWN ) + { + power_test_event = MALI_PMM_EVENT_OS_POWER_UP; + } + else + { + power_test_event = MALI_PMM_EVENT_OS_POWER_DOWN; + } + _mali_osk_timer_add( power_test_timer, 5000 ); + + return MALI_TRUE; + } + + return MALI_FALSE; +} + +void power_test_end() +{ + _mali_osk_timer_del( power_test_timer ); + _mali_osk_timer_term( power_test_timer ); + power_test_timer = NULL; +} + +#endif + +void _mali_ukk_pmm_event_message( _mali_uk_pmm_message_s *args ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + _mali_osk_notification_t *msg; + mali_pmm_message_t *event; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(args); + + MALIPMM_DEBUG_PRINT( ("PMM: sending message\n") ); + +#if MALI_PMM_TRACE && MALI_PMM_TRACE_SENT_EVENTS + _mali_pmm_trace_event_message( args, MALI_FALSE ); +#endif + + msg = _mali_osk_notification_create( MALI_PMM_NOTIFICATION_TYPE, sizeof( mali_pmm_message_t ) ); + + if( msg ) + { + event = (mali_pmm_message_t *)msg->result_buffer; + event->id = args->id; + event->ts = _mali_osk_time_tickcount(); + event->data = args->data; + + _mali_osk_atomic_inc( &(pmm->messages_queued) ); + + if( args->id > MALI_PMM_EVENT_INTERNALS ) + { + /* Internal PMM message */ + _mali_osk_notification_queue_send( pmm->iqueue, msg ); + #if (MALI_PMM_TRACE || MALI_STATE_TRACKING) + pmm->imessages_sent++; + #endif + } + else + { + /* Real event */ + _mali_osk_notification_queue_send( pmm->queue, msg ); + #if (MALI_PMM_TRACE || MALI_STATE_TRACKING) + pmm->messages_sent++; + #endif + } + } + else + { + MALI_PRINT_ERROR( ("PMM: Could not send message %d", args->id) ); + /* Make note of this OOM - which has caused a missed event */ + pmm->missed++; + } + + /* Schedule time to look at the event or the fact we couldn't create an event */ + _mali_osk_irq_schedulework( pmm->irq ); +} + +mali_pmm_state _mali_pmm_state( void ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + if( pmm && (mali_subsystem_pmm_id != -1) ) + { + return pmm->state; + } + + /* No working subsystem yet */ + return MALI_PMM_STATE_UNAVAILABLE; +} + + +mali_pmm_core_mask _mali_pmm_cores_list( void ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + return pmm->cores_registered; +} + +mali_pmm_core_mask _mali_pmm_cores_powered( void ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + return pmm->cores_powered; +} + + +_mali_osk_errcode_t _mali_pmm_list_policies( + u32 policy_list_size, + mali_pmm_policy *policy_list, + u32 *policies_available ) +{ + /* TBD - This is currently a stub function for basic power management */ + + MALI_ERROR( _MALI_OSK_ERR_UNSUPPORTED ); +} + +_mali_osk_errcode_t _mali_pmm_set_policy( mali_pmm_policy policy ) +{ + /* TBD - This is currently a stub function for basic power management */ + +/* TBD - When this is not a stub... include tracing... +#if MALI_PMM_TRACE + _mali_pmm_trace_policy_change( old, newpolicy ); +#endif +*/ + MALI_ERROR( _MALI_OSK_ERR_UNSUPPORTED ); +} + +_mali_osk_errcode_t _mali_pmm_get_policy( mali_pmm_policy *policy ) +{ + if( policy ) + { + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + if( pmm ) + { + *policy = pmm->policy; + MALI_SUCCESS; + } + else + { + *policy = MALI_PMM_POLICY_NONE; + MALI_ERROR( _MALI_OSK_ERR_FAULT ); + } + } + + /* No return argument */ + MALI_ERROR( _MALI_OSK_ERR_INVALID_ARGS ); +} + +#if ( MALI_PMM_TRACE || MALI_STATE_TRACKING ) + +/* Event names - order must match mali_pmm_event_id enum */ +static char *pmm_trace_events[] = { + "OS_POWER_UP", + "OS_POWER_DOWN", + "JOB_SCHEDULED", + "JOB_QUEUED", + "JOB_FINISHED", + "TIMEOUT", +}; + +/* State names - order must match mali_pmm_state enum */ +static char *pmm_trace_state[] = { + "UNAVAILABLE", + "SYSTEM ON", + "SYSTEM OFF", + "SYSTEM TRANSITION", +}; + +/* Policy names - order must match mali_pmm_policy enum */ +static char *pmm_trace_policy[] = { + "NONE", + "ALWAYS ON", + "JOB CONTROL", +}; + +/* Status names - order must match mali_pmm_status enum */ +static char *pmm_trace_status[] = { + "MALI_PMM_STATUS_IDLE", /**< PMM is waiting next event */ + "MALI_PMM_STATUS_POLICY_POWER_DOWN", /**< Policy initiated power down */ + "MALI_PMM_STATUS_POLICY_POWER_UP", /**< Policy initiated power down */ + "MALI_PMM_STATUS_OS_WAITING", /**< PMM is waiting for OS power up */ + "MALI_PMM_STATUS_OS_POWER_DOWN", /**< OS initiated power down */ + "MALI_PMM_STATUS_RUNTIME_IDLE_IN_PROGRESS", + "MALI_PMM_STATUS_DVFS_PAUSE", /**< PMM DVFS Status Pause */ + "MALI_PMM_STATUS_OS_POWER_UP", /**< OS initiated power up */ + "MALI_PMM_STATUS_OFF", /**< PMM is not active */ +}; + +#endif /* MALI_PMM_TRACE || MALI_STATE_TRACKING */ +#if MALI_PMM_TRACE + +/* UK event names - order must match mali_pmm_event_id enum */ +static char *pmm_trace_events_uk[] = { + "UKS", + "UK_EXAMPLE", +}; + +/* Internal event names - order must match mali_pmm_event_id enum */ +static char *pmm_trace_events_internal[] = { + "INTERNALS", + "INTERNAL_POWER_UP_ACK", + "INTERNAL_POWER_DOWN_ACK", +}; + +void _mali_pmm_trace_hardware_change( mali_pmm_core_mask old, mali_pmm_core_mask newstate ) +{ + const char *dname; + const char *cname; + const char *ename; + + if( old != newstate ) + { + if( newstate == 0 ) + { + dname = "NO cores"; + } + else + { + dname = pmm_trace_get_core_name( newstate ); + } + + /* These state checks only work if the assumption that only cores can be + * turned on or turned off in seperate actions is true. If core power states can + * be toggled (some one, some off) at the same time, this check does not work + */ + if( old > newstate ) + { + /* Cores have turned off */ + cname = pmm_trace_get_core_name( old - newstate ); + ename = "OFF"; + } + else + { + /* Cores have turned on */ + cname = pmm_trace_get_core_name( newstate - old ); + ename = "ON"; + } + MALI_PRINT( ("PMM Trace: Hardware %s ON, %s just turned %s. { 0x%08x -> 0x%08x }", dname, cname, ename, old, newstate) ); + } +} + +void _mali_pmm_trace_state_change( mali_pmm_state old, mali_pmm_state newstate ) +{ + if( old != newstate ) + { + MALI_PRINT( ("PMM Trace: State changed from %s to %s", pmm_trace_state[old], pmm_trace_state[newstate]) ); + } +} + +void _mali_pmm_trace_policy_change( mali_pmm_policy old, mali_pmm_policy newpolicy ) +{ + if( old != newpolicy ) + { + MALI_PRINT( ("PMM Trace: Policy changed from %s to %s", pmm_trace_policy[old], pmm_trace_policy[newpolicy]) ); + } +} + +void _mali_pmm_trace_event_message( mali_pmm_message_t *event, mali_bool received ) +{ + const char *ename; + const char *dname; + const char *tname; + const char *format = "PMM Trace: Event %s { (%d) %s, %d ticks, (0x%x) %s }"; + + MALI_DEBUG_ASSERT_POINTER(event); + + tname = (received) ? "received" : "sent"; + + if( event->id >= MALI_PMM_EVENT_INTERNALS ) + { + ename = pmm_trace_events_internal[((int)event->id) - MALI_PMM_EVENT_INTERNALS]; + } + else if( event->id >= MALI_PMM_EVENT_UKS ) + { + ename = pmm_trace_events_uk[((int)event->id) - MALI_PMM_EVENT_UKS]; + } + else + { + ename = pmm_trace_events[event->id]; + } + + switch( event->id ) + { + case MALI_PMM_EVENT_OS_POWER_UP: + case MALI_PMM_EVENT_OS_POWER_DOWN: + dname = "os event"; + break; + + case MALI_PMM_EVENT_JOB_SCHEDULED: + case MALI_PMM_EVENT_JOB_QUEUED: + case MALI_PMM_EVENT_JOB_FINISHED: + case MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK: + case MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK: + dname = pmm_trace_get_core_name( (mali_pmm_core_mask)event->data ); + break; + + case MALI_PMM_EVENT_TIMEOUT: + dname = "timeout start"; + /* Print data with a different format */ + format = "PMM Trace: Event %s { (%d) %s, %d ticks, %d ticks %s }"; + break; + default: + dname = "unknown data"; + } + + MALI_PRINT( (format, tname, (u32)event->id, ename, event->ts, (u32)event->data, dname) ); +} + +#endif /* MALI_PMM_TRACE */ + + +/****************** Mali Kernel API *****************/ + +_mali_osk_errcode_t malipmm_kernel_subsystem_start( mali_kernel_subsystem_identifier id ) +{ + mali_subsystem_pmm_id = id; + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(PMU, malipmm_create)); + MALI_SUCCESS; +} + +_mali_osk_errcode_t malipmm_create(_mali_osk_resource_t *resource) +{ + /* Create PMM state memory */ + MALI_DEBUG_ASSERT( pmm_state == NULL ); + pmm_state = (_mali_pmm_internal_state_t *) _mali_osk_malloc(sizeof(*pmm_state)); + MALI_CHECK_NON_NULL( pmm_state, _MALI_OSK_ERR_NOMEM ); + + /* All values get 0 as default */ + _mali_osk_memset(pmm_state, 0, sizeof(*pmm_state)); + + /* Set up the initial PMM state */ + pmm_state->waiting = 0; + pmm_state->status = MALI_PMM_STATUS_IDLE; + pmm_state->state = MALI_PMM_STATE_UNAVAILABLE; /* Until a core registers */ + + /* Set up policy via compile time option for the moment */ +#if MALI_PMM_ALWAYS_ON + pmm_state->policy = MALI_PMM_POLICY_ALWAYS_ON; +#else + pmm_state->policy = MALI_PMM_POLICY_JOB_CONTROL; +#endif + +#if MALI_PMM_TRACE + _mali_pmm_trace_policy_change( MALI_PMM_POLICY_NONE, pmm_state->policy ); +#endif + + /* Set up assumes all values are initialized to NULL or MALI_FALSE, so + * we can exit halfway through set up and perform clean up + */ + +#if USING_MALI_PMU + if( mali_pmm_pmu_init(resource) != _MALI_OSK_ERR_OK ) goto pmm_fail_cleanup; + pmm_state->pmu_initialized = MALI_TRUE; +#endif + pmm_state->queue = _mali_osk_notification_queue_init(); + if( !pmm_state->queue ) goto pmm_fail_cleanup; + + pmm_state->iqueue = _mali_osk_notification_queue_init(); + if( !pmm_state->iqueue ) goto pmm_fail_cleanup; + + /* We are creating an IRQ handler just for the worker thread it gives us */ + pmm_state->irq = _mali_osk_irq_init( _MALI_OSK_IRQ_NUMBER_PMM, + malipmm_irq_uhandler, + malipmm_irq_bhandler, + NULL, + NULL, + (void *)pmm_state, /* PMM state is passed to IRQ */ + "PMM handler" ); + + if( !pmm_state->irq ) goto pmm_fail_cleanup; + + pmm_state->lock = _mali_osk_lock_init((_mali_osk_lock_flags_t)(_MALI_OSK_LOCKFLAG_READERWRITER | _MALI_OSK_LOCKFLAG_ORDERED), 0, 75); + if( !pmm_state->lock ) goto pmm_fail_cleanup; + + if( _mali_osk_atomic_init( &(pmm_state->messages_queued), 0 ) != _MALI_OSK_ERR_OK ) + { + goto pmm_fail_cleanup; + } + + MALIPMM_DEBUG_PRINT( ("PMM: subsystem created, policy=%d\n", pmm_state->policy) ); + + MALI_SUCCESS; + +pmm_fail_cleanup: + MALI_PRINT_ERROR( ("PMM: subsystem failed to be created\n") ); + if( pmm_state ) + { + if( pmm_state->lock ) _mali_osk_lock_term( pmm_state->lock ); + if( pmm_state->irq ) _mali_osk_irq_term( pmm_state->irq ); + if( pmm_state->queue ) _mali_osk_notification_queue_term( pmm_state->queue ); + if( pmm_state->iqueue ) _mali_osk_notification_queue_term( pmm_state->iqueue ); +#if USING_MALI_PMU + if( pmm_state->pmu_initialized ) + { + _mali_osk_resource_type_t t = PMU; + mali_pmm_pmu_deinit(&t); + } +#endif /* USING_MALI_PMU */ + + _mali_osk_free(pmm_state); + pmm_state = NULL; + } + MALI_ERROR( _MALI_OSK_ERR_FAULT ); +} + +_mali_osk_errcode_t malipmm_kernel_load_complete( mali_kernel_subsystem_identifier id ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + MALIPMM_DEBUG_PRINT( ("PMM: subsystem loaded, policy initializing\n") ); + +#if PMM_OS_TEST + power_test_start(); +#endif + + /* Initialize the profile now the system has loaded - so that cores are + * not turned off during start up + */ + return pmm_policy_init( pmm ); +} + +void malipmm_force_powerup( void ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_LOCK(pmm); + pmm->status = MALI_PMM_STATUS_OFF; + MALI_PMM_UNLOCK(pmm); + + /* flush PMM workqueue */ + _mali_osk_flush_workqueue( pmm->irq ); + + if (pmm->cores_powered == 0) + { + malipmm_powerup(pmm->cores_registered); + } +} + +void malipmm_kernel_subsystem_terminate( mali_kernel_subsystem_identifier id ) +{ + /* Check this is the right system */ + MALI_DEBUG_ASSERT( id == mali_subsystem_pmm_id ); + MALI_DEBUG_ASSERT_POINTER(pmm_state); + + if( pmm_state ) + { +#if PMM_OS_TEST + power_test_end(); +#endif + /* Get the lock so we can shutdown */ + MALI_PMM_LOCK(pmm_state); +#if MALI_STATE_TRACKING + pmm_state->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + pmm_state->status = MALI_PMM_STATUS_OFF; +#if MALI_STATE_TRACKING + pmm_state->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + MALI_PMM_UNLOCK(pmm_state); + _mali_osk_pmm_ospmm_cleanup(); + pmm_policy_term(pmm_state); + _mali_osk_irq_term( pmm_state->irq ); + _mali_osk_notification_queue_term( pmm_state->queue ); + _mali_osk_notification_queue_term( pmm_state->iqueue ); + if (pmm_state->cores_registered) malipmm_powerdown(pmm_state->cores_registered,MALI_POWER_MODE_LIGHT_SLEEP); +#if USING_MALI_PMU + if( pmm_state->pmu_initialized ) + { + _mali_osk_resource_type_t t = PMU; + mali_pmm_pmu_deinit(&t); + } +#endif /* USING_MALI_PMU */ + + _mali_osk_atomic_term( &(pmm_state->messages_queued) ); + MALI_PMM_LOCK_TERM(pmm_state); + _mali_osk_free(pmm_state); + pmm_state = NULL; + } + + MALIPMM_DEBUG_PRINT( ("PMM: subsystem terminated\n") ); +} + +_mali_osk_errcode_t malipmm_powerup( u32 cores ) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + + /* If all the cores are powered down, power up the MALI */ + if (pmm->cores_powered == 0) + { + mali_platform_power_mode_change(MALI_POWER_MODE_ON); +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + /* Initiate the power up */ + _mali_osk_pmm_dev_activate(); +#endif + } + +#if USING_MALI_PMU + err = mali_pmm_pmu_powerup( cores ); +#endif + return err; +} + +_mali_osk_errcode_t malipmm_powerdown( u32 cores, mali_power_mode power_mode ) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; +#if USING_MALI_PMU + err = mali_pmm_pmu_powerdown( cores ); +#endif + + /* If all cores are powered down, power off the MALI */ + if (pmm->cores_powered == 0) + { +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + /* Initiate the power down */ + _mali_osk_pmm_dev_idle(); +#endif + mali_platform_power_mode_change(power_mode); + } + return err; +} + +_mali_osk_errcode_t malipmm_core_register( mali_pmm_core_id core ) +{ + _mali_osk_errcode_t err; + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + + if( pmm == NULL ) + { + /* PMM state has not been created, this is because the PMU resource has not been + * created yet. + * This probably means that the PMU resource has not been specfied as the first + * resource in the config file + */ + MALI_PRINT_ERROR( ("PMM: Cannot register core %s because the PMU resource has not been\n initialized. Please make sure the PMU resource is the first resource in the\n resource configuration.\n", + pmm_trace_get_core_name(core)) ); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + MALI_PMM_LOCK(pmm); + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + + /* Check if the core is registered more than once in PMM */ + MALI_DEBUG_ASSERT( (pmm->cores_registered & core) == 0 ); + + MALIPMM_DEBUG_PRINT( ("PMM: core registered: (0x%x) %s\n", core, pmm_trace_get_core_name(core)) ); + +#if !MALI_PMM_NO_PMU + /* Make sure the core is powered up */ + err = malipmm_powerup( core ); +#else + err = _MALI_OSK_ERR_OK; +#endif + if( _MALI_OSK_ERR_OK == err ) + { +#if MALI_PMM_TRACE + mali_pmm_core_mask old_power = pmm->cores_powered; +#endif + /* Assume a registered core is now powered up and idle */ + pmm->cores_registered |= core; + pmm->cores_idle |= core; + pmm->cores_powered |= core; + pmm_update_system_state( pmm ); + +#if MALI_PMM_TRACE + _mali_pmm_trace_hardware_change( old_power, pmm->cores_powered ); +#endif + } + else + { + MALI_PRINT_ERROR( ("PMM: Error(%d) powering up registered core: (0x%x) %s\n", + err, core, pmm_trace_get_core_name(core)) ); + } + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + + return err; +} + +void malipmm_core_unregister( mali_pmm_core_id core ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + MALI_PMM_LOCK(pmm); +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + + /* Check if the core is registered in PMM */ + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, core ); + + MALIPMM_DEBUG_PRINT( ("PMM: core unregistered: (0x%x) %s\n", core, pmm_trace_get_core_name(core)) ); + + { +#if MALI_PMM_TRACE + mali_pmm_core_mask old_power = pmm->cores_powered; +#endif + /* Remove the core from the system */ + pmm->cores_idle &= (~core); + pmm->cores_powered &= (~core); + pmm->cores_pend_down &= (~core); + pmm->cores_pend_up &= (~core); + pmm->cores_ack_down &= (~core); + pmm->cores_ack_up &= (~core); + + pmm_update_system_state( pmm ); + +#if MALI_PMM_TRACE + _mali_pmm_trace_hardware_change( old_power, pmm->cores_powered ); +#endif + } + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); +} +void malipmm_core_power_down_okay( mali_pmm_core_id core ) +{ + _mali_uk_pmm_message_s event = { + NULL, + MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK, + 0 }; + + event.data = core; + + _mali_ukk_pmm_event_message( &event ); +} + +void malipmm_set_policy_check() +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + pmm->check_policy = MALI_TRUE; + + /* To check the policy we need to schedule some work */ + _mali_osk_irq_schedulework( pmm->irq ); +} + +_mali_osk_errcode_t malipmm_irq_uhandler(void *data) +{ + MALIPMM_DEBUG_PRINT( ("PMM: uhandler - not expected to be used\n") ); + + MALI_SUCCESS; +} + +void malipmm_irq_bhandler(void *data) +{ + _mali_pmm_internal_state_t *pmm; + pmm = (_mali_pmm_internal_state_t *)data; + MALI_DEBUG_ASSERT_POINTER(pmm); + +#if PMM_OS_TEST + if( power_test_check() ) return; +#endif + + MALI_PMM_LOCK(pmm); +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + /* Quick out when we are shutting down */ + if( pmm->status == MALI_PMM_STATUS_OFF ) + { + + #if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; + #endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + return; + } + + MALIPMM_DEBUG_PRINT( ("PMM: bhandler - Processing event\n") ); + + if( pmm->missed > 0 ) + { + MALI_PRINT_ERROR( ("PMM: Failed to send %d events", pmm->missed) ); + pmm_fatal_reset( pmm ); + } + + if( pmm->check_policy ) + { + pmm->check_policy = MALI_FALSE; + pmm_policy_check_policy(pmm); + } + else + { + /* Perform event processing */ + pmm_event_process(); + if( pmm->fatal_power_err ) + { + /* Try a reset */ + pmm_fatal_reset( pmm ); + } + } + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); +} + +static void pmm_event_process( void ) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + _mali_osk_notification_t *msg = NULL; + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + mali_pmm_message_t *event; + u32 process_messages; + + MALI_DEBUG_ASSERT_POINTER(pmm); + + + /* Max number of messages to process before exiting - as we shouldn't stay + * processing the messages for a long time + */ + process_messages = _mali_osk_atomic_read( &(pmm->messages_queued) ); + + while( process_messages > 0 ) + { + /* Check internal message queue first */ + err = _mali_osk_notification_queue_dequeue( pmm->iqueue, &msg ); + + if( err != _MALI_OSK_ERR_OK ) + { + if( pmm->status == MALI_PMM_STATUS_IDLE || pmm->status == MALI_PMM_STATUS_OS_WAITING || pmm->status == MALI_PMM_STATUS_DVFS_PAUSE) + { + if( pmm->waiting > 0 ) pmm->waiting--; + + /* We aren't busy changing state, so look at real events */ + err = _mali_osk_notification_queue_dequeue( pmm->queue, &msg ); + + if( err != _MALI_OSK_ERR_OK ) + { + pmm->no_events++; + MALIPMM_DEBUG_PRINT( ("PMM: event_process - No message to process\n") ); + /* Nothing to do - so return */ + return; + } + else + { + #if (MALI_PMM_TRACE || MALI_STATE_TRACKING) + pmm->messages_received++; + #endif + } + } + else + { + /* Waiting for an internal message */ + pmm->waiting++; + MALIPMM_DEBUG_PRINT( ("PMM: event_process - Waiting for internal message, messages queued=%d\n", pmm->waiting) ); + return; + } + } + else + { + #if (MALI_PMM_TRACE || MALI_STATE_TRACKING) + pmm->imessages_received++; + #endif + } + + MALI_DEBUG_ASSERT_POINTER( msg ); + /* Check the message type matches */ + MALI_DEBUG_ASSERT( msg->notification_type == MALI_PMM_NOTIFICATION_TYPE ); + + event = msg->result_buffer; + + _mali_osk_atomic_dec( &(pmm->messages_queued) ); + process_messages--; + + #if MALI_PMM_TRACE + /* Trace before we process the event in case we have an error */ + _mali_pmm_trace_event_message( event, MALI_TRUE ); + #endif + err = pmm_policy_process( pmm, event ); + + + if( err != _MALI_OSK_ERR_OK ) + { + MALI_PRINT_ERROR( ("PMM: Error(%d) in policy %d when processing event message with id: %d", + err, pmm->policy, event->id) ); + } + + /* Delete notification */ + _mali_osk_notification_delete ( msg ); + + if( pmm->fatal_power_err ) + { + /* Nothing good has happened - exit */ + return; + } + + + #if MALI_PMM_TRACE + MALI_PRINT( ("PMM Trace: Event processed, msgs (sent/read) = %d/%d, int msgs (sent/read) = %d/%d, no events = %d, waiting = %d\n", + pmm->messages_sent, pmm->messages_received, pmm->imessages_sent, pmm->imessages_received, pmm->no_events, pmm->waiting) ); + #endif + } + + if( pmm->status == MALI_PMM_STATUS_IDLE && pmm->waiting > 0 ) + { + /* For events we ignored whilst we were busy, add a new + * scheduled time to look at them */ + _mali_osk_irq_schedulework( pmm->irq ); + } +} + +#if MALI_STATE_TRACKING +u32 malipmm_subsystem_dump_state(char *buf, u32 size) +{ + int len = 0; + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + + if( !pmm ) + { + len += _mali_osk_snprintf(buf + len, size + len, "PMM: Null state\n"); + } + else + { + len += _mali_osk_snprintf(buf+len, size+len, "Locks:\n PMM lock acquired: %s\n", + pmm->mali_pmm_lock_acquired ? "true" : "false"); + len += _mali_osk_snprintf(buf+len, size+len, + "PMM state:\n Previous status: %s\n Status: %s\n Current event: %s\n Policy: %s\n Check policy: %s\n State: %s\n", + pmm_trace_status[pmm->mali_last_pmm_status], pmm_trace_status[pmm->status], + pmm_trace_events[pmm->mali_new_event_status], pmm_trace_policy[pmm->policy], + pmm->check_policy ? "true" : "false", pmm_trace_state[pmm->state]); + len += _mali_osk_snprintf(buf+len, size+len, + "PMM cores:\n Cores registered: %d\n Cores powered: %d\n Cores idle: %d\n" + " Cores pending down: %d\n Cores pending up: %d\n Cores ack down: %d\n Cores ack up: %d\n", + pmm->cores_registered, pmm->cores_powered, pmm->cores_idle, pmm->cores_pend_down, + pmm->cores_pend_up, pmm->cores_ack_down, pmm->cores_ack_up); + len += _mali_osk_snprintf(buf+len, size+len, "PMM misc:\n PMU init: %s\n Messages queued: %d\n" + " Waiting: %d\n No events: %d\n Missed events: %d\n Fatal power error: %s\n", + pmm->pmu_initialized ? "true" : "false", _mali_osk_atomic_read(&(pmm->messages_queued)), + pmm->waiting, pmm->no_events, pmm->missed, pmm->fatal_power_err ? "true" : "false"); + } + return len; +} +#endif /* MALI_STATE_TRACKING */ + +#endif /* USING_MALI_PMM */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm.h b/drivers/gpu/arm/mali/common/pmm/mali_pmm.h new file mode 100644 index 000000000000..4a667bbe5d28 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm.h @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm.h + * Defines the power management module for the kernel device driver + */ + +#ifndef __MALI_PMM_H__ +#define __MALI_PMM_H__ + +/* For mali_pmm_message_data and MALI_PMM_EVENT_UK_* defines */ +#include "mali_uk_types.h" +#include "mali_platform.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @defgroup pmmapi Power Management Module APIs + * + * @{ + */ + +/** OS event tester */ +#define PMM_OS_TEST 0 + +/** @brief Compile option to turn on/off tracing */ +#define MALI_PMM_TRACE 0 +#define MALI_PMM_TRACE_SENT_EVENTS 0 + +/** @brief Compile option to switch between always on or job control PMM policy */ +#define MALI_PMM_ALWAYS_ON 0 + +/** @brief Overrides hardware PMU and uses software simulation instead + * @note This even stops intialization of PMU and cores being powered on at start up + */ +#define MALI_PMM_NO_PMU 0 + +/** @brief PMM debug print to control debug message level */ +#define MALIPMM_DEBUG_PRINT(args) \ + MALI_DEBUG_PRINT(3, args) + + +/** @brief power management event message identifiers. + */ +/* These must match up with the pmm_trace_events & pmm_trace_events_internal + * arrays + */ +typedef enum mali_pmm_event_id +{ + MALI_PMM_EVENT_OS_POWER_UP = 0, /**< OS power up event */ + MALI_PMM_EVENT_OS_POWER_DOWN = 1, /**< OS power down event */ + MALI_PMM_EVENT_JOB_SCHEDULED = 2, /**< Job scheduled to run event */ + MALI_PMM_EVENT_JOB_QUEUED = 3, /**< Job queued (but not run) event */ + MALI_PMM_EVENT_JOB_FINISHED = 4, /**< Job finished event */ + MALI_PMM_EVENT_TIMEOUT = 5, /**< Time out timer has expired */ + MALI_PMM_EVENT_DVFS_PAUSE = 6, /**< Mali device pause event */ + MALI_PMM_EVENT_DVFS_RESUME = 7, /**< Mali device resume event */ + + MALI_PMM_EVENT_UKS = 200, /**< Events from the user-side start here */ + MALI_PMM_EVENT_UK_EXAMPLE = _MALI_PMM_EVENT_UK_EXAMPLE, + + MALI_PMM_EVENT_INTERNALS = 1000, + MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK = 1001, /**< Internal power up acknowledgement */ + MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK = 1002, /**< Internal power down acknowledgment */ +} mali_pmm_event_id; + + +/** @brief Use this when the power up/down callbacks do not need any OS data. */ +#define MALI_PMM_NO_OS_DATA 1 + + +/* @brief Geometry and pixel processor identifiers for the PMM + * + * @note these match the ARM Mali 400 PMU hardware definitions, apart from the "SYSTEM" + */ +typedef enum mali_pmm_core_id_tag +{ + MALI_PMM_CORE_SYSTEM = 0x00000000, /**< All of the Mali hardware */ + MALI_PMM_CORE_GP = 0x00000001, /**< Mali GP2 */ + MALI_PMM_CORE_L2 = 0x00000002, /**< Level 2 cache */ + MALI_PMM_CORE_PP0 = 0x00000004, /**< Mali 200 pixel processor 0 */ + MALI_PMM_CORE_PP1 = 0x00000008, /**< Mali 200 pixel processor 1 */ + MALI_PMM_CORE_PP2 = 0x00000010, /**< Mali 200 pixel processor 2 */ + MALI_PMM_CORE_PP3 = 0x00000020, /**< Mali 200 pixel processor 3 */ + MALI_PMM_CORE_PP_ALL = 0x0000003C /**< Mali 200 pixel processors 0-3 */ +} mali_pmm_core_id; + + +/* @brief PMM bitmask of mali_pmm_core_ids + */ +typedef u32 mali_pmm_core_mask; + +/* @brief PMM event timestamp type + */ +typedef u32 mali_pmm_timestamp; + +/** @brief power management event message struct + */ +typedef struct _mali_pmm_message +{ + mali_pmm_event_id id; /**< event id */ + mali_pmm_message_data data; /**< specific data associated with the event */ + mali_pmm_timestamp ts; /**< timestamp the event was placed in the event queue */ +} mali_pmm_message_t; + + + +/** @brief the state of the power management module. + */ +/* These must match up with the pmm_trace_state array */ +typedef enum mali_pmm_state_tag +{ + MALI_PMM_STATE_UNAVAILABLE = 0, /**< PMM is not available */ + MALI_PMM_STATE_SYSTEM_ON = 1, /**< All of the Mali hardware is on */ + MALI_PMM_STATE_SYSTEM_OFF = 2, /**< All of the Mali hardware is off */ + MALI_PMM_STATE_SYSTEM_TRANSITION = 3 /**< System is changing state */ +} mali_pmm_state; + + +/** @brief a power management policy. + */ +/* These must match up with the pmm_trace_policy array */ +typedef enum mali_pmm_policy_tag +{ + MALI_PMM_POLICY_NONE = 0, /**< No policy */ + MALI_PMM_POLICY_ALWAYS_ON = 1, /**< Always on policy */ + MALI_PMM_POLICY_JOB_CONTROL = 2, /**< Job control policy */ + MALI_PMM_POLICY_RUNTIME_JOB_CONTROL = 3 /**< Run time power management control policy */ +} mali_pmm_policy; + +/** @brief Function to power up MALI + * + * @param cores core mask to power up the cores + * + * @return error code if MALI fails to power up + */ +_mali_osk_errcode_t malipmm_powerup( u32 cores ); + +/** @brief Function to power down MALI + * + * @param cores core mask to power down the cores + * @param The power mode to which MALI transitions + * + * @return error code if MALI fails to power down + */ +_mali_osk_errcode_t malipmm_powerdown( u32 cores, mali_power_mode power_mode ); + +/** @brief Function to report to the OS when the power down has finished + * + * @param data The event message data that initiated the power down + */ +void _mali_osk_pmm_power_down_done(mali_pmm_message_data data); + +/** @brief Function to report to the OS when the power up has finished + * + * @param data The event message data that initiated the power up + */ +void _mali_osk_pmm_power_up_done(mali_pmm_message_data data); + +/** @brief Function to report that DVFS operation done + * + * @param data The event message data + */ +void _mali_osk_pmm_dvfs_operation_done(mali_pmm_message_data data); + +#if MALI_POWER_MGMT_TEST_SUITE +/** @brief Function to notify power management events + * + * @param data The event message data + */ +void _mali_osk_pmm_policy_events_notifications(mali_pmm_event_id event_id); + +#endif + +/** @brief Function to power up MALI + * + * @note powers up the MALI during MALI device driver is unloaded + */ +void malipmm_force_powerup( void ); + +/** @brief Function to report the OS that device is idle + * + * @note inform the OS that device is idle + */ +_mali_osk_errcode_t _mali_osk_pmm_dev_idle( void ); + +/** @brief Function to report the OS to activate device + * + * @note inform the os that device needs to be activated + */ +void _mali_osk_pmm_dev_activate( void ); + +/** @brief Function to report OS PMM for cleanup + * + * @note Function to report OS PMM for cleanup + */ +void _mali_osk_pmm_ospmm_cleanup( void ); + +/** @brief Queries the current state of the PMM software + * + * @note the state of the PMM can change after this call has returned + * + * @return the current PMM state value + */ +mali_pmm_state _mali_pmm_state( void ); + +/** @brief List of cores that are registered with the PMM + * + * This will return the cores that have been currently registered with the PMM, + * which is a bitwise OR of the mali_pmm_core_id_tags. A value of 0x0 means that + * there are no cores registered. + * + * @note the list of cores can change after this call has returned + * + * @return a bit mask representing all the cores that have been registered with the PMM + */ +mali_pmm_core_mask _mali_pmm_cores_list( void ); + +/** @brief List of cores that are powered up in the PMM + * + * This will return the subset of the cores that can be listed using mali_pmm_cores_ + * list, that have power. It is a bitwise OR of the mali_pmm_core_id_tags. A value of + * 0x0 means that none of the cores registered are powered. + * + * @note the list of cores can change after this call has returned + * + * @return a bit mask representing all the cores that are powered up + */ +mali_pmm_core_mask _mali_pmm_cores_powered( void ); + + +/** @brief List of power management policies that are supported by the PMM + * + * Given an empty array of policies - policy_list - which contains the number + * of entries as specified by - policy_list_size, this function will populate + * the list with the available policies. If the policy_list is too small for + * all the policies then only policy_list_size entries will be returned. If the + * policy_list is bigger than the number of available policies then, the extra + * entries will be set to MALI_PMM_POLICY_NONE. + * The function will also update available_policies with the number of policies + * that are available, even if it exceeds the policy_list_size. + * The function will succeed if all policies could be returned, else it will + * fail if none or only a subset of policies could be returned. + * The function will also fail if no policy_list is supplied, though + * available_policies is optional. + * + * @note this is a STUB function and is not yet implemented + * + * @param policy_list_size is the number of policies that can be returned in + * the policy_list argument + * @param policy_list is an array of policies that should be populated with + * the list of policies that are supported by the PMM + * @param policies_available optional argument, if non-NULL will be set to the + * number of policies available + * @return _MALI_OSK_ERR_OK if the policies could be listed, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t _mali_pmm_list_policies( + u32 policy_list_size, + mali_pmm_policy *policy_list, + u32 *policies_available ); + +/** @brief Set the power management policy in the PMM + * + * Given a valid supported policy, this function will change the PMM to use + * this new policy + * The function will fail if the policy given is invalid or unsupported. + * + * @note this is a STUB function and is not yet implemented + * + * @param policy the new policy to be set + * @return _MALI_OSK_ERR_OK if the policy could be set, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t _mali_pmm_set_policy( mali_pmm_policy policy ); + +/** @brief Get the current power management policy in the PMM + * + * Given a pointer to a policy data type, this function will return the current + * policy that is in effect for the PMM. This maybe out of date if there is a + * pending set policy call that has not been serviced. + * The function will fail if the policy given is NULL. + * + * @note the policy of the PMM can change after this call has returned + * + * @param policy a pointer to a policy that can be updated to the current + * policy + * @return _MALI_OSK_ERR_OK if the policy could be returned, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t _mali_pmm_get_policy( mali_pmm_policy *policy ); + +#if MALI_PMM_TRACE + +/** @brief Indicates when a hardware state change occurs in the PMM + * + * @param old a mask of the cores indicating the previous state of the cores + * @param newstate a mask of the cores indicating the new current state of the cores + */ +void _mali_pmm_trace_hardware_change( mali_pmm_core_mask old, mali_pmm_core_mask newstate ); + +/** @brief Indicates when a state change occurs in the PMM + * + * @param old the previous state for the PMM + * @param newstate the new current state of the PMM + */ +void _mali_pmm_trace_state_change( mali_pmm_state old, mali_pmm_state newstate ); + +/** @brief Indicates when a policy change occurs in the PMM + * + * @param old the previous policy for the PMM + * @param newpolicy the new current policy of the PMM + */ +void _mali_pmm_trace_policy_change( mali_pmm_policy old, mali_pmm_policy newpolicy ); + +/** @brief Records when an event message is read by the event system + * + * @param event the message details + * @param received MALI_TRUE when the message is received by the PMM, else it is being sent + */ +void _mali_pmm_trace_event_message( mali_pmm_message_t *event, mali_bool received ); + +#endif /* MALI_PMM_TRACE */ + +/** @brief Dumps the current state of OS PMM thread + */ +#if MALI_STATE_TRACKING +u32 mali_pmm_dump_os_thread_state( char *buf, u32 size ); +#endif /* MALI_STATE_TRACKING */ + +/** @} */ /* end group pmmapi */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_H__ */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_pmu.c b/drivers/gpu/arm/mali/common/pmm/mali_pmm_pmu.c new file mode 100644 index 000000000000..da8957724e8f --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_pmu.c @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_pmu.c + * Mali driver functions for Mali 400 PMU hardware + */ +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_platform.h" + +#if USING_MALI_PMU +#if USING_MALI_PMM + +#include "mali_pmm.h" + +/* Internal test on/off */ +#define PMU_TEST 0 + +#if MALI_POWER_MGMT_TEST_SUITE +#include "mali_platform_pmu_internal_testing.h" +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + +/** @brief PMU hardware info + */ +typedef struct platform_pmu +{ + u32 reg_base_addr; /**< PMU registers base address */ + u32 reg_size; /**< PMU registers size */ + const char *name; /**< PMU name */ + u32 irq_num; /**< PMU irq number */ + + mali_io_address reg_mapped; /**< IO-mapped pointer to registers */ +} platform_pmu_t; + +static platform_pmu_t *pmu_info = NULL; + +/** @brief Register layout for hardware PMU + */ +typedef enum { + PMU_REG_ADDR_MGMT_POWER_UP = 0x00, /*< Power up register */ + PMU_REG_ADDR_MGMT_POWER_DOWN = 0x04, /*< Power down register */ + PMU_REG_ADDR_MGMT_STATUS = 0x08, /*< Core sleep status register */ + PMU_REG_ADDR_MGMT_INT_MASK = 0x0C, /*< Interrupt mask register */ + PMU_REG_ADDR_MGMT_INT_RAWSTAT = 0x10, /*< Interrupt raw status register */ + PMU_REG_ADDR_MGMT_INT_STAT = 0x14, /*< Interrupt status register */ + PMU_REG_ADDR_MGMT_INT_CLEAR = 0x18, /*< Interrupt clear register */ + PMU_REG_ADDR_MGMT_SW_DELAY = 0x1C, /*< Software delay register */ + PMU_REG_ADDR_MGMT_MASTER_PWR_UP = 0x24, /*< Master power up register */ + PMU_REGISTER_ADDRESS_SPACE_SIZE = 0x28, /*< Size of register space */ +} pmu_reg_addr_mgmt_addr; + +/* Internal functions */ +static u32 pmu_reg_read(platform_pmu_t *pmu, u32 relative_address); +static void pmu_reg_write(platform_pmu_t *pmu, u32 relative_address, u32 new_val); +static mali_pmm_core_mask pmu_translate_cores_to_pmu(mali_pmm_core_mask cores); +#if PMU_TEST +static void pmm_pmu_dump_regs( platform_pmu_t *pmu ); +static pmm_pmu_test( platform_pmu_t *pmu, u32 cores ); +#endif + +_mali_osk_errcode_t mali_pmm_pmu_init(_mali_osk_resource_t *resource) +{ + + if( resource->type == PMU ) + { + if( (resource->base == 0) || + (resource->description == NULL) ) + { + /* NOTE: We currently don't care about any other resource settings */ + MALI_PRINT_ERROR(("PLATFORM mali400-pmu: Missing PMU set up information\n")); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + pmu_info = (platform_pmu_t *)_mali_osk_malloc(sizeof(*pmu_info)); + MALI_CHECK_NON_NULL( pmu_info, _MALI_OSK_ERR_NOMEM ); + + /* All values get 0 as default */ + _mali_osk_memset(pmu_info, 0, sizeof(*pmu_info)); + + pmu_info->reg_base_addr = resource->base; + pmu_info->reg_size = (u32)PMU_REGISTER_ADDRESS_SPACE_SIZE; + pmu_info->name = resource->description; + pmu_info->irq_num = resource->irq; + + if( _MALI_OSK_ERR_OK != _mali_osk_mem_reqregion(pmu_info->reg_base_addr, pmu_info->reg_size, pmu_info->name) ) + { + MALI_PRINT_ERROR(("PLATFORM mali400-pmu: Could not request register region (0x%08X - 0x%08X) for %s\n", + pmu_info->reg_base_addr, pmu_info->reg_base_addr + pmu_info->reg_size - 1, pmu_info->name)); + goto cleanup; + } + else + { + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: Success: request_mem_region: (0x%08X - 0x%08X) for %s\n", + pmu_info->reg_base_addr, pmu_info->reg_base_addr + pmu_info->reg_size - 1, pmu_info->name)); + } + + pmu_info->reg_mapped = _mali_osk_mem_mapioregion( pmu_info->reg_base_addr, pmu_info->reg_size, pmu_info->name ); + + if( 0 == pmu_info->reg_mapped ) + { + MALI_PRINT_ERROR(("PLATFORM mali400-pmu: Could not ioremap registers for %s .\n", pmu_info->name)); + _mali_osk_mem_unreqregion( pmu_info->reg_base_addr, pmu_info->reg_size ); + goto cleanup; + } + else + { + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: Success: ioremap_nocache: Internal ptr: (0x%08X - 0x%08X) for %s\n", + (u32) pmu_info->reg_mapped, + ((u32)pmu_info->reg_mapped)+ pmu_info->reg_size - 1, + pmu_info->name)); + } + + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: Success: Mapping registers to %s\n", pmu_info->name)); + +#if PMU_TEST + pmu_test(pmu_info, (MALI_PMM_CORE_GP)); + pmu_test(pmu_info, (MALI_PMM_CORE_GP|MALI_PMM_CORE_L2|MALI_PMM_CORE_PP0)); +#endif + + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: Initialized - %s\n", pmu_info->name) ); + } + else + { + /* Didn't expect a different resource */ + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + MALI_SUCCESS; + +cleanup: + _mali_osk_free(pmu_info); + pmu_info = NULL; + MALI_ERROR(_MALI_OSK_ERR_NOMEM); +} + +_mali_osk_errcode_t mali_pmm_pmu_deinit(_mali_osk_resource_type_t *type) +{ + if (*type == PMU) + { + if( pmu_info ) + { + _mali_osk_mem_unmapioregion(pmu_info->reg_base_addr, pmu_info->reg_size, pmu_info->reg_mapped); + _mali_osk_mem_unreqregion(pmu_info->reg_base_addr, pmu_info->reg_size); + _mali_osk_free(pmu_info); + pmu_info = NULL; + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: Terminated PMU\n") ); + } + } + else + { + /* Didn't expect a different resource */ + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + MALI_SUCCESS; + +} + +_mali_osk_errcode_t mali_pmm_pmu_powerdown(u32 cores) +{ + u32 stat; + u32 timeout; + u32 cores_pmu; + + MALI_DEBUG_ASSERT_POINTER(pmu_info); + MALI_DEBUG_ASSERT( cores != 0 ); /* Shouldn't receive zero from PMM */ + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: power down (0x%x)\n", cores) ); + + cores_pmu = pmu_translate_cores_to_pmu(cores); + pmu_reg_write( pmu_info, (u32)PMU_REG_ADDR_MGMT_POWER_DOWN, cores_pmu ); + + /* Wait for cores to be powered down */ + timeout = 10; /* 10ms */ + do + { + /* Get status of sleeping cores */ + stat = pmu_reg_read( pmu_info, (u32)PMU_REG_ADDR_MGMT_STATUS ); + stat &= cores_pmu; + if( stat == cores_pmu ) break; /* All cores we wanted are now asleep */ + _mali_osk_time_ubusydelay(1000); /* 1ms */ + timeout--; + } while( timeout > 0 ); + + if( timeout == 0 ) MALI_ERROR(_MALI_OSK_ERR_TIMEOUT); + + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_pmm_pmu_powerup(u32 cores) +{ + u32 cores_pmu; + u32 stat; + u32 timeout; + + MALI_DEBUG_ASSERT_POINTER(pmu_info); + MALI_DEBUG_ASSERT( cores != 0 ); /* Shouldn't receive zero from PMM */ + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: power up (0x%x)\n", cores) ); + + /* Don't use interrupts - just poll status */ + pmu_reg_write( pmu_info, (u32)PMU_REG_ADDR_MGMT_INT_MASK, 0 ); + cores_pmu = pmu_translate_cores_to_pmu(cores); + pmu_reg_write( pmu_info, (u32)PMU_REG_ADDR_MGMT_POWER_UP, cores_pmu ); + + timeout = 10; /* 10ms */ + do + { + /* Get status of sleeping cores */ + stat = pmu_reg_read( pmu_info, (u32)PMU_REG_ADDR_MGMT_STATUS ); + stat &= cores_pmu; + if( stat == 0 ) break; /* All cores we wanted are now awake */ + _mali_osk_time_ubusydelay(1000); /* 1ms */ + timeout--; + } while( timeout > 0 ); + + if( timeout == 0 ) MALI_ERROR(_MALI_OSK_ERR_TIMEOUT); + + MALI_SUCCESS; +} + + +/***** INTERNAL *****/ + +/** @brief Internal PMU function to translate the cores bit mask + * into something the hardware PMU understands + * + * @param cores PMM cores bitmask + * @return PMU hardware cores bitmask + */ +static u32 pmu_translate_cores_to_pmu(mali_pmm_core_mask cores) +{ + /* For Mali 400 PMU the cores mask is already the same as what + * the hardware PMU expects. + * For other hardware, some translation can be done here, by + * translating the MALI_PMM_CORE_* bits into specific hardware + * bits + */ + return cores; +} + +/** @brief Internal PMU function to read a PMU register + * + * @param pmu handle that identifies the PMU hardware + * @param relative_address relative PMU hardware address to read from + * @return 32-bit value that was read from the address + */ +static u32 pmu_reg_read(platform_pmu_t *pmu, u32 relative_address) +{ + u32 read_val; + + MALI_DEBUG_ASSERT_POINTER(pmu); + MALI_DEBUG_ASSERT((relative_address & 0x03) == 0); + MALI_DEBUG_ASSERT(relative_address < pmu->reg_size); + + read_val = _mali_osk_mem_ioread32(pmu->reg_mapped, relative_address); + + MALI_DEBUG_PRINT( 5, ("PMU: reg_read: %s Addr:0x%04X Val:0x%08x\n", + pmu->name, relative_address, read_val)); + + return read_val; +} + +/** @brief Internal PMU function to write to a PMU register + * + * @param pmu handle that identifies the PMU hardware + * @param relative_address relative PMU hardware address to write to + * @param new_val new 32-bit value to write into the address + */ +static void pmu_reg_write(platform_pmu_t *pmu, u32 relative_address, u32 new_val) +{ + MALI_DEBUG_ASSERT_POINTER(pmu); + MALI_DEBUG_ASSERT((relative_address & 0x03) == 0); + MALI_DEBUG_ASSERT(relative_address < pmu->reg_size); + + MALI_DEBUG_PRINT( 5, ("PMU: reg_write: %s Addr:0x%04X Val:0x%08x\n", + pmu->name, relative_address, new_val)); + + _mali_osk_mem_iowrite32(pmu->reg_mapped, relative_address, new_val); +} + +#if PMU_TEST + +/***** TEST *****/ + +static void pmu_dump_regs( platform_pmu_t *pmu ) +{ + u32 addr; + for( addr = 0x0; addr < PMU_REGISTER_ADDRESS_SPACE_SIZE; addr += 0x4 ) + { + MALI_PRINT( ("PMU_REG: 0x%08x: 0x%04x\n", (addr + pmu->reg_base_addr), pmu_reg_read( pmu, addr ) ) ); + } +} + +/* This function is an internal test for the PMU without any Mali h/w interaction */ +static void pmu_test( platform_pmu_t *pmu, u32 cores ) +{ + u32 stat; + u32 timeout; + + MALI_PRINT( ("PMU_TEST: Start\n") ); + + pmu_dump_regs( pmu ); + + MALI_PRINT( ("PMU_TEST: Power down cores: 0x%x\n", cores) ); + _mali_pmm_pmu_power_down( pmu, cores, MALI_TRUE ); + + stat = pmu_reg_read( pmu, (u32)PMU_REG_ADDR_MGMT_STATUS ); + MALI_PRINT( ("PMU_TEST: %s\n", (stat & cores) == cores ? "SUCCESS" : "FAIL" ) ); + + pmu_dump_regs( pmu ); + + MALI_PRINT( ("PMU_TEST: Power up cores: 0x%x\n", cores) ); + _mali_pmm_pmu_power_up( pmu, cores, MALI_FALSE ); + + MALI_PRINT( ("PMU_TEST: Waiting for power up...\n") ); + timeout = 1000; /* 1 sec */ + while( !_mali_pmm_pmu_irq_power_up(pmu) && timeout > 0 ) + { + _mali_osk_time_ubusydelay(1000); /* 1ms */ + timeout--; + } + + MALI_PRINT( ("PMU_TEST: Waited %dms for interrupt\n", (1000-timeout)) ); + stat = pmu_reg_read( pmu, (u32)PMU_REG_ADDR_MGMT_STATUS ); + MALI_PRINT( ("PMU_TEST: %s\n", (stat & cores) == 0 ? "SUCCESS" : "FAIL" ) ); + + _mali_pmm_pmu_irq_power_up_clear(pmu); + + pmu_dump_regs( pmu ); + + MALI_PRINT( ("PMU_TEST: Finish\n") ); +} +#endif /* PMU_TEST */ + +#if MALI_POWER_MGMT_TEST_SUITE + +u32 pmu_get_power_up_down_info(void) +{ + return pmu_reg_read(pmu_info, (u32)PMU_REG_ADDR_MGMT_STATUS); +} + +#endif /* MALI_POWER_MGMT_TEST_SUITE */ +#endif /* USING_MALI_PMM */ +#endif /* USING_MALI_PMU */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_pmu.h b/drivers/gpu/arm/mali/common/pmm/mali_pmm_pmu.h new file mode 100644 index 000000000000..112074b2010c --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_pmu.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010-2011 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_platform.h + * Platform specific Mali driver functions + */ + +#include "mali_osk.h" + +#if !USING_MALI_PMM +/* @brief System power up/down cores that can be passed into mali_platform_powerdown/up() */ +#define MALI_PLATFORM_SYSTEM 0 +#endif + +#if USING_MALI_PMM +#if USING_MALI_PMU +#include "mali_pmm.h" + +/** @brief Platform specific setup and initialisation of MALI + * + * This is called from the entrypoint of the driver to initialize the platform + * When using PMM, it is also called from the PMM start up to initialise the + * system PMU + * + * @param resource This is NULL when called on first driver start up, else it will + * be a pointer to a PMU resource + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_pmm_pmu_init(_mali_osk_resource_t *resource); + +/** @brief Platform specific deinitialisation of MALI + * + * This is called on the exit of the driver to terminate the platform + * When using PMM, it is also called from the PMM termination code to clean up the + * system PMU + * + * @param type This is NULL when called on driver exit, else it will + * be a pointer to a PMU resource type (not the full resource) + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_pmm_pmu_deinit(_mali_osk_resource_type_t *type); + +/** @brief Platform specific powerdown sequence of MALI + * + * Called as part of platform init if there is no PMM support, else the + * PMM will call it. + * + * @param cores This is MALI_PLATFORM_SYSTEM when called without PMM, else it will + * be a mask of cores to power down based on the mali_pmm_core_id enum + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_pmm_pmu_powerdown(u32 cores); + +/** @brief Platform specific powerup sequence of MALI + * + * Called as part of platform deinit if there is no PMM support, else the + * PMM will call it. + * + * @param cores This is MALI_PLATFORM_SYSTEM when called without PMM, else it will + * be a mask of cores to power down based on the mali_pmm_core_id enum + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_pmm_pmu_powerup(u32 cores); + +#if MALI_POWER_MGMT_TEST_SUITE +#if USING_MALI_PMM +#if USING_MALI_PMU +/** @brief function to get status of individual cores + * + * This function is used by power management test suite to get the status of powered up/down the number + * of cores + * @param utilization The workload utilization of the Mali GPU. 0 = no utilization, 256 = full utilization. + */ +u32 pmu_get_power_up_down_info(void); +#endif +#endif +#endif +#endif +#endif diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy.c b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy.c new file mode 100644 index 000000000000..8958ee8bd8d6 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_policy.c + * Implementation of the common routines for power management module + * policies + */ + +#if USING_MALI_PMM + +#include "mali_ukk.h" +#include "mali_kernel_common.h" + +#include "mali_pmm.h" +#include "mali_pmm_system.h" +#include "mali_pmm_state.h" +#include "mali_pmm_policy.h" + +#include "mali_pmm_policy_alwayson.h" +#include "mali_pmm_policy_jobcontrol.h" + +/* Call back function for timer expiration */ +static void pmm_policy_timer_callback( void *arg ); + +_mali_osk_errcode_t pmm_policy_timer_init( _pmm_policy_timer_t *pptimer, u32 timeout, mali_pmm_event_id id ) +{ + MALI_DEBUG_ASSERT_POINTER(pptimer); + + /* All values get 0 as default */ + _mali_osk_memset(pptimer, 0, sizeof(*pptimer)); + + pptimer->timer = _mali_osk_timer_init(); + if( pptimer->timer ) + { + _mali_osk_timer_setcallback( pptimer->timer, pmm_policy_timer_callback, (void *)pptimer ); + pptimer->timeout = timeout; + pptimer->event_id = id; + MALI_SUCCESS; + } + + return _MALI_OSK_ERR_FAULT; +} + +static void pmm_policy_timer_callback( void *arg ) +{ + _pmm_policy_timer_t *pptimer = (_pmm_policy_timer_t *)arg; + + MALI_DEBUG_ASSERT_POINTER(pptimer); + MALI_DEBUG_ASSERT( pptimer->set ); + + /* Set timer expired and flag there is a policy to check */ + pptimer->expired = MALI_TRUE; + malipmm_set_policy_check(); +} + + +void pmm_policy_timer_term( _pmm_policy_timer_t *pptimer ) +{ + MALI_DEBUG_ASSERT_POINTER(pptimer); + + _mali_osk_timer_del( pptimer->timer ); + _mali_osk_timer_term( pptimer->timer ); + pptimer->timer = NULL; +} + +mali_bool pmm_policy_timer_start( _pmm_policy_timer_t *pptimer ) +{ + MALI_DEBUG_ASSERT_POINTER(pptimer); + MALI_DEBUG_ASSERT_POINTER(pptimer->timer); + + if( !(pptimer->set) ) + { + pptimer->set = MALI_TRUE; + pptimer->expired = MALI_FALSE; + pptimer->start = _mali_osk_time_tickcount(); + _mali_osk_timer_add( pptimer->timer, pptimer->timeout ); + return MALI_TRUE; + } + + return MALI_FALSE; +} + +mali_bool pmm_policy_timer_stop( _pmm_policy_timer_t *pptimer ) +{ + MALI_DEBUG_ASSERT_POINTER(pptimer); + MALI_DEBUG_ASSERT_POINTER(pptimer->timer); + + if( pptimer->set ) + { + _mali_osk_timer_del( pptimer->timer ); + pptimer->set = MALI_FALSE; + pptimer->expired = MALI_FALSE; + return MALI_TRUE; + } + + return MALI_FALSE; +} + +mali_bool pmm_policy_timer_raise_event( _pmm_policy_timer_t *pptimer ) +{ + MALI_DEBUG_ASSERT_POINTER(pptimer); + + if( pptimer->expired ) + { + _mali_uk_pmm_message_s event = { + NULL, + MALI_PMM_EVENT_TIMEOUT, /* Assume timeout id, but set it below */ + 0 }; + + event.id = pptimer->event_id; + event.data = (mali_pmm_message_data)pptimer->start; + + /* Don't need to do any other notification with this timer */ + pptimer->expired = MALI_FALSE; + /* Unset timer so it is free to be set again */ + pptimer->set = MALI_FALSE; + + _mali_ukk_pmm_event_message( &event ); + + return MALI_TRUE; + } + + return MALI_FALSE; +} + +mali_bool pmm_policy_timer_valid( u32 timer_start, u32 other_start ) +{ + return (_mali_osk_time_after( other_start, timer_start ) == 0); +} + + +_mali_osk_errcode_t pmm_policy_init(_mali_pmm_internal_state_t *pmm) +{ + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(pmm); + + switch( pmm->policy ) + { + case MALI_PMM_POLICY_ALWAYS_ON: + { + err = pmm_policy_init_always_on(); + } + break; + + case MALI_PMM_POLICY_JOB_CONTROL: + { + err = pmm_policy_init_job_control(pmm); + } + break; + + case MALI_PMM_POLICY_NONE: + default: + err = _MALI_OSK_ERR_FAULT; + } + + return err; +} + +void pmm_policy_term(_mali_pmm_internal_state_t *pmm) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + + switch( pmm->policy ) + { + case MALI_PMM_POLICY_ALWAYS_ON: + { + pmm_policy_term_always_on(); + } + break; + + case MALI_PMM_POLICY_JOB_CONTROL: + { + pmm_policy_term_job_control(); + } + break; + + case MALI_PMM_POLICY_NONE: + default: + MALI_PRINT_ERROR( ("PMM: Invalid policy terminated %d\n", pmm->policy) ); + } +} + + +_mali_osk_errcode_t pmm_policy_process(_mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event) +{ + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(event); + + switch( pmm->policy ) + { + case MALI_PMM_POLICY_ALWAYS_ON: + { + err = pmm_policy_process_always_on( pmm, event ); + } + break; + + case MALI_PMM_POLICY_JOB_CONTROL: + { + err = pmm_policy_process_job_control( pmm, event ); + } + break; + + case MALI_PMM_POLICY_NONE: + default: + err = _MALI_OSK_ERR_FAULT; + } + + return err; +} + + +void pmm_policy_check_policy( _mali_pmm_internal_state_t *pmm ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + + switch( pmm->policy ) + { + case MALI_PMM_POLICY_JOB_CONTROL: + { + pmm_policy_check_job_control(); + } + break; + + default: + /* Nothing needs to be done */ + break; + } +} + + +#endif /* USING_MALI_PMM */ + diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy.h b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy.h new file mode 100644 index 000000000000..b3598fab1581 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_policy.h + * Defines the power management module policies + */ + +#ifndef __MALI_PMM_POLICY_H__ +#define __MALI_PMM_POLICY_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup pmmapi Power Management Module APIs + * + * @{ + * + * @defgroup pmmapi_policy Power Management Module Policies + * + * @{ + */ + +/** @brief Generic timer for use with policies + */ +typedef struct _pmm_policy_timer +{ + u32 timeout; /**< Timeout for this timer in ticks */ + mali_pmm_event_id event_id; /**< Event id that will be raised when timer expires */ + _mali_osk_timer_t *timer; /**< Timer */ + mali_bool set; /**< Timer set */ + mali_bool expired; /**< Timer expired - event needs to be raised */ + u32 start; /**< Timer start ticks */ +} _pmm_policy_timer_t; + +/** @brief Policy timer initialization + * + * This will create a timer for use in policies, but won't start it + * + * @param pptimer An empty timer structure to be initialized + * @param timeout Timeout in ticks for the timer + * @param id Event id that will be raised on timeout + * @return _MALI_OSK_ERR_OK if the policy could be initialized, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_timer_init( _pmm_policy_timer_t *pptimer, u32 timeout, mali_pmm_event_id id ); + +/** @brief Policy timer termination + * + * This will clean up a timer that was previously used in policies, it + * will also stop it if started + * + * @param pptimer An initialized timer structure to be terminated + */ +void pmm_policy_timer_term( _pmm_policy_timer_t *pptimer ); + +/** @brief Policy timer start + * + * This will start a previously created timer for use in policies + * When the timer expires after the initialized timeout it will raise + * a PMM event of the event id given on initialization + * As data for the event it will pass the start time of the timer + * + * @param pptimer A previously initialized policy timer + * @return MALI_TRUE if the timer was started, MALI_FALSE if it is already started + */ +mali_bool pmm_policy_timer_start( _pmm_policy_timer_t *pptimer ); + +/** @brief Policy timer stop + * + * This will stop a previously created timer for use in policies + * + * @param pptimer A previously started policy timer + * @return MALI_TRUE if the timer was stopped, MALI_FALSE if it is already stopped + */ +mali_bool pmm_policy_timer_stop( _pmm_policy_timer_t *pptimer ); + +/** @brief Policy timer stop + * + * This raise an event for an expired timer + * + * @param pptimer An expired policy timer + * @return MALI_TRUE if an event was raised, else MALI_FALSE + */ +mali_bool pmm_policy_timer_raise_event( _pmm_policy_timer_t *pptimer ); + +/** @brief Policy timer valid checker + * + * This will check that a timer was started after a given time + * + * @param timer_start Time the timer was started + * @param other_start Time when another event or action occurred + * @return MALI_TRUE if the timer was started after the other time, else MALI_FALSE + */ +mali_bool pmm_policy_timer_valid( u32 timer_start, u32 other_start ); + + +/** @brief Common policy initialization + * + * This will initialize the current policy + * + * @note Any previously initialized policy should be terminated first + * + * @return _MALI_OSK_ERR_OK if the policy could be initialized, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_init( _mali_pmm_internal_state_t *pmm ); + +/** @brief Common policy termination + * + * This will terminate the current policy. + * @note This can be called when a policy has not been initialized + */ +void pmm_policy_term( _mali_pmm_internal_state_t *pmm ); + +/** @brief Common policy state changer + * + * Given the next available event message, this routine passes it to + * the current policy for processing + * + * @param pmm internal PMM state + * @param event PMM event to process + * @return _MALI_OSK_ERR_OK if the policy state completed okay, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_process( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ); + + +/** @brief Common policy checker + * + * If a policy timer fires then this function will be called to + * allow the policy to take the correct action + * + * @param pmm internal PMM state + */ +void pmm_policy_check_policy( _mali_pmm_internal_state_t *pmm ); + +/** @} */ /* End group pmmapi_policy */ +/** @} */ /* End group pmmapi */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_POLICY_H__ */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_alwayson.c b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_alwayson.c new file mode 100644 index 000000000000..bca5f8a8b6c4 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_alwayson.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_policy_alwayson.c + * Implementation of the power management module policy - always on + */ + +#if USING_MALI_PMM + +#include "mali_ukk.h" +#include "mali_kernel_common.h" + +#include "mali_pmm.h" +#include "mali_pmm_system.h" +#include "mali_pmm_state.h" +#include "mali_pmm_policy_alwayson.h" + +_mali_osk_errcode_t pmm_policy_init_always_on(void) +{ + /* Nothing to set up */ + MALI_SUCCESS; +} + +void pmm_policy_term_always_on(void) +{ + /* Nothing to tear down */ +} + +_mali_osk_errcode_t pmm_policy_process_always_on( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(event); + + switch( event->id ) + { + case MALI_PMM_EVENT_OS_POWER_DOWN: + /* We aren't going to do anything, but signal so we don't block the OS + * NOTE: This may adversely affect any jobs Mali is currently running + */ + _mali_osk_pmm_power_down_done( event->data ); + break; + + case MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK: + case MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK: + /* Not expected in this policy */ + MALI_DEBUG_ASSERT( MALI_FALSE ); + break; + + case MALI_PMM_EVENT_OS_POWER_UP: + /* Nothing to do */ + _mali_osk_pmm_power_up_done( event->data ); + break; + + case MALI_PMM_EVENT_JOB_SCHEDULED: + case MALI_PMM_EVENT_JOB_QUEUED: + case MALI_PMM_EVENT_JOB_FINISHED: + /* Nothing to do - we are always on */ + break; + + case MALI_PMM_EVENT_TIMEOUT: + /* Not expected in this policy */ + MALI_DEBUG_ASSERT( MALI_FALSE ); + break; + + default: + MALI_ERROR(_MALI_OSK_ERR_ITEM_NOT_FOUND); + } + + MALI_SUCCESS; +} + +#endif diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_alwayson.h b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_alwayson.h new file mode 100644 index 000000000000..9dc78a46e8a3 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_alwayson.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_policy_alwayson.h + * Defines the power management module policy for always on + */ + +#ifndef __MALI_PMM_POLICY_ALWAYSON_H__ +#define __MALI_PMM_POLICY_ALWAYSON_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup pmmapi_policy Power Management Module Policies + * + * @{ + */ + +/** @brief Always on policy initialization + * + * @return _MALI_OSK_ERR_OK if the policy could be initialized, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_init_always_on(void); + +/** @brief Always on policy termination + */ +void pmm_policy_term_always_on(void); + +/** @brief Always on policy state changer + * + * Given the next available event message, this routine processes it + * for the policy and changes state as needed. + * + * Always on policy will ignore all events and keep the Mali cores on + * all the time + * + * @param pmm internal PMM state + * @param event PMM event to process + * @return _MALI_OSK_ERR_OK if the policy state completed okay, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_process_always_on( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ); + +/** @} */ /* End group pmmapi_policies */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_POLICY_ALWAYSON_H__ */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_jobcontrol.c b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_jobcontrol.c new file mode 100644 index 000000000000..3a194f9672e8 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_jobcontrol.c @@ -0,0 +1,470 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_policy_jobcontrol.c + * Implementation of the power management module policy - job control + */ + +#if USING_MALI_PMM + +#include "mali_ukk.h" +#include "mali_kernel_common.h" +#include "mali_platform.h" + +#include "mali_pmm.h" +#include "mali_pmm_system.h" +#include "mali_pmm_state.h" +#include "mali_pmm_policy.h" +#include "mali_pmm_policy_jobcontrol.h" + +typedef struct _pmm_policy_data_job_control +{ + _pmm_policy_timer_t latency; /**< Latency timeout timer for all cores */ + u32 core_active_start; /**< Last time a core was set to active */ + u32 timeout; /**< Timeout in ticks for latency timer */ +} _pmm_policy_data_job_control_t; + + +/* @ brief Local data for this policy + */ +static _pmm_policy_data_job_control_t *data_job_control = NULL; + +/* @brief Set up the timeout if it hasn't already been set and if there are active cores */ +static void job_control_timeout_setup( _mali_pmm_internal_state_t *pmm, _pmm_policy_timer_t *pptimer ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(pptimer); + + /* Do we have an inactivity time out and some powered cores? */ + if( pptimer->timeout > 0 && pmm->cores_powered != 0 ) + { + /* Is the system idle and all the powered cores are idle? */ + if( pmm->status == MALI_PMM_STATUS_IDLE && pmm->cores_idle == pmm->cores_powered ) + { + if( pmm_policy_timer_start(pptimer) ) + { + MALIPMM_DEBUG_PRINT( ("PMM policy - Job control: Setting in-activity latency timer\n") ); + } + } + else + { + /* We are not idle so there is no need for an inactivity timer + */ + if( pmm_policy_timer_stop(pptimer) ) + { + MALIPMM_DEBUG_PRINT( ("PMM policy - Job control: Removing in-activity latency timer\n") ); + } + } + } +} + +/* @brief Check the validity of the timeout - and if there is one set */ +static mali_bool job_control_timeout_valid( _mali_pmm_internal_state_t *pmm, _pmm_policy_timer_t *pptimer, u32 timer_start ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(pptimer); + + /* Not a valid timer! */ + if( pptimer->timeout == 0 ) return MALI_FALSE; + + /* Are some cores powered and are they all idle? */ + if( (pmm->cores_powered != 0) && (pmm->cores_idle == pmm->cores_powered) ) + { + /* Has latency timeout started after the last core was active? */ + if( pmm_policy_timer_valid( timer_start, data_job_control->core_active_start ) ) + { + return MALI_TRUE; + } + else + { + MALIPMM_DEBUG_PRINT( ("PMM: In-activity latency time out ignored - out of date\n") ); + } + } + else + { + if( pmm->cores_powered == 0 ) + { + MALIPMM_DEBUG_PRINT( ("PMM: In-activity latency time out ignored - cores already off\n") ); + } + else + { + MALIPMM_DEBUG_PRINT( ("PMM: In-activity latency time out ignored - cores active\n") ); + } + } + + return MALI_FALSE; +} + +_mali_osk_errcode_t pmm_policy_init_job_control( _mali_pmm_internal_state_t *pmm ) +{ + _mali_osk_errcode_t err; + MALI_DEBUG_ASSERT_POINTER( pmm ); + MALI_DEBUG_ASSERT( data_job_control == NULL ); + + data_job_control = (_pmm_policy_data_job_control_t *) _mali_osk_malloc(sizeof(*data_job_control)); + MALI_CHECK_NON_NULL( data_job_control, _MALI_OSK_ERR_NOMEM ); + + data_job_control->core_active_start = _mali_osk_time_tickcount(); + data_job_control->timeout = MALI_PMM_POLICY_JOBCONTROL_INACTIVITY_TIMEOUT; + + err = pmm_policy_timer_init( &data_job_control->latency, data_job_control->timeout, MALI_PMM_EVENT_TIMEOUT ); + if( err != _MALI_OSK_ERR_OK ) + { + _mali_osk_free( data_job_control ); + data_job_control = NULL; + return err; + } + + /* Start the latency timeout */ + job_control_timeout_setup( pmm, &data_job_control->latency ); + + MALI_SUCCESS; +} + +void pmm_policy_term_job_control(void) +{ + if( data_job_control != NULL ) + { + pmm_policy_timer_term( &data_job_control->latency ); + _mali_osk_free( data_job_control ); + data_job_control = NULL; + } +} + +static void pmm_policy_job_control_job_queued( _mali_pmm_internal_state_t *pmm ) +{ + mali_pmm_core_mask cores; + mali_pmm_core_mask cores_subset; + + /* Make sure that all cores are powered in this + * simple policy + */ + cores = pmm->cores_registered; + cores_subset = pmm_cores_to_power_up( pmm, cores ); + if( cores_subset != 0 ) + { + /* There are some cores that need powering up */ + if( !pmm_invoke_power_up( pmm ) ) + { + /* Need to wait until finished */ + pmm->status = MALI_PMM_STATUS_POLICY_POWER_UP; + } + } +} + +_mali_osk_errcode_t pmm_policy_process_job_control( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ) +{ + mali_pmm_core_mask cores; + mali_pmm_core_mask cores_subset; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(event); + MALI_DEBUG_ASSERT_POINTER(data_job_control); + + MALIPMM_DEBUG_PRINT( ("PMM: Job control policy process start - status=%d\n", pmm->status) ); + + /* Mainly the data is the cores */ + cores = pmm_cores_from_event_data( pmm, event ); + +#if MALI_STATE_TRACKING + pmm->mali_last_pmm_status = pmm->status; +#endif /* MALI_STATE_TRACKING */ + + switch( pmm->status ) + { + /**************** IDLE ****************/ + case MALI_PMM_STATUS_IDLE: + switch( event->id ) + { + case MALI_PMM_EVENT_OS_POWER_UP: + /* Not expected in this state */ + break; + + case MALI_PMM_EVENT_JOB_SCHEDULED: + + /* Update idle cores to indicate active - remove these! */ + pmm_cores_set_active( pmm, cores ); + /* Remember when this happened */ + data_job_control->core_active_start = event->ts; +#if MALI_POWER_MGMT_TEST_SUITE + _mali_osk_pmm_policy_events_notifications(MALI_PMM_EVENT_JOB_SCHEDULED); +#endif + + /*** FALL THROUGH to QUEUED to check POWER UP ***/ + + case MALI_PMM_EVENT_JOB_QUEUED: + + pmm_policy_job_control_job_queued( pmm ); +#if MALI_POWER_MGMT_TEST_SUITE + _mali_osk_pmm_policy_events_notifications(MALI_PMM_EVENT_JOB_QUEUED); +#endif + break; + + case MALI_PMM_EVENT_DVFS_PAUSE: + + cores_subset = pmm_cores_to_power_down( pmm, cores, MALI_FALSE ); + if ( cores_subset != 0 ) + { + if ( !pmm_power_down_okay( pmm ) ) + { + pmm->is_dvfs_active = 1; + pmm->status = MALI_PMM_STATUS_OS_POWER_DOWN; + pmm_save_os_event_data( pmm, event->data ); + break; + } + } + pmm->status = MALI_PMM_STATUS_DVFS_PAUSE; + _mali_osk_pmm_dvfs_operation_done(0); + break; + + case MALI_PMM_EVENT_OS_POWER_DOWN: + + /* Need to power down all cores even if we need to wait for them */ + cores_subset = pmm_cores_to_power_down( pmm, cores, MALI_FALSE ); + if( cores_subset != 0 ) + { + /* There are some cores that need powering down */ + if( !pmm_invoke_power_down( pmm, MALI_POWER_MODE_DEEP_SLEEP ) ) + { + /* We need to wait until they are idle */ + + pmm->status = MALI_PMM_STATUS_OS_POWER_DOWN; + /* Save the OS data to respond later */ + pmm_save_os_event_data( pmm, event->data ); + /* Exit this case - as we have to wait */ + break; + } + } + else + { + mali_platform_power_mode_change(MALI_POWER_MODE_DEEP_SLEEP); + + } + /* Set waiting status */ + pmm->status = MALI_PMM_STATUS_OS_WAITING; + /* All cores now down - respond to OS power event */ + _mali_osk_pmm_power_down_done( event->data ); + break; + + case MALI_PMM_EVENT_JOB_FINISHED: + + /* Update idle cores - add these! */ + pmm_cores_set_idle( pmm, cores ); +#if MALI_POWER_MGMT_TEST_SUITE + _mali_osk_pmm_policy_events_notifications(MALI_PMM_EVENT_JOB_FINISHED); +#endif + if( data_job_control->timeout > 0 ) + { + /* Wait for time out to fire */ + break; + } + /* For job control policy - turn off all cores */ + cores = pmm->cores_powered; + + /*** FALL THROUGH to TIMEOUT TEST as NO TIMEOUT ***/ + + case MALI_PMM_EVENT_TIMEOUT: + + /* Main job control policy - turn off cores after inactivity */ + if( job_control_timeout_valid( pmm, &data_job_control->latency, (u32)event->data ) ) + { + /* Valid timeout of inactivity - so find out if we can power down + * immedately - if we can't then this means the cores are still in fact + * active + */ + cores_subset = pmm_cores_to_power_down( pmm, cores, MALI_TRUE ); + if( cores_subset != 0 ) + { + /* Check if we can really power down, if not then we are not + * really in-active + */ + if( !pmm_invoke_power_down( pmm, MALI_POWER_MODE_LIGHT_SLEEP ) ) + { + pmm_power_down_cancel( pmm ); + } + } + /* else there are no cores powered up! */ + } +#if MALI_POWER_MGMT_TEST_SUITE + _mali_osk_pmm_policy_events_notifications(MALI_PMM_EVENT_TIMEOUT); +#endif + break; + + default: + /* Unexpected event */ + MALI_ERROR(_MALI_OSK_ERR_ITEM_NOT_FOUND); + } + break; + + /******************DVFS PAUSE**************/ + case MALI_PMM_STATUS_DVFS_PAUSE: + switch ( event->id ) + { + case MALI_PMM_EVENT_DVFS_RESUME: + + if ( pmm->cores_powered != 0 ) + { + pmm->cores_ack_down =0; + pmm_power_down_cancel( pmm ); + pmm->status = MALI_PMM_STATUS_IDLE; + } + else + { + pmm_policy_job_control_job_queued( pmm ); + } + _mali_osk_pmm_dvfs_operation_done( 0 ); + break; + + case MALI_PMM_EVENT_OS_POWER_DOWN: + /* Set waiting status */ + pmm->status = MALI_PMM_STATUS_OS_WAITING; + if ( pmm->cores_powered != 0 ) + { + if ( pmm_invoke_power_down( pmm, MALI_POWER_MODE_DEEP_SLEEP ) ) + { + _mali_osk_pmm_power_down_done( 0 ); + break; + } + } + else + { + mali_platform_power_mode_change(MALI_POWER_MODE_DEEP_SLEEP); + } + _mali_osk_pmm_power_down_done( 0 ); + break; + default: + break; + } + break; + + /**************** POWER UP ****************/ + case MALI_PMM_STATUS_OS_POWER_UP: + case MALI_PMM_STATUS_POLICY_POWER_UP: + switch( event->id ) + { + case MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK: + /* Make sure cores powered off equal what we expect */ + MALI_DEBUG_ASSERT( cores == pmm->cores_pend_up ); + pmm_cores_set_up_ack( pmm, cores ); + + if( pmm_invoke_power_up( pmm ) ) + { + if( pmm->status == MALI_PMM_STATUS_OS_POWER_UP ) + { + /* Get the OS data and respond to the power up */ + _mali_osk_pmm_power_up_done( pmm_retrieve_os_event_data( pmm ) ); + } + pmm->status = MALI_PMM_STATUS_IDLE; + } + break; + + default: + /* Unexpected event */ + MALI_ERROR(_MALI_OSK_ERR_ITEM_NOT_FOUND); + } + break; + + /**************** POWER DOWN ****************/ + case MALI_PMM_STATUS_OS_POWER_DOWN: + case MALI_PMM_STATUS_POLICY_POWER_DOWN: + switch( event->id ) + { + + case MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK: + + pmm_cores_set_down_ack( pmm, cores ); + + if ( pmm->is_dvfs_active == 1 ) + { + if( pmm_power_down_okay( pmm ) ) + { + pmm->is_dvfs_active = 0; + pmm->status = MALI_PMM_STATUS_DVFS_PAUSE; + _mali_osk_pmm_dvfs_operation_done( pmm_retrieve_os_event_data( pmm ) ); + } + break; + } + + /* Now check if we can power down */ + if( pmm_invoke_power_down( pmm, MALI_POWER_MODE_DEEP_SLEEP ) ) + { + if( pmm->status == MALI_PMM_STATUS_OS_POWER_DOWN ) + { + /* Get the OS data and respond to the power down */ + _mali_osk_pmm_power_down_done( pmm_retrieve_os_event_data( pmm ) ); + } + pmm->status = MALI_PMM_STATUS_OS_WAITING; + } + break; + + default: + /* Unexpected event */ + MALI_ERROR(_MALI_OSK_ERR_ITEM_NOT_FOUND); + } + break; + + case MALI_PMM_STATUS_OS_WAITING: + switch( event->id ) + { + case MALI_PMM_EVENT_OS_POWER_UP: + cores_subset = pmm_cores_to_power_up( pmm, cores ); + if( cores_subset != 0 ) + { + /* There are some cores that need powering up */ + if( !pmm_invoke_power_up( pmm ) ) + { + /* Need to wait until power up complete */ + pmm->status = MALI_PMM_STATUS_OS_POWER_UP; + /* Save the OS data to respond later */ + pmm_save_os_event_data( pmm, event->data ); + /* Exit this case - as we have to wait */ + break; + } + } + pmm->status = MALI_PMM_STATUS_IDLE; + /* All cores now up - respond to OS power up event */ + _mali_osk_pmm_power_up_done( event->data ); + break; + + default: + /* All other messages are ignored in this state */ + break; + } + break; + + default: + /* Unexpected state */ + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* Set in-activity latency timer - if required */ + job_control_timeout_setup( pmm, &data_job_control->latency ); + + /* Update the PMM state */ + pmm_update_system_state( pmm ); +#if MALI_STATE_TRACKING + pmm->mali_new_event_status = event->id; +#endif /* MALI_STATE_TRACKING */ + + MALIPMM_DEBUG_PRINT( ("PMM: Job control policy process end - status=%d and event=%d\n", pmm->status,event->id) ); + + MALI_SUCCESS; +} + +void pmm_policy_check_job_control() +{ + MALI_DEBUG_ASSERT_POINTER(data_job_control); + + /* Latency timer must have expired raise the event */ + pmm_policy_timer_raise_event(&data_job_control->latency); +} + + +#endif /* USING_MALI_PMM */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_jobcontrol.h b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_jobcontrol.h new file mode 100644 index 000000000000..71af3b15d032 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_jobcontrol.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_policy.h + * Defines the power management module policies + */ + +#ifndef __MALI_PMM_POLICY_JOBCONTROL_H__ +#define __MALI_PMM_POLICY_JOBCONTROL_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup pmmapi_policy Power Management Module Policies + * + * @{ + */ + +/** @brief The jobcontrol policy inactivity latency timeout (in ticks) + * before the hardware is switched off + * + * @note Setting this low whilst tracing or producing debug output can + * cause alot of timeouts to fire which can affect the PMM behaviour + */ +#define MALI_PMM_POLICY_JOBCONTROL_INACTIVITY_TIMEOUT 50 + +/** @brief Job control policy initialization + * + * @return _MALI_OSK_ERR_OK if the policy could be initialized, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_init_job_control(_mali_pmm_internal_state_t *pmm); + +/** @brief Job control policy termination + */ +void pmm_policy_term_job_control(void); + +/** @brief Job control policy state changer + * + * Given the next available event message, this routine processes it + * for the policy and changes state as needed. + * + * Job control policy depends on events from the Mali cores, and will + * power down all cores after an inactivity latency timeout. It will + * power the cores back on again when a job is scheduled to run. + * + * @param pmm internal PMM state + * @param event PMM event to process + * @return _MALI_OSK_ERR_OK if the policy state completed okay, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_process_job_control( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ); + +/** @brief Job control policy checker + * + * The latency timer has fired and we need to raise the correct event to + * handle it + * + * @param pmm internal PMM state + */ +void pmm_policy_check_job_control(void); + +/** @} */ /* End group pmmapi_policy */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_POLICY_JOBCONTROL_H__ */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_state.c b/drivers/gpu/arm/mali/common/pmm/mali_pmm_state.c new file mode 100644 index 000000000000..c50a56799aec --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_state.c @@ -0,0 +1,721 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_state.c + * Implementation of the power management module internal state + */ + +#if USING_MALI_PMM + +#include "mali_ukk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_subsystem.h" + +#include "mali_pmm.h" +#include "mali_pmm_state.h" +#include "mali_pmm_system.h" + +#include "mali_kernel_core.h" +#include "mali_platform.h" + +#define SIZEOF_CORES_LIST 6 + +/* NOTE: L2 *MUST* be first on the list so that it + * is correctly powered on first and powered off last + */ +static mali_pmm_core_id cores_list[] = { MALI_PMM_CORE_L2, + MALI_PMM_CORE_GP, + MALI_PMM_CORE_PP0, + MALI_PMM_CORE_PP1, + MALI_PMM_CORE_PP2, + MALI_PMM_CORE_PP3 }; + + + +void pmm_update_system_state( _mali_pmm_internal_state_t *pmm ) +{ + mali_pmm_state state; + + MALI_DEBUG_ASSERT_POINTER(pmm); + + if( pmm->cores_registered == 0 ) + { + state = MALI_PMM_STATE_UNAVAILABLE; + } + else if( pmm->cores_powered == 0 ) + { + state = MALI_PMM_STATE_SYSTEM_OFF; + } + else if( pmm->cores_powered == pmm->cores_registered ) + { + state = MALI_PMM_STATE_SYSTEM_ON; + } + else + { + /* Some other state where not everything is on or off */ + state = MALI_PMM_STATE_SYSTEM_TRANSITION; + } + +#if MALI_PMM_TRACE + _mali_pmm_trace_state_change( pmm->state, state ); +#endif + pmm->state = state; +} + +mali_pmm_core_mask pmm_cores_from_event_data( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ) +{ + mali_pmm_core_mask cores; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(event); + + switch( event->id ) + { + case MALI_PMM_EVENT_OS_POWER_UP: + case MALI_PMM_EVENT_OS_POWER_DOWN: + /* All cores - the system */ + cores = pmm->cores_registered; + break; + + case MALI_PMM_EVENT_JOB_SCHEDULED: + case MALI_PMM_EVENT_JOB_QUEUED: + case MALI_PMM_EVENT_JOB_FINISHED: + case MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK: + case MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK: + /* Currently the main event data is only the cores + * for these messages + */ + cores = (mali_pmm_core_mask)event->data; + if( cores == MALI_PMM_CORE_SYSTEM ) + { + cores = pmm->cores_registered; + } + else if( cores == MALI_PMM_CORE_PP_ALL ) + { + /* Get the subset of registered PP cores */ + cores = (pmm->cores_registered & MALI_PMM_CORE_PP_ALL); + } + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + break; + + default: + /* Assume timeout messages - report cores still powered */ + cores = pmm->cores_powered; + break; + } + + return cores; +} + +mali_pmm_core_mask pmm_cores_to_power_up( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ) +{ + mali_pmm_core_mask cores_subset; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + /* Check that cores aren't pending power down when asked for power up */ + MALI_DEBUG_ASSERT( pmm->cores_pend_down == 0 ); + + cores_subset = (~(pmm->cores_powered) & cores); + if( cores_subset != 0 ) + { + /* There are some cores that need powering up */ + pmm->cores_pend_up = cores_subset; + } + + return cores_subset; +} + +mali_pmm_core_mask pmm_cores_to_power_down( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores, mali_bool immediate_only ) +{ + mali_pmm_core_mask cores_subset; + _mali_osk_errcode_t err; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + /* Check that cores aren't pending power up when asked for power down */ + MALI_DEBUG_ASSERT( pmm->cores_pend_up == 0 ); + + cores_subset = (pmm->cores_powered & cores); + if( cores_subset != 0 ) + { + int n; + volatile mali_pmm_core_mask *ppowered = &(pmm->cores_powered); + + /* There are some cores that need powering up, but we may + * need to wait until they are idle + */ + for( n = SIZEOF_CORES_LIST-1; n >= 0; n-- ) + { + if( (cores_list[n] & cores_subset) != 0 ) + { + /* Core is to be powered down */ + pmm->cores_pend_down |= cores_list[n]; + + /* Can't hold the power lock, when acessing subsystem mutex via + * the core power call. + * Due to terminatation of driver requiring a subsystem mutex + * and then power lock held to unregister a core. + * This does mean that the following function could fail + * as the core is unregistered before we tell it to power + * down, but it does not matter as we are terminating + */ +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + /* Signal the core to power down + * If it is busy (not idle) it will set a pending power down flag + * (as long as we don't want to only immediately power down). + * If it isn't busy it will move out of the idle queue right + * away + */ + err = mali_core_signal_power_down( cores_list[n], immediate_only ); + MALI_PMM_LOCK(pmm); + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + + /* Re-read cores_subset in case it has changed */ + cores_subset = (*ppowered & cores); + + if( err == _MALI_OSK_ERR_OK ) + { + /* We moved an idle core to the power down queue + * which means it is now acknowledged (if it is still + * registered) + */ + pmm->cores_ack_down |= (cores_list[n] & cores_subset); + } + else + { + MALI_DEBUG_PRINT(1,("PMM: In pmm_cores_to_power_down, the error and cores powered are..%x....%x",err,*ppowered)); + MALI_DEBUG_ASSERT( err == _MALI_OSK_ERR_BUSY || + (err == _MALI_OSK_ERR_FAULT && + (*ppowered & cores_list[n]) == 0) ); + /* If we didn't move a core - it must be active, so + * leave it pending, so we get an acknowledgement (when + * not in immediate only mode) + * Alternatively we are shutting down and the core has + * been unregistered + */ + } + } + } + } + + return cores_subset; +} + +void pmm_power_down_cancel( _mali_pmm_internal_state_t *pmm ) +{ + int n; + mali_pmm_core_mask pd, ad; + _mali_osk_errcode_t err; + volatile mali_pmm_core_mask *pregistered; + + MALI_DEBUG_ASSERT_POINTER(pmm); + + MALIPMM_DEBUG_PRINT( ("PMM: Cancelling power down\n") ); + + pd = pmm->cores_pend_down; + ad = pmm->cores_ack_down; + /* Clear the pending cores so that they don't move to the off + * queue if they haven't already + */ + pmm->cores_pend_down = 0; + pmm->cores_ack_down = 0; + pregistered = &(pmm->cores_registered); + + /* Power up all the pending power down cores - just so + * we make sure the system is in a known state, as a + * pending core might have sent an acknowledged message + * which hasn't been read yet. + */ + for( n = 0; n < SIZEOF_CORES_LIST; n++ ) + { + if( (cores_list[n] & pd) != 0 ) + { + /* Can't hold the power lock, when acessing subsystem mutex via + * the core power call. + * Due to terminatation of driver requiring a subsystem mutex + * and then power lock held to unregister a core. + * This does mean that the following power up function could fail + * as the core is unregistered before we tell it to power + * up, but it does not matter as we are terminating + */ +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + /* As we are cancelling - only move the cores back to the queue - + * no reset needed + */ + err = mali_core_signal_power_up( cores_list[n], MALI_TRUE ); + MALI_PMM_LOCK(pmm); +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + /* Update pending list with the current registered cores */ + pd &= (*pregistered); + + if( err != _MALI_OSK_ERR_OK ) + { + MALI_DEBUG_ASSERT( (err == _MALI_OSK_ERR_BUSY && + ((cores_list[n] & ad) == 0)) || + (err == _MALI_OSK_ERR_FAULT && + (*pregistered & cores_list[n]) == 0) ); + /* If we didn't power up a core - it must be active and + * hasn't actually tried to power down - this is expected + * for cores that haven't acknowledged + * Alternatively we are shutting down and the core has + * been unregistered + */ + } + } + } + /* Only used in debug builds */ + MALI_IGNORE(ad); +} + + +mali_bool pmm_power_down_okay( _mali_pmm_internal_state_t *pmm ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + + return ( pmm->cores_pend_down == pmm->cores_ack_down ? MALI_TRUE : MALI_FALSE ); +} + +mali_bool pmm_invoke_power_down( _mali_pmm_internal_state_t *pmm, mali_power_mode power_mode ) +{ + _mali_osk_errcode_t err; + MALI_DEBUG_ASSERT_POINTER(pmm); + + /* Check that cores are pending power down during power down invoke */ + MALI_DEBUG_ASSERT( pmm->cores_pend_down != 0 ); + /* Check that cores are not pending power up during power down invoke */ + MALI_DEBUG_ASSERT( pmm->cores_pend_up == 0 ); + + if( !pmm_power_down_okay( pmm ) ) + { + MALIPMM_DEBUG_PRINT( ("PMM: Waiting for cores to go idle for power off - 0x%08x / 0x%08x\n", + pmm->cores_pend_down, pmm->cores_ack_down) ); + return MALI_FALSE; + } + else + { + pmm->cores_powered &= ~(pmm->cores_pend_down); +#if !MALI_PMM_NO_PMU + err = malipmm_powerdown( pmm->cores_pend_down, power_mode); +#else + err = _MALI_OSK_ERR_OK; +#endif + + if( err == _MALI_OSK_ERR_OK ) + { +#if MALI_PMM_TRACE + mali_pmm_core_mask old_power = pmm->cores_powered; +#endif + /* Remove powered down cores from idle and powered list */ + pmm->cores_idle &= ~(pmm->cores_pend_down); + /* Reset pending/acknowledged status */ + pmm->cores_pend_down = 0; + pmm->cores_ack_down = 0; +#if MALI_PMM_TRACE + _mali_pmm_trace_hardware_change( old_power, pmm->cores_powered ); +#endif + } + else + { + pmm->cores_powered |= pmm->cores_pend_down; + MALI_PRINT_ERROR( ("PMM: Failed to get PMU to power down cores - (0x%x) %s", + pmm->cores_pend_down, pmm_trace_get_core_name(pmm->cores_pend_down)) ); + pmm->fatal_power_err = MALI_TRUE; + } + } + + return MALI_TRUE; +} + + +mali_bool pmm_power_up_okay( _mali_pmm_internal_state_t *pmm ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + + return ( pmm->cores_pend_up == pmm->cores_ack_up ? MALI_TRUE : MALI_FALSE ); +} + + +mali_bool pmm_invoke_power_up( _mali_pmm_internal_state_t *pmm ) +{ + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(pmm); + + /* Check that cores are pending power up during power up invoke */ + MALI_DEBUG_ASSERT( pmm->cores_pend_up != 0 ); + /* Check that cores are not pending power down during power up invoke */ + MALI_DEBUG_ASSERT( pmm->cores_pend_down == 0 ); + + if( pmm_power_up_okay( pmm ) ) + { + /* Power up has completed - sort out subsystem core status */ + + int n; + /* Use volatile to access, so that it is updated if any cores are unregistered */ + volatile mali_pmm_core_mask *ppendup = &(pmm->cores_pend_up); +#if MALI_PMM_TRACE + mali_pmm_core_mask old_power = pmm->cores_powered; +#endif + /* Move cores into idle queues */ + for( n = 0; n < SIZEOF_CORES_LIST; n++ ) + { + if( (cores_list[n] & (*ppendup)) != 0 ) + { + /* Can't hold the power lock, when acessing subsystem mutex via + * the core power call. + * Due to terminatation of driver requiring a subsystem mutex + * and then power lock held to unregister a core. + * This does mean that the following function could fail + * as the core is unregistered before we tell it to power + * up, but it does not matter as we are terminating + */ +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + err = mali_core_signal_power_up( cores_list[n], MALI_FALSE ); + MALI_PMM_LOCK(pmm); + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + + if( err != _MALI_OSK_ERR_OK ) + { + MALI_DEBUG_PRINT(1,("In pmm_invoke_power_up:: The error and pending cores to be powered up are...%x...%x",err,*ppendup)); + MALI_DEBUG_ASSERT( (err == _MALI_OSK_ERR_FAULT && + (*ppendup & cores_list[n]) == 0) ); + /* We only expect this to fail when we are shutting down + * and the core has been unregistered + */ + } + } + } + /* Finished power up - add cores to idle and powered list */ + pmm->cores_powered |= (*ppendup); + pmm->cores_idle |= (*ppendup); + /* Reset pending/acknowledge status */ + pmm->cores_pend_up = 0; + pmm->cores_ack_up = 0; + +#if MALI_PMM_TRACE + _mali_pmm_trace_hardware_change( old_power, pmm->cores_powered ); +#endif + return MALI_TRUE; + } + else + { +#if !MALI_PMM_NO_PMU + /* Power up must now be done */ + err = malipmm_powerup( pmm->cores_pend_up ); +#else + err = _MALI_OSK_ERR_OK; +#endif + if( err != _MALI_OSK_ERR_OK ) + { + MALI_PRINT_ERROR( ("PMM: Failed to get PMU to power up cores - (0x%x) %s", + pmm->cores_pend_up, pmm_trace_get_core_name(pmm->cores_pend_up)) ); + pmm->fatal_power_err = MALI_TRUE; + } + else + { + /* TBD - Update core status immediately rather than use event message */ + _mali_uk_pmm_message_s event = { + NULL, + MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK, + 0 }; + /* All the cores that were pending power up, have now completed power up */ + event.data = pmm->cores_pend_up; + _mali_ukk_pmm_event_message( &event ); + MALIPMM_DEBUG_PRINT( ("PMM: Sending ACK to power up") ); + } + } + + /* Always return false, as we need an interrupt to acknowledge + * when power up is complete + */ + return MALI_FALSE; +} + +mali_pmm_core_mask pmm_cores_set_active( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + pmm->cores_idle &= (~cores); + return pmm->cores_idle; +} + +mali_pmm_core_mask pmm_cores_set_idle( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + pmm->cores_idle |= (cores); + return pmm->cores_idle; +} + +mali_pmm_core_mask pmm_cores_set_down_ack( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + /* Check core is not pending a power down */ + MALI_DEBUG_ASSERT( (pmm->cores_pend_down & cores) != 0 ); + /* Check core has not acknowledged power down more than once */ + MALI_DEBUG_ASSERT( (pmm->cores_ack_down & cores) == 0 ); + + pmm->cores_ack_down |= (cores); + + return pmm->cores_ack_down; +} + +void pmm_fatal_reset( _mali_pmm_internal_state_t *pmm ) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + _mali_osk_notification_t *msg = NULL; + mali_pmm_status status; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALIPMM_DEBUG_PRINT( ("PMM: Fatal Reset called") ); + + MALI_DEBUG_ASSERT( pmm->status != MALI_PMM_STATUS_OFF ); + + /* Reset the common status */ + pmm->waiting = 0; + pmm->missed = 0; + pmm->fatal_power_err = MALI_FALSE; + pmm->no_events = 0; + pmm->check_policy = MALI_FALSE; + pmm->cores_pend_down = 0; + pmm->cores_pend_up = 0; + pmm->cores_ack_down = 0; + pmm->cores_ack_up = 0; + pmm->is_dvfs_active = 0; +#if MALI_PMM_TRACE + pmm->messages_sent = 0; + pmm->messages_received = 0; + pmm->imessages_sent = 0; + pmm->imessages_received = 0; + MALI_PRINT( ("PMM Trace: *** Fatal reset occurred ***") ); +#endif + + /* Set that we are unavailable whilst resetting */ + pmm->state = MALI_PMM_STATE_UNAVAILABLE; + status = pmm->status; + pmm->status = MALI_PMM_STATUS_OFF; + + /* We want all cores powered */ + pmm->cores_powered = pmm->cores_registered; + /* The cores may not be idle, but this state will be rectified later */ + pmm->cores_idle = pmm->cores_registered; + + /* So power on any cores that are registered */ + if( pmm->cores_registered != 0 ) + { + int n; + volatile mali_pmm_core_mask *pregistered = &(pmm->cores_registered); +#if !MALI_PMM_NO_PMU + err = malipmm_powerup( pmm->cores_registered ); +#endif + if( err != _MALI_OSK_ERR_OK ) + { + /* This is very bad as we can't even be certain the cores are now + * powered up + */ + MALI_PRINT_ERROR( ("PMM: Failed to perform PMM reset!\n") ); + /* TBD driver exit? */ + } + + for( n = SIZEOF_CORES_LIST-1; n >= 0; n-- ) + { + if( (cores_list[n] & (*pregistered)) != 0 ) + { +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + /* Core is now active - so try putting it in the idle queue */ + err = mali_core_signal_power_up( cores_list[n], MALI_FALSE ); + MALI_PMM_LOCK(pmm); +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + /* We either succeeded, or we were not off anyway, or we have + * just be deregistered + */ + MALI_DEBUG_ASSERT( (err == _MALI_OSK_ERR_OK) || + (err == _MALI_OSK_ERR_BUSY) || + (err == _MALI_OSK_ERR_FAULT && + (*pregistered & cores_list[n]) == 0) ); + } + } + } + + /* Unblock any pending OS event */ + if( status == MALI_PMM_STATUS_OS_POWER_UP ) + { + /* Get the OS data and respond to the power up */ + _mali_osk_pmm_power_up_done( pmm_retrieve_os_event_data( pmm ) ); + } + if( status == MALI_PMM_STATUS_OS_POWER_DOWN ) + { + /* Get the OS data and respond to the power down + * NOTE: We are not powered down at this point due to power problems, + * so we are lying to the system, but something bad has already + * happened and we are trying unstick things + * TBD - Add busy loop to power down cores? + */ + _mali_osk_pmm_power_down_done( pmm_retrieve_os_event_data( pmm ) ); + } + + /* Purge the event queues */ + do + { + if( _mali_osk_notification_queue_dequeue( pmm->iqueue, &msg ) == _MALI_OSK_ERR_OK ) + { + _mali_osk_notification_delete ( msg ); + break; + } + } while (MALI_TRUE); + + do + { + if( _mali_osk_notification_queue_dequeue( pmm->queue, &msg ) == _MALI_OSK_ERR_OK ) + { + _mali_osk_notification_delete ( msg ); + break; + } + } while (MALI_TRUE); + + /* Return status/state to normal */ + pmm->status = MALI_PMM_STATUS_IDLE; + pmm_update_system_state(pmm); +} + +mali_pmm_core_mask pmm_cores_set_up_ack( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + /* Check core is not pending a power up */ + MALI_DEBUG_ASSERT( (pmm->cores_pend_up & cores) != 0 ); + /* Check core has not acknowledged power up more than once */ + MALI_DEBUG_ASSERT( (pmm->cores_ack_up & cores) == 0 ); + + pmm->cores_ack_up |= (cores); + + return pmm->cores_ack_up; +} + +void pmm_save_os_event_data(_mali_pmm_internal_state_t *pmm, mali_pmm_message_data data) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + /* Check that there is no saved data */ + MALI_DEBUG_ASSERT( pmm->os_data == 0 ); + /* Can't store zero data - as retrieve check will fail */ + MALI_DEBUG_ASSERT( data != 0 ); + + pmm->os_data = data; +} + +mali_pmm_message_data pmm_retrieve_os_event_data(_mali_pmm_internal_state_t *pmm) +{ + mali_pmm_message_data data; + + MALI_DEBUG_ASSERT_POINTER(pmm); + /* Check that there is saved data */ + MALI_DEBUG_ASSERT( pmm->os_data != 0 ); + + /* Get data, and clear the saved version */ + data = pmm->os_data; + pmm->os_data = 0; + + return data; +} + +/* Create list of core names to look up + * We are doing it this way to overcome the need for + * either string allocation, or stack space, so we + * use constant strings instead + */ +typedef struct pmm_trace_corelist +{ + mali_pmm_core_mask id; + const char *name; +} pmm_trace_corelist_t; + +static pmm_trace_corelist_t pmm_trace_cores[] = { + { MALI_PMM_CORE_SYSTEM, "SYSTEM" }, + { MALI_PMM_CORE_GP, "GP" }, + { MALI_PMM_CORE_L2, "L2" }, + { MALI_PMM_CORE_PP0, "PP0" }, + { MALI_PMM_CORE_PP1, "PP1" }, + { MALI_PMM_CORE_PP2, "PP2" }, + { MALI_PMM_CORE_PP3, "PP3" }, + { MALI_PMM_CORE_PP_ALL, "PP (all)" }, + { (MALI_PMM_CORE_GP | MALI_PMM_CORE_L2 | MALI_PMM_CORE_PP0), + "GP+L2+PP0" }, + { (MALI_PMM_CORE_GP | MALI_PMM_CORE_PP0), + "GP+PP0" }, + { (MALI_PMM_CORE_GP | MALI_PMM_CORE_L2 | MALI_PMM_CORE_PP0 | MALI_PMM_CORE_PP1), + "GP+L2+PP0+PP1" }, + { (MALI_PMM_CORE_GP | MALI_PMM_CORE_PP0 | MALI_PMM_CORE_PP1), + "GP+PP0+PP1" }, + { 0, NULL } /* Terminator of list */ +}; + +const char *pmm_trace_get_core_name( mali_pmm_core_mask cores ) +{ + const char *dname = NULL; + int cl; + + /* Look up name in corelist */ + cl = 0; + while( pmm_trace_cores[cl].name != NULL ) + { + if( pmm_trace_cores[cl].id == cores ) + { + dname = pmm_trace_cores[cl].name; + break; + } + cl++; + } + + if( dname == NULL ) + { + /* We don't know a good short-hand for the configuration */ + dname = "[multi-core]"; + } + + return dname; +} + +#endif /* USING_MALI_PMM */ + diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_state.h b/drivers/gpu/arm/mali/common/pmm/mali_pmm_state.h new file mode 100644 index 000000000000..0fb62d2a6423 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_state.h @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_state.h + * Defines the internal power management module state + */ + +#ifndef __MALI_PMM_STATE_H__ +#define __MALI_PMM_STATE_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup pmmapi Power Management Module APIs + * + * @{ + * + * @defgroup pmmapi_state Power Management Module State + * + * @{ + */ + +/* Check that the subset is really a subset of cores */ +#define MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( cores, subset ) \ + MALI_DEBUG_ASSERT( ((~(cores)) & (subset)) == 0 ) + + +/* Locking macros */ +#define MALI_PMM_LOCK(pmm) \ + _mali_osk_lock_wait( pmm->lock, _MALI_OSK_LOCKMODE_RW ) +#define MALI_PMM_UNLOCK(pmm) \ + _mali_osk_lock_signal( pmm->lock, _MALI_OSK_LOCKMODE_RW ) +#define MALI_PMM_LOCK_TERM(pmm) \ + _mali_osk_lock_term( pmm->lock ) + +/* Notification type for messages */ +#define MALI_PMM_NOTIFICATION_TYPE 0 + +/** @brief Status of the PMM state machine + */ +typedef enum mali_pmm_status_tag +{ + MALI_PMM_STATUS_IDLE, /**< PMM is waiting next event */ + MALI_PMM_STATUS_POLICY_POWER_DOWN, /**< Policy initiated power down */ + MALI_PMM_STATUS_POLICY_POWER_UP, /**< Policy initiated power down */ + MALI_PMM_STATUS_OS_WAITING, /**< PMM is waiting for OS power up */ + MALI_PMM_STATUS_OS_POWER_DOWN, /**< OS initiated power down */ + MALI_PMM_STATUS_DVFS_PAUSE, /**< PMM DVFS Status Pause */ + MALI_PMM_STATUS_OS_POWER_UP, /**< OS initiated power up */ + MALI_PMM_STATUS_OFF, /**< PMM is not active */ +} mali_pmm_status; + + +/** @brief Internal state of the PMM + */ +typedef struct _mali_pmm_internal_state +{ + mali_pmm_status status; /**< PMM state machine */ + mali_pmm_policy policy; /**< PMM policy */ + mali_bool check_policy; /**< PMM policy needs checking */ + mali_pmm_state state; /**< PMM state */ + mali_pmm_core_mask cores_registered; /**< Bitmask of cores registered */ + mali_pmm_core_mask cores_powered; /**< Bitmask of cores powered up */ + mali_pmm_core_mask cores_idle; /**< Bitmask of cores idle */ + mali_pmm_core_mask cores_pend_down; /**< Bitmask of cores pending power down */ + mali_pmm_core_mask cores_pend_up; /**< Bitmask of cores pending power up */ + mali_pmm_core_mask cores_ack_down; /**< Bitmask of cores acknowledged power down */ + mali_pmm_core_mask cores_ack_up; /**< Bitmask of cores acknowledged power up */ + + _mali_osk_notification_queue_t *queue; /**< PMM event queue */ + _mali_osk_notification_queue_t *iqueue; /**< PMM internal event queue */ + _mali_osk_irq_t *irq; /**< PMM irq handler */ + _mali_osk_lock_t *lock; /**< PMM lock */ + + mali_pmm_message_data os_data; /**< OS data sent via the OS events */ + + mali_bool pmu_initialized; /**< PMU initialized */ + + _mali_osk_atomic_t messages_queued; /**< PMM event messages queued */ + u32 waiting; /**< PMM waiting events - due to busy */ + u32 no_events; /**< PMM called to process when no events */ + + u32 missed; /**< PMM missed events due to OOM */ + mali_bool fatal_power_err; /**< PMM has had a fatal power error? */ + u32 is_dvfs_active; /**< PMM DVFS activity */ + +#if MALI_STATE_TRACKING + mali_pmm_status mali_last_pmm_status; /**< The previous PMM status */ + mali_pmm_event_id mali_new_event_status;/**< The type of the last PMM event */ + mali_bool mali_pmm_lock_acquired; /**< Is the PMM lock held somewhere or not */ +#endif + +#if (MALI_PMM_TRACE || MALI_STATE_TRACKING) + u32 messages_sent; /**< Total event messages sent */ + u32 messages_received; /**< Total event messages received */ + u32 imessages_sent; /**< Total event internal messages sent */ + u32 imessages_received; /**< Total event internal messages received */ +#endif +} _mali_pmm_internal_state_t; + +/** @brief Sets that a policy needs a check before processing events + * + * A timer or something has expired that needs dealing with + */ +void malipmm_set_policy_check(void); + +/** @brief Update the PMM externally viewable state depending on the current PMM internal state + * + * @param pmm internal PMM state + * @return MALI_TRUE if the timeout is valid, else MALI_FALSE + */ +void pmm_update_system_state( _mali_pmm_internal_state_t *pmm ); + +/** @brief Returns the core mask from the event data - if applicable + * + * @param pmm internal PMM state + * @param event event message to get the core mask from + * @return mask of cores that is relevant to this event message + */ +mali_pmm_core_mask pmm_cores_from_event_data( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ); + +/** @brief Sort out which cores need to be powered up from the given core mask + * + * All cores that can be powered up will be put into a pending state + * + * @param pmm internal PMM state + * @param cores mask of cores to check if they need to be powered up + * @return mask of cores that need to be powered up, this can be 0 if all cores + * are powered up already + */ +mali_pmm_core_mask pmm_cores_to_power_up( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ); + +/** @brief Sort out which cores need to be powered down from the given core mask + * + * All cores that can be powered down will be put into a pending state. If they + * can be powered down immediately they will also be acknowledged that they can be + * powered down. If the immediate_only flag is set, then only those cores that + * can be acknowledged for power down will be put into a pending state. + * + * @param pmm internal PMM state + * @param cores mask of cores to check if they need to be powered down + * @param immediate_only MALI_TRUE means that only cores that can power down now will + * be put into a pending state + * @return mask of cores that need to be powered down, this can be 0 if all cores + * are powered down already + */ +mali_pmm_core_mask pmm_cores_to_power_down( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores, mali_bool immediate_only ); + +/** @brief Cancel an invokation to power down (pmm_invoke_power_down) + * + * @param pmm internal PMM state + */ +void pmm_power_down_cancel( _mali_pmm_internal_state_t *pmm ); + +/** @brief Check if a call to invoke power down should succeed, or fail + * + * This will report MALI_FALSE if some of the cores are still active and need + * to acknowledge that they are ready to power down + * + * @param pmm internal PMM state + * @return MALI_TRUE if the pending cores to power down have acknowledged they + * can power down, else MALI_FALSE + */ +mali_bool pmm_power_down_okay( _mali_pmm_internal_state_t *pmm ); + +/** @brief Try to make all the pending cores power down + * + * If all the pending cores have acknowledged they can power down, this will call the + * PMU power down function to turn them off + * + * @param pmm internal PMM state + * @return MALI_TRUE if the pending cores have been powered down, else MALI_FALSE + */ +mali_bool pmm_invoke_power_down( _mali_pmm_internal_state_t *pmm, mali_power_mode power_mode ); + +/** @brief Check if all the pending cores to power up have done so + * + * This will report MALI_FALSE if some of the cores are still powered off + * and have not acknowledged that they have powered up + * + * @param pmm internal PMM state + * @return MALI_TRUE if the pending cores to power up have acknowledged they + * are now powered up, else MALI_FALSE + */ +mali_bool pmm_power_up_okay( _mali_pmm_internal_state_t *pmm ); + +/** @brief Try to make all the pending cores power up + * + * If all the pending cores have acknowledged they have powered up, this will + * make the cores start processing jobs again, else this will call the PMU + * power up function to turn them on, and the PMM is then expected to wait for an + * interrupt to acknowledge the power up + * + * @param pmm internal PMM state + * @return MALI_TRUE if the pending cores have been powered up, else MALI_FALSE + */ +mali_bool pmm_invoke_power_up( _mali_pmm_internal_state_t *pmm ); + +/** @brief Set the cores that are now active in the system + * + * Updates which cores are active and returns which cores are still idle + * + * @param pmm internal PMM state + * @param cores mask of cores to set to active + * @return mask of all the cores that are idle + */ +mali_pmm_core_mask pmm_cores_set_active( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ); + +/** @brief Set the cores that are now idle in the system + * + * Updates which cores are idle and returns which cores are still idle + * + * @param pmm internal PMM state + * @param cores mask of cores to set to idle + * @return mask of all the cores that are idle + */ +mali_pmm_core_mask pmm_cores_set_idle( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ); + +/** @brief Set the cores that have acknowledged a pending power down + * + * Updates which cores have acknowledged the pending power down and are now ready + * to be turned off + * + * @param pmm internal PMM state + * @param cores mask of cores that have acknowledged the pending power down + * @return mask of all the cores that have acknowledged the power down + */ +mali_pmm_core_mask pmm_cores_set_down_ack( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ); + +/** @brief Set the cores that have acknowledged a pending power up + * + * Updates which cores have acknowledged the pending power up and are now + * fully powered and ready to run jobs + * + * @param pmm internal PMM state + * @param cores mask of cores that have acknowledged the pending power up + * @return mask of all the cores that have acknowledged the power up + */ +mali_pmm_core_mask pmm_cores_set_up_ack( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ); + + +/** @brief Tries to reset the PMM and PMU hardware to a known state after any fatal issues + * + * This will try and make all the cores powered up and reset the PMM state + * to its initial state after core registration - all cores powered but not + * pending or active. + * All events in the event queues will be thrown away. + * + * @note: Any pending power down will be cancelled including the OS calling for power down + */ +void pmm_fatal_reset( _mali_pmm_internal_state_t *pmm ); + +/** @brief Save the OS specific data for an OS power up/down event + * + * @param pmm internal PMM state + * @param data OS specific event data + */ +void pmm_save_os_event_data(_mali_pmm_internal_state_t *pmm, mali_pmm_message_data data); + +/** @brief Retrieve the OS specific data for an OS power up/down event + * + * This will clear the stored OS data, as well as return it. + * + * @param pmm internal PMM state + * @return OS specific event data that was saved previously + */ +mali_pmm_message_data pmm_retrieve_os_event_data(_mali_pmm_internal_state_t *pmm); + + +/** @brief Get a human readable name for the cores in a core mask + * + * @param core the core mask + * @return string containing a name relating to the given core mask + */ +const char *pmm_trace_get_core_name( mali_pmm_core_mask core ); + +/** @} */ /* End group pmmapi_state */ +/** @} */ /* End group pmmapi */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_STATE_H__ */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_system.h b/drivers/gpu/arm/mali/common/pmm/mali_pmm_system.h new file mode 100644 index 000000000000..1be5b847c889 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_system.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_pmm_system.h + * Defines the power management module system functions + */ + +#ifndef __MALI_PMM_SYSTEM_H__ +#define __MALI_PMM_SYSTEM_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup pmmapi Power Management Module APIs + * + * @{ + * + * @defgroup pmmapi_system Power Management Module System Functions + * + * @{ + */ + +extern struct mali_kernel_subsystem mali_subsystem_pmm; + +/** @brief Register a core with the PMM, which will power up + * the core + * + * @param core the core to register with the PMM + * @return error if the core cannot be powered up + */ +_mali_osk_errcode_t malipmm_core_register( mali_pmm_core_id core ); + +/** @brief Unregister a core with the PMM + * + * @param core the core to unregister with the PMM + */ +void malipmm_core_unregister( mali_pmm_core_id core ); + +/** @brief Acknowledge that a power down is okay to happen + * + * A core should not be running a job, or be in the idle queue when this + * is called. + * + * @param core the core that can now be powered down + */ +void malipmm_core_power_down_okay( mali_pmm_core_id core ); + +/** @} */ /* End group pmmapi_system */ +/** @} */ /* End group pmmapi */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_H__ */ diff --git a/drivers/gpu/arm/mali/include/cinstr/mali_cinstr_profiling_events_m200.h b/drivers/gpu/arm/mali/include/cinstr/mali_cinstr_profiling_events_m200.h new file mode 100644 index 000000000000..49d982ec52c6 --- /dev/null +++ b/drivers/gpu/arm/mali/include/cinstr/mali_cinstr_profiling_events_m200.h @@ -0,0 +1,114 @@ +/* + * This confidential and proprietary software may be used only as + * authorised by a licensing agreement from ARM Limited + * (C) COPYRIGHT 2010-2012 ARM Limited + * ALL RIGHTS RESERVED + * The entire notice above must be reproduced on all authorised + * copies and copies may only be made to the extent permitted + * by a licensing agreement from ARM Limited. + */ + +#ifndef _CINSTR_PROFILING_EVENTS_M200_H_ +#define _CINSTR_PROFILING_EVENTS_M200_H_ + +/* + * The event ID is a 32 bit value consisting of different fields + * reserved, 4 bits, for future use + * event type, 4 bits, cinstr_profiling_event_type_t + * event channel, 8 bits, the source of the event. + * event data, 16 bit field, data depending on event type + */ + +/** + * Specifies what kind of event this is + */ +typedef enum +{ + MALI_PROFILING_EVENT_TYPE_SINGLE = 0 << 24, + MALI_PROFILING_EVENT_TYPE_START = 1 << 24, + MALI_PROFILING_EVENT_TYPE_STOP = 2 << 24, + MALI_PROFILING_EVENT_TYPE_SUSPEND = 3 << 24, + MALI_PROFILING_EVENT_TYPE_RESUME = 4 << 24, +} cinstr_profiling_event_type_t; + + +/** + * Secifies the channel/source of the event + */ +typedef enum +{ + MALI_PROFILING_EVENT_CHANNEL_SOFTWARE = 0 << 16, + MALI_PROFILING_EVENT_CHANNEL_GP0 = 1 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP0 = 5 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP1 = 6 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP2 = 7 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP3 = 8 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP4 = 9 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP5 = 10 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP6 = 11 << 16, + MALI_PROFILING_EVENT_CHANNEL_PP7 = 12 << 16, + MALI_PROFILING_EVENT_CHANNEL_GPU = 21 << 16, +} cinstr_profiling_event_channel_t; + + +#define MALI_PROFILING_MAKE_EVENT_CHANNEL_GP(num) (((MALI_PROFILING_EVENT_CHANNEL_GP0 >> 16) + (num)) << 16) +#define MALI_PROFILING_MAKE_EVENT_CHANNEL_PP(num) (((MALI_PROFILING_EVENT_CHANNEL_PP0 >> 16) + (num)) << 16) + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from software channel + */ +typedef enum +{ + MALI_PROFILING_EVENT_REASON_SINGLE_SW_NONE = 0, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_NEW_FRAME = 1, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_FLUSH = 2, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_EGL_SWAP_BUFFERS = 3, + MALI_PROFILING_EVENT_REASON_SINGLE_SW_FB_EVENT = 4 +} cinstr_profiling_event_reason_single_sw_t; + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_START/STOP is used from software channel + */ +typedef enum +{ + MALI_PROFILING_EVENT_REASON_START_STOP_SW_NONE = 0, + MALI_PROFILING_EVENT_REASON_START_STOP_MALI = 1, +} cinstr_profiling_event_reason_start_stop_sw_t; + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SUSPEND/RESUME is used from software channel + */ +typedef enum +{ + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_NONE = 0, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_PIPELINE_FULL = 1, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VSYNC = 26, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_FB_IFRAME_WAIT= 27, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_FB_IFRAME_SYNC= 28, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VG_WAIT_FILTER_CLEANUP = 29, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_VG_WAIT_TEXTURE = 30, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_GLES_WAIT_MIPLEVEL = 31, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_GLES_WAIT_READPIXELS = 32, + MALI_PROFILING_EVENT_REASON_SUSPEND_RESUME_SW_EGL_WAIT_SWAP_IMMEDIATE= 33, +} cinstr_profiling_event_reason_suspend_resume_sw_t; + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from a HW channel (GPx+PPx) + */ +typedef enum +{ + MALI_PROFILING_EVENT_REASON_SINGLE_HW_NONE = 0, + MALI_PROFILING_EVENT_REASON_SINGLE_HW_INTERRUPT = 1, + MALI_PROFILING_EVENT_REASON_SINGLE_HW_FLUSH = 2, +} cinstr_profiling_event_reason_single_hw_t; + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from the GPU channel + */ +typedef enum +{ + MALI_PROFILING_EVENT_REASON_SINGLE_GPU_NONE = 0, + MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE = 1, +} cinstr_profiling_event_reason_single_gpu_t; + +#endif /*_CINSTR_PROFILING_EVENTS_M200_H_*/ diff --git a/drivers/gpu/arm/mali/linux/license/gpl/mali_kernel_license.h b/drivers/gpu/arm/mali/linux/license/gpl/mali_kernel_license.h new file mode 100644 index 000000000000..e9e5e55a0822 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/license/gpl/mali_kernel_license.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_kernel_license.h + * Defines for the macro MODULE_LICENSE. + */ + +#ifndef __MALI_KERNEL_LICENSE_H__ +#define __MALI_KERNEL_LICENSE_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define MALI_KERNEL_LINUX_LICENSE "GPL" +#define MALI_LICENSE_IS_GPL 1 + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_KERNEL_LICENSE_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_device_pause_resume.c b/drivers/gpu/arm/mali/linux/mali_device_pause_resume.c new file mode 100644 index 000000000000..da9cdd99d270 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_device_pause_resume.c @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_device_pause_resume.c + * Implementation of the Mali pause/resume functionality + */ +#if USING_MALI_PMM +#include <linux/version.h> +#include <linux/sched.h> +#include <linux/module.h> +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_platform.h" +#include "mali_linux_pm.h" +#include "mali_device_pause_resume.h" +#include "mali_pmm.h" +#include "mali_kernel_license.h" +#ifdef CONFIG_PM +#if MALI_LICENSE_IS_GPL + +/* Mali Pause Resume APIs */ +int mali_dev_pause() +{ + int err = 0; + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + if ((mali_dvfs_device_state == _MALI_DEVICE_SUSPEND) + || (mali_device_state == _MALI_DEVICE_SUSPEND) ) + { + err = -EPERM; + } + if ((mali_dvfs_device_state == _MALI_DEVICE_RESUME) && (!err)) + { + mali_device_suspend(MALI_PMM_EVENT_DVFS_PAUSE, &dvfs_pm_thread); + mali_dvfs_device_state = _MALI_DEVICE_SUSPEND; + } + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return err; +} + +EXPORT_SYMBOL(mali_dev_pause); + +int mali_dev_resume() +{ + int err = 0; + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + if ((mali_dvfs_device_state == _MALI_DEVICE_RESUME) + || (mali_device_state == _MALI_DEVICE_SUSPEND) ) + { + err = -EPERM; + } + if (!err) + { + mali_device_resume(MALI_PMM_EVENT_DVFS_RESUME, &dvfs_pm_thread); + mali_dvfs_device_state = _MALI_DEVICE_RESUME; + } + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return err; +} + +EXPORT_SYMBOL(mali_dev_resume); + +#endif /* MALI_LICENSE_IS_GPL */ +#endif /* CONFIG_PM */ +#endif /* USING_MALI_PMM */ diff --git a/drivers/gpu/arm/mali/linux/mali_device_pause_resume.h b/drivers/gpu/arm/mali/linux/mali_device_pause_resume.h new file mode 100644 index 000000000000..c770cb6f5328 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_device_pause_resume.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_DEVICE_PAUSE_RESUME_H__ +#define __MALI_DEVICE_PAUSE_RESUME_H__ + +#if USING_MALI_PMM +int mali_dev_pause(void); +int mali_dev_resume(void); +#endif /* USING_MALI_PMM */ + +#endif /* __MALI_DEVICE_PAUSE_RESUME_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_ioctl.h b/drivers/gpu/arm/mali/linux/mali_kernel_ioctl.h new file mode 100644 index 000000000000..5386566010aa --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_kernel_ioctl.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_IOCTL_H__ +#define __MALI_KERNEL_IOCTL_H__ + +#include <linux/types.h> +#include <linux/ioctl.h> +#include <linux/fs.h> /* file system operations */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @file mali_kernel_ioctl.h + * Interface to the Linux device driver. + * This file describes the interface needed to use the Linux device driver. + * Its interface is designed to used by the HAL implementation through a thin arch layer. + */ + +/** + * ioctl commands + */ + +#define MALI_IOC_BASE 0x82 +#define MALI_IOC_CORE_BASE (_MALI_UK_CORE_SUBSYSTEM + MALI_IOC_BASE) +#define MALI_IOC_MEMORY_BASE (_MALI_UK_MEMORY_SUBSYSTEM + MALI_IOC_BASE) +#define MALI_IOC_PP_BASE (_MALI_UK_PP_SUBSYSTEM + MALI_IOC_BASE) +#define MALI_IOC_GP_BASE (_MALI_UK_GP_SUBSYSTEM + MALI_IOC_BASE) +#define MALI_IOC_PROFILING_BASE (_MALI_UK_PROFILING_SUBSYSTEM + MALI_IOC_BASE) +#define MALI_IOC_VSYNC_BASE (_MALI_UK_VSYNC_SUBSYSTEM + MALI_IOC_BASE) + +#define MALI_IOC_GET_SYSTEM_INFO_SIZE _IOR (MALI_IOC_CORE_BASE, _MALI_UK_GET_SYSTEM_INFO_SIZE, _mali_uk_get_system_info_s *) +#define MALI_IOC_GET_SYSTEM_INFO _IOR (MALI_IOC_CORE_BASE, _MALI_UK_GET_SYSTEM_INFO, _mali_uk_get_system_info_s *) +#define MALI_IOC_WAIT_FOR_NOTIFICATION _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_WAIT_FOR_NOTIFICATION, _mali_uk_wait_for_notification_s *) +#define MALI_IOC_GET_API_VERSION _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_GET_API_VERSION, _mali_uk_get_api_version_s *) +#define MALI_IOC_POST_NOTIFICATION _IOWR(MALI_IOC_CORE_BASE, _MALI_UK_POST_NOTIFICATION, _mali_uk_post_notification_s *) +#define MALI_IOC_MEM_GET_BIG_BLOCK _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_GET_BIG_BLOCK, _mali_uk_get_big_block_s *) +#define MALI_IOC_MEM_FREE_BIG_BLOCK _IOW (MALI_IOC_MEMORY_BASE, _MALI_UK_FREE_BIG_BLOCK, _mali_uk_free_big_block_s *) +#define MALI_IOC_MEM_INIT _IOR (MALI_IOC_MEMORY_BASE, _MALI_UK_INIT_MEM, _mali_uk_init_mem_s *) +#define MALI_IOC_MEM_TERM _IOW (MALI_IOC_MEMORY_BASE, _MALI_UK_TERM_MEM, _mali_uk_term_mem_s *) +#define MALI_IOC_MEM_MAP_EXT _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_MAP_EXT_MEM, _mali_uk_map_external_mem_s *) +#define MALI_IOC_MEM_UNMAP_EXT _IOW (MALI_IOC_MEMORY_BASE, _MALI_UK_UNMAP_EXT_MEM, _mali_uk_unmap_external_mem_s *) +#define MALI_IOC_MEM_QUERY_MMU_PAGE_TABLE_DUMP_SIZE _IOR (MALI_IOC_MEMORY_BASE, _MALI_UK_QUERY_MMU_PAGE_TABLE_DUMP_SIZE, _mali_uk_query_mmu_page_table_dump_size_s *) +#define MALI_IOC_MEM_DUMP_MMU_PAGE_TABLE _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_DUMP_MMU_PAGE_TABLE, _mali_uk_dump_mmu_page_table_s *) +#define MALI_IOC_MEM_ATTACH_UMP _IOWR(MALI_IOC_MEMORY_BASE, _MALI_UK_ATTACH_UMP_MEM, _mali_uk_attach_ump_mem_s *) +#define MALI_IOC_MEM_RELEASE_UMP _IOW(MALI_IOC_MEMORY_BASE, _MALI_UK_RELEASE_UMP_MEM, _mali_uk_release_ump_mem_s *) +#define MALI_IOC_PP_START_JOB _IOWR(MALI_IOC_PP_BASE, _MALI_UK_PP_START_JOB, _mali_uk_pp_start_job_s *) +#define MALI_IOC_PP_NUMBER_OF_CORES_GET _IOR (MALI_IOC_PP_BASE, _MALI_UK_GET_PP_NUMBER_OF_CORES, _mali_uk_get_pp_number_of_cores_s *) +#define MALI_IOC_PP_CORE_VERSION_GET _IOR (MALI_IOC_PP_BASE, _MALI_UK_GET_PP_CORE_VERSION, _mali_uk_get_pp_core_version_s * ) +#define MALI_IOC_PP_ABORT_JOB _IOW (MALI_IOC_PP_BASE, _MALI_UK_PP_ABORT_JOB, _mali_uk_pp_abort_job_s * ) +#define MALI_IOC_GP2_START_JOB _IOWR(MALI_IOC_GP_BASE, _MALI_UK_GP_START_JOB, _mali_uk_gp_start_job_s *) +#define MALI_IOC_GP2_ABORT_JOB _IOWR(MALI_IOC_GP_BASE, _MALI_UK_GP_ABORT_JOB, _mali_uk_gp_abort_job_s *) +#define MALI_IOC_GP2_NUMBER_OF_CORES_GET _IOR (MALI_IOC_GP_BASE, _MALI_UK_GET_GP_NUMBER_OF_CORES, _mali_uk_get_gp_number_of_cores_s *) +#define MALI_IOC_GP2_CORE_VERSION_GET _IOR (MALI_IOC_GP_BASE, _MALI_UK_GET_GP_CORE_VERSION, _mali_uk_get_gp_core_version_s *) +#define MALI_IOC_GP2_SUSPEND_RESPONSE _IOW (MALI_IOC_GP_BASE, _MALI_UK_GP_SUSPEND_RESPONSE,_mali_uk_gp_suspend_response_s *) +#define MALI_IOC_PROFILING_START _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_START, _mali_uk_profiling_start_s *) +#define MALI_IOC_PROFILING_ADD_EVENT _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_ADD_EVENT, _mali_uk_profiling_add_event_s*) +#define MALI_IOC_PROFILING_STOP _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_STOP, _mali_uk_profiling_stop_s *) +#define MALI_IOC_PROFILING_GET_EVENT _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_GET_EVENT, _mali_uk_profiling_get_event_s *) +#define MALI_IOC_PROFILING_CLEAR _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_CLEAR, _mali_uk_profiling_clear_s *) +#define MALI_IOC_PROFILING_GET_CONFIG _IOWR(MALI_IOC_PROFILING_BASE, _MALI_UK_PROFILING_GET_CONFIG, _mali_uk_profiling_get_config_s *) +#define MALI_IOC_VSYNC_EVENT_REPORT _IOW (MALI_IOC_VSYNC_BASE, _MALI_UK_VSYNC_EVENT_REPORT, _mali_uk_vsync_event_report_s *) + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_KERNEL_IOCTL_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_linux.c b/drivers/gpu/arm/mali/linux/mali_kernel_linux.c new file mode 100644 index 000000000000..6d108ff7a37e --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_kernel_linux.c @@ -0,0 +1,494 @@ +/** + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_kernel_linux.c + * Implementation of the Linux device driver entrypoints + */ +#include <linux/module.h> /* kernel module definitions */ +#include <linux/fs.h> /* file system operations */ +#include <linux/cdev.h> /* character device definitions */ +#include <linux/mm.h> /* memory mananger definitions */ +#include <linux/device.h> + +/* the mali kernel subsystem types */ +#include "mali_kernel_subsystem.h" + +/* A memory subsystem always exists, so no need to conditionally include it */ +#include "mali_kernel_common.h" +#include "mali_kernel_session_manager.h" +#include "mali_kernel_core.h" + +#include "mali_osk.h" +#include "mali_kernel_linux.h" +#include "mali_ukk.h" +#include "mali_kernel_ioctl.h" +#include "mali_ukk_wrappers.h" +#include "mali_kernel_pm.h" + +#include "mali_kernel_sysfs.h" + +/* */ +#include "mali_kernel_license.h" + +/* Streamline support for the Mali driver */ +#if defined(CONFIG_TRACEPOINTS) +/* Ask Linux to create the tracepoints */ +#define CREATE_TRACE_POINTS +#include "mali_linux_trace.h" +#endif /* CONFIG_TRACEPOINTS */ + +/* from the __malidrv_build_info.c file that is generated during build */ +//extern const char *__malidrv_build_info(void); + +/* Module parameter to control log level */ +int mali_debug_level = 2; +module_param(mali_debug_level, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */ +MODULE_PARM_DESC(mali_debug_level, "Higher number, more dmesg output"); + +/* By default the module uses any available major, but it's possible to set it at load time to a specific number */ +int mali_major = 244; +module_param(mali_major, int, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(mali_major, "Device major number"); + +int mali_benchmark = 0; +module_param(mali_benchmark, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */ +MODULE_PARM_DESC(mali_benchmark, "Bypass Mali hardware when non-zero"); + +extern int mali_hang_check_interval; +module_param(mali_hang_check_interval, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_hang_check_interval, "Interval at which to check for progress after the hw watchdog has been triggered"); + +extern int mali_max_job_runtime; +module_param(mali_max_job_runtime, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_max_job_runtime, "Maximum allowed job runtime in msecs.\nJobs will be killed after this no matter what"); + +#if defined(USING_MALI400_L2_CACHE) +extern int mali_l2_max_reads; +module_param(mali_l2_max_reads, int, S_IRUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_l2_max_reads, "Maximum reads for Mali L2 cache"); +#endif + +#if MALI_TIMELINE_PROFILING_ENABLED +extern int mali_boot_profiling; +module_param(mali_boot_profiling, int, S_IRUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(mali_boot_profiling, "Start profiling as a part of Mali driver initialization"); +#endif + +static char mali_dev_name[] = "mali"; /* should be const, but the functions we call requires non-cost */ + +/* the mali device */ +static struct mali_dev device; + + +static int mali_open(struct inode *inode, struct file *filp); +static int mali_release(struct inode *inode, struct file *filp); +#ifdef HAVE_UNLOCKED_IOCTL +static long mali_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#else +static int mali_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +#endif + +static int mali_mmap(struct file * filp, struct vm_area_struct * vma); + +/* Linux char file operations provided by the Mali module */ +struct file_operations mali_fops = +{ + .owner = THIS_MODULE, + .open = mali_open, + .release = mali_release, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = mali_ioctl, +#else + .ioctl = mali_ioctl, +#endif + .mmap = mali_mmap +}; + + +int mali_driver_init(void) +{ + int err; +#if USING_MALI_PMM +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM + err = _mali_dev_platform_register(); + if (err) + { + return err; + } +#endif +#endif +#endif + err = mali_kernel_constructor(); + if (_MALI_OSK_ERR_OK != err) + { +#if USING_MALI_PMM +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM + _mali_dev_platform_unregister(); +#endif +#endif +#endif + MALI_PRINT(("Failed to initialize driver (error %d)\n", err)); + return -EFAULT; + } + + /* print build options */ +// MALI_DEBUG_PRINT(2, ("%s\n", __malidrv_build_info())); + + return 0; +} + +void mali_driver_exit(void) +{ + mali_kernel_destructor(); + +#if MALI_LICENSE_IS_GPL +#if USING_MALI_PMM +#ifdef CONFIG_PM + _mali_dev_platform_unregister(); +#endif +#endif + + flush_workqueue(mali_wq); + destroy_workqueue(mali_wq); + mali_wq = NULL; +#endif +} + +/* called from _mali_osk_init */ +int initialize_kernel_device(void) +{ + int err; + dev_t dev = 0; + if (0 == mali_major) + { + /* auto select a major */ + err = alloc_chrdev_region(&dev, 0/*first minor*/, 1/*count*/, mali_dev_name); + mali_major = MAJOR(dev); + } + else + { + /* use load time defined major number */ + dev = MKDEV(mali_major, 0); + err = register_chrdev_region(dev, 1/*count*/, mali_dev_name); + } + + if (err) + { + goto init_chrdev_err; + } + + memset(&device, 0, sizeof(device)); + + /* initialize our char dev data */ + cdev_init(&device.cdev, &mali_fops); + device.cdev.owner = THIS_MODULE; + device.cdev.ops = &mali_fops; + + /* register char dev with the kernel */ + err = cdev_add(&device.cdev, dev, 1/*count*/); + if (err) + { + goto init_cdev_err; + } + + err = mali_sysfs_register(&device, dev, mali_dev_name); + if (err) + { + goto init_sysfs_err; + } + + /* Success! */ + return 0; + +init_sysfs_err: + cdev_del(&device.cdev); +init_cdev_err: + unregister_chrdev_region(dev, 1/*count*/); +init_chrdev_err: + return err; +} + +/* called from _mali_osk_term */ +void terminate_kernel_device(void) +{ + dev_t dev = MKDEV(mali_major, 0); + + mali_sysfs_unregister(&device, dev, mali_dev_name); + + /* unregister char device */ + cdev_del(&device.cdev); + /* free major */ + unregister_chrdev_region(dev, 1/*count*/); + return; +} + +/** @note munmap handler is done by vma close handler */ +static int mali_mmap(struct file * filp, struct vm_area_struct * vma) +{ + struct mali_session_data * session_data; + _mali_uk_mem_mmap_s args = {0, }; + + session_data = (struct mali_session_data *)filp->private_data; + if (NULL == session_data) + { + MALI_PRINT_ERROR(("mmap called without any session data available\n")); + return -EFAULT; + } + + MALI_DEBUG_PRINT(3, ("MMap() handler: start=0x%08X, phys=0x%08X, size=0x%08X\n", (unsigned int)vma->vm_start, (unsigned int)(vma->vm_pgoff << PAGE_SHIFT), (unsigned int)(vma->vm_end - vma->vm_start)) ); + + /* Re-pack the arguments that mmap() packed for us */ + args.ctx = session_data; + args.phys_addr = vma->vm_pgoff << PAGE_SHIFT; + args.size = vma->vm_end - vma->vm_start; + args.ukk_private = vma; + + /* Call the common mmap handler */ + MALI_CHECK(_MALI_OSK_ERR_OK ==_mali_ukk_mem_mmap( &args ), -EFAULT); + + return 0; +} + +static int mali_open(struct inode *inode, struct file *filp) +{ + struct mali_session_data * session_data; + _mali_osk_errcode_t err; + + /* input validation */ + if (0 != MINOR(inode->i_rdev)) return -ENODEV; + + /* allocated struct to track this session */ + err = _mali_ukk_open((void **)&session_data); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + /* initialize file pointer */ + filp->f_pos = 0; + + /* link in our session data */ + filp->private_data = (void*)session_data; + + return 0; +} + +static int mali_release(struct inode *inode, struct file *filp) +{ + _mali_osk_errcode_t err; + + /* input validation */ + if (0 != MINOR(inode->i_rdev)) return -ENODEV; + + err = _mali_ukk_close((void **)&filp->private_data); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + return 0; +} + +int map_errcode( _mali_osk_errcode_t err ) +{ + switch(err) + { + case _MALI_OSK_ERR_OK : return 0; + case _MALI_OSK_ERR_FAULT: return -EFAULT; + case _MALI_OSK_ERR_INVALID_FUNC: return -ENOTTY; + case _MALI_OSK_ERR_INVALID_ARGS: return -EINVAL; + case _MALI_OSK_ERR_NOMEM: return -ENOMEM; + case _MALI_OSK_ERR_TIMEOUT: return -ETIMEDOUT; + case _MALI_OSK_ERR_RESTARTSYSCALL: return -ERESTARTSYS; + case _MALI_OSK_ERR_ITEM_NOT_FOUND: return -ENOENT; + default: return -EFAULT; + } +} + +#ifdef HAVE_UNLOCKED_IOCTL +static long mali_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#else +static int mali_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ + int err; + struct mali_session_data *session_data; + +#ifndef HAVE_UNLOCKED_IOCTL + /* inode not used */ + (void)inode; +#endif + + MALI_DEBUG_PRINT(7, ("Ioctl received 0x%08X 0x%08lX\n", cmd, arg)); + + session_data = (struct mali_session_data *)filp->private_data; + if (NULL == session_data) + { + MALI_DEBUG_PRINT(7, ("filp->private_data was NULL\n")); + return -ENOTTY; + } + + if (NULL == (void *)arg) + { + MALI_DEBUG_PRINT(7, ("arg was NULL\n")); + return -ENOTTY; + } + + switch(cmd) + { + case MALI_IOC_GET_SYSTEM_INFO_SIZE: + err = get_system_info_size_wrapper(session_data, (_mali_uk_get_system_info_size_s __user *)arg); + break; + + case MALI_IOC_GET_SYSTEM_INFO: + err = get_system_info_wrapper(session_data, (_mali_uk_get_system_info_s __user *)arg); + break; + + case MALI_IOC_WAIT_FOR_NOTIFICATION: + err = wait_for_notification_wrapper(session_data, (_mali_uk_wait_for_notification_s __user *)arg); + break; + + case MALI_IOC_GET_API_VERSION: + err = get_api_version_wrapper(session_data, (_mali_uk_get_api_version_s __user *)arg); + break; + + case MALI_IOC_POST_NOTIFICATION: + err = post_notification_wrapper(session_data, (_mali_uk_post_notification_s __user *)arg); + break; + +#if MALI_TIMELINE_PROFILING_ENABLED + case MALI_IOC_PROFILING_START: + err = profiling_start_wrapper(session_data, (_mali_uk_profiling_start_s __user *)arg); + break; + + case MALI_IOC_PROFILING_ADD_EVENT: + err = profiling_add_event_wrapper(session_data, (_mali_uk_profiling_add_event_s __user *)arg); + break; + + case MALI_IOC_PROFILING_STOP: + err = profiling_stop_wrapper(session_data, (_mali_uk_profiling_stop_s __user *)arg); + break; + + case MALI_IOC_PROFILING_GET_EVENT: + err = profiling_get_event_wrapper(session_data, (_mali_uk_profiling_get_event_s __user *)arg); + break; + + case MALI_IOC_PROFILING_CLEAR: + err = profiling_clear_wrapper(session_data, (_mali_uk_profiling_clear_s __user *)arg); + break; + + case MALI_IOC_PROFILING_GET_CONFIG: + err = profiling_get_config_wrapper(session_data, (_mali_uk_profiling_get_config_s __user *)arg); + break; +#endif + + case MALI_IOC_MEM_INIT: + err = mem_init_wrapper(session_data, (_mali_uk_init_mem_s __user *)arg); + break; + + case MALI_IOC_MEM_TERM: + err = mem_term_wrapper(session_data, (_mali_uk_term_mem_s __user *)arg); + break; + + case MALI_IOC_MEM_MAP_EXT: + err = mem_map_ext_wrapper(session_data, (_mali_uk_map_external_mem_s __user *)arg); + break; + + case MALI_IOC_MEM_UNMAP_EXT: + err = mem_unmap_ext_wrapper(session_data, (_mali_uk_unmap_external_mem_s __user *)arg); + break; + + case MALI_IOC_MEM_QUERY_MMU_PAGE_TABLE_DUMP_SIZE: + err = mem_query_mmu_page_table_dump_size_wrapper(session_data, (_mali_uk_query_mmu_page_table_dump_size_s __user *)arg); + break; + + case MALI_IOC_MEM_DUMP_MMU_PAGE_TABLE: + err = mem_dump_mmu_page_table_wrapper(session_data, (_mali_uk_dump_mmu_page_table_s __user *)arg); + break; + + case MALI_IOC_MEM_GET_BIG_BLOCK: + err = mem_get_big_block_wrapper(filp, (_mali_uk_get_big_block_s __user *)arg); + break; + + case MALI_IOC_MEM_FREE_BIG_BLOCK: + err = mem_free_big_block_wrapper(session_data, (_mali_uk_free_big_block_s __user *)arg); + break; + +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 + + case MALI_IOC_MEM_ATTACH_UMP: + err = mem_attach_ump_wrapper(session_data, (_mali_uk_attach_ump_mem_s __user *)arg); + break; + + case MALI_IOC_MEM_RELEASE_UMP: + err = mem_release_ump_wrapper(session_data, (_mali_uk_release_ump_mem_s __user *)arg); + break; + +#else + + case MALI_IOC_MEM_ATTACH_UMP: + case MALI_IOC_MEM_RELEASE_UMP: /* FALL-THROUGH */ + MALI_DEBUG_PRINT(2, ("UMP not supported\n")); + err = -ENOTTY; + break; +#endif + + case MALI_IOC_PP_START_JOB: + err = pp_start_job_wrapper(session_data, (_mali_uk_pp_start_job_s __user *)arg); + break; + + case MALI_IOC_PP_ABORT_JOB: + err = pp_abort_job_wrapper(session_data, (_mali_uk_pp_abort_job_s __user *)arg); + break; + + case MALI_IOC_PP_NUMBER_OF_CORES_GET: + err = pp_get_number_of_cores_wrapper(session_data, (_mali_uk_get_pp_number_of_cores_s __user *)arg); + break; + + case MALI_IOC_PP_CORE_VERSION_GET: + err = pp_get_core_version_wrapper(session_data, (_mali_uk_get_pp_core_version_s __user *)arg); + break; + + case MALI_IOC_GP2_START_JOB: + err = gp_start_job_wrapper(session_data, (_mali_uk_gp_start_job_s __user *)arg); + break; + + case MALI_IOC_GP2_ABORT_JOB: + err = gp_abort_job_wrapper(session_data, (_mali_uk_gp_abort_job_s __user *)arg); + break; + + case MALI_IOC_GP2_NUMBER_OF_CORES_GET: + err = gp_get_number_of_cores_wrapper(session_data, (_mali_uk_get_gp_number_of_cores_s __user *)arg); + break; + + case MALI_IOC_GP2_CORE_VERSION_GET: + err = gp_get_core_version_wrapper(session_data, (_mali_uk_get_gp_core_version_s __user *)arg); + break; + + case MALI_IOC_GP2_SUSPEND_RESPONSE: + err = gp_suspend_response_wrapper(session_data, (_mali_uk_gp_suspend_response_s __user *)arg); + break; + + case MALI_IOC_VSYNC_EVENT_REPORT: + err = vsync_event_report_wrapper(session_data, (_mali_uk_vsync_event_report_s __user *)arg); + break; + + default: + MALI_DEBUG_PRINT(2, ("No handler for ioctl 0x%08X 0x%08lX\n", cmd, arg)); + err = -ENOTTY; + }; + + return err; +} + + +module_init(mali_driver_init); +module_exit(mali_driver_exit); + +MODULE_LICENSE(MALI_KERNEL_LINUX_LICENSE); +MODULE_AUTHOR("ARM Ltd."); +MODULE_VERSION(SVN_REV_STRING); diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_linux.h b/drivers/gpu/arm/mali/linux/mali_kernel_linux.h new file mode 100644 index 000000000000..b63baa90b7f4 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_kernel_linux.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_LINUX_H__ +#define __MALI_KERNEL_LINUX_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <linux/cdev.h> /* character device definitions */ +#include "mali_kernel_license.h" +#include "mali_osk.h" + +struct mali_dev +{ + struct cdev cdev; +#if MALI_LICENSE_IS_GPL + struct class * mali_class; +#endif +}; + +#if MALI_LICENSE_IS_GPL +/* Defined in mali_osk_irq.h */ +extern struct workqueue_struct * mali_wq; +#endif + +_mali_osk_errcode_t initialize_kernel_device(void); +void terminate_kernel_device(void); + +void mali_osk_low_level_mem_init(void); +void mali_osk_low_level_mem_term(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_KERNEL_LINUX_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_pm.c b/drivers/gpu/arm/mali/linux/mali_kernel_pm.c new file mode 100644 index 000000000000..ea92368ccae7 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_kernel_pm.c @@ -0,0 +1,645 @@ +/** + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_kernel_pm.c + * Implementation of the Linux Power Management for Mali GPU kernel driver + */ + +#include <linux/module.h> +#if USING_MALI_PMM +#include <linux/sched.h> + +#ifdef CONFIG_PM_RUNTIME +#include <linux/pm_runtime.h> +#endif /* CONFIG_PM_RUNTIME */ + +#include <linux/platform_device.h> +#include <linux/version.h> +#include <asm/current.h> +#include <linux/suspend.h> + +#include "mali_platform.h" +#include "mali_osk.h" +#include "mali_uk_types.h" +#include "mali_pmm.h" +#include "mali_ukk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_license.h" +#include "mali_kernel_pm.h" +#include "mali_device_pause_resume.h" +#include "mali_linux_pm.h" + +#if MALI_GPU_UTILIZATION +#include "mali_kernel_utilization.h" +#endif /* MALI_GPU_UTILIZATION */ + +#if MALI_POWER_MGMT_TEST_SUITE +#ifdef CONFIG_PM +#include "mali_linux_pm_testsuite.h" +#include "mali_platform_pmu_internal_testing.h" +unsigned int pwr_mgmt_status_reg = 0; +#endif /* CONFIG_PM */ +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + +static int is_os_pmm_thread_waiting = 0; + +/* kernel should be configured with power management support */ +#ifdef CONFIG_PM + +#if MALI_LICENSE_IS_GPL + +/* Linux kernel major version */ +#define LINUX_KERNEL_MAJOR_VERSION 2 + +/* Linux kernel minor version */ +#define LINUX_KERNEL_MINOR_VERSION 6 + +/* Linux kernel development version */ +#define LINUX_KERNEL_DEVELOPMENT_VERSION 29 + +#ifdef CONFIG_PM_DEBUG +static const char* const mali_states[_MALI_MAX_DEBUG_OPERATIONS] = { + [_MALI_DEVICE_SUSPEND] = "suspend", + [_MALI_DEVICE_RESUME] = "resume", + [_MALI_DVFS_PAUSE_EVENT] = "dvfs_pause", + [_MALI_DVFS_RESUME_EVENT] = "dvfs_resume", +}; + +#endif /* CONFIG_PM_DEBUG */ + +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON +static int mali_pwr_suspend_notifier(struct notifier_block *nb,unsigned long event,void* dummy); + +static struct notifier_block mali_pwr_notif_block = { + .notifier_call = mali_pwr_suspend_notifier +}; +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + +/* Power management thread pointer */ +struct task_struct *pm_thread; + +/* dvfs power management thread */ +struct task_struct *dvfs_pm_thread; + +/* is wake up needed */ +short is_wake_up_needed = 0; +int timeout_fired = 2; +unsigned int is_mali_pmm_testsuite_enabled = 0; + +_mali_device_power_states mali_device_state = _MALI_DEVICE_RESUME; +_mali_device_power_states mali_dvfs_device_state = _MALI_DEVICE_RESUME; +_mali_osk_lock_t *lock; + +#if MALI_POWER_MGMT_TEST_SUITE + +const char* const mali_pmm_recording_events[_MALI_DEVICE_MAX_PMM_EVENTS] = { + [_MALI_DEVICE_PMM_TIMEOUT_EVENT] = "timeout", + [_MALI_DEVICE_PMM_JOB_SCHEDULING_EVENTS] = "job_scheduling", + [_MALI_DEVICE_PMM_REGISTERED_CORES] = "cores", + +}; + +unsigned int mali_timeout_event_recording_on = 0; +unsigned int mali_job_scheduling_events_recording_on = 0; +unsigned int is_mali_pmu_present = 0; +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + +/* Function prototypes */ +static int mali_pm_probe(struct platform_device *pdev); +static int mali_pm_remove(struct platform_device *pdev); + +/* Mali device suspend function */ +static int mali_pm_suspend(struct device *dev); + +/* Mali device resume function */ +static int mali_pm_resume(struct device *dev); + +/* Run time suspend and resume functions */ +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON +static int mali_device_runtime_suspend(struct device *dev); +static int mali_device_runtime_resume(struct device *dev); +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + +/* OS suspend and resume callbacks */ +#if !MALI_PMM_RUNTIME_JOB_CONTROL_ON +#ifndef CONFIG_PM_RUNTIME +#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION)) +static int mali_pm_os_suspend(struct platform_device *pdev, pm_message_t state); +#else +static int mali_pm_os_suspend(struct device *dev); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION)) +static int mali_pm_os_resume(struct platform_device *pdev); +#else +static int mali_pm_os_resume(struct device *dev); +#endif +#endif /* CONFIG_PM_RUNTIME */ +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ + +/* OS Hibernation suspend callback */ +static int mali_pm_os_suspend_on_hibernation(struct device *dev); + +/* OS Hibernation resume callback */ +static int mali_pm_os_resume_on_hibernation(struct device *dev); + +static void _mali_release_pm(struct device* device); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION)) +static const struct dev_pm_ops mali_dev_pm_ops = { + +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + .runtime_suspend = mali_device_runtime_suspend, + .runtime_resume = mali_device_runtime_resume, +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + +#ifndef CONFIG_PM_RUNTIME +#if !MALI_PMM_RUNTIME_JOB_CONTROL_ON + .suspend = mali_pm_os_suspend, + .resume = mali_pm_os_resume, +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + .freeze = mali_pm_os_suspend_on_hibernation, + .poweroff = mali_pm_os_suspend_on_hibernation, + .thaw = mali_pm_os_resume_on_hibernation, + .restore = mali_pm_os_resume_on_hibernation, +}; +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION)) +struct pm_ext_ops mali_pm_operations = { + .base = { + .freeze = mali_pm_os_suspend_on_hibernation, + .thaw = mali_pm_os_resume_on_hibernation, + .poweroff = mali_pm_os_resume_on_hibernation, + .restore = mali_pm_os_resume_on_hibernation, + }, +}; +#endif + +static struct platform_driver mali_plat_driver = { + .probe = mali_pm_probe, + .remove = mali_pm_remove, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION)) +#ifndef CONFIG_PM_RUNTIME +#if !MALI_PMM_RUNTIME_JOB_CONTROL_ON + .suspend = mali_pm_os_suspend, + .resume = mali_pm_os_resume, +#endif /* CONFIG_PM_RUNTIME */ +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ + .pm = &mali_pm_operations, +#endif + + .driver = { + .name = "mali_dev", + .owner = THIS_MODULE, + .bus = &platform_bus_type, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION)) + .pm = &mali_dev_pm_ops, +#endif + }, +}; + +/* Mali GPU platform device */ +struct platform_device mali_gpu_device = { + .name = "mali_dev", + .id = 0, + .dev.release = _mali_release_pm +}; + +/** This function is called when platform device is unregistered. This function + * is necessary when the platform device is unregistered. + */ +static void _mali_release_pm(struct device *device) +{ + MALI_DEBUG_PRINT(4, ("OSPMM: MALI Platform device removed\n" )); +} + +#if MALI_POWER_MGMT_TEST_SUITE +void mali_is_pmu_present(void) +{ + int temp = 0; + temp = pmu_get_power_up_down_info(); + if (4095 == temp) + { + is_mali_pmu_present = 0; + } + else + { + is_mali_pmu_present = 1; + } +} +#endif /* MALI_POWER_MGMT_TEST_SUITE */ +#endif /* MALI_LICENSE_IS_GPL */ + +#if MALI_LICENSE_IS_GPL + +static int mali_wait_for_power_management_policy_event(void) +{ + int err = 0; + for (; ;) + { + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) + { + err = -EINTR; + break; + } + if (is_wake_up_needed == 1) + { + break; + } + schedule(); + } + __set_current_state(TASK_RUNNING); + is_wake_up_needed =0; + return err; +} + +/** This function is invoked when mali device is suspended + */ +int mali_device_suspend(unsigned int event_id, struct task_struct **pwr_mgmt_thread) +{ + int err = 0; + _mali_uk_pmm_message_s event = { + NULL, + event_id, + timeout_fired}; + *pwr_mgmt_thread = current; + MALI_DEBUG_PRINT(4, ("OSPMM: MALI device is being suspended\n" )); + _mali_ukk_pmm_event_message(&event); + is_os_pmm_thread_waiting = 1; + err = mali_wait_for_power_management_policy_event(); + is_os_pmm_thread_waiting = 0; + return err; +} + +/** This function is called when Operating system wants to power down + * the mali GPU device. + */ +static int mali_pm_suspend(struct device *dev) +{ + int err = 0; + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); +#if MALI_GPU_UTILIZATION + mali_utilization_suspend(); +#endif /* MALI_GPU_UTILIZATION */ + if ((mali_device_state == _MALI_DEVICE_SUSPEND)) + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return err; + } + err = mali_device_suspend(MALI_PMM_EVENT_OS_POWER_DOWN, &pm_thread); + mali_device_state = _MALI_DEVICE_SUSPEND; + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return err; +} + +#ifndef CONFIG_PM_RUNTIME +#if !MALI_PMM_RUNTIME_JOB_CONTROL_ON +#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION)) +static int mali_pm_os_suspend(struct platform_device *pdev, pm_message_t state) +#else +static int mali_pm_os_suspend(struct device *dev) +#endif +{ + int err = 0; + err = mali_pm_suspend(NULL); + return err; +} +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON +static int mali_pwr_suspend_notifier(struct notifier_block *nb,unsigned long event,void* dummy) +{ + int err = 0; + switch (event) + { + case PM_SUSPEND_PREPARE: + err = mali_pm_suspend(NULL); + break; + + case PM_POST_SUSPEND: + err = mali_pm_resume(NULL); + break; + default: + break; + } + return 0; +} +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + +/** This function is called when mali GPU device is to be resumed. + */ +int mali_device_resume(unsigned int event_id, struct task_struct **pwr_mgmt_thread) +{ + int err = 0; + _mali_uk_pmm_message_s event = { + NULL, + event_id, + timeout_fired}; + *pwr_mgmt_thread = current; + MALI_DEBUG_PRINT(4, ("OSPMM: MALI device is being resumed\n" )); + _mali_ukk_pmm_event_message(&event); + MALI_DEBUG_PRINT(4, ("OSPMM: MALI Power up event is scheduled\n" )); + is_os_pmm_thread_waiting = 1; + err = mali_wait_for_power_management_policy_event(); + is_os_pmm_thread_waiting = 0; + return err; +} + +/** This function is called when mali GPU device is to be resumed + */ + +static int mali_pm_resume(struct device *dev) +{ + int err = 0; + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + if (mali_device_state == _MALI_DEVICE_RESUME) + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return err; + } + err = mali_device_resume(MALI_PMM_EVENT_OS_POWER_UP, &pm_thread); + mali_device_state = _MALI_DEVICE_RESUME; + mali_dvfs_device_state = _MALI_DEVICE_RESUME; + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return err; +} + +#ifndef CONFIG_PM_RUNTIME +#if !MALI_PMM_RUNTIME_JOB_CONTROL_ON +#if (LINUX_VERSION_CODE < KERNEL_VERSION(LINUX_KERNEL_MAJOR_VERSION,LINUX_KERNEL_MINOR_VERSION,LINUX_KERNEL_DEVELOPMENT_VERSION)) +static int mali_pm_os_resume(struct platform_device *pdev) +#else +static int mali_pm_os_resume(struct device *dev) +#endif +{ + int err = 0; + err = mali_pm_resume(NULL); + return err; +} +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + +static int mali_pm_os_suspend_on_hibernation(struct device *dev) +{ + int err = 0; + err = mali_pm_suspend(NULL); + return err; +} + +static int mali_pm_os_resume_on_hibernation(struct device *dev) +{ + int err = 0; + err = mali_pm_resume(NULL); + return err; +} + +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON +/** This function is called when runtime suspend of mali device is required. + */ +static int mali_device_runtime_suspend(struct device *dev) +{ + MALI_DEBUG_PRINT(4, ("PMMDEBUG: Mali device Run time suspended \n" )); + return 0; +} + +/** This function is called when runtime resume of mali device is required. + */ +static int mali_device_runtime_resume(struct device *dev) +{ + MALI_DEBUG_PRINT(4, ("PMMDEBUG: Mali device Run time Resumed \n" )); + return 0; +} +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + +#ifdef CONFIG_PM_DEBUG + +/** This function is used for debugging purposes when the user want to see + * which power management operations are supported for + * mali device. + */ +static ssize_t show_file(struct device *dev, struct device_attribute *attr, char *buf) +{ + char *str = buf; +#if !MALI_POWER_MGMT_TEST_SUITE + int pm_counter = 0; + for (pm_counter = 0; pm_counter<_MALI_MAX_DEBUG_OPERATIONS; pm_counter++) + { + str += sprintf(str, "%s ", mali_states[pm_counter]); + } +#else + str += sprintf(str, "%d ",pwr_mgmt_status_reg); +#endif + if (str != buf) + { + *(str-1) = '\n'; + } + return (str-buf); +} + +/** This function is called when user wants to suspend the mali GPU device in order + * to simulate the power up and power down events. + */ +static ssize_t store_file(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int err = 0; + +#if MALI_POWER_MGMT_TEST_SUITE + int test_flag_dvfs = 0; + pwr_mgmt_status_reg = 0; + mali_is_pmu_present(); + +#endif + if (!strncmp(buf,mali_states[_MALI_DEVICE_SUSPEND],strlen(mali_states[_MALI_DEVICE_SUSPEND]))) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI suspend Power operation is scheduled\n" )); + err = mali_pm_suspend(NULL); + } + +#if MALI_POWER_MGMT_TEST_SUITE + else if (!strncmp(buf,mali_pmm_recording_events[_MALI_DEVICE_PMM_REGISTERED_CORES],strlen(mali_pmm_recording_events[_MALI_DEVICE_PMM_REGISTERED_CORES]))) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI Device get number of registerd cores\n" )); + pwr_mgmt_status_reg = _mali_pmm_cores_list(); + return count; + } + else if (!strncmp(buf,mali_pmm_recording_events[_MALI_DEVICE_PMM_TIMEOUT_EVENT],strlen(mali_pmm_recording_events[_MALI_DEVICE_PMM_TIMEOUT_EVENT]))) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI timeout event recording is enabled\n" )); + mali_timeout_event_recording_on = 1; + } + else if (!strncmp(buf,mali_pmm_recording_events[_MALI_DEVICE_PMM_JOB_SCHEDULING_EVENTS],strlen(mali_pmm_recording_events[_MALI_DEVICE_PMM_JOB_SCHEDULING_EVENTS]))) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI Job scheduling events recording is enabled\n" )); + mali_job_scheduling_events_recording_on = 1; + } +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + + else if (!strncmp(buf,mali_states[_MALI_DEVICE_RESUME],strlen(mali_states[_MALI_DEVICE_RESUME]))) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI Resume Power operation is scheduled\n" )); + err = mali_pm_resume(NULL); + } + else if (!strncmp(buf,mali_states[_MALI_DVFS_PAUSE_EVENT],strlen(mali_states[_MALI_DVFS_PAUSE_EVENT]))) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI DVFS Pause Power operation is scheduled\n" )); + err = mali_dev_pause(); +#if MALI_POWER_MGMT_TEST_SUITE + test_flag_dvfs = 1; +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + } + else if (!strncmp(buf,mali_states[_MALI_DVFS_RESUME_EVENT],strlen(mali_states[_MALI_DVFS_RESUME_EVENT]))) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: MALI DVFS Resume Power operation is scheduled\n" )); + err = mali_dev_resume(); +#if MALI_POWER_MGMT_TEST_SUITE + test_flag_dvfs = 1; +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + } + else + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: Invalid Power Mode Operation selected\n" )); + } +#if MALI_POWER_MGMT_TEST_SUITE + if (test_flag_dvfs == 1) + { + if (err) + { + pwr_mgmt_status_reg = 2; + } + else + { + pwr_mgmt_status_reg = 1; + } + } + else + { + if (1 == is_mali_pmu_present) + { + pwr_mgmt_status_reg = pmu_get_power_up_down_info(); + } + } +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + return count; +} + +/* Device attribute file */ +static DEVICE_ATTR(file, 0644, show_file, store_file); +#endif /* CONFIG_PM_DEBUG */ + +static int mali_pm_remove(struct platform_device *pdev) +{ +#ifdef CONFIG_PM_DEBUG + device_remove_file(&mali_gpu_device.dev, &dev_attr_file); +#endif /* CONFIG_PM_DEBUG */ +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + pm_runtime_disable(&pdev->dev); +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + return 0; +} + +/** This function is called when the device is probed */ +static int mali_pm_probe(struct platform_device *pdev) +{ +#ifdef CONFIG_PM_DEBUG + int err; + err = device_create_file(&mali_gpu_device.dev, &dev_attr_file); + if (err) + { + MALI_DEBUG_PRINT(4, ("PMMDEBUG: Error in creating device file\n" )); + } +#endif /* CONFIG_PM_DEBUG */ + return 0; +} + +/** This function is called when Mali GPU device is initialized + */ +int _mali_dev_platform_register(void) +{ + int err; +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + set_mali_parent_power_domain((void *)&mali_gpu_device); +#endif + +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + err = register_pm_notifier(&mali_pwr_notif_block); + if (err) + { + return err; + } +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + err = platform_device_register(&mali_gpu_device); + lock = _mali_osk_lock_init((_mali_osk_lock_flags_t)( _MALI_OSK_LOCKFLAG_READERWRITER | _MALI_OSK_LOCKFLAG_ORDERED), 0, 0); + if (!err) + { + err = platform_driver_register(&mali_plat_driver); + if (err) + { + _mali_osk_lock_term(lock); +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + unregister_pm_notifier(&mali_pwr_notif_block); +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + platform_device_unregister(&mali_gpu_device); + } + } + return err; +} + +/** This function is called when Mali GPU device is unloaded + */ +void _mali_dev_platform_unregister(void) +{ + _mali_osk_lock_term(lock); + +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + unregister_pm_notifier(&mali_pwr_notif_block); +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ + + platform_driver_unregister(&mali_plat_driver); + platform_device_unregister(&mali_gpu_device); +} + +int mali_get_ospmm_thread_state(void) +{ + return is_os_pmm_thread_waiting; +} + +#endif /* MALI_LICENSE_IS_GPL */ +#endif /* CONFIG_PM */ + +#if MALI_STATE_TRACKING +u32 mali_pmm_dump_os_thread_state( char *buf, u32 size ) +{ + return snprintf(buf, size, "OSPMM: OS PMM thread is waiting: %s\n", is_os_pmm_thread_waiting ? "true" : "false"); +} +#endif /* MALI_STATE_TRACKING */ +#endif /* USING_MALI_PMM */ diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_pm.h b/drivers/gpu/arm/mali/linux/mali_kernel_pm.h new file mode 100644 index 000000000000..7ee1572a534f --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_kernel_pm.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_PM_H__ +#define __MALI_KERNEL_PM_H__ + +#ifdef USING_MALI_PMM +int _mali_dev_platform_register(void); +void _mali_dev_platform_unregister(void); +#endif /* USING_MALI_PMM */ + +#endif /* __MALI_KERNEL_PM_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_sysfs.c b/drivers/gpu/arm/mali/linux/mali_kernel_sysfs.c new file mode 100644 index 000000000000..2a61a87e6f3f --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_kernel_sysfs.c @@ -0,0 +1,402 @@ +/** + * Copyright (C) 2011-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +/** + * @file mali_kernel_sysfs.c + * Implementation of some sysfs data exports + */ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/device.h> +#include "mali_kernel_license.h" +#include "mali_kernel_linux.h" +#include "mali_ukk.h" + +#if MALI_LICENSE_IS_GPL + +#include <linux/seq_file.h> +#include <linux/debugfs.h> +#include <asm/uaccess.h> +#include <linux/slab.h> +#include "mali_kernel_subsystem.h" +#include "mali_kernel_sysfs.h" +#include "mali_osk_profiling.h" + +static struct dentry *mali_debugfs_dir = NULL; + +#if MALI_STATE_TRACKING +static int mali_seq_internal_state_show(struct seq_file *seq_file, void *v) +{ + u32 len = 0; + u32 size; + char *buf; + + size = seq_get_buf(seq_file, &buf); + + if(!size) + { + return -ENOMEM; + } + + /* Create the internal state dump. */ + len = snprintf(buf+len, size-len, "Mali device driver %s\n", SVN_REV_STRING); + len += snprintf(buf+len, size-len, "License: %s\n\n", MALI_KERNEL_LINUX_LICENSE); + + len += _mali_kernel_core_dump_state(buf + len, size - len); + + seq_commit(seq_file, len); + + return 0; +} + +static int mali_seq_internal_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, mali_seq_internal_state_show, NULL); +} + +static const struct file_operations mali_seq_internal_state_fops = { + .owner = THIS_MODULE, + .open = mali_seq_internal_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif /* MALI_STATE_TRACKING */ + + +#if MALI_INTERNAL_TIMELINE_PROFILING_ENABLED +static ssize_t profiling_record_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[64]; + int r; + + r = sprintf(buf, "%u\n", _mali_osk_profiling_is_recording() ? 1 : 0); + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static ssize_t profiling_record_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[64]; + unsigned long val; + int ret; + + if (cnt >= sizeof(buf)) + { + return -EINVAL; + } + + if (copy_from_user(&buf, ubuf, cnt)) + { + return -EFAULT; + } + + buf[cnt] = 0; + + ret = strict_strtoul(buf, 10, &val); + if (ret < 0) + { + return ret; + } + + if (val != 0) + { + u32 limit = MALI_PROFILING_MAX_BUFFER_ENTRIES; /* This can be made configurable at a later stage if we need to */ + + /* check if we are already recording */ + if (MALI_TRUE == _mali_osk_profiling_is_recording()) + { + MALI_DEBUG_PRINT(3, ("Recording of profiling events already in progress\n")); + return -EFAULT; + } + + /* check if we need to clear out an old recording first */ + if (MALI_TRUE == _mali_osk_profiling_have_recording()) + { + if (_MALI_OSK_ERR_OK != _mali_osk_profiling_clear()) + { + MALI_DEBUG_PRINT(3, ("Failed to clear existing recording of profiling events\n")); + return -EFAULT; + } + } + + /* start recording profiling data */ + if (_MALI_OSK_ERR_OK != _mali_osk_profiling_start(&limit)) + { + MALI_DEBUG_PRINT(3, ("Failed to start recording of profiling events\n")); + return -EFAULT; + } + + MALI_DEBUG_PRINT(3, ("Profiling recording started (max %u events)\n", limit)); + } + else + { + /* stop recording profiling data */ + u32 count = 0; + if (_MALI_OSK_ERR_OK != _mali_osk_profiling_stop(&count)) + { + MALI_DEBUG_PRINT(2, ("Failed to stop recording of profiling events\n")); + return -EFAULT; + } + + MALI_DEBUG_PRINT(2, ("Profiling recording stopped (recorded %u events)\n", count)); + } + + *ppos += cnt; + return cnt; +} + +static const struct file_operations profiling_record_fops = { + .owner = THIS_MODULE, + .read = profiling_record_read, + .write = profiling_record_write, +}; + +static void *profiling_events_start(struct seq_file *s, loff_t *pos) +{ + loff_t *spos; + + /* check if we have data avaiable */ + if (MALI_TRUE != _mali_osk_profiling_have_recording()) + { + return NULL; + } + + spos = kmalloc(sizeof(loff_t), GFP_KERNEL); + if (NULL == spos) + { + return NULL; + } + + *spos = *pos; + return spos; +} + +static void *profiling_events_next(struct seq_file *s, void *v, loff_t *pos) +{ + loff_t *spos = v; + + /* check if we have data avaiable */ + if (MALI_TRUE != _mali_osk_profiling_have_recording()) + { + return NULL; + } + + /* check if the next entry actually is avaiable */ + if (_mali_osk_profiling_get_count() <= (u32)(*spos + 1)) + { + return NULL; + } + + *pos = ++*spos; + return spos; +} + +static void profiling_events_stop(struct seq_file *s, void *v) +{ + kfree(v); +} + +static int profiling_events_show(struct seq_file *seq_file, void *v) +{ + loff_t *spos = v; + u32 index; + u64 timestamp; + u32 event_id; + u32 data[5]; + + index = (u32)*spos; + + /* Retrieve all events */ + if (_MALI_OSK_ERR_OK == _mali_osk_profiling_get_event(index, ×tamp, &event_id, data)) + { + seq_printf(seq_file, "%llu %u %u %u %u %u %u\n", timestamp, event_id, data[0], data[1], data[2], data[3], data[4]); + return 0; + } + + return 0; +} + +static const struct seq_operations profiling_events_seq_ops = { + .start = profiling_events_start, + .next = profiling_events_next, + .stop = profiling_events_stop, + .show = profiling_events_show +}; + +static int profiling_events_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &profiling_events_seq_ops); +} + +static const struct file_operations profiling_events_fops = { + .owner = THIS_MODULE, + .open = profiling_events_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static ssize_t profiling_proc_default_enable_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[64]; + int r; + + r = sprintf(buf, "%u\n", _mali_osk_profiling_get_default_enable_state() ? 1 : 0); + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static ssize_t profiling_proc_default_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[64]; + unsigned long val; + int ret; + + if (cnt >= sizeof(buf)) + { + return -EINVAL; + } + + if (copy_from_user(&buf, ubuf, cnt)) + { + return -EFAULT; + } + + buf[cnt] = 0; + + ret = strict_strtoul(buf, 10, &val); + if (ret < 0) + { + return ret; + } + + _mali_osk_profiling_set_default_enable_state(val != 0 ? MALI_TRUE : MALI_FALSE); + + *ppos += cnt; + return cnt; +} + +static const struct file_operations profiling_proc_default_enable_fops = { + .owner = THIS_MODULE, + .read = profiling_proc_default_enable_read, + .write = profiling_proc_default_enable_write, +}; +#endif + +static ssize_t memory_used_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[64]; + size_t r; + u32 mem = _mali_ukk_report_memory_usage(); + + r = snprintf(buf, 64, "%u\n", mem); + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static const struct file_operations memory_usage_fops = { + .owner = THIS_MODULE, + .read = memory_used_read, +}; + +int mali_sysfs_register(struct mali_dev *device, dev_t dev, const char *mali_dev_name) +{ + int err = 0; + struct device * mdev; + + device->mali_class = class_create(THIS_MODULE, mali_dev_name); + if (IS_ERR(device->mali_class)) + { + err = PTR_ERR(device->mali_class); + goto init_class_err; + } + mdev = device_create(device->mali_class, NULL, dev, NULL, mali_dev_name); + if (IS_ERR(mdev)) + { + err = PTR_ERR(mdev); + goto init_mdev_err; + } + + mali_debugfs_dir = debugfs_create_dir(mali_dev_name, NULL); + if(ERR_PTR(-ENODEV) == mali_debugfs_dir) + { + /* Debugfs not supported. */ + mali_debugfs_dir = NULL; + } + else + { + if(NULL != mali_debugfs_dir) + { + /* Debugfs directory created successfully; create files now */ +#if MALI_INTERNAL_TIMELINE_PROFILING_ENABLED + struct dentry *mali_profiling_dir = debugfs_create_dir("profiling", mali_debugfs_dir); + if (mali_profiling_dir != NULL) + { + struct dentry *mali_profiling_proc_dir = debugfs_create_dir("proc", mali_profiling_dir); + if (mali_profiling_proc_dir != NULL) + { + struct dentry *mali_profiling_proc_default_dir = debugfs_create_dir("default", mali_profiling_proc_dir); + if (mali_profiling_proc_default_dir != NULL) + { + debugfs_create_file("enable", 0600, mali_profiling_proc_default_dir, NULL, &profiling_proc_default_enable_fops); + } + } + debugfs_create_file("record", 0600, mali_profiling_dir, NULL, &profiling_record_fops); + debugfs_create_file("events", 0400, mali_profiling_dir, NULL, &profiling_events_fops); + } +#endif + +#if MALI_STATE_TRACKING + debugfs_create_file("state_dump", 0400, mali_debugfs_dir, NULL, &mali_seq_internal_state_fops); +#endif + + debugfs_create_file("memory_usage", 0400, mali_debugfs_dir, NULL, &memory_usage_fops); + } + } + + /* Success! */ + return 0; + + /* Error handling */ +init_mdev_err: + class_destroy(device->mali_class); +init_class_err: + + return err; +} + +int mali_sysfs_unregister(struct mali_dev *device, dev_t dev, const char *mali_dev_name) +{ + if(NULL != mali_debugfs_dir) + { + debugfs_remove_recursive(mali_debugfs_dir); + } + device_destroy(device->mali_class, dev); + class_destroy(device->mali_class); + + return 0; +} + +#else + +/* Dummy implementations for when the sysfs API isn't available. */ + +int mali_sysfs_register(struct mali_dev *device, dev_t dev, const char *mali_dev_name) +{ + return 0; +} + +int mali_sysfs_unregister(struct mali_dev *device, dev_t dev, const char *mali_dev_name) +{ + return 0; +} + + +#endif diff --git a/drivers/gpu/arm/mali/linux/mali_kernel_sysfs.h b/drivers/gpu/arm/mali/linux/mali_kernel_sysfs.h new file mode 100644 index 000000000000..26a60745ffc0 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_kernel_sysfs.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_KERNEL_SYSFS_H__ +#define __MALI_KERNEL_SYSFS_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define MALI_PROC_DIR "driver/mali" + +int mali_sysfs_register(struct mali_dev *mali_class, dev_t dev, const char *mali_dev_name); + +int mali_sysfs_unregister(struct mali_dev *mali_class, dev_t dev, const char *mali_dev_name); + + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_KERNEL_LINUX_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_linux_pm.h b/drivers/gpu/arm/mali/linux/mali_linux_pm.h new file mode 100644 index 000000000000..ad69853333e8 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_linux_pm.h @@ -0,0 +1,53 @@ + +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_LINUX_PM_H__ +#define __MALI_LINUX_PM_H__ + +#if USING_MALI_PMM + +#ifdef CONFIG_PM +/* Number of power states supported for making power up and down */ +typedef enum +{ + _MALI_DEVICE_SUSPEND, /* Suspend */ + _MALI_DEVICE_RESUME, /* Resume */ + _MALI_DEVICE_MAX_POWER_STATES, /* Maximum power states */ +} _mali_device_power_states; + +/* Number of DVFS events */ +typedef enum +{ + _MALI_DVFS_PAUSE_EVENT = _MALI_DEVICE_MAX_POWER_STATES, /* DVFS Pause event */ + _MALI_DVFS_RESUME_EVENT, /* DVFS Resume event */ + _MALI_MAX_DEBUG_OPERATIONS, +} _mali_device_dvfs_events; + +extern _mali_device_power_states mali_device_state; +extern _mali_device_power_states mali_dvfs_device_state; +extern _mali_osk_lock_t *lock; +extern short is_wake_up_needed; +extern int timeout_fired; +extern struct platform_device mali_gpu_device; + +/* dvfs pm thread */ +extern struct task_struct *dvfs_pm_thread; + +/* Power management thread */ +extern struct task_struct *pm_thread; + +int mali_device_suspend(u32 event_id, struct task_struct **pwr_mgmt_thread); +int mali_device_resume(u32 event_id, struct task_struct **pwr_mgmt_thread); +int mali_get_ospmm_thread_state(void); + +#endif /* CONFIG_PM */ +#endif /* USING_MALI_PMM */ +#endif /* __MALI_LINUX_PM_H___ */ diff --git a/drivers/gpu/arm/mali/linux/mali_linux_pm_testsuite.h b/drivers/gpu/arm/mali/linux/mali_linux_pm_testsuite.h new file mode 100644 index 000000000000..e88ce94281fa --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_linux_pm_testsuite.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef __MALI_LINUX_PM_TESTSUITE_H__ +#define __MALI_LINUX_PM_TESTSUITE_H__ + +#if USING_MALI_PMM +#if MALI_POWER_MGMT_TEST_SUITE +#ifdef CONFIG_PM + +typedef enum +{ + _MALI_DEVICE_PMM_TIMEOUT_EVENT, + _MALI_DEVICE_PMM_JOB_SCHEDULING_EVENTS, + _MALI_DEVICE_PMM_REGISTERED_CORES, + _MALI_DEVICE_MAX_PMM_EVENTS + +} _mali_device_pmm_recording_events; + +extern unsigned int mali_timeout_event_recording_on; +extern unsigned int mali_job_scheduling_events_recording_on; +extern unsigned int pwr_mgmt_status_reg; +extern unsigned int is_mali_pmm_testsuite_enabled; +extern unsigned int is_mali_pmu_present; + +#endif /* CONFIG_PM */ +#endif /* MALI_POWER_MGMT_TEST_SUITE */ +#endif /* USING_MALI_PMM */ +#endif /* __MALI_LINUX_PM_TESTSUITE_H__ */ + + diff --git a/drivers/gpu/arm/mali/linux/mali_linux_trace.h b/drivers/gpu/arm/mali/linux/mali_linux_trace.h new file mode 100644 index 000000000000..045e6da3d1a5 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_linux_trace.h @@ -0,0 +1,124 @@ +/** + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms of + * such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained + * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#if !defined (MALI_LINUX_TRACE_H) || defined (TRACE_HEADER_MULTI_READ) +#define MALI_LINUX_TRACE_H + +#include <linux/stringify.h> +#include <linux/tracepoint.h> + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mali +#define TRACE_SYSTEM_STRING __stringfy(TRACE_SYSTEM) + +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE mali_linux_trace + +/** + * Define the tracepoint used to communicate the status of a GPU. Called + * when a GPU turns on or turns off. + * + * @param event_id The type of the event. This parameter is a bitfield + * encoding the type of the event. + * + * @param d0 First data parameter. + * @param d1 Second data parameter. + * @param d2 Third data parameter. + * @param d3 Fourth data parameter. + * @param d4 Fifth data parameter. + */ +TRACE_EVENT(mali_timeline_event, + + TP_PROTO(unsigned int event_id, unsigned int d0, unsigned int d1, + unsigned int d2, unsigned int d3, unsigned int d4), + + TP_ARGS(event_id, d0, d1, d2, d3, d4), + + TP_STRUCT__entry( + __field(unsigned int, event_id) + __field(unsigned int, d0) + __field(unsigned int, d1) + __field(unsigned int, d2) + __field(unsigned int, d3) + __field(unsigned int, d4) + ), + + TP_fast_assign( + __entry->event_id = event_id; + __entry->d0 = d0; + __entry->d1 = d1; + __entry->d2 = d2; + __entry->d3 = d3; + __entry->d4 = d4; + ), + + TP_printk("event=%d", __entry->event_id) +); + +/** + * Define a tracepoint used to regsiter the value of a hardware counter. + * Hardware counters belonging to the vertex or fragment processor are + * reported via this tracepoint each frame, whilst L2 cache hardware + * counters are reported continuously. + * + * @param counter_id The counter ID. + * @param value The value of the counter. + */ +TRACE_EVENT(mali_hw_counter, + + TP_PROTO(unsigned int counter_id, unsigned int value), + + TP_ARGS(counter_id, value), + + TP_STRUCT__entry( + __field(unsigned int, counter_id) + __field(unsigned int, value) + ), + + TP_fast_assign( + __entry->counter_id = counter_id; + __entry->value = value; + ), + + TP_printk("event %d = %d", __entry->counter_id, __entry->value) +); + +/** + * Define a tracepoint used to register the value of a software counter. + * + * @param counter_id The counter ID. + * @param value The value of the counter. + */ +TRACE_EVENT(mali_sw_counter, + + TP_PROTO(unsigned int counter_id, signed long long value), + + TP_ARGS(counter_id, value), + + TP_STRUCT__entry( + __field(unsigned int, counter_id) + __field(signed long long, value) + ), + + TP_fast_assign( + __entry->counter_id = counter_id; + __entry->value = value; + ), + + TP_printk("event %d = %lld", __entry->counter_id, __entry->value) +); + +#endif /* MALI_LINUX_TRACE_H */ + +/* This part must exist outside the header guard. */ +#include <trace/define_trace.h> diff --git a/drivers/gpu/arm/mali/linux/mali_osk_atomics.c b/drivers/gpu/arm/mali/linux/mali_osk_atomics.c new file mode 100644 index 000000000000..32f8e6bdac7c --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_atomics.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_atomics.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include "mali_osk.h" +#include <asm/atomic.h> +#include "mali_kernel_common.h" + +void _mali_osk_atomic_dec( _mali_osk_atomic_t *atom ) +{ + atomic_dec((atomic_t *)&atom->u.val); +} + +u32 _mali_osk_atomic_dec_return( _mali_osk_atomic_t *atom ) +{ + return atomic_dec_return((atomic_t *)&atom->u.val); +} + +void _mali_osk_atomic_inc( _mali_osk_atomic_t *atom ) +{ + atomic_inc((atomic_t *)&atom->u.val); +} + +u32 _mali_osk_atomic_inc_return( _mali_osk_atomic_t *atom ) +{ + return atomic_inc_return((atomic_t *)&atom->u.val); +} + +_mali_osk_errcode_t _mali_osk_atomic_init( _mali_osk_atomic_t *atom, u32 val ) +{ + MALI_CHECK_NON_NULL(atom, _MALI_OSK_ERR_INVALID_ARGS); + atomic_set((atomic_t *)&atom->u.val, val); + return _MALI_OSK_ERR_OK; +} + +u32 _mali_osk_atomic_read( _mali_osk_atomic_t *atom ) +{ + return atomic_read((atomic_t *)&atom->u.val); +} + +void _mali_osk_atomic_term( _mali_osk_atomic_t *atom ) +{ + MALI_IGNORE(atom); +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_indir_mmap.c b/drivers/gpu/arm/mali/linux/mali_osk_indir_mmap.c new file mode 100644 index 000000000000..ecb44818a6b9 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_indir_mmap.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include <linux/version.h> + +#include <linux/slab.h> +#include <linux/pagemap.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/sched.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/atomic.h> + +#include "mali_osk.h" +#include "mali_ukk.h" +#include "mali_kernel_common.h" + +/** + * @file mali_osk_specific.c + * Implementation of per-OS Kernel level specifics + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) +#define do_mmap do_mmap_pgoff +#endif + +_mali_osk_errcode_t _mali_osk_specific_indirect_mmap( _mali_uk_mem_mmap_s *args ) +{ + /* args->ctx ignored here; args->ukk_private required instead */ + /* we need to lock the mmap semaphore before calling the do_mmap function */ + down_write(¤t->mm->mmap_sem); + + args->mapping = (void __user *)do_mmap( + (struct file *)args->ukk_private, + 0, /* start mapping from any address after NULL */ + args->size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + args->phys_addr + ); + + /* and unlock it after the call */ + up_write(¤t->mm->mmap_sem); + + /* No cookie required here */ + args->cookie = 0; + /* uku_private meaningless, so zero */ + args->uku_private = NULL; + + if ( (NULL == args->mapping) || IS_ERR((void *)args->mapping) ) + { + return _MALI_OSK_ERR_FAULT; + } + + /* Success */ + return _MALI_OSK_ERR_OK; +} + + +_mali_osk_errcode_t _mali_osk_specific_indirect_munmap( _mali_uk_mem_munmap_s *args ) +{ + /* args->ctx and args->cookie ignored here */ + + if ((NULL != current) && (NULL != current->mm)) + { + /* remove mapping of mali memory from the process' view */ + /* lock mmap semaphore before call */ + /* lock mmap_sem before calling do_munmap */ + down_write(¤t->mm->mmap_sem); + do_munmap( + current->mm, + (unsigned long)args->mapping, + args->size + ); + /* and unlock after call */ + up_write(¤t->mm->mmap_sem); + MALI_DEBUG_PRINT(5, ("unmapped\n")); + } + else + { + MALI_DEBUG_PRINT(2, ("Freeing of a big block while no user process attached, assuming crash cleanup in progress\n")); + } + + return _MALI_OSK_ERR_OK; /* always succeeds */ +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_indir_mmap.h b/drivers/gpu/arm/mali/linux/mali_osk_indir_mmap.h new file mode 100644 index 000000000000..5c0b1f0340d1 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_indir_mmap.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_specific.h + * Defines per-OS Kernel level specifics, such as unusual workarounds for + * certain OSs. + */ + +#ifndef __MALI_OSK_INDIR_MMAP_H__ +#define __MALI_OSK_INDIR_MMAP_H__ + +#include "mali_uk_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * Linux specific means for calling _mali_ukk_mem_mmap/munmap + * + * The presence of _MALI_OSK_SPECIFIC_INDIRECT_MMAP indicates that + * _mali_osk_specific_indirect_mmap and _mali_osk_specific_indirect_munmap + * should be used instead of _mali_ukk_mem_mmap/_mali_ukk_mem_munmap. + * + * The arguments are the same as _mali_ukk_mem_mmap/_mali_ukk_mem_munmap. + * + * In ALL operating system other than Linux, it is expected that common code + * should be able to call _mali_ukk_mem_mmap/_mali_ukk_mem_munmap directly. + * Such systems should NOT define _MALI_OSK_SPECIFIC_INDIRECT_MMAP. + */ +_mali_osk_errcode_t _mali_osk_specific_indirect_mmap( _mali_uk_mem_mmap_s *args ); +_mali_osk_errcode_t _mali_osk_specific_indirect_munmap( _mali_uk_mem_munmap_s *args ); + + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_OSK_INDIR_MMAP_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_osk_irq.c b/drivers/gpu/arm/mali/linux/mali_osk_irq.c new file mode 100644 index 000000000000..ed43b4fa0c4c --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_irq.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_irq.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include <linux/slab.h> /* For memory allocation */ +#include <linux/workqueue.h> +#include <linux/version.h> + +#include "mali_osk.h" +#include "mali_kernel_core.h" +#include "mali_kernel_common.h" +#include "mali_kernel_license.h" +#include "mali_kernel_linux.h" +#include "linux/interrupt.h" + +typedef struct _mali_osk_irq_t_struct +{ + u32 irqnum; + void *data; + _mali_osk_irq_uhandler_t uhandler; + _mali_osk_irq_bhandler_t bhandler; + struct work_struct work_queue_irq_handle; /* Workqueue for the bottom half of the IRQ-handling. This job is activated when this core gets an IRQ.*/ +} mali_osk_irq_object_t; + +#if MALI_LICENSE_IS_GPL +static struct workqueue_struct *pmm_wq=NULL; +struct workqueue_struct *mali_wq = NULL; +#endif + +typedef void (*workqueue_func_t)(void *); +typedef irqreturn_t (*irq_handler_func_t)(int, void *, struct pt_regs *); +static irqreturn_t irq_handler_upper_half (int port_name, void* dev_id ); /* , struct pt_regs *regs*/ + +#if defined(INIT_DELAYED_WORK) +static void irq_handler_bottom_half ( struct work_struct *work ); +#else +static void irq_handler_bottom_half ( void * input ); +#endif + +/** + * Linux kernel version has marked SA_SHIRQ as deprecated, IRQF_SHARED should be used. + * This is to handle older kernels which haven't done this swap. + */ +#ifndef IRQF_SHARED +#define IRQF_SHARED SA_SHIRQ +#endif /* IRQF_SHARED */ + +_mali_osk_irq_t *_mali_osk_irq_init( u32 irqnum, _mali_osk_irq_uhandler_t uhandler, _mali_osk_irq_bhandler_t bhandler, _mali_osk_irq_trigger_t trigger_func, _mali_osk_irq_ack_t ack_func, void *data, const char *description ) +{ + mali_osk_irq_object_t *irq_object; + + irq_object = kmalloc(sizeof(mali_osk_irq_object_t), GFP_KERNEL); + if (NULL == irq_object) return NULL; + +#if MALI_LICENSE_IS_GPL + if (NULL == mali_wq) + { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) + mali_wq = alloc_workqueue("mali", WQ_UNBOUND, 0); +#else + mali_wq = create_workqueue("mali"); +#endif + if(NULL == mali_wq) + { + MALI_PRINT_ERROR(("Unable to create Mali workqueue\n")); + return NULL; + } + } +#endif + + /* workqueue API changed in 2.6.20, support both versions: */ +#if defined(INIT_DELAYED_WORK) + /* New syntax: INIT_WORK( struct work_struct *work, void (*function)(struct work_struct *)) */ + INIT_WORK( &irq_object->work_queue_irq_handle, irq_handler_bottom_half); +#else + /* Old syntax: INIT_WORK( struct work_struct *work, void (*function)(void *), void *data) */ + INIT_WORK( &irq_object->work_queue_irq_handle, irq_handler_bottom_half, irq_object); +#endif /* defined(INIT_DELAYED_WORK) */ + + if (-1 == irqnum) + { + /* Probe for IRQ */ + if ( (NULL != trigger_func) && (NULL != ack_func) ) + { + unsigned long probe_count = 3; + _mali_osk_errcode_t err; + int irq; + + MALI_DEBUG_PRINT(2, ("Probing for irq\n")); + + do + { + unsigned long mask; + + mask = probe_irq_on(); + trigger_func(data); + + _mali_osk_time_ubusydelay(5); + + irq = probe_irq_off(mask); + err = ack_func(data); + } + while (irq < 0 && (err == _MALI_OSK_ERR_OK) && probe_count--); + + if (irq < 0 || (_MALI_OSK_ERR_OK != err)) irqnum = -1; + else irqnum = irq; + } + else irqnum = -1; /* no probe functions, fault */ + + if (-1 != irqnum) + { + /* found an irq */ + MALI_DEBUG_PRINT(2, ("Found irq %d\n", irqnum)); + } + else + { + MALI_DEBUG_PRINT(2, ("Probe for irq failed\n")); + } + } + + irq_object->irqnum = irqnum; + irq_object->uhandler = uhandler; + irq_object->bhandler = bhandler; + irq_object->data = data; + + /* Is this a real IRQ handler we need? */ + if (!mali_benchmark && irqnum != _MALI_OSK_IRQ_NUMBER_FAKE && irqnum != _MALI_OSK_IRQ_NUMBER_PMM) + { + if (-1 == irqnum) + { + MALI_DEBUG_PRINT(2, ("No IRQ for core '%s' found during probe\n", description)); + kfree(irq_object); + return NULL; + } + + if (0 != request_irq(irqnum, irq_handler_upper_half, IRQF_SHARED, description, irq_object)) + { + MALI_DEBUG_PRINT(2, ("Unable to install IRQ handler for core '%s'\n", description)); + kfree(irq_object); + return NULL; + } + } + +#if MALI_LICENSE_IS_GPL + if ( _MALI_OSK_IRQ_NUMBER_PMM == irqnum ) + { + pmm_wq = create_singlethread_workqueue("mali-pmm-wq"); + } +#endif + + return irq_object; +} + +void _mali_osk_irq_schedulework( _mali_osk_irq_t *irq ) +{ + mali_osk_irq_object_t *irq_object = (mali_osk_irq_object_t *)irq; +#if MALI_LICENSE_IS_GPL + if ( irq_object->irqnum == _MALI_OSK_IRQ_NUMBER_PMM ) + { + queue_work( pmm_wq,&irq_object->work_queue_irq_handle ); + } + else + { + queue_work(mali_wq, &irq_object->work_queue_irq_handle); + } +#else + schedule_work(&irq_object->work_queue_irq_handle); +#endif +} + +void _mali_osk_flush_workqueue( _mali_osk_irq_t *irq ) +{ +#if MALI_LICENSE_IS_GPL + mali_osk_irq_object_t *irq_object = (mali_osk_irq_object_t *)irq; + if(irq_object->irqnum == _MALI_OSK_IRQ_NUMBER_PMM ) + { + flush_workqueue(pmm_wq); + } + else + { + flush_workqueue(mali_wq); + } +#endif +} + +void _mali_osk_irq_term( _mali_osk_irq_t *irq ) +{ + mali_osk_irq_object_t *irq_object = (mali_osk_irq_object_t *)irq; + +#if MALI_LICENSE_IS_GPL + if(irq_object->irqnum == _MALI_OSK_IRQ_NUMBER_PMM ) + { + flush_workqueue(pmm_wq); + destroy_workqueue(pmm_wq); + } +#endif + if (!mali_benchmark) + { + free_irq(irq_object->irqnum, irq_object); + } + kfree(irq_object); + flush_scheduled_work(); +} + + +/** This function is called directly in interrupt context from the OS just after + * the CPU get the hw-irq from mali, or other devices on the same IRQ-channel. + * It is registered one of these function for each mali core. When an interrupt + * arrives this function will be called equal times as registered mali cores. + * That means that we only check one mali core in one function call, and the + * core we check for each turn is given by the \a dev_id variable. + * If we detect an pending interrupt on the given core, we mask the interrupt + * out by settging the core's IRQ_MASK register to zero. + * Then we schedule the mali_core_irq_handler_bottom_half to run as high priority + * work queue job. + */ +static irqreturn_t irq_handler_upper_half (int port_name, void* dev_id ) /* , struct pt_regs *regs*/ +{ + mali_osk_irq_object_t *irq_object = (mali_osk_irq_object_t *)dev_id; + + if (irq_object->uhandler(irq_object->data) == _MALI_OSK_ERR_OK) + { + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +/* Is executed when an interrupt occur on one core */ +/* workqueue API changed in 2.6.20, support both versions: */ +#if defined(INIT_DELAYED_WORK) +static void irq_handler_bottom_half ( struct work_struct *work ) +#else +static void irq_handler_bottom_half ( void * input ) +#endif +{ + mali_osk_irq_object_t *irq_object; + +#if defined(INIT_DELAYED_WORK) + irq_object = _MALI_OSK_CONTAINER_OF(work, mali_osk_irq_object_t, work_queue_irq_handle); +#else + if ( NULL == input ) + { + MALI_PRINT_ERROR(("IRQ: Null pointer! Illegal!")); + return; /* Error */ + } + irq_object = (mali_osk_irq_object_t *) input; +#endif + + irq_object->bhandler(irq_object->data); +} + diff --git a/drivers/gpu/arm/mali/linux/mali_osk_locks.c b/drivers/gpu/arm/mali/linux/mali_osk_locks.c new file mode 100644 index 000000000000..30af91deb6f2 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_locks.c @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_locks.c + * Implemenation of the OS abstraction layer for the kernel device driver + */ + +/* needed to detect kernel version specific code */ +#include <linux/version.h> + +#include <linux/spinlock.h> +#include <linux/rwsem.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include <linux/semaphore.h> +#else /* pre 2.6.26 the file was in the arch specific location */ +#include <asm/semaphore.h> +#endif + +#include <linux/slab.h> +#include "mali_osk.h" +#include "mali_kernel_common.h" + +/* These are all the locks we implement: */ +typedef enum +{ + _MALI_OSK_INTERNAL_LOCKTYPE_SPIN, /* Mutex, implicitly non-interruptable, use spin_lock/spin_unlock */ + _MALI_OSK_INTERNAL_LOCKTYPE_SPIN_IRQ, /* Mutex, IRQ version of spinlock, use spin_lock_irqsave/spin_unlock_irqrestore */ + _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX, /* Interruptable, use up()/down_interruptable() */ + _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT, /* Non-Interruptable, use up()/down() */ + _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT_RW, /* Non-interruptable, Reader/Writer, use {up,down}{read,write}() */ + + /* Linux supports, but we do not support: + * Non-Interruptable Reader/Writer spinlock mutexes - RW optimization will be switched off + */ + + /* Linux does not support: + * One-locks, of any sort - no optimization for this fact will be made. + */ + +} _mali_osk_internal_locktype; + +struct _mali_osk_lock_t_struct +{ + _mali_osk_internal_locktype type; + unsigned long flags; + union + { + spinlock_t spinlock; + struct semaphore sema; + struct rw_semaphore rw_sema; + } obj; + MALI_DEBUG_CODE( + /** original flags for debug checking */ + _mali_osk_lock_flags_t orig_flags; + ); /* MALI_DEBUG_CODE */ +}; + +_mali_osk_lock_t *_mali_osk_lock_init( _mali_osk_lock_flags_t flags, u32 initial, u32 order ) +{ + _mali_osk_lock_t *lock = NULL; + + /* Validate parameters: */ + /* Flags acceptable */ + MALI_DEBUG_ASSERT( 0 == ( flags & ~(_MALI_OSK_LOCKFLAG_SPINLOCK + | _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ + | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE + | _MALI_OSK_LOCKFLAG_READERWRITER + | _MALI_OSK_LOCKFLAG_ORDERED + | _MALI_OSK_LOCKFLAG_ONELOCK )) ); + /* Spinlocks are always non-interruptable */ + MALI_DEBUG_ASSERT( (((flags & _MALI_OSK_LOCKFLAG_SPINLOCK) || (flags & _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ)) && (flags & _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE)) + || !(flags & _MALI_OSK_LOCKFLAG_SPINLOCK)); + /* Parameter initial SBZ - for future expansion */ + MALI_DEBUG_ASSERT( 0 == initial ); + + lock = kmalloc(sizeof(_mali_osk_lock_t), GFP_KERNEL); + + if ( NULL == lock ) + { + return lock; + } + + /* Determine type of mutex: */ + /* defaults to interruptable mutex if no flags are specified */ + + if ( (flags & _MALI_OSK_LOCKFLAG_SPINLOCK) ) + { + /* Non-interruptable Spinlocks override all others */ + lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_SPIN; + spin_lock_init( &lock->obj.spinlock ); + } + else if ( (flags & _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ ) ) + { + lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_SPIN_IRQ; + lock->flags = 0; + spin_lock_init( &lock->obj.spinlock ); + } + else if ( (flags & _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE) + && (flags & _MALI_OSK_LOCKFLAG_READERWRITER) ) + { + lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT_RW; + init_rwsem( &lock->obj.rw_sema ); + } + else + { + /* Usual mutex types */ + if ( (flags & _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE) ) + { + lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT; + } + else + { + lock->type = _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX; + } + + /* Initially unlocked */ + sema_init( &lock->obj.sema, 1 ); + } + + MALI_DEBUG_CODE( + /* Debug tracking of flags */ + lock->orig_flags = flags; + ); /* MALI_DEBUG_CODE */ + + return lock; +} + +_mali_osk_errcode_t _mali_osk_lock_wait( _mali_osk_lock_t *lock, _mali_osk_lock_mode_t mode) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + + /* Parameter validation */ + MALI_DEBUG_ASSERT_POINTER( lock ); + + MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_RW == mode + || _MALI_OSK_LOCKMODE_RO == mode ); + + /* Only allow RO locks when the initial object was a Reader/Writer lock + * Since information is lost on the internal locktype, we use the original + * information, which is only stored when built for DEBUG */ + MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_RW == mode + || (_MALI_OSK_LOCKMODE_RO == mode && (_MALI_OSK_LOCKFLAG_READERWRITER & lock->orig_flags)) ); + + switch ( lock->type ) + { + case _MALI_OSK_INTERNAL_LOCKTYPE_SPIN: + spin_lock(&lock->obj.spinlock); + break; + case _MALI_OSK_INTERNAL_LOCKTYPE_SPIN_IRQ: + spin_lock_irqsave(&lock->obj.spinlock, lock->flags); + break; + + case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX: + if ( down_interruptible(&lock->obj.sema) ) + { + err = _MALI_OSK_ERR_RESTARTSYSCALL; + } + break; + + case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT: + down(&lock->obj.sema); + break; + + case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT_RW: + if (mode == _MALI_OSK_LOCKMODE_RO) + { + down_read(&lock->obj.rw_sema); + } + else + { + down_write(&lock->obj.rw_sema); + } + break; + + default: + /* Reaching here indicates a programming error, so you will not get here + * on non-DEBUG builds */ + MALI_DEBUG_PRINT_ERROR( ("Invalid internal lock type: %.8X", lock->type ) ); + break; + } + + return err; +} + +void _mali_osk_lock_signal( _mali_osk_lock_t *lock, _mali_osk_lock_mode_t mode ) +{ + /* Parameter validation */ + MALI_DEBUG_ASSERT_POINTER( lock ); + + MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_RW == mode + || _MALI_OSK_LOCKMODE_RO == mode ); + + /* Only allow RO locks when the initial object was a Reader/Writer lock + * Since information is lost on the internal locktype, we use the original + * information, which is only stored when built for DEBUG */ + MALI_DEBUG_ASSERT( _MALI_OSK_LOCKMODE_RW == mode + || (_MALI_OSK_LOCKMODE_RO == mode && (_MALI_OSK_LOCKFLAG_READERWRITER & lock->orig_flags)) ); + + switch ( lock->type ) + { + case _MALI_OSK_INTERNAL_LOCKTYPE_SPIN: + spin_unlock(&lock->obj.spinlock); + break; + case _MALI_OSK_INTERNAL_LOCKTYPE_SPIN_IRQ: + spin_unlock_irqrestore(&lock->obj.spinlock, lock->flags); + break; + + case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX: + /* FALLTHROUGH */ + case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT: + up(&lock->obj.sema); + break; + + case _MALI_OSK_INTERNAL_LOCKTYPE_MUTEX_NONINT_RW: + if (mode == _MALI_OSK_LOCKMODE_RO) + { + up_read(&lock->obj.rw_sema); + } + else + { + up_write(&lock->obj.rw_sema); + } + break; + + default: + /* Reaching here indicates a programming error, so you will not get here + * on non-DEBUG builds */ + MALI_DEBUG_PRINT_ERROR( ("Invalid internal lock type: %.8X", lock->type ) ); + break; + } +} + +void _mali_osk_lock_term( _mali_osk_lock_t *lock ) +{ + /* Parameter validation */ + MALI_DEBUG_ASSERT_POINTER( lock ); + + /* Linux requires no explicit termination of spinlocks, semaphores, or rw_semaphores */ + kfree(lock); +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_low_level_mem.c b/drivers/gpu/arm/mali/linux/mali_osk_low_level_mem.c new file mode 100644 index 000000000000..524aa6e5eef1 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_low_level_mem.c @@ -0,0 +1,590 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_low_level_mem.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +/* needed to detect kernel version specific code */ +#include <linux/version.h> + +#include <asm/io.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/dma-mapping.h> + +#include "mali_osk.h" +#include "mali_ukk.h" /* required to hook in _mali_ukk_mem_mmap handling */ +#include "mali_kernel_common.h" +#include "mali_kernel_linux.h" + +static void mali_kernel_memory_vma_open(struct vm_area_struct * vma); +static void mali_kernel_memory_vma_close(struct vm_area_struct * vma); + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +static int mali_kernel_memory_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf); +#else +static unsigned long mali_kernel_memory_cpu_page_fault_handler(struct vm_area_struct * vma, unsigned long address); +#endif + + +typedef struct mali_vma_usage_tracker +{ + int references; + u32 cookie; +} mali_vma_usage_tracker; + + +/* Linked list structure to hold details of all OS allocations in a particular + * mapping + */ +struct AllocationList +{ + struct AllocationList *next; + u32 offset; + u32 physaddr; +}; + +typedef struct AllocationList AllocationList; + +/* Private structure to store details of a mapping region returned + * from _mali_osk_mem_mapregion_init + */ +struct MappingInfo +{ + struct vm_area_struct *vma; + struct AllocationList *list; +}; + +typedef struct MappingInfo MappingInfo; + + +static u32 _kernel_page_allocate(void); +static void _kernel_page_release(u32 physical_address); +static AllocationList * _allocation_list_item_get(void); +static void _allocation_list_item_release(AllocationList * item); + + +/* Variable declarations */ +static DEFINE_SPINLOCK(allocation_list_spinlock); +static AllocationList * pre_allocated_memory = (AllocationList*) NULL ; +static int pre_allocated_memory_size_current = 0; +#ifdef MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB + static int pre_allocated_memory_size_max = MALI_OS_MEMORY_KERNEL_BUFFER_SIZE_IN_MB * 1024 * 1024; +#else + static int pre_allocated_memory_size_max = 6 * 1024 * 1024; /* 6 MiB */ +#endif + +static struct vm_operations_struct mali_kernel_vm_ops = +{ + .open = mali_kernel_memory_vma_open, + .close = mali_kernel_memory_vma_close, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + .fault = mali_kernel_memory_cpu_page_fault_handler +#else + .nopfn = mali_kernel_memory_cpu_page_fault_handler +#endif +}; + + +void mali_osk_low_level_mem_init(void) +{ + pre_allocated_memory = (AllocationList*) NULL ; +} + +void mali_osk_low_level_mem_term(void) +{ + while ( NULL != pre_allocated_memory ) + { + AllocationList *item; + item = pre_allocated_memory; + pre_allocated_memory = item->next; + _kernel_page_release(item->physaddr); + _mali_osk_free( item ); + } + pre_allocated_memory_size_current = 0; +} + +static u32 _kernel_page_allocate(void) +{ + struct page *new_page; + u32 linux_phys_addr; + + new_page = alloc_page(GFP_HIGHUSER | __GFP_ZERO | __GFP_REPEAT | __GFP_NOWARN | __GFP_COLD); + + if ( NULL == new_page ) + { + return 0; + } + + /* Ensure page is flushed from CPU caches. */ + linux_phys_addr = dma_map_page(NULL, new_page, 0, PAGE_SIZE, DMA_BIDIRECTIONAL); + + return linux_phys_addr; +} + +static void _kernel_page_release(u32 physical_address) +{ + struct page *unmap_page; + + #if 1 + dma_unmap_page(NULL, physical_address, PAGE_SIZE, DMA_BIDIRECTIONAL); + #endif + + unmap_page = pfn_to_page( physical_address >> PAGE_SHIFT ); + MALI_DEBUG_ASSERT_POINTER( unmap_page ); + __free_page( unmap_page ); +} + +static AllocationList * _allocation_list_item_get(void) +{ + AllocationList *item = NULL; + unsigned long flags; + + spin_lock_irqsave(&allocation_list_spinlock,flags); + if ( pre_allocated_memory ) + { + item = pre_allocated_memory; + pre_allocated_memory = pre_allocated_memory->next; + pre_allocated_memory_size_current -= PAGE_SIZE; + + spin_unlock_irqrestore(&allocation_list_spinlock,flags); + return item; + } + spin_unlock_irqrestore(&allocation_list_spinlock,flags); + + item = _mali_osk_malloc( sizeof(AllocationList) ); + if ( NULL == item) + { + return NULL; + } + + item->physaddr = _kernel_page_allocate(); + if ( 0 == item->physaddr ) + { + /* Non-fatal error condition, out of memory. Upper levels will handle this. */ + _mali_osk_free( item ); + return NULL; + } + return item; +} + +static void _allocation_list_item_release(AllocationList * item) +{ + unsigned long flags; + spin_lock_irqsave(&allocation_list_spinlock,flags); + if ( pre_allocated_memory_size_current < pre_allocated_memory_size_max) + { + item->next = pre_allocated_memory; + pre_allocated_memory = item; + pre_allocated_memory_size_current += PAGE_SIZE; + spin_unlock_irqrestore(&allocation_list_spinlock,flags); + return; + } + spin_unlock_irqrestore(&allocation_list_spinlock,flags); + + _kernel_page_release(item->physaddr); + _mali_osk_free( item ); +} + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +static int mali_kernel_memory_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf) +#else +static unsigned long mali_kernel_memory_cpu_page_fault_handler(struct vm_area_struct * vma, unsigned long address) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + void __user * address; + address = vmf->virtual_address; +#endif + /* + * We always fail the call since all memory is pre-faulted when assigned to the process. + * Only the Mali cores can use page faults to extend buffers. + */ + + MALI_DEBUG_PRINT(1, ("Page-fault in Mali memory region caused by the CPU.\n")); + MALI_DEBUG_PRINT(1, ("Tried to access %p (process local virtual address) which is not currently mapped to any Mali memory.\n", (void*)address)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + return VM_FAULT_SIGBUS; +#else + return NOPFN_SIGBUS; +#endif +} + +static void mali_kernel_memory_vma_open(struct vm_area_struct * vma) +{ + mali_vma_usage_tracker * vma_usage_tracker; + MALI_DEBUG_PRINT(4, ("Open called on vma %p\n", vma)); + + vma_usage_tracker = (mali_vma_usage_tracker*)vma->vm_private_data; + vma_usage_tracker->references++; + + return; +} + +static void mali_kernel_memory_vma_close(struct vm_area_struct * vma) +{ + _mali_uk_mem_munmap_s args = {0, }; + mali_memory_allocation * descriptor; + mali_vma_usage_tracker * vma_usage_tracker; + MALI_DEBUG_PRINT(3, ("Close called on vma %p\n", vma)); + + vma_usage_tracker = (mali_vma_usage_tracker*)vma->vm_private_data; + + BUG_ON(!vma_usage_tracker); + BUG_ON(0 == vma_usage_tracker->references); + + vma_usage_tracker->references--; + + if (0 != vma_usage_tracker->references) + { + MALI_DEBUG_PRINT(3, ("Ignoring this close, %d references still exists\n", vma_usage_tracker->references)); + return; + } + + /** @note args->context unused, initialized to 0. + * Instead, we use the memory_session from the cookie */ + + descriptor = (mali_memory_allocation *)vma_usage_tracker->cookie; + + args.cookie = (u32)descriptor; + args.mapping = descriptor->mapping; + args.size = descriptor->size; + + _mali_ukk_mem_munmap( &args ); + + /* vma_usage_tracker is free()d by _mali_osk_mem_mapregion_term(). + * In the case of the memory engine, it is called as the release function that has been registered with the engine*/ +} + + +void _mali_osk_mem_barrier( void ) +{ + mb(); +} + +void _mali_osk_write_mem_barrier( void ) +{ + wmb(); +} + +mali_io_address _mali_osk_mem_mapioregion( u32 phys, u32 size, const char *description ) +{ + return (mali_io_address)ioremap_nocache(phys, size); +} + +void _mali_osk_mem_unmapioregion( u32 phys, u32 size, mali_io_address virt ) +{ + iounmap((void*)virt); +} + +mali_io_address _mali_osk_mem_allocioregion( u32 *phys, u32 size ) +{ + void * virt; + MALI_DEBUG_ASSERT_POINTER( phys ); + MALI_DEBUG_ASSERT( 0 == (size & ~_MALI_OSK_CPU_PAGE_MASK) ); + MALI_DEBUG_ASSERT( 0 != size ); + + /* dma_alloc_* uses a limited region of address space. On most arch/marchs + * 2 to 14 MiB is available. This should be enough for the page tables, which + * currently is the only user of this function. */ + virt = dma_alloc_coherent(NULL, size, phys, GFP_KERNEL | GFP_DMA ); + + MALI_DEBUG_PRINT(3, ("Page table virt: 0x%x = dma_alloc_coherent(size:%d, phys:0x%x, )\n", virt, size, phys)); + + if ( NULL == virt ) + { + MALI_DEBUG_PRINT(5, ("allocioregion: Failed to allocate Pagetable memory, size=0x%.8X\n", size )); + return 0; + } + + MALI_DEBUG_ASSERT( 0 == (*phys & ~_MALI_OSK_CPU_PAGE_MASK) ); + + return (mali_io_address)virt; +} + +void _mali_osk_mem_freeioregion( u32 phys, u32 size, mali_io_address virt ) +{ + MALI_DEBUG_ASSERT_POINTER( (void*)virt ); + MALI_DEBUG_ASSERT( 0 != size ); + MALI_DEBUG_ASSERT( 0 == (phys & ( (1 << PAGE_SHIFT) - 1 )) ); + + dma_free_coherent(NULL, size, virt, phys); +} + +_mali_osk_errcode_t inline _mali_osk_mem_reqregion( u32 phys, u32 size, const char *description ) +{ + return ((NULL == request_mem_region(phys, size, description)) ? _MALI_OSK_ERR_NOMEM : _MALI_OSK_ERR_OK); +} + +void inline _mali_osk_mem_unreqregion( u32 phys, u32 size ) +{ + release_mem_region(phys, size); +} + +void inline _mali_osk_mem_iowrite32_relaxed( volatile mali_io_address addr, u32 offset, u32 val ) +{ + __raw_writel(cpu_to_le32(val),((u8*)addr) + offset); +} + +u32 inline _mali_osk_mem_ioread32( volatile mali_io_address addr, u32 offset ) +{ + return ioread32(((u8*)addr) + offset); +} + +void inline _mali_osk_mem_iowrite32( volatile mali_io_address addr, u32 offset, u32 val ) +{ + iowrite32(val, ((u8*)addr) + offset); +} + +void _mali_osk_cache_flushall( void ) +{ + /** @note Cached memory is not currently supported in this implementation */ +} + +void _mali_osk_cache_ensure_uncached_range_flushed( void *uncached_mapping, u32 offset, u32 size ) +{ + _mali_osk_write_mem_barrier(); +} + +_mali_osk_errcode_t _mali_osk_mem_mapregion_init( mali_memory_allocation * descriptor ) +{ + struct vm_area_struct *vma; + mali_vma_usage_tracker * vma_usage_tracker; + MappingInfo *mappingInfo; + + if (NULL == descriptor) return _MALI_OSK_ERR_FAULT; + + MALI_DEBUG_ASSERT( 0 != (descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE) ); + + vma = (struct vm_area_struct*)descriptor->process_addr_mapping_info; + + if (NULL == vma ) return _MALI_OSK_ERR_FAULT; + + /* Re-write the process_addr_mapping_info */ + mappingInfo = _mali_osk_calloc( 1, sizeof(MappingInfo) ); + + if ( NULL == mappingInfo ) return _MALI_OSK_ERR_FAULT; + + vma_usage_tracker = _mali_osk_calloc( 1, sizeof(mali_vma_usage_tracker) ); + + if (NULL == vma_usage_tracker) + { + MALI_DEBUG_PRINT(2, ("Failed to allocate memory to track memory usage\n")); + _mali_osk_free( mappingInfo ); + return _MALI_OSK_ERR_FAULT; + } + + mappingInfo->vma = vma; + descriptor->process_addr_mapping_info = mappingInfo; + + /* Do the va range allocation - in this case, it was done earlier, so we copy in that information */ + descriptor->mapping = (void __user*)vma->vm_start; + /* list member is already NULL */ + + /* + set some bits which indicate that: + The memory is IO memory, meaning that no paging is to be performed and the memory should not be included in crash dumps + The memory is reserved, meaning that it's present and can never be paged out (see also previous entry) + */ + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + vma->vm_flags |= VM_DONTCOPY; + + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_ops = &mali_kernel_vm_ops; /* Operations used on any memory system */ + + vma_usage_tracker->references = 1; /* set initial reference count to be 1 as vma_open won't be called for the first mmap call */ + vma_usage_tracker->cookie = (u32)descriptor; /* cookie for munmap */ + + vma->vm_private_data = vma_usage_tracker; + + return _MALI_OSK_ERR_OK; +} + +void _mali_osk_mem_mapregion_term( mali_memory_allocation * descriptor ) +{ + struct vm_area_struct* vma; + mali_vma_usage_tracker * vma_usage_tracker; + MappingInfo *mappingInfo; + + if (NULL == descriptor) return; + + MALI_DEBUG_ASSERT( 0 != (descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE) ); + + mappingInfo = (MappingInfo *)descriptor->process_addr_mapping_info; + + MALI_DEBUG_ASSERT_POINTER( mappingInfo ); + + /* Linux does the right thing as part of munmap to remove the mapping + * All that remains is that we remove the vma_usage_tracker setup in init() */ + vma = mappingInfo->vma; + + MALI_DEBUG_ASSERT_POINTER( vma ); + + /* ASSERT that there are no allocations on the list. Unmap should've been + * called on all OS allocations. */ + MALI_DEBUG_ASSERT( NULL == mappingInfo->list ); + + vma_usage_tracker = vma->vm_private_data; + + /* We only get called if mem_mapregion_init succeeded */ + _mali_osk_free(vma_usage_tracker); + + _mali_osk_free( mappingInfo ); + return; +} + +_mali_osk_errcode_t _mali_osk_mem_mapregion_map( mali_memory_allocation * descriptor, u32 offset, u32 *phys_addr, u32 size ) +{ + struct vm_area_struct *vma; + MappingInfo *mappingInfo; + + if (NULL == descriptor) return _MALI_OSK_ERR_FAULT; + + MALI_DEBUG_ASSERT_POINTER( phys_addr ); + + MALI_DEBUG_ASSERT( 0 != (descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE) ); + + MALI_DEBUG_ASSERT( 0 == (size & ~_MALI_OSK_CPU_PAGE_MASK) ); + + MALI_DEBUG_ASSERT( 0 == (offset & ~_MALI_OSK_CPU_PAGE_MASK)); + + if (NULL == descriptor->mapping) return _MALI_OSK_ERR_INVALID_ARGS; + + if (size > (descriptor->size - offset)) + { + MALI_DEBUG_PRINT(1,("_mali_osk_mem_mapregion_map: virtual memory area not large enough to map physical 0x%x size %x into area 0x%x at offset 0x%xr\n", + *phys_addr, size, descriptor->mapping, offset)); + return _MALI_OSK_ERR_FAULT; + } + + mappingInfo = (MappingInfo *)descriptor->process_addr_mapping_info; + + MALI_DEBUG_ASSERT_POINTER( mappingInfo ); + + vma = mappingInfo->vma; + + if (NULL == vma ) return _MALI_OSK_ERR_FAULT; + + MALI_DEBUG_PRINT(7, ("Process map: mapping 0x%08X to process address 0x%08lX length 0x%08X\n", *phys_addr, (long unsigned int)(descriptor->mapping + offset), size)); + + if ( MALI_MEMORY_ALLOCATION_OS_ALLOCATED_PHYSADDR_MAGIC == *phys_addr ) + { + _mali_osk_errcode_t ret; + AllocationList *alloc_item; + u32 linux_phys_frame_num; + + alloc_item = _allocation_list_item_get(); + if (NULL == alloc_item) + { + MALI_DEBUG_PRINT(1, ("Failed to allocate list item\n")); + return _MALI_OSK_ERR_NOMEM; + } + + linux_phys_frame_num = alloc_item->physaddr >> PAGE_SHIFT; + + ret = ( remap_pfn_range( vma, ((u32)descriptor->mapping) + offset, linux_phys_frame_num, size, vma->vm_page_prot) ) ? _MALI_OSK_ERR_FAULT : _MALI_OSK_ERR_OK; + + if ( ret != _MALI_OSK_ERR_OK) + { + _allocation_list_item_release(alloc_item); + return ret; + } + + /* Put our alloc_item into the list of allocations on success */ + alloc_item->next = mappingInfo->list; + alloc_item->offset = offset; + + /*alloc_item->physaddr = linux_phys_addr;*/ + mappingInfo->list = alloc_item; + + /* Write out new physical address on success */ + *phys_addr = alloc_item->physaddr; + + return ret; + } + + /* Otherwise, Use the supplied physical address */ + + /* ASSERT that supplied phys_addr is page aligned */ + MALI_DEBUG_ASSERT( 0 == ((*phys_addr) & ~_MALI_OSK_CPU_PAGE_MASK) ); + + return ( remap_pfn_range( vma, ((u32)descriptor->mapping) + offset, *phys_addr >> PAGE_SHIFT, size, vma->vm_page_prot) ) ? _MALI_OSK_ERR_FAULT : _MALI_OSK_ERR_OK; + +} + +void _mali_osk_mem_mapregion_unmap( mali_memory_allocation * descriptor, u32 offset, u32 size, _mali_osk_mem_mapregion_flags_t flags ) +{ + MappingInfo *mappingInfo; + + if (NULL == descriptor) return; + + MALI_DEBUG_ASSERT( 0 != (descriptor->flags & MALI_MEMORY_ALLOCATION_FLAG_MAP_INTO_USERSPACE) ); + + MALI_DEBUG_ASSERT( 0 == (size & ~_MALI_OSK_CPU_PAGE_MASK) ); + + MALI_DEBUG_ASSERT( 0 == (offset & ~_MALI_OSK_CPU_PAGE_MASK) ); + + if (NULL == descriptor->mapping) return; + + if (size > (descriptor->size - offset)) + { + MALI_DEBUG_PRINT(1,("_mali_osk_mem_mapregion_unmap: virtual memory area not large enough to unmap size %x from area 0x%x at offset 0x%x\n", + size, descriptor->mapping, offset)); + return; + } + mappingInfo = (MappingInfo *)descriptor->process_addr_mapping_info; + + MALI_DEBUG_ASSERT_POINTER( mappingInfo ); + + if ( 0 != (flags & _MALI_OSK_MEM_MAPREGION_FLAG_OS_ALLOCATED_PHYSADDR) ) + { + /* This physical RAM was allocated in _mali_osk_mem_mapregion_map and + * so needs to be unmapped + */ + while (size) + { + /* First find the allocation in the list of allocations */ + AllocationList *alloc = mappingInfo->list; + AllocationList **prev = &(mappingInfo->list); + while (NULL != alloc && alloc->offset != offset) + { + prev = &(alloc->next); + alloc = alloc->next; + } + if (alloc == NULL) { + MALI_DEBUG_PRINT(1, ("Unmapping memory that isn't mapped\n")); + size -= _MALI_OSK_CPU_PAGE_SIZE; + offset += _MALI_OSK_CPU_PAGE_SIZE; + continue; + } + + _kernel_page_release(alloc->physaddr); + + /* Remove the allocation from the list */ + *prev = alloc->next; + _mali_osk_free( alloc ); + + /* Move onto the next allocation */ + size -= _MALI_OSK_CPU_PAGE_SIZE; + offset += _MALI_OSK_CPU_PAGE_SIZE; + } + } + + /* Linux does the right thing as part of munmap to remove the mapping */ + + return; +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_mali.c b/drivers/gpu/arm/mali/linux/mali_osk_mali.c new file mode 100644 index 000000000000..06a27bc4f917 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_mali.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_mali.c + * Implementation of the OS abstraction layer which is specific for the Mali kernel device driver + */ +#include <linux/kernel.h> +#include <asm/uaccess.h> + +#include "mali_kernel_common.h" /* MALI_xxx macros */ +#include "mali_osk.h" /* kernel side OS functions */ +#include "mali_uk_types.h" +#include "mali_kernel_linux.h" /* exports initialize/terminate_kernel_device() definition of mali_osk_low_level_mem_init() and term */ +#include <mach/irqs.h> +#include <mach/mali/config.h> /* contains the configuration of the arch we are compiling for */ + +/* is called from mali_kernel_constructor in common code */ +_mali_osk_errcode_t _mali_osk_init( void ) +{ + if (0 != initialize_kernel_device()) MALI_ERROR(_MALI_OSK_ERR_FAULT); + + mali_osk_low_level_mem_init(); + + MALI_SUCCESS; +} + +/* is called from mali_kernel_deconstructor in common code */ +void _mali_osk_term( void ) +{ + mali_osk_low_level_mem_term(); + terminate_kernel_device(); +} + +_mali_osk_errcode_t _mali_osk_resources_init( _mali_osk_resource_t **arch_config, u32 *num_resources ) +{ + *num_resources = sizeof(arch_configuration) / sizeof(arch_configuration[0]); + *arch_config = arch_configuration; + return _MALI_OSK_ERR_OK; +} + +void _mali_osk_resources_term( _mali_osk_resource_t **arch_config, u32 num_resources ) +{ + /* Nothing to do */ +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_math.c b/drivers/gpu/arm/mali/linux/mali_osk_math.c new file mode 100644 index 000000000000..bb25e7dc7ddc --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_math.c @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_math.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include "mali_osk.h" +#include <linux/bitops.h> + +u32 inline _mali_osk_clz( u32 input ) +{ + return 32-fls(input); +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_memory.c b/drivers/gpu/arm/mali/linux/mali_osk_memory.c new file mode 100644 index 000000000000..5354e855e8dc --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_memory.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_memory.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include "mali_osk.h" +#include <linux/slab.h> +#include <linux/vmalloc.h> + +void inline *_mali_osk_calloc( u32 n, u32 size ) +{ + return kcalloc(n, size, GFP_KERNEL); +} + +void inline *_mali_osk_malloc( u32 size ) +{ + return kmalloc(size, GFP_KERNEL); +} + +void inline _mali_osk_free( void *ptr ) +{ + kfree(ptr); +} + +void inline *_mali_osk_valloc( u32 size ) +{ + return vmalloc(size); +} + +void inline _mali_osk_vfree( void *ptr ) +{ + vfree(ptr); +} + +void inline *_mali_osk_memcpy( void *dst, const void *src, u32 len ) +{ + return memcpy(dst, src, len); +} + +void inline *_mali_osk_memset( void *s, u32 c, u32 n ) +{ + return memset(s, c, n); +} + +mali_bool _mali_osk_mem_check_allocated( u32 max_allocated ) +{ + /* No need to prevent an out-of-memory dialogue appearing on Linux, + * so we always return MALI_TRUE. + */ + return MALI_TRUE; +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_misc.c b/drivers/gpu/arm/mali/linux/mali_osk_misc.c new file mode 100644 index 000000000000..286e45d0c08a --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_misc.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_misc.c + * Implementation of the OS abstraction layer for the kernel device driver + */ +#include <linux/kernel.h> +#include <asm/uaccess.h> +#include <asm/cacheflush.h> +#include <linux/sched.h> +#include <linux/module.h> +#include "mali_osk.h" + +void _mali_osk_dbgmsg( const char *fmt, ... ) +{ + va_list args; + va_start(args, fmt); + vprintk(fmt, args); + va_end(args); +} + +u32 _mali_osk_snprintf( char *buf, u32 size, const char *fmt, ... ) +{ + int res; + va_list args; + va_start(args, fmt); + + res = vsnprintf(buf, (size_t)size, fmt, args); + + va_end(args); + return res; +} + +void _mali_osk_abort(void) +{ + /* make a simple fault by dereferencing a NULL pointer */ + *(int *)0 = 0; +} + +void _mali_osk_break(void) +{ + _mali_osk_abort(); +} + +u32 _mali_osk_get_pid(void) +{ + /* Thread group ID is the process ID on Linux */ + return (u32)current->tgid; +} + +u32 _mali_osk_get_tid(void) +{ + /* pid is actually identifying the thread on Linux */ + return (u32)current->pid; +} + +void * _mali_osk_get_task(void) +{ + return current; +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_notification.c b/drivers/gpu/arm/mali/linux/mali_osk_notification.c new file mode 100644 index 000000000000..fb4907eefce8 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_notification.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_notification.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include "mali_osk.h" +#include "mali_kernel_common.h" + +/* needed to detect kernel version specific code */ +#include <linux/version.h> + +#include <linux/sched.h> +#include <linux/slab.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include <linux/semaphore.h> +#else /* pre 2.6.26 the file was in the arch specific location */ +#include <asm/semaphore.h> +#endif + +/** + * Declaration of the notification queue object type + * Contains a linked list of notification pending delivery to user space. + * It also contains a wait queue of exclusive waiters blocked in the ioctl + * When a new notification is posted a single thread is resumed. + */ +struct _mali_osk_notification_queue_t_struct +{ + struct semaphore mutex; /**< Mutex protecting the list */ + wait_queue_head_t receive_queue; /**< Threads waiting for new entries to the queue */ + struct list_head head; /**< List of notifications waiting to be picked up */ +}; + +typedef struct _mali_osk_notification_wrapper_t_struct +{ + struct list_head list; /**< Internal linked list variable */ + _mali_osk_notification_t data; /**< Notification data */ +} _mali_osk_notification_wrapper_t; + +_mali_osk_notification_queue_t *_mali_osk_notification_queue_init( void ) +{ + _mali_osk_notification_queue_t * result; + + result = (_mali_osk_notification_queue_t *)kmalloc(sizeof(_mali_osk_notification_queue_t), GFP_KERNEL); + if (NULL == result) return NULL; + + sema_init(&result->mutex, 1); + init_waitqueue_head(&result->receive_queue); + INIT_LIST_HEAD(&result->head); + + return result; +} + +_mali_osk_notification_t *_mali_osk_notification_create( u32 type, u32 size ) +{ + /* OPT Recycling of notification objects */ + _mali_osk_notification_wrapper_t *notification; + + notification = (_mali_osk_notification_wrapper_t *)kmalloc( sizeof(_mali_osk_notification_wrapper_t) + size, GFP_KERNEL ); + if (NULL == notification) + { + MALI_DEBUG_PRINT(1, ("Failed to create a notification object\n")); + return NULL; + } + + /* Init the list */ + INIT_LIST_HEAD(¬ification->list); + + if (0 != size) + { + notification->data.result_buffer = ((u8*)notification) + sizeof(_mali_osk_notification_wrapper_t); + } + else + { + notification->data.result_buffer = NULL; + } + + /* set up the non-allocating fields */ + notification->data.notification_type = type; + notification->data.result_buffer_size = size; + + /* all ok */ + return &(notification->data); +} + +void _mali_osk_notification_delete( _mali_osk_notification_t *object ) +{ + _mali_osk_notification_wrapper_t *notification; + MALI_DEBUG_ASSERT_POINTER( object ); + + notification = container_of( object, _mali_osk_notification_wrapper_t, data ); + + /* Remove from the list */ + list_del(¬ification->list); + /* Free the container */ + kfree(notification); +} + +void _mali_osk_notification_queue_term( _mali_osk_notification_queue_t *queue ) +{ + MALI_DEBUG_ASSERT_POINTER( queue ); + + /* not much to do, just free the memory */ + kfree(queue); +} + +void _mali_osk_notification_queue_send( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t *object ) +{ + _mali_osk_notification_wrapper_t *notification; + MALI_DEBUG_ASSERT_POINTER( queue ); + MALI_DEBUG_ASSERT_POINTER( object ); + + notification = container_of( object, _mali_osk_notification_wrapper_t, data ); + + /* lock queue access */ + down(&queue->mutex); + /* add to list */ + list_add_tail(¬ification->list, &queue->head); + /* unlock the queue */ + up(&queue->mutex); + + /* and wake up one possible exclusive waiter */ + wake_up(&queue->receive_queue); +} + +static int _mali_notification_queue_is_empty( _mali_osk_notification_queue_t *queue ) +{ + int ret; + + down(&queue->mutex); + ret = list_empty(&queue->head); + up(&queue->mutex); + return ret; +} + +#if MALI_STATE_TRACKING +mali_bool _mali_osk_notification_queue_is_empty( _mali_osk_notification_queue_t *queue ) +{ + return _mali_notification_queue_is_empty(queue) ? MALI_TRUE : MALI_FALSE; +} +#endif + +_mali_osk_errcode_t _mali_osk_notification_queue_dequeue( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result ) +{ + _mali_osk_errcode_t ret = _MALI_OSK_ERR_ITEM_NOT_FOUND; + _mali_osk_notification_wrapper_t *wrapper_object; + + down(&queue->mutex); + + if (!list_empty(&queue->head)) + { + wrapper_object = list_entry(queue->head.next, _mali_osk_notification_wrapper_t, list); + *result = &(wrapper_object->data); + list_del_init(&wrapper_object->list); + ret = _MALI_OSK_ERR_OK; + } + + up(&queue->mutex); + + return ret; +} + +_mali_osk_errcode_t _mali_osk_notification_queue_receive( _mali_osk_notification_queue_t *queue, _mali_osk_notification_t **result ) +{ + /* check input */ + MALI_DEBUG_ASSERT_POINTER( queue ); + MALI_DEBUG_ASSERT_POINTER( result ); + + /* default result */ + *result = NULL; + + while (_MALI_OSK_ERR_OK != _mali_osk_notification_queue_dequeue(queue, result)) + { + if (wait_event_interruptible(queue->receive_queue, !_mali_notification_queue_is_empty(queue))) + { + return _MALI_OSK_ERR_RESTARTSYSCALL; + } + } + + return _MALI_OSK_ERR_OK; /* all ok */ +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_pm.c b/drivers/gpu/arm/mali/linux/mali_osk_pm.c new file mode 100644 index 000000000000..e5b8ea1a78a0 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_pm.c @@ -0,0 +1,208 @@ +/** + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_pm.c + * Implementation of the callback functions from common power management + */ + +#include <linux/sched.h> + +#ifdef CONFIG_PM_RUNTIME +#include <linux/pm_runtime.h> +#endif /* CONFIG_PM_RUNTIME */ + +#include <linux/platform_device.h> + +#include "mali_platform.h" +#include "mali_osk.h" +#include "mali_uk_types.h" +#include "mali_pmm.h" +#include "mali_kernel_common.h" +#include "mali_kernel_license.h" +#include "mali_linux_pm.h" +#include "mali_linux_pm_testsuite.h" + +#if MALI_LICENSE_IS_GPL +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON +#ifdef CONFIG_PM_RUNTIME +static int is_runtime =0; +#endif /* CONFIG_PM_RUNTIME */ +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* MALI_LICENSE_IS_GPL */ + +#if MALI_POWER_MGMT_TEST_SUITE + +#ifdef CONFIG_PM +unsigned int mali_pmm_events_triggered_mask = 0; +#endif /* CONFIG_PM */ + +void _mali_osk_pmm_policy_events_notifications(mali_pmm_event_id mali_pmm_event) +{ +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM + + switch (mali_pmm_event) + { + case MALI_PMM_EVENT_JOB_QUEUED: + if (mali_job_scheduling_events_recording_on == 1) + { + mali_pmm_events_triggered_mask |= (1<<0); + } + break; + + case MALI_PMM_EVENT_JOB_SCHEDULED: + if (mali_job_scheduling_events_recording_on == 1) + { + mali_pmm_events_triggered_mask |= (1<<1); + } + break; + + case MALI_PMM_EVENT_JOB_FINISHED: + if (mali_job_scheduling_events_recording_on == 1) + { + mali_pmm_events_triggered_mask |= (1<<2); + mali_job_scheduling_events_recording_on = 0; + pwr_mgmt_status_reg = mali_pmm_events_triggered_mask; + } + break; + + case MALI_PMM_EVENT_TIMEOUT: + if (mali_timeout_event_recording_on == 1) + { + pwr_mgmt_status_reg = (1<<3); + mali_timeout_event_recording_on = 0; + } + break; + + default: + + break; + + } +#endif /* CONFIG_PM */ + +#endif /* MALI_LICENSE_IS_GPL */ +} +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + +/** This function is called when the Mali device has completed power up + * operation. + */ +void _mali_osk_pmm_power_up_done(mali_pmm_message_data data) +{ +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM + is_wake_up_needed = 1; + wake_up_process(pm_thread); + MALI_DEBUG_PRINT(4, ("OSPMM: MALI OSK Power up Done\n" )); + return; +#endif /* CONFIG_PM */ +#endif /* MALI_LICENSE_IS_GPL */ +} + +/** This function is called when the Mali device has completed power down + * operation. + */ +void _mali_osk_pmm_power_down_done(mali_pmm_message_data data) +{ +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM + is_wake_up_needed = 1; +#if MALI_POWER_MGMT_TEST_SUITE + if (is_mali_pmu_present == 0) + { + pwr_mgmt_status_reg = _mali_pmm_cores_list(); + } +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + wake_up_process(pm_thread); + MALI_DEBUG_PRINT(4, ("OSPMM: MALI Power down Done\n" )); + return; + +#endif /* CONFIG_PM */ +#endif /* MALI_LICENSE_IS_GPL */ +} + +/** This function is invoked when mali device is idle. +*/ +_mali_osk_errcode_t _mali_osk_pmm_dev_idle(void) +{ + _mali_osk_errcode_t err = 0; +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + + err = pm_runtime_put_sync(&(mali_gpu_device.dev)); + if(err) + { + MALI_DEBUG_PRINT(4, ("OSPMM: Error in _mali_osk_pmm_dev_idle\n" )); + } +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ +#endif /* MALI_LICENSE_IS_GPL */ + return err; +} + +/** This funtion is invoked when mali device needs to be activated. +*/ +void _mali_osk_pmm_dev_activate(void) +{ + +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM_RUNTIME +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + int err = 0; + if(is_runtime == 0) + { + pm_suspend_ignore_children(&(mali_gpu_device.dev), true); + pm_runtime_enable(&(mali_gpu_device.dev)); + pm_runtime_get_sync(&(mali_gpu_device.dev)); + is_runtime = 1; + } + else + { + err = pm_runtime_get_sync(&(mali_gpu_device.dev)); + } + if(err) + { + MALI_DEBUG_PRINT(4, ("OSPMM: Error in _mali_osk_pmm_dev_activate\n" )); + } +#endif /* MALI_PMM_RUNTIME_JOB_CONTROL_ON */ +#endif /* CONFIG_PM_RUNTIME */ +#endif /* MALI_LICENSE_IS_GPL */ +} + +void _mali_osk_pmm_ospmm_cleanup( void ) +{ +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM + int thread_state; + thread_state = mali_get_ospmm_thread_state(); + if (thread_state) + { + _mali_osk_pmm_dvfs_operation_done(0); + } +#endif /* CONFIG_PM */ +#endif /* MALI_LICENSE_IS_GPL */ +} + +void _mali_osk_pmm_dvfs_operation_done(mali_pmm_message_data data) +{ +#if MALI_LICENSE_IS_GPL +#ifdef CONFIG_PM + is_wake_up_needed = 1; + wake_up_process(dvfs_pm_thread); + MALI_DEBUG_PRINT(4, ("OSPMM: MALI OSK DVFS Operation done\n" )); + return; +#endif /* CONFIG_PM */ +#endif /* MALI_LICENSE_IS_GPL */ +} + + diff --git a/drivers/gpu/arm/mali/linux/mali_osk_profiling_gator.c b/drivers/gpu/arm/mali/linux/mali_osk_profiling_gator.c new file mode 100644 index 000000000000..5c1618c2eb8c --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_profiling_gator.c @@ -0,0 +1,248 @@ +/** + * Copyright (C) 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the + * GNU General Public License version 2 as published by the Free Software + * Foundation, and any use by you of this program is subject to the terms of + * such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained + * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#define DONT_USE_L2_CACHE_COUNTERS /* These traces can cause lock-ups so disable them. */ + +#include <linux/module.h> + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_ukk.h" +#include "mali_osk_profiling.h" +#include "mali_linux_trace.h" + +#if defined(USING_MALI400_L2_CACHE) && !defined(DONT_USE_L2_CACHE_COUNTERS) +#include "mali_kernel_l2_cache.h" +#endif /* USING_MALI400_L2_CACHE */ + +#define COUNTER_DISABLED (-1) + +/** + * Since there are only two physical hardware counters per GPU block, we + * need to multiplex the range of possible events that can be collected by + * each counter. This multiplexing is achieved by means of the following + * table, which holds the event ID that should be collected by each hardware + * counter. + * + * Note that this table should be updated with any change to the above + * _mali_osk_counter_id enumeration. + */ +s32 _mali_osk_hw_counter_table[] = { + COUNTER_DISABLED, /* ACTIVITY_VP */ + COUNTER_DISABLED, /* ACTIVITY_FP0 */ + COUNTER_DISABLED, /* ACTIVITY_FP1 */ + COUNTER_DISABLED, /* ACTIVITY_FP2 */ + COUNTER_DISABLED, /* ACTIVITY_FP3 */ + COUNTER_DISABLED, /* COUNTER_L2_C0 */ + COUNTER_DISABLED, /* COUNTER_L2_C1 */ + COUNTER_DISABLED, /* COUNTER_VP_C0 */ + COUNTER_DISABLED, /* COUNTER_VP_C1 */ + COUNTER_DISABLED, /* COUNTER_FP0_C0 */ + COUNTER_DISABLED, /* COUNTER_FP0_C1 */ + COUNTER_DISABLED, /* COUNTER_FP1_C0 */ + COUNTER_DISABLED, /* COUNTER_FP1_C1 */ + COUNTER_DISABLED, /* COUNTER_FP2_C0 */ + COUNTER_DISABLED, /* COUNTER_FP2_C1 */ + COUNTER_DISABLED, /* COUNTER_FP3_C0 */ + COUNTER_DISABLED, /* COUNTER_FP3_C1 */ +}; + +mali_bool _mali_osk_profiling_query_hw_counter(u32 counter_id, u32 *event_id) +{ + /* Check that the counter is in range... */ + if (counter_id >= FIRST_HW_COUNTER && counter_id <= LAST_HW_COUNTER) + { + s32 id = _mali_osk_hw_counter_table[counter_id]; + + /* ...and enabled */ + if (id != COUNTER_DISABLED) + { + /* Update the pointer to the event ID */ + *event_id = (u32)id; + + return MALI_TRUE; + } + } + + /* The counter was disabled or out of range */ + return MALI_FALSE; +} + +_mali_osk_errcode_t _mali_osk_profiling_init(mali_bool auto_start) +{ + /* Nothing to do */ + return _MALI_OSK_ERR_OK; +} + +void _mali_osk_profiling_term(void) +{ + /* Nothing to do */ +} + +_mali_osk_errcode_t _mali_osk_profiling_start(u32 * limit) +{ + /* Nothing to do */ + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _mali_osk_profiling_stop(u32 *count) +{ + /* Nothing to do */ + return _MALI_OSK_ERR_OK; +} + +u32 _mali_osk_profiling_get_count(void) +{ + return 0; +} + +_mali_osk_errcode_t _mali_osk_profiling_get_event(u32 index, u64* timestamp, + u32* event_id, u32 data[5]) +{ + /* Nothing to do */ + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _mali_osk_profiling_clear(void) +{ + /* Nothing to do */ + return _MALI_OSK_ERR_OK; +} + +mali_bool _mali_osk_profiling_is_recording(void) +{ + return MALI_FALSE; +} + +mali_bool _mali_osk_profiling_have_recording(void) +{ + return MALI_FALSE; +} + +void _mali_osk_profiling_set_default_enable_state(mali_bool enable) +{ + /* Nothing to do */ +} + +mali_bool _mali_osk_profiling_get_default_enable_state(void) +{ + return MALI_FALSE; +} + +_mali_osk_errcode_t _mali_ukk_profiling_start(_mali_uk_profiling_start_s *args) +{ + return _mali_osk_profiling_start(&args->limit); +} + +_mali_osk_errcode_t _mali_ukk_profiling_add_event(_mali_uk_profiling_add_event_s *args) +{ + /* Always add process and thread identificator in the first two data elements for events from user space */ + _mali_osk_profiling_add_event(args->event_id, _mali_osk_get_pid(), _mali_osk_get_tid(), args->data[2], args->data[3], args->data[4]); + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _mali_ukk_profiling_stop(_mali_uk_profiling_stop_s *args) +{ + return _mali_osk_profiling_stop(&args->count); +} + +_mali_osk_errcode_t _mali_ukk_profiling_get_event(_mali_uk_profiling_get_event_s *args) +{ + return _mali_osk_profiling_get_event(args->index, &args->timestamp, &args->event_id, args->data); +} + +_mali_osk_errcode_t _mali_ukk_profiling_clear(_mali_uk_profiling_clear_s *args) +{ + return _mali_osk_profiling_clear(); +} + +_mali_osk_errcode_t _mali_ukk_profiling_get_config(_mali_uk_profiling_get_config_s *args) +{ + return _MALI_OSK_ERR_UNSUPPORTED; +} + +/** + * Called by gator.ko to populate the _mali_osk_hw_counter_table. + * + * @param counter_id The counter ID. + * @param event_id Event ID that the counter should count. + * + * @return 1 on success, 0 on failure. + */ +int _mali_profiling_set_event(u32 counter_id, s32 event_id) +{ +#if defined(USING_MALI400_L2_CACHE) && !defined(DONT_USE_L2_CACHE_COUNTERS) + /* + * The L2 cache counters have special handling in the driver. Since we + * receive new event IDs for each counter one at a time, we need to know + * what the L2 counters are currently programmed to read. This way we + * can supply the current value to the counter we _aren't_ trying to + * program; mali_kernel_l2_cache_set_perf_counters will dutifully ignore + * that counter. + */ + u32 current_src0, current_src1, current_val0, current_val1; + + mali_kernel_l2_cache_get_perf_counters(¤t_src0, ¤t_val0, + ¤t_src1, ¤t_val1); + + if (counter_id == COUNTER_L2_C0) + { + mali_kernel_l2_cache_set_perf_counters(event_id, current_src1, 0); + + return 1; + } + else if (counter_id == COUNTER_L2_C1) + { + mali_kernel_l2_cache_set_perf_counters(current_src0, event_id, 0); + + return 1; + } +#endif /* USING_MALI400_L2_CACHE */ + + /* Check that the counter is in range */ + if (counter_id >= FIRST_HW_COUNTER && counter_id <= LAST_HW_COUNTER) + { + /* + * This does not actually update the hardware with the new event ID; + * it will query what event ID it should be counting on each frame + * via _mali_osk_profiling_query_hw_counter. + */ + _mali_osk_hw_counter_table[counter_id] = event_id; + + return 1; + } + + return 0; +} + +#if defined(USING_MALI400_L2_CACHE) && !defined(DONT_USE_L2_CACHE_COUNTERS) +/** + * Called by gator.ko to retrieve the L2 cache counter values. The L2 cache + * counters are unique in that they are polled by gator, rather than being + * transmitted via the tracepoint mechanism. + * + * @param src0 First L2 cache counter ID. + * @param val0 First L2 cache counter value. + * @param src1 Second L2 cache counter ID. + * @param val1 Second L2 cache counter value. + */ +void _mali_profiling_get_counters(u32 *src0, u32 *val0, u32 *src1, u32 *val1) +{ + mali_kernel_l2_cache_get_perf_counters(src0, val0, src1, val1); +} + +EXPORT_SYMBOL(_mali_profiling_get_counters); +#endif /* USING_MALI400_L2_CACHE */ + +EXPORT_SYMBOL(_mali_profiling_set_event); diff --git a/drivers/gpu/arm/mali/linux/mali_osk_profiling_internal.c b/drivers/gpu/arm/mali/linux/mali_osk_profiling_internal.c new file mode 100644 index 000000000000..c162ffd8fd1a --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_profiling_internal.c @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_mali.h" +#include "mali_ukk.h" +#include "mali_timestamp.h" +#include "mali_osk_profiling.h" + +typedef struct mali_profiling_entry +{ + u64 timestamp; + u32 event_id; + u32 data[5]; +} mali_profiling_entry; + + +typedef enum mali_profiling_state +{ + MALI_PROFILING_STATE_UNINITIALIZED, + MALI_PROFILING_STATE_IDLE, + MALI_PROFILING_STATE_RUNNING, + MALI_PROFILING_STATE_RETURN, +} mali_profiling_state; + + +static _mali_osk_lock_t *lock = NULL; +static mali_profiling_state prof_state = MALI_PROFILING_STATE_UNINITIALIZED; +static mali_profiling_entry* profile_entries = NULL; +static u32 profile_entry_count = 0; +static _mali_osk_atomic_t profile_insert_index; +static _mali_osk_atomic_t profile_entries_written; +static mali_bool mali_profiling_default_enable = MALI_FALSE; + +_mali_osk_errcode_t _mali_osk_profiling_init(mali_bool auto_start) +{ + profile_entries = NULL; + profile_entry_count = 0; + _mali_osk_atomic_init(&profile_insert_index, 0); + _mali_osk_atomic_init(&profile_entries_written, 0); + + lock = _mali_osk_lock_init( _MALI_OSK_LOCKFLAG_SPINLOCK | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, 0 ); + if (NULL == lock) + { + return _MALI_OSK_ERR_FAULT; + } + + prof_state = MALI_PROFILING_STATE_IDLE; + + if (MALI_TRUE == auto_start) + { + u32 limit = MALI_PROFILING_MAX_BUFFER_ENTRIES; /* Use maximum buffer size */ + + mali_profiling_default_enable = MALI_TRUE; /* save this so user space can query this on their startup */ + if (_MALI_OSK_ERR_OK != _mali_osk_profiling_start(&limit)) + { + return _MALI_OSK_ERR_FAULT; + } + } + + return _MALI_OSK_ERR_OK; +} + +void _mali_osk_profiling_term(void) +{ + prof_state = MALI_PROFILING_STATE_UNINITIALIZED; + + /* wait for all elements to be completely inserted into array */ + while (_mali_osk_atomic_read(&profile_insert_index) != _mali_osk_atomic_read(&profile_entries_written)) + { + /* do nothing */; + } + + if (NULL != profile_entries) + { + _mali_osk_vfree(profile_entries); + profile_entries = NULL; + } + + if (NULL != lock) + { + _mali_osk_lock_term(lock); + lock = NULL; + } +} + +inline _mali_osk_errcode_t _mali_osk_profiling_start(u32 * limit) +{ + _mali_osk_errcode_t ret; + + mali_profiling_entry *new_profile_entries = _mali_osk_valloc(*limit * sizeof(mali_profiling_entry)); + + if(NULL == new_profile_entries) + { + return _MALI_OSK_ERR_NOMEM; + } + + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + + if (prof_state != MALI_PROFILING_STATE_IDLE) + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_vfree(new_profile_entries); + return _MALI_OSK_ERR_INVALID_ARGS; /* invalid to call this function in this state */ + } + + if (*limit > MALI_PROFILING_MAX_BUFFER_ENTRIES) + { + *limit = MALI_PROFILING_MAX_BUFFER_ENTRIES; + } + + profile_entries = new_profile_entries; + profile_entry_count = *limit; + + ret = _mali_timestamp_reset(); + + if (ret == _MALI_OSK_ERR_OK) + { + prof_state = MALI_PROFILING_STATE_RUNNING; + } + else + { + _mali_osk_vfree(profile_entries); + profile_entries = NULL; + } + + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return ret; +} + +inline void _mali_osk_profiling_add_event(u32 event_id, u32 data0, u32 data1, u32 data2, u32 data3, u32 data4) +{ + u32 cur_index = _mali_osk_atomic_inc_return(&profile_insert_index) - 1; + + if (prof_state != MALI_PROFILING_STATE_RUNNING || cur_index >= profile_entry_count) + { + /* + * Not in recording mode, or buffer is full + * Decrement index again, and early out + */ + _mali_osk_atomic_dec(&profile_insert_index); + return; + } + + profile_entries[cur_index].timestamp = _mali_timestamp_get(); + profile_entries[cur_index].event_id = event_id; + profile_entries[cur_index].data[0] = data0; + profile_entries[cur_index].data[1] = data1; + profile_entries[cur_index].data[2] = data2; + profile_entries[cur_index].data[3] = data3; + profile_entries[cur_index].data[4] = data4; + + _mali_osk_atomic_inc(&profile_entries_written); +} + +inline void _mali_osk_profiling_report_hw_counter(u32 counter_id, u32 value) +{ + /* Not implemented */ +} + +inline mali_bool _mali_osk_profiling_query_hw_counter(u32 counter_id, u32 *event_id) +{ + return _MALI_OSK_ERR_UNSUPPORTED; +} + +inline _mali_osk_errcode_t _mali_osk_profiling_stop(u32 * count) +{ + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + + if (prof_state != MALI_PROFILING_STATE_RUNNING) + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return _MALI_OSK_ERR_INVALID_ARGS; /* invalid to call this function in this state */ + } + + /* go into return state (user to retreive events), no more events will be added after this */ + prof_state = MALI_PROFILING_STATE_RETURN; + + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + + /* wait for all elements to be completely inserted into array */ + while (_mali_osk_atomic_read(&profile_insert_index) != _mali_osk_atomic_read(&profile_entries_written)) + { + /* do nothing */; + } + + *count = _mali_osk_atomic_read(&profile_insert_index); + + return _MALI_OSK_ERR_OK; +} + +inline u32 _mali_osk_profiling_get_count(void) +{ + u32 retval = 0; + + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + if (prof_state == MALI_PROFILING_STATE_RETURN) + { + retval = _mali_osk_atomic_read(&profile_entries_written); + } + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + + return retval; +} + +inline _mali_osk_errcode_t _mali_osk_profiling_get_event(u32 index, u64* timestamp, u32* event_id, u32 data[5]) +{ + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + + if (prof_state != MALI_PROFILING_STATE_RETURN) + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return _MALI_OSK_ERR_INVALID_ARGS; /* invalid to call this function in this state */ + } + + if (index >= _mali_osk_atomic_read(&profile_entries_written)) + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return _MALI_OSK_ERR_FAULT; + } + + *timestamp = profile_entries[index].timestamp; + *event_id = profile_entries[index].event_id; + data[0] = profile_entries[index].data[0]; + data[1] = profile_entries[index].data[1]; + data[2] = profile_entries[index].data[2]; + data[3] = profile_entries[index].data[3]; + data[4] = profile_entries[index].data[4]; + + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return _MALI_OSK_ERR_OK; +} + +inline _mali_osk_errcode_t _mali_osk_profiling_clear(void) +{ + _mali_osk_lock_wait(lock, _MALI_OSK_LOCKMODE_RW); + + if (prof_state != MALI_PROFILING_STATE_RETURN) + { + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return _MALI_OSK_ERR_INVALID_ARGS; /* invalid to call this function in this state */ + } + + prof_state = MALI_PROFILING_STATE_IDLE; + profile_entry_count = 0; + _mali_osk_atomic_init(&profile_insert_index, 0); + _mali_osk_atomic_init(&profile_entries_written, 0); + if (NULL != profile_entries) + { + _mali_osk_vfree(profile_entries); + profile_entries = NULL; + } + + _mali_osk_lock_signal(lock, _MALI_OSK_LOCKMODE_RW); + return _MALI_OSK_ERR_OK; +} + +mali_bool _mali_osk_profiling_is_recording(void) +{ + return prof_state == MALI_PROFILING_STATE_RUNNING ? MALI_TRUE : MALI_FALSE; +} + +mali_bool _mali_osk_profiling_have_recording(void) +{ + return prof_state == MALI_PROFILING_STATE_RETURN ? MALI_TRUE : MALI_FALSE; +} + +void _mali_osk_profiling_set_default_enable_state(mali_bool enable) +{ + mali_profiling_default_enable = enable; +} + +mali_bool _mali_osk_profiling_get_default_enable_state(void) +{ + return mali_profiling_default_enable; +} + +_mali_osk_errcode_t _mali_ukk_profiling_start(_mali_uk_profiling_start_s *args) +{ + return _mali_osk_profiling_start(&args->limit); +} + +_mali_osk_errcode_t _mali_ukk_profiling_add_event(_mali_uk_profiling_add_event_s *args) +{ + /* Always add process and thread identificator in the first two data elements for events from user space */ + _mali_osk_profiling_add_event(args->event_id, _mali_osk_get_pid(), _mali_osk_get_tid(), args->data[2], args->data[3], args->data[4]); + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _mali_ukk_profiling_stop(_mali_uk_profiling_stop_s *args) +{ + return _mali_osk_profiling_stop(&args->count); +} + +_mali_osk_errcode_t _mali_ukk_profiling_get_event(_mali_uk_profiling_get_event_s *args) +{ + return _mali_osk_profiling_get_event(args->index, &args->timestamp, &args->event_id, args->data); +} + +_mali_osk_errcode_t _mali_ukk_profiling_clear(_mali_uk_profiling_clear_s *args) +{ + return _mali_osk_profiling_clear(); +} + +_mali_osk_errcode_t _mali_ukk_profiling_get_config(_mali_uk_profiling_get_config_s *args) +{ + args->enable_events = mali_profiling_default_enable; + return _MALI_OSK_ERR_OK; +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_specific.h b/drivers/gpu/arm/mali/linux/mali_osk_specific.h new file mode 100644 index 000000000000..a7ad2fc0cb39 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_specific.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_specific.h + * Defines per-OS Kernel level specifics, such as unusual workarounds for + * certain OSs. + */ + +#ifndef __MALI_OSK_SPECIFIC_H__ +#define __MALI_OSK_SPECIFIC_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define MALI_STATIC_INLINE static inline +#define MALI_NON_STATIC_INLINE inline + +#ifdef __cplusplus +} +#endif + +/** The list of events supported by the Mali DDK. */ +typedef enum +{ + /* Vertex processor activity */ + ACTIVITY_VP = 0, + + /* Fragment processor activity */ + ACTIVITY_FP0, + ACTIVITY_FP1, + ACTIVITY_FP2, + ACTIVITY_FP3, + + /* L2 cache counters */ + COUNTER_L2_C0, + COUNTER_L2_C1, + + /* Vertex processor counters */ + COUNTER_VP_C0, + COUNTER_VP_C1, + + /* Fragment processor counters */ + COUNTER_FP0_C0, + COUNTER_FP0_C1, + COUNTER_FP1_C0, + COUNTER_FP1_C1, + COUNTER_FP2_C0, + COUNTER_FP2_C1, + COUNTER_FP3_C0, + COUNTER_FP3_C1, + + /* + * If more hardware counters are added, the _mali_osk_hw_counter_table + * below should also be updated. + */ + + /* EGL software counters */ + COUNTER_EGL_BLIT_TIME, + + /* GLES software counters */ + COUNTER_GLES_DRAW_ELEMENTS_CALLS, + COUNTER_GLES_DRAW_ELEMENTS_NUM_INDICES, + COUNTER_GLES_DRAW_ELEMENTS_NUM_TRANSFORMED, + COUNTER_GLES_DRAW_ARRAYS_CALLS, + COUNTER_GLES_DRAW_ARRAYS_NUM_TRANSFORMED, + COUNTER_GLES_DRAW_POINTS, + COUNTER_GLES_DRAW_LINES, + COUNTER_GLES_DRAW_LINE_LOOP, + COUNTER_GLES_DRAW_LINE_STRIP, + COUNTER_GLES_DRAW_TRIANGLES, + COUNTER_GLES_DRAW_TRIANGLE_STRIP, + COUNTER_GLES_DRAW_TRIANGLE_FAN, + COUNTER_GLES_NON_VBO_DATA_COPY_TIME, + COUNTER_GLES_UNIFORM_BYTES_COPIED_TO_MALI, + COUNTER_GLES_UPLOAD_TEXTURE_TIME, + COUNTER_GLES_UPLOAD_VBO_TIME, + COUNTER_GLES_NUM_FLUSHES, + COUNTER_GLES_NUM_VSHADERS_GENERATED, + COUNTER_GLES_NUM_FSHADERS_GENERATED, + COUNTER_GLES_VSHADER_GEN_TIME, + COUNTER_GLES_FSHADER_GEN_TIME, + COUNTER_GLES_INPUT_TRIANGLES, + COUNTER_GLES_VXCACHE_HIT, + COUNTER_GLES_VXCACHE_MISS, + COUNTER_GLES_VXCACHE_COLLISION, + COUNTER_GLES_CULLED_TRIANGLES, + COUNTER_GLES_CULLED_LINES, + COUNTER_GLES_BACKFACE_TRIANGLES, + COUNTER_GLES_GBCLIP_TRIANGLES, + COUNTER_GLES_GBCLIP_LINES, + COUNTER_GLES_TRIANGLES_DRAWN, + COUNTER_GLES_DRAWCALL_TIME, + COUNTER_GLES_TRIANGLES_COUNT, + COUNTER_GLES_INDEPENDENT_TRIANGLES_COUNT, + COUNTER_GLES_STRIP_TRIANGLES_COUNT, + COUNTER_GLES_FAN_TRIANGLES_COUNT, + COUNTER_GLES_LINES_COUNT, + COUNTER_GLES_INDEPENDENT_LINES_COUNT, + COUNTER_GLES_STRIP_LINES_COUNT, + COUNTER_GLES_LOOP_LINES_COUNT, + + /* Framebuffer capture pseudo-counter */ + COUNTER_FILMSTRIP, + + NUMBER_OF_EVENTS +} _mali_osk_counter_id; + +#define FIRST_ACTIVITY_EVENT ACTIVITY_VP +#define LAST_ACTIVITY_EVENT ACTIVITY_FP3 + +#define FIRST_HW_COUNTER COUNTER_L2_C0 +#define LAST_HW_COUNTER COUNTER_FP3_C1 + +#define FIRST_SW_COUNTER COUNTER_EGL_BLIT_TIME +#define LAST_SW_COUNTER COUNTER_GLES_LOOP_LINES_COUNT + +#define FIRST_SPECIAL_COUNTER COUNTER_FILMSTRIP +#define LAST_SPECIAL_COUNTER COUNTER_FILMSTRIP + +#endif /* __MALI_OSK_SPECIFIC_H__ */ diff --git a/drivers/gpu/arm/mali/linux/mali_osk_time.c b/drivers/gpu/arm/mali/linux/mali_osk_time.c new file mode 100644 index 000000000000..b399b8748801 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_time.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_time.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include "mali_osk.h" +#include <linux/jiffies.h> +#include <linux/time.h> +#include <asm/delay.h> + +int _mali_osk_time_after( u32 ticka, u32 tickb ) +{ + return time_after((unsigned long)ticka, (unsigned long)tickb); +} + +u32 _mali_osk_time_mstoticks( u32 ms ) +{ + return msecs_to_jiffies(ms); +} + +u32 _mali_osk_time_tickstoms( u32 ticks ) +{ + return jiffies_to_msecs(ticks); +} + +u32 _mali_osk_time_tickcount( void ) +{ + return jiffies; +} + +void _mali_osk_time_ubusydelay( u32 usecs ) +{ + udelay(usecs); +} + +u64 _mali_osk_time_get_ns( void ) +{ + struct timespec tsval; + getnstimeofday(&tsval); + return (u64)timespec_to_ns(&tsval); +} diff --git a/drivers/gpu/arm/mali/linux/mali_osk_timers.c b/drivers/gpu/arm/mali/linux/mali_osk_timers.c new file mode 100644 index 000000000000..2353fdb6ecf7 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_osk_timers.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_osk_timers.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +#include <linux/timer.h> +#include <linux/slab.h> +#include "mali_osk.h" +#include "mali_kernel_common.h" + +struct _mali_osk_timer_t_struct +{ + struct timer_list timer; +}; + +typedef void (*timer_timeout_function_t)(unsigned long); + +_mali_osk_timer_t *_mali_osk_timer_init(void) +{ + _mali_osk_timer_t *t = (_mali_osk_timer_t*)kmalloc(sizeof(_mali_osk_timer_t), GFP_KERNEL); + if (NULL != t) init_timer(&t->timer); + return t; +} + +void _mali_osk_timer_add( _mali_osk_timer_t *tim, u32 ticks_to_expire ) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + tim->timer.expires = _mali_osk_time_tickcount() + ticks_to_expire; + add_timer(&(tim->timer)); +} + +void _mali_osk_timer_mod( _mali_osk_timer_t *tim, u32 expiry_tick) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + mod_timer(&(tim->timer), expiry_tick); +} + +void _mali_osk_timer_del( _mali_osk_timer_t *tim ) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + del_timer_sync(&(tim->timer)); +} + +void _mali_osk_timer_setcallback( _mali_osk_timer_t *tim, _mali_osk_timer_callback_t callback, void *data ) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + tim->timer.data = (unsigned long)data; + tim->timer.function = (timer_timeout_function_t)callback; +} + +void _mali_osk_timer_term( _mali_osk_timer_t *tim ) +{ + MALI_DEBUG_ASSERT_POINTER(tim); + kfree(tim); +} diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_core.c b/drivers/gpu/arm/mali/linux/mali_ukk_core.c new file mode 100644 index 000000000000..6c7fed5d0b6e --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_ukk_core.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include <linux/fs.h> /* file system operations */ +#include <linux/slab.h> /* memort allocation functions */ +#include <asm/uaccess.h> /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_session_manager.h" +#include "mali_ukk_wrappers.h" + +int get_api_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_api_version_s __user *uargs) +{ + _mali_uk_get_api_version_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != get_user(kargs.version, &uargs->version)) return -EFAULT; + + kargs.ctx = session_data; + err = _mali_ukk_get_api_version(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.version, &uargs->version)) return -EFAULT; + if (0 != put_user(kargs.compatible, &uargs->compatible)) return -EFAULT; + + return 0; +} + +int get_system_info_size_wrapper(struct mali_session_data *session_data, _mali_uk_get_system_info_size_s __user *uargs) +{ + _mali_uk_get_system_info_size_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_get_system_info_size(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.size, &uargs->size)) return -EFAULT; + + return 0; +} + +int get_system_info_wrapper(struct mali_session_data *session_data, _mali_uk_get_system_info_s __user *uargs) +{ + _mali_uk_get_system_info_s kargs; + _mali_osk_errcode_t err; + _mali_system_info *system_info_user; + _mali_system_info *system_info_kernel; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != get_user(kargs.system_info, &uargs->system_info)) return -EFAULT; + if (0 != get_user(kargs.size, &uargs->size)) return -EFAULT; + + /* A temporary kernel buffer for the system_info datastructure is passed through the system_info + * member. The ukk_private member will point to the user space destination of this buffer so + * that _mali_ukk_get_system_info() can correct the pointers in the system_info correctly + * for user space. + */ + system_info_kernel = kmalloc(kargs.size, GFP_KERNEL); + if (NULL == system_info_kernel) return -EFAULT; + + system_info_user = kargs.system_info; + kargs.system_info = system_info_kernel; + kargs.ukk_private = (u32)system_info_user; + kargs.ctx = session_data; + + err = _mali_ukk_get_system_info(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + kfree(system_info_kernel); + return map_errcode(err); + } + + if (0 != copy_to_user(system_info_user, system_info_kernel, kargs.size)) + { + kfree(system_info_kernel); + return -EFAULT; + } + + kfree(system_info_kernel); + return 0; +} + +int wait_for_notification_wrapper(struct mali_session_data *session_data, _mali_uk_wait_for_notification_s __user *uargs) +{ + _mali_uk_wait_for_notification_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_wait_for_notification(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if(_MALI_NOTIFICATION_CORE_SHUTDOWN_IN_PROGRESS != kargs.type) + { + kargs.ctx = NULL; /* prevent kernel address to be returned to user space */ + if (0 != copy_to_user(uargs, &kargs, sizeof(_mali_uk_wait_for_notification_s))) return -EFAULT; + } + else + { + if (0 != put_user(kargs.type, &uargs->type)) return -EFAULT; + } + + return 0; +} + +int post_notification_wrapper(struct mali_session_data *session_data, _mali_uk_post_notification_s __user *uargs) +{ + _mali_uk_post_notification_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + + if (0 != get_user(kargs.type, &uargs->type)) + { + return -EFAULT; + } + + err = _mali_ukk_post_notification(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + return 0; +} diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_gp.c b/drivers/gpu/arm/mali/linux/mali_ukk_gp.c new file mode 100644 index 000000000000..13a8e25bc411 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_ukk_gp.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include <linux/fs.h> /* file system operations */ +#include <asm/uaccess.h> /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_session_manager.h" +#include "mali_ukk_wrappers.h" + +int gp_start_job_wrapper(struct mali_session_data *session_data, _mali_uk_gp_start_job_s __user *uargs) +{ + _mali_uk_gp_start_job_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + if (!access_ok(VERIFY_WRITE, uargs, sizeof(_mali_uk_gp_start_job_s))) + { + return -EFAULT; + } + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_gp_start_job_s))) return -EFAULT; + + kargs.ctx = session_data; + err = _mali_ukk_gp_start_job(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + kargs.ctx = NULL; /* prevent kernel address to be returned to user space */ + if (0 != copy_to_user(uargs, &kargs, sizeof(_mali_uk_gp_start_job_s))) + { + /* + * If this happens, then user space will not know that the job was actually started, + * and if we return a queued job, then user space will still think that one is still queued. + * This will typically lead to a deadlock in user space. + * This could however only happen if user space deliberately passes a user buffer which + * passes the access_ok(VERIFY_WRITE) check, but isn't fully writable at the time of copy_to_user(). + * The official Mali driver will never attempt to do that, and kernel space should not be affected. + * That is why we do not bother to do a complex rollback in this very very very rare case. + */ + return -EFAULT; + } + + return 0; +} + +int gp_abort_job_wrapper(struct mali_session_data *session_data, _mali_uk_gp_abort_job_s __user *uargs) +{ + _mali_uk_gp_abort_job_s kargs; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_gp_abort_job_s))) return -EFAULT; + + kargs.ctx = session_data; + _mali_ukk_gp_abort_job(&kargs); + + return 0; +} + + +int gp_get_core_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_gp_core_version_s __user *uargs) +{ + _mali_uk_get_gp_core_version_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_get_gp_core_version(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + /* no known transactions to roll-back */ + + if (0 != put_user(kargs.version, &uargs->version)) return -EFAULT; + + return 0; +} + +int gp_suspend_response_wrapper(struct mali_session_data *session_data, _mali_uk_gp_suspend_response_s __user *uargs) +{ + _mali_uk_gp_suspend_response_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_gp_suspend_response_s))) return -EFAULT; + + kargs.ctx = session_data; + err = _mali_ukk_gp_suspend_response(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.cookie, &uargs->cookie)) return -EFAULT; + + /* no known transactions to roll-back */ + return 0; +} + +int gp_get_number_of_cores_wrapper(struct mali_session_data *session_data, _mali_uk_get_gp_number_of_cores_s __user *uargs) +{ + _mali_uk_get_gp_number_of_cores_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_get_gp_number_of_cores(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + /* no known transactions to roll-back */ + + if (0 != put_user(kargs.number_of_cores, &uargs->number_of_cores)) return -EFAULT; + + return 0; +} diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_mem.c b/drivers/gpu/arm/mali/linux/mali_ukk_mem.c new file mode 100644 index 000000000000..6ec41a59339b --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_ukk_mem.c @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include <linux/fs.h> /* file system operations */ +#include <asm/uaccess.h> /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_session_manager.h" +#include "mali_ukk_wrappers.h" + +int mem_init_wrapper(struct mali_session_data *session_data, _mali_uk_init_mem_s __user *uargs) +{ + _mali_uk_init_mem_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_init_mem(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + if (0 != put_user(kargs.mali_address_base, &uargs->mali_address_base)) goto mem_init_rollback; + if (0 != put_user(kargs.memory_size, &uargs->memory_size)) goto mem_init_rollback; + + return 0; + +mem_init_rollback: + { + _mali_uk_term_mem_s kargs; + kargs.ctx = session_data; + err = _mali_ukk_term_mem(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + MALI_DEBUG_PRINT(4, ("reverting _mali_ukk_init_mem, as a result of failing put_user(), failed\n")); + } + } + return -EFAULT; +} + +int mem_term_wrapper(struct mali_session_data *session_data, _mali_uk_term_mem_s __user *uargs) +{ + _mali_uk_term_mem_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_term_mem(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + return 0; +} + +int mem_map_ext_wrapper(struct mali_session_data *session_data, _mali_uk_map_external_mem_s __user * argument) +{ + _mali_uk_map_external_mem_s uk_args; + _mali_osk_errcode_t err_code; + + /* validate input */ + /* the session_data pointer was validated by caller */ + MALI_CHECK_NON_NULL( argument, -EINVAL); + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&uk_args, (void __user *)argument, sizeof(_mali_uk_map_external_mem_s)) ) + { + return -EFAULT; + } + + uk_args.ctx = session_data; + err_code = _mali_ukk_map_external_mem( &uk_args ); + + if (0 != put_user(uk_args.cookie, &argument->cookie)) + { + if (_MALI_OSK_ERR_OK == err_code) + { + /* Rollback */ + _mali_uk_unmap_external_mem_s uk_args_unmap; + + uk_args_unmap.ctx = session_data; + uk_args_unmap.cookie = uk_args.cookie; + err_code = _mali_ukk_unmap_external_mem( &uk_args_unmap ); + if (_MALI_OSK_ERR_OK != err_code) + { + MALI_DEBUG_PRINT(4, ("reverting _mali_ukk_unmap_external_mem, as a result of failing put_user(), failed\n")); + } + } + return -EFAULT; + } + + /* Return the error that _mali_ukk_free_big_block produced */ + return map_errcode(err_code); +} + +int mem_unmap_ext_wrapper(struct mali_session_data *session_data, _mali_uk_unmap_external_mem_s __user * argument) +{ + _mali_uk_unmap_external_mem_s uk_args; + _mali_osk_errcode_t err_code; + + /* validate input */ + /* the session_data pointer was validated by caller */ + MALI_CHECK_NON_NULL( argument, -EINVAL); + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&uk_args, (void __user *)argument, sizeof(_mali_uk_unmap_external_mem_s)) ) + { + return -EFAULT; + } + + uk_args.ctx = session_data; + err_code = _mali_ukk_unmap_external_mem( &uk_args ); + + /* Return the error that _mali_ukk_free_big_block produced */ + return map_errcode(err_code); +} + +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 +int mem_release_ump_wrapper(struct mali_session_data *session_data, _mali_uk_release_ump_mem_s __user * argument) +{ + _mali_uk_release_ump_mem_s uk_args; + _mali_osk_errcode_t err_code; + + /* validate input */ + /* the session_data pointer was validated by caller */ + MALI_CHECK_NON_NULL( argument, -EINVAL); + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&uk_args, (void __user *)argument, sizeof(_mali_uk_release_ump_mem_s)) ) + { + return -EFAULT; + } + + uk_args.ctx = session_data; + err_code = _mali_ukk_release_ump_mem( &uk_args ); + + /* Return the error that _mali_ukk_free_big_block produced */ + return map_errcode(err_code); +} + +int mem_attach_ump_wrapper(struct mali_session_data *session_data, _mali_uk_attach_ump_mem_s __user * argument) +{ + _mali_uk_attach_ump_mem_s uk_args; + _mali_osk_errcode_t err_code; + + /* validate input */ + /* the session_data pointer was validated by caller */ + MALI_CHECK_NON_NULL( argument, -EINVAL); + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&uk_args, (void __user *)argument, sizeof(_mali_uk_attach_ump_mem_s)) ) + { + return -EFAULT; + } + + uk_args.ctx = session_data; + err_code = _mali_ukk_attach_ump_mem( &uk_args ); + + if (0 != put_user(uk_args.cookie, &argument->cookie)) + { + if (_MALI_OSK_ERR_OK == err_code) + { + /* Rollback */ + _mali_uk_release_ump_mem_s uk_args_unmap; + + uk_args_unmap.ctx = session_data; + uk_args_unmap.cookie = uk_args.cookie; + err_code = _mali_ukk_release_ump_mem( &uk_args_unmap ); + if (_MALI_OSK_ERR_OK != err_code) + { + MALI_DEBUG_PRINT(4, ("reverting _mali_ukk_attach_mem, as a result of failing put_user(), failed\n")); + } + } + return -EFAULT; + } + + /* Return the error that _mali_ukk_map_external_ump_mem produced */ + return map_errcode(err_code); +} +#endif /* MALI_USE_UNIFIED_MEMORY_PROVIDER */ + +int mem_query_mmu_page_table_dump_size_wrapper(struct mali_session_data *session_data, _mali_uk_query_mmu_page_table_dump_size_s __user * uargs) +{ + _mali_uk_query_mmu_page_table_dump_size_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + kargs.ctx = session_data; + + err = _mali_ukk_query_mmu_page_table_dump_size(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.size, &uargs->size)) return -EFAULT; + + return 0; +} + +int mem_dump_mmu_page_table_wrapper(struct mali_session_data *session_data, _mali_uk_dump_mmu_page_table_s __user * uargs) +{ + _mali_uk_dump_mmu_page_table_s kargs; + _mali_osk_errcode_t err; + void *buffer; + int rc = -EFAULT; + + /* validate input */ + MALI_CHECK_NON_NULL(uargs, -EINVAL); + /* the session_data pointer was validated by caller */ + + kargs.buffer = NULL; + + /* get location of user buffer */ + if (0 != get_user(buffer, &uargs->buffer)) goto err_exit; + /* get size of mmu page table info buffer from user space */ + if ( 0 != get_user(kargs.size, &uargs->size) ) goto err_exit; + /* verify we can access the whole of the user buffer */ + if (!access_ok(VERIFY_WRITE, buffer, kargs.size)) goto err_exit; + + /* allocate temporary buffer (kernel side) to store mmu page table info */ + kargs.buffer = _mali_osk_valloc(kargs.size); + if (NULL == kargs.buffer) + { + rc = -ENOMEM; + goto err_exit; + } + + kargs.ctx = session_data; + err = _mali_ukk_dump_mmu_page_table(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + rc = map_errcode(err); + goto err_exit; + } + + /* copy mmu page table info back to user space and update pointers */ + if (0 != copy_to_user(uargs->buffer, kargs.buffer, kargs.size) ) goto err_exit; + if (0 != put_user((kargs.register_writes - (u32 *)kargs.buffer) + (u32 *)uargs->buffer, &uargs->register_writes)) goto err_exit; + if (0 != put_user((kargs.page_table_dump - (u32 *)kargs.buffer) + (u32 *)uargs->buffer, &uargs->page_table_dump)) goto err_exit; + if (0 != put_user(kargs.register_writes_size, &uargs->register_writes_size)) goto err_exit; + if (0 != put_user(kargs.page_table_dump_size, &uargs->page_table_dump_size)) goto err_exit; + rc = 0; + +err_exit: + if (kargs.buffer) _mali_osk_vfree(kargs.buffer); + return rc; +} + + + +int mem_get_big_block_wrapper( struct file * filp, _mali_uk_get_big_block_s __user * argument ) +{ + _mali_uk_get_big_block_s uk_args; + _mali_osk_errcode_t err_code; + + /* validate input */ + /* the session_data pointer was validated by caller */ + MALI_CHECK_NON_NULL( argument, -EINVAL); + + /* get call arguments from user space. copy_from_user returns how many bytes which where NOT copied */ + if ( 0 != copy_from_user(&uk_args, (void __user *)argument, sizeof(_mali_uk_get_big_block_s)) ) + { + return -EFAULT; + } + + /* This interface inserts something into the ukk_private word */ + uk_args.ukk_private = (u32)filp; + uk_args.ctx = filp->private_data; + err_code = _mali_ukk_get_big_block( &uk_args ); + + /* Do not leak the private word back into user space */ + uk_args.ukk_private = 0; + + if ( _MALI_OSK_ERR_OK != err_code ) + { + return map_errcode(err_code); + } + + /* From this point on, we must roll-back any failing action to preserve the + * meaning of the U/K interface (e.g. when excluded) */ + + /* transfer response back to user space */ + if ( 0 != copy_to_user(argument, &uk_args, sizeof(_mali_uk_get_big_block_s)) ) + { + /* Roll-back - the _mali_uk_get_big_block call succeeded, so all + * values in uk_args will be correct */ + _mali_uk_free_big_block_s uk_args_rollback = {0, }; + + uk_args_rollback.ctx = uk_args.ctx; + uk_args_rollback.cookie = uk_args.cookie; + err_code = _mali_ukk_free_big_block( &uk_args_rollback ); + + if ( _MALI_OSK_ERR_OK != err_code ) + { + /* error in DEBUG and RELEASE */ + MALI_PRINT_ERROR( ("Failed to rollback get_big_block: %.8X\n", (u32)err_code) ); + } + return -EFAULT; + } + + return 0; +} + +int mem_free_big_block_wrapper(struct mali_session_data *session_data, _mali_uk_free_big_block_s __user * argument) +{ + _mali_uk_free_big_block_s uk_args; + _mali_osk_errcode_t err_code; + + /* validate input */ + /* the session_data pointer was validated by caller */ + MALI_CHECK_NON_NULL( argument, -EINVAL ); + + /* get call arguments from user space. get_user returns 0 on success */ + if ( 0 != get_user(uk_args.cookie, &argument->cookie) ) + { + return -EFAULT; + } + + uk_args.ctx = session_data; + err_code = _mali_ukk_free_big_block( &uk_args ); + + /* Return the error that _mali_ukk_free_big_block produced */ + return map_errcode(err_code); +} diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_pp.c b/drivers/gpu/arm/mali/linux/mali_ukk_pp.c new file mode 100644 index 000000000000..98e01739de3a --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_ukk_pp.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include <linux/fs.h> /* file system operations */ +#include <asm/uaccess.h> /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_session_manager.h" +#include "mali_ukk_wrappers.h" + +int pp_start_job_wrapper(struct mali_session_data *session_data, _mali_uk_pp_start_job_s __user *uargs) +{ + _mali_uk_pp_start_job_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + if (!access_ok(VERIFY_WRITE, uargs, sizeof(_mali_uk_pp_start_job_s))) + { + return -EFAULT; + } + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_pp_start_job_s))) return -EFAULT; + + kargs.ctx = session_data; + err = _mali_ukk_pp_start_job(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.returned_user_job_ptr, &uargs->returned_user_job_ptr) || + 0 != put_user(kargs.status, &uargs->status)) + { + /* + * If this happens, then user space will not know that the job was actually started, + * and if we return a queued job, then user space will still think that one is still queued. + * This will typically lead to a deadlock in user space. + * This could however only happen if user space deliberately passes a user buffer which + * passes the access_ok(VERIFY_WRITE) check, but isn't fully writable at the time of copy_to_user(). + * The official Mali driver will never attempt to do that, and kernel space should not be affected. + * That is why we do not bother to do a complex rollback in this very very very rare case. + */ + return -EFAULT; + } + + return 0; +} + +int pp_abort_job_wrapper(struct mali_session_data *session_data, _mali_uk_pp_abort_job_s __user *uargs) +{ + _mali_uk_pp_abort_job_s kargs; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_pp_abort_job_s))) return -EFAULT; + + kargs.ctx = session_data; + _mali_ukk_pp_abort_job(&kargs); + + return 0; +} + +int pp_get_number_of_cores_wrapper(struct mali_session_data *session_data, _mali_uk_get_pp_number_of_cores_s __user *uargs) +{ + _mali_uk_get_pp_number_of_cores_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_get_pp_number_of_cores(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.number_of_cores, &uargs->number_of_cores)) return -EFAULT; + + return 0; +} + +int pp_get_core_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_pp_core_version_s __user *uargs) +{ + _mali_uk_get_pp_core_version_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + MALI_CHECK_NON_NULL(session_data, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_get_pp_core_version(&kargs); + if (_MALI_OSK_ERR_OK != err) return map_errcode(err); + + if (0 != put_user(kargs.version, &uargs->version)) return -EFAULT; + + return 0; +} diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_profiling.c b/drivers/gpu/arm/mali/linux/mali_ukk_profiling.c new file mode 100644 index 000000000000..ab71b75ff973 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_ukk_profiling.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include <linux/fs.h> /* file system operations */ +#include <asm/uaccess.h> /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_session_manager.h" +#include "mali_ukk_wrappers.h" + +int profiling_start_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_start_s __user *uargs) +{ + _mali_uk_profiling_start_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_profiling_start_s))) + { + return -EFAULT; + } + + kargs.ctx = session_data; + err = _mali_ukk_profiling_start(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + if (0 != put_user(kargs.limit, &uargs->limit)) + { + return -EFAULT; + } + + return 0; +} + +int profiling_add_event_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_add_event_s __user *uargs) +{ + _mali_uk_profiling_add_event_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_profiling_add_event_s))) + { + return -EFAULT; + } + + kargs.ctx = session_data; + err = _mali_ukk_profiling_add_event(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + return 0; +} + +int profiling_stop_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_stop_s __user *uargs) +{ + _mali_uk_profiling_stop_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_profiling_stop(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + if (0 != put_user(kargs.count, &uargs->count)) + { + return -EFAULT; + } + + return 0; +} + +int profiling_get_event_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_get_event_s __user *uargs) +{ + _mali_uk_profiling_get_event_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != get_user(kargs.index, &uargs->index)) + { + return -EFAULT; + } + + kargs.ctx = session_data; + + err = _mali_ukk_profiling_get_event(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + kargs.ctx = NULL; /* prevent kernel address to be returned to user space */ + if (0 != copy_to_user(uargs, &kargs, sizeof(_mali_uk_profiling_get_event_s))) + { + return -EFAULT; + } + + return 0; +} + +int profiling_clear_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_clear_s __user *uargs) +{ + _mali_uk_profiling_clear_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_profiling_clear(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + return 0; +} + +int profiling_get_config_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_get_config_s __user *uargs) +{ + _mali_uk_profiling_get_config_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + kargs.ctx = session_data; + err = _mali_ukk_profiling_get_config(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + if (0 != put_user(kargs.enable_events, &uargs->enable_events)) + { + return -EFAULT; + } + + return 0; +} + diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_vsync.c b/drivers/gpu/arm/mali/linux/mali_ukk_vsync.c new file mode 100644 index 000000000000..d27fac1dae88 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_ukk_vsync.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2011-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include <linux/fs.h> /* file system operations */ +#include <asm/uaccess.h> /* user space access */ + +#include "mali_ukk.h" +#include "mali_osk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_session_manager.h" +#include "mali_ukk_wrappers.h" + + +int vsync_event_report_wrapper(struct mali_session_data *session_data, _mali_uk_vsync_event_report_s __user *uargs) +{ + _mali_uk_vsync_event_report_s kargs; + _mali_osk_errcode_t err; + + MALI_CHECK_NON_NULL(uargs, -EINVAL); + + if (0 != copy_from_user(&kargs, uargs, sizeof(_mali_uk_vsync_event_report_s))) + { + return -EFAULT; + } + + kargs.ctx = session_data; + err = _mali_ukk_vsync_event_report(&kargs); + if (_MALI_OSK_ERR_OK != err) + { + return map_errcode(err); + } + + return 0; +} + diff --git a/drivers/gpu/arm/mali/linux/mali_ukk_wrappers.h b/drivers/gpu/arm/mali/linux/mali_ukk_wrappers.h new file mode 100644 index 000000000000..af56efdde430 --- /dev/null +++ b/drivers/gpu/arm/mali/linux/mali_ukk_wrappers.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_ukk_wrappers.h + * Defines the wrapper functions for each user-kernel function + */ + +#ifndef __MALI_UKK_WRAPPERS_H__ +#define __MALI_UKK_WRAPPERS_H__ + +#include "mali_uk_types.h" +#include "mali_osk.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +int get_system_info_size_wrapper(struct mali_session_data *session_data, _mali_uk_get_system_info_size_s __user *uargs); +int get_system_info_wrapper(struct mali_session_data *session_data, _mali_uk_get_system_info_s __user *uargs); +int wait_for_notification_wrapper(struct mali_session_data *session_data, _mali_uk_wait_for_notification_s __user *uargs); +int get_api_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_api_version_s __user *uargs); +int post_notification_wrapper(struct mali_session_data *session_data, _mali_uk_post_notification_s __user *uargs); +int mem_init_wrapper(struct mali_session_data *session_data, _mali_uk_init_mem_s __user *uargs); +int mem_term_wrapper(struct mali_session_data *session_data, _mali_uk_term_mem_s __user *uargs); +int mem_map_ext_wrapper(struct mali_session_data *session_data, _mali_uk_map_external_mem_s __user * argument); +int mem_unmap_ext_wrapper(struct mali_session_data *session_data, _mali_uk_unmap_external_mem_s __user * argument); +int mem_query_mmu_page_table_dump_size_wrapper(struct mali_session_data *session_data, _mali_uk_query_mmu_page_table_dump_size_s __user * uargs); +int mem_dump_mmu_page_table_wrapper(struct mali_session_data *session_data, _mali_uk_dump_mmu_page_table_s __user * uargs); + +#if MALI_USE_UNIFIED_MEMORY_PROVIDER != 0 +int mem_attach_ump_wrapper(struct mali_session_data *session_data, _mali_uk_attach_ump_mem_s __user * argument); +int mem_release_ump_wrapper(struct mali_session_data *session_data, _mali_uk_release_ump_mem_s __user * argument); +#endif /* MALI_USE_UNIFIED_MEMORY_PROVIDER */ + +int mem_get_big_block_wrapper( struct file * filp, _mali_uk_get_big_block_s __user * argument ); +int mem_free_big_block_wrapper( struct mali_session_data *session_data, _mali_uk_free_big_block_s __user * argument); +int pp_start_job_wrapper(struct mali_session_data *session_data, _mali_uk_pp_start_job_s __user *uargs); +int pp_abort_job_wrapper(struct mali_session_data *session_data, _mali_uk_pp_abort_job_s __user *uargs); +int pp_get_number_of_cores_wrapper(struct mali_session_data *session_data, _mali_uk_get_pp_number_of_cores_s __user *uargs); +int pp_get_core_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_pp_core_version_s __user *uargs); +int gp_start_job_wrapper(struct mali_session_data *session_data, _mali_uk_gp_start_job_s __user *uargs); +int gp_abort_job_wrapper(struct mali_session_data *session_data, _mali_uk_gp_abort_job_s __user *uargs); +int gp_get_number_of_cores_wrapper(struct mali_session_data *session_data, _mali_uk_get_gp_number_of_cores_s __user *uargs); +int gp_get_core_version_wrapper(struct mali_session_data *session_data, _mali_uk_get_gp_core_version_s __user *uargs); +int gp_suspend_response_wrapper(struct mali_session_data *session_data, _mali_uk_gp_suspend_response_s __user *uargs); + +int profiling_start_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_start_s __user *uargs); +int profiling_add_event_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_add_event_s __user *uargs); +int profiling_stop_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_stop_s __user *uargs); +int profiling_get_event_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_get_event_s __user *uargs); +int profiling_clear_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_clear_s __user *uargs); +int profiling_get_config_wrapper(struct mali_session_data *session_data, _mali_uk_profiling_get_config_s __user *uargs); + +int vsync_event_report_wrapper(struct mali_session_data *session_data, _mali_uk_vsync_event_report_s __user *uargs); + +int map_errcode( _mali_osk_errcode_t err ); + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_UKK_WRAPPERS_H__ */ diff --git a/drivers/gpu/arm/mali/platform/default/mali_platform.c b/drivers/gpu/arm/mali/platform/default/mali_platform.c new file mode 100644 index 000000000000..d966f25f6c44 --- /dev/null +++ b/drivers/gpu/arm/mali/platform/default/mali_platform.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_platform.c + * Platform specific Mali driver functions for a default platform + */ +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_platform.h" + + +_mali_osk_errcode_t mali_platform_init(void) +{ + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_deinit(void) +{ + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_platform_power_mode_change(mali_power_mode power_mode) +{ + MALI_SUCCESS; +} + +void mali_gpu_utilization_handler(u32 utilization) +{ +} + +void set_mali_parent_power_domain(void* dev) +{ +} + + diff --git a/drivers/gpu/arm/mali/platform/mali_platform.h b/drivers/gpu/arm/mali/platform/mali_platform.h new file mode 100644 index 000000000000..078bcefa11b1 --- /dev/null +++ b/drivers/gpu/arm/mali/platform/mali_platform.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_platform.h + * Platform specific Mali driver functions + */ + +#ifndef __MALI_PLATFORM_H__ +#define __MALI_PLATFORM_H__ + +#include "mali_osk.h" + +#if !USING_MALI_PMM +/* @brief System power up/down cores that can be passed into mali_platform_powerdown/up() */ +#define MALI_PLATFORM_SYSTEM 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief description of power change reasons + */ +typedef enum mali_power_mode_tag +{ + MALI_POWER_MODE_ON, + MALI_POWER_MODE_LIGHT_SLEEP, + MALI_POWER_MODE_DEEP_SLEEP, +} mali_power_mode; + +/** @brief Platform specific setup and initialisation of MALI + * + * This is called from the entrypoint of the driver to initialize the platform + * + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_platform_init(void); + +/** @brief Platform specific deinitialisation of MALI + * + * This is called on the exit of the driver to terminate the platform + * + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_platform_deinit(void); + +/** @brief Platform specific powerdown sequence of MALI + * + * Call as part of platform init if there is no PMM support, else the + * PMM will call it. + * There are three power modes defined: + * 1) MALI_POWER_MODE_ON + * 2) MALI_POWER_MODE_LIGHT_SLEEP + * 3) MALI_POWER_MODE_DEEP_SLEEP + * MALI power management module transitions to MALI_POWER_MODE_LIGHT_SLEEP mode when MALI is idle + * for idle timer (software timer defined in mali_pmm_policy_jobcontrol.h) duration, MALI transitions + * to MALI_POWER_MODE_LIGHT_SLEEP mode during timeout if there are no more jobs queued. + * MALI power management module transitions to MALI_POWER_MODE_DEEP_SLEEP mode when OS does system power + * off. + * Customer has to add power down code when MALI transitions to MALI_POWER_MODE_LIGHT_SLEEP or MALI_POWER_MODE_DEEP_SLEEP + * mode. + * MALI_POWER_MODE_ON mode is entered when the MALI is to powered up. Some customers want to control voltage regulators during + * the whole system powers on/off. Customer can track in this function whether the MALI is powered up from + * MALI_POWER_MODE_LIGHT_SLEEP or MALI_POWER_MODE_DEEP_SLEEP mode and manage the voltage regulators as well. + * @param power_mode defines the power modes + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_platform_power_mode_change(mali_power_mode power_mode); + + +/** @brief Platform specific handling of GPU utilization data + * + * When GPU utilization data is enabled, this function will be + * periodically called. + * + * @param utilization The workload utilization of the Mali GPU. 0 = no utilization, 256 = full utilization. + */ +void mali_gpu_utilization_handler(u32 utilization); + +/** @brief Setting the power domain of MALI + * + * This function sets the power domain of MALI if Linux run time power management is enabled + * + * @param dev Reference to struct platform_device (defined in linux) used by MALI GPU + */ +void set_mali_parent_power_domain(void* dev); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/drivers/gpu/arm/mali/readme.txt b/drivers/gpu/arm/mali/readme.txt new file mode 100644 index 000000000000..fadafe67a97b --- /dev/null +++ b/drivers/gpu/arm/mali/readme.txt @@ -0,0 +1,30 @@ +Building the Mali Device Driver for Linux +----------------------------------------- + +Build the Mali Device Driver for Linux by running the following make command: + +KDIR=<kdir_path> USING_UMP=<ump_option> USING_PMM=<pmm_option> BUILD=<build_option> \ +TARGET_PLATFORM=<target_platform> CONFIG=<your_config> make + +where + kdir_path: Path to your Linux Kernel directory + ump_option: 1 = Enable UMP support(*) + 0 = disable UMP support + pmm_option: 1 = Enable power management + 0 = Disable power management + build_option: debug = debug build of driver + release = release build of driver + target_platform: Name of the sub-folder in platform/ that contains the + required mali_platform.c file. + your_config: Name of the sub-folder to find the required config.h(**) file + ("arch-" will be prepended) + +(*) For newer Linux Kernels, the Module.symvers file for the UMP device driver + must be available. The UMP_SYMVERS_FILE variable in the Makefile should + point to this file. This file is generated when the UMP driver is built. + +(**) The config.h file contains the configuration parameters needed, like where the + Mali GPU is located, interrupts it uses, memory and so on. + +The result will be a mali.ko file, which can be loaded into the Linux kernel +by using the insmod command. diff --git a/drivers/gpu/arm/mali/regs/mali_200_regs.h b/drivers/gpu/arm/mali/regs/mali_200_regs.h new file mode 100644 index 000000000000..36981043266d --- /dev/null +++ b/drivers/gpu/arm/mali/regs/mali_200_regs.h @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _MALI200_REGS_H_ +#define _MALI200_REGS_H_ + +/** + * Enum for management register addresses. + */ +enum mali200_mgmt_reg +{ + MALI200_REG_ADDR_MGMT_VERSION = 0x1000, + MALI200_REG_ADDR_MGMT_CURRENT_REND_LIST_ADDR = 0x1004, + MALI200_REG_ADDR_MGMT_STATUS = 0x1008, + MALI200_REG_ADDR_MGMT_CTRL_MGMT = 0x100c, + + MALI200_REG_ADDR_MGMT_INT_RAWSTAT = 0x1020, + MALI200_REG_ADDR_MGMT_INT_CLEAR = 0x1024, + MALI200_REG_ADDR_MGMT_INT_MASK = 0x1028, + MALI200_REG_ADDR_MGMT_INT_STATUS = 0x102c, + + MALI200_REG_ADDR_MGMT_WRITE_BOUNDARY_LOW = 0x1044, + + MALI200_REG_ADDR_MGMT_BUS_ERROR_STATUS = 0x1050, + + MALI200_REG_ADDR_MGMT_PERF_CNT_0_ENABLE = 0x1080, + MALI200_REG_ADDR_MGMT_PERF_CNT_0_SRC = 0x1084, + MALI200_REG_ADDR_MGMT_PERF_CNT_0_VALUE = 0x108c, + + MALI200_REG_ADDR_MGMT_PERF_CNT_1_ENABLE = 0x10a0, + MALI200_REG_ADDR_MGMT_PERF_CNT_1_SRC = 0x10a4, + MALI200_REG_ADDR_MGMT_PERF_CNT_1_VALUE = 0x10ac, + + MALI200_REG_SIZEOF_REGISTER_BANK = 0x10f0 + +}; + +#define MALI200_REG_VAL_PERF_CNT_ENABLE 1 + +enum mali200_mgmt_ctrl_mgmt { + MALI200_REG_VAL_CTRL_MGMT_STOP_BUS = (1<<0), +#if defined(USING_MALI200) + MALI200_REG_VAL_CTRL_MGMT_FLUSH_CACHES = (1<<3), +#endif + MALI200_REG_VAL_CTRL_MGMT_FORCE_RESET = (1<<5), + MALI200_REG_VAL_CTRL_MGMT_START_RENDERING = (1<<6), +#if defined(USING_MALI400) + MALI400PP_REG_VAL_CTRL_MGMT_SOFT_RESET = (1<<7), +#endif +}; + +enum mali200_mgmt_irq { + MALI200_REG_VAL_IRQ_END_OF_FRAME = (1<<0), + MALI200_REG_VAL_IRQ_END_OF_TILE = (1<<1), + MALI200_REG_VAL_IRQ_HANG = (1<<2), + MALI200_REG_VAL_IRQ_FORCE_HANG = (1<<3), + MALI200_REG_VAL_IRQ_BUS_ERROR = (1<<4), + MALI200_REG_VAL_IRQ_BUS_STOP = (1<<5), + MALI200_REG_VAL_IRQ_CNT_0_LIMIT = (1<<6), + MALI200_REG_VAL_IRQ_CNT_1_LIMIT = (1<<7), + MALI200_REG_VAL_IRQ_WRITE_BOUNDARY_ERROR = (1<<8), + MALI400PP_REG_VAL_IRQ_INVALID_PLIST_COMMAND = (1<<9), + MALI400PP_REG_VAL_IRQ_CALL_STACK_UNDERFLOW = (1<<10), + MALI400PP_REG_VAL_IRQ_CALL_STACK_OVERFLOW = (1<<11), + MALI400PP_REG_VAL_IRQ_RESET_COMPLETED = (1<<12), +}; + +#if defined USING_MALI200 +#define MALI200_REG_VAL_IRQ_MASK_ALL ((enum mali200_mgmt_irq) (\ + MALI200_REG_VAL_IRQ_END_OF_FRAME |\ + MALI200_REG_VAL_IRQ_END_OF_TILE |\ + MALI200_REG_VAL_IRQ_HANG |\ + MALI200_REG_VAL_IRQ_FORCE_HANG |\ + MALI200_REG_VAL_IRQ_BUS_ERROR |\ + MALI200_REG_VAL_IRQ_BUS_STOP |\ + MALI200_REG_VAL_IRQ_CNT_0_LIMIT |\ + MALI200_REG_VAL_IRQ_CNT_1_LIMIT |\ + MALI200_REG_VAL_IRQ_WRITE_BOUNDARY_ERROR)) +#elif defined USING_MALI400 +#define MALI200_REG_VAL_IRQ_MASK_ALL ((enum mali200_mgmt_irq) (\ + MALI200_REG_VAL_IRQ_END_OF_FRAME |\ + MALI200_REG_VAL_IRQ_END_OF_TILE |\ + MALI200_REG_VAL_IRQ_HANG |\ + MALI200_REG_VAL_IRQ_FORCE_HANG |\ + MALI200_REG_VAL_IRQ_BUS_ERROR |\ + MALI200_REG_VAL_IRQ_BUS_STOP |\ + MALI200_REG_VAL_IRQ_CNT_0_LIMIT |\ + MALI200_REG_VAL_IRQ_CNT_1_LIMIT |\ + MALI200_REG_VAL_IRQ_WRITE_BOUNDARY_ERROR |\ + MALI400PP_REG_VAL_IRQ_INVALID_PLIST_COMMAND |\ + MALI400PP_REG_VAL_IRQ_CALL_STACK_UNDERFLOW |\ + MALI400PP_REG_VAL_IRQ_CALL_STACK_OVERFLOW |\ + MALI400PP_REG_VAL_IRQ_RESET_COMPLETED)) +#else +#error "No supported mali core defined" +#endif + +#if defined USING_MALI200 +#define MALI200_REG_VAL_IRQ_MASK_USED ((enum mali200_mgmt_irq) (\ + MALI200_REG_VAL_IRQ_END_OF_FRAME |\ + MALI200_REG_VAL_IRQ_HANG |\ + MALI200_REG_VAL_IRQ_FORCE_HANG |\ + MALI200_REG_VAL_IRQ_BUS_ERROR |\ + MALI200_REG_VAL_IRQ_WRITE_BOUNDARY_ERROR)) +#elif defined USING_MALI400 +#define MALI200_REG_VAL_IRQ_MASK_USED ((enum mali200_mgmt_irq) (\ + MALI200_REG_VAL_IRQ_END_OF_FRAME |\ + MALI200_REG_VAL_IRQ_HANG |\ + MALI200_REG_VAL_IRQ_FORCE_HANG |\ + MALI200_REG_VAL_IRQ_BUS_ERROR |\ + MALI200_REG_VAL_IRQ_BUS_STOP |\ + MALI200_REG_VAL_IRQ_WRITE_BOUNDARY_ERROR |\ + MALI400PP_REG_VAL_IRQ_INVALID_PLIST_COMMAND |\ + MALI400PP_REG_VAL_IRQ_CALL_STACK_UNDERFLOW |\ + MALI400PP_REG_VAL_IRQ_CALL_STACK_OVERFLOW)) +#else +#error "No supported mali core defined" +#endif + +#define MALI200_REG_VAL_IRQ_MASK_NONE ((enum mali200_mgmt_irq)(0)) + +enum mali200_mgmt_status { + MALI200_REG_VAL_STATUS_RENDERING_ACTIVE = (1<<0), + MALI200_REG_VAL_STATUS_BUS_STOPPED = (1<<4), +}; + +enum mali200_render_unit +{ + MALI200_REG_ADDR_FRAME = 0x0000, +}; + +#if defined USING_MALI200 +#define MALI200_NUM_REGS_FRAME ((0x04C/4)+1) +#elif defined USING_MALI400 +#define MALI200_NUM_REGS_FRAME ((0x058/4)+1) +#else +#error "No supported mali core defined" +#endif + +enum mali200_wb_unit { + MALI200_REG_ADDR_WB0 = 0x0100, + MALI200_REG_ADDR_WB1 = 0x0200, + MALI200_REG_ADDR_WB2 = 0x0300 +}; + +/** The number of registers in one single writeback unit */ +#ifndef MALI200_NUM_REGS_WBx +#define MALI200_NUM_REGS_WBx ((0x02C/4)+1) +#endif + +/* This should be in the top 16 bit of the version register of Mali PP */ +#if defined USING_MALI200 +#define MALI_PP_PRODUCT_ID 0xC807 +#elif defined USING_MALI400 +#define MALI300_PP_PRODUCT_ID 0xCE07 +#define MALI400_PP_PRODUCT_ID 0xCD07 +#define MALI_PP_PRODUCT_ID MALI400_PP_PRODUCT_ID +#else +#error "No supported mali core defined" +#endif + + +#endif /* _MALI200_REGS_H_ */ diff --git a/drivers/gpu/arm/mali/regs/mali_gp_regs.h b/drivers/gpu/arm/mali/regs/mali_gp_regs.h new file mode 100644 index 000000000000..2b1c021c2042 --- /dev/null +++ b/drivers/gpu/arm/mali/regs/mali_gp_regs.h @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _MALIGP2_CONROL_REGS_H_ +#define _MALIGP2_CONROL_REGS_H_ + +/** + * These are the different geometry processor controll registers. + * Their usage is to control and monitor the operation of the + * Vertex Shader and the Polygon List Builer in the geometry processor. + * Addresses are in 32-bit word relative sizes. + * @see [P0081] "Geometry Processor Data Structures" for details + */ + +typedef enum { + MALIGP2_REG_ADDR_MGMT_VSCL_START_ADDR = 0x00, + MALIGP2_REG_ADDR_MGMT_VSCL_END_ADDR = 0x04, + MALIGP2_REG_ADDR_MGMT_PLBUCL_START_ADDR = 0x08, + MALIGP2_REG_ADDR_MGMT_PLBUCL_END_ADDR = 0x0c, + MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_START_ADDR = 0x10, + MALIGP2_REG_ADDR_MGMT_PLBU_ALLOC_END_ADDR = 0x14, + MALIGP2_REG_ADDR_MGMT_CMD = 0x20, + MALIGP2_REG_ADDR_MGMT_INT_RAWSTAT = 0x24, + MALIGP2_REG_ADDR_MGMT_INT_CLEAR = 0x28, + MALIGP2_REG_ADDR_MGMT_INT_MASK = 0x2C, + MALIGP2_REG_ADDR_MGMT_INT_STAT = 0x30, + MALIGP2_REG_ADDR_MGMT_WRITE_BOUND_LOW = 0x34, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_ENABLE = 0x3C, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_ENABLE = 0x40, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_SRC = 0x44, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_SRC = 0x48, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_VALUE = 0x4C, + MALIGP2_REG_ADDR_MGMT_PERF_CNT_1_VALUE = 0x50, + MALIGP2_REG_ADDR_MGMT_STATUS = 0x68, + MALIGP2_REG_ADDR_MGMT_VERSION = 0x6C, + MALIGP2_REG_ADDR_MGMT_VSCL_START_ADDR_READ = 0x80, + MALIGP2_REG_ADDR_MGMT_PLBCL_START_ADDR_READ = 0x84, + MALIGP2_CONTR_AXI_BUS_ERROR_STAT = 0x94, + MALIGP2_REGISTER_ADDRESS_SPACE_SIZE = 0x98, +} maligp_reg_addr_mgmt_addr; + +#define MALIGP2_REG_VAL_PERF_CNT_ENABLE 1 + +/** + * Commands to geometry processor. + * @see MALIGP2_CTRL_REG_CMD + */ +typedef enum +{ + MALIGP2_REG_VAL_CMD_START_VS = (1<< 0), + MALIGP2_REG_VAL_CMD_START_PLBU = (1<< 1), + MALIGP2_REG_VAL_CMD_UPDATE_PLBU_ALLOC = (1<< 4), + MALIGP2_REG_VAL_CMD_RESET = (1<< 5), + MALIGP2_REG_VAL_CMD_FORCE_HANG = (1<< 6), + MALIGP2_REG_VAL_CMD_STOP_BUS = (1<< 9), +#if defined(USING_MALI400) + MALI400GP_REG_VAL_CMD_SOFT_RESET = (1<<10), +#endif +} mgp_contr_reg_val_cmd; + + +/** @defgroup MALIGP2_IRQ + * Interrupt status of geometry processor. + * @see MALIGP2_CTRL_REG_INT_RAWSTAT, MALIGP2_REG_ADDR_MGMT_INT_CLEAR, + * MALIGP2_REG_ADDR_MGMT_INT_MASK, MALIGP2_REG_ADDR_MGMT_INT_STAT + * @{ + */ +#define MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST (1 << 0) +#define MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST (1 << 1) +#define MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM (1 << 2) +#define MALIGP2_REG_VAL_IRQ_VS_SEM_IRQ (1 << 3) +#define MALIGP2_REG_VAL_IRQ_PLBU_SEM_IRQ (1 << 4) +#define MALIGP2_REG_VAL_IRQ_HANG (1 << 5) +#define MALIGP2_REG_VAL_IRQ_FORCE_HANG (1 << 6) +#define MALIGP2_REG_VAL_IRQ_PERF_CNT_0_LIMIT (1 << 7) +#define MALIGP2_REG_VAL_IRQ_PERF_CNT_1_LIMIT (1 << 8) +#define MALIGP2_REG_VAL_IRQ_WRITE_BOUND_ERR (1 << 9) +#define MALIGP2_REG_VAL_IRQ_SYNC_ERROR (1 << 10) +#define MALIGP2_REG_VAL_IRQ_AXI_BUS_ERROR (1 << 11) +#if defined USING_MALI400 +#define MALI400GP_REG_VAL_IRQ_AXI_BUS_STOPPED (1 << 12) +#define MALI400GP_REG_VAL_IRQ_VS_INVALID_CMD (1 << 13) +#define MALI400GP_REG_VAL_IRQ_PLB_INVALID_CMD (1 << 14) +#define MALI400GP_REG_VAL_IRQ_RESET_COMPLETED (1 << 19) +#define MALI400GP_REG_VAL_IRQ_SEMAPHORE_UNDERFLOW (1 << 20) +#define MALI400GP_REG_VAL_IRQ_SEMAPHORE_OVERFLOW (1 << 21) +#define MALI400GP_REG_VAL_IRQ_PTR_ARRAY_OUT_OF_BOUNDS (1 << 22) +#elif !defined USING_MALI200 +#error "No supported mali core defined" +#endif + +/* Mask defining all IRQs in MaliGP2 */ +#if defined USING_MALI200 +#define MALIGP2_REG_VAL_IRQ_MASK_ALL \ + (\ + MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM | \ + MALIGP2_REG_VAL_IRQ_VS_SEM_IRQ | \ + MALIGP2_REG_VAL_IRQ_PLBU_SEM_IRQ | \ + MALIGP2_REG_VAL_IRQ_HANG | \ + MALIGP2_REG_VAL_IRQ_FORCE_HANG | \ + MALIGP2_REG_VAL_IRQ_PERF_CNT_0_LIMIT | \ + MALIGP2_REG_VAL_IRQ_PERF_CNT_1_LIMIT | \ + MALIGP2_REG_VAL_IRQ_WRITE_BOUND_ERR | \ + MALIGP2_REG_VAL_IRQ_SYNC_ERROR | \ + MALIGP2_REG_VAL_IRQ_AXI_BUS_ERROR) +#elif defined USING_MALI400 +#define MALIGP2_REG_VAL_IRQ_MASK_ALL \ + (\ + MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM | \ + MALIGP2_REG_VAL_IRQ_VS_SEM_IRQ | \ + MALIGP2_REG_VAL_IRQ_PLBU_SEM_IRQ | \ + MALIGP2_REG_VAL_IRQ_HANG | \ + MALIGP2_REG_VAL_IRQ_FORCE_HANG | \ + MALIGP2_REG_VAL_IRQ_PERF_CNT_0_LIMIT | \ + MALIGP2_REG_VAL_IRQ_PERF_CNT_1_LIMIT | \ + MALIGP2_REG_VAL_IRQ_WRITE_BOUND_ERR | \ + MALIGP2_REG_VAL_IRQ_SYNC_ERROR | \ + MALIGP2_REG_VAL_IRQ_AXI_BUS_ERROR | \ + MALI400GP_REG_VAL_IRQ_AXI_BUS_STOPPED | \ + MALI400GP_REG_VAL_IRQ_VS_INVALID_CMD | \ + MALI400GP_REG_VAL_IRQ_PLB_INVALID_CMD | \ + MALI400GP_REG_VAL_IRQ_RESET_COMPLETED | \ + MALI400GP_REG_VAL_IRQ_SEMAPHORE_UNDERFLOW | \ + MALI400GP_REG_VAL_IRQ_SEMAPHORE_OVERFLOW | \ + MALI400GP_REG_VAL_IRQ_PTR_ARRAY_OUT_OF_BOUNDS) +#else +#error "No supported mali core defined" +#endif + +/* Mask defining the IRQs in MaliGP2 which we use*/ +#if defined USING_MALI200 +#define MALIGP2_REG_VAL_IRQ_MASK_USED \ + (\ + MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM | \ + MALIGP2_REG_VAL_IRQ_HANG | \ + MALIGP2_REG_VAL_IRQ_FORCE_HANG | \ + MALIGP2_REG_VAL_IRQ_WRITE_BOUND_ERR | \ + MALIGP2_REG_VAL_IRQ_SYNC_ERROR | \ + MALIGP2_REG_VAL_IRQ_AXI_BUS_ERROR) +#elif defined USING_MALI400 +#define MALIGP2_REG_VAL_IRQ_MASK_USED \ + (\ + MALIGP2_REG_VAL_IRQ_VS_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_END_CMD_LST | \ + MALIGP2_REG_VAL_IRQ_PLBU_OUT_OF_MEM | \ + MALIGP2_REG_VAL_IRQ_HANG | \ + MALIGP2_REG_VAL_IRQ_FORCE_HANG | \ + MALIGP2_REG_VAL_IRQ_WRITE_BOUND_ERR | \ + MALIGP2_REG_VAL_IRQ_SYNC_ERROR | \ + MALIGP2_REG_VAL_IRQ_AXI_BUS_ERROR | \ + MALI400GP_REG_VAL_IRQ_VS_INVALID_CMD | \ + MALI400GP_REG_VAL_IRQ_PLB_INVALID_CMD | \ + MALI400GP_REG_VAL_IRQ_SEMAPHORE_UNDERFLOW | \ + MALI400GP_REG_VAL_IRQ_SEMAPHORE_OVERFLOW | \ + MALI400GP_REG_VAL_IRQ_PTR_ARRAY_OUT_OF_BOUNDS) +#else +#error "No supported mali core defined" +#endif + +/* Mask defining non IRQs on MaliGP2*/ +#define MALIGP2_REG_VAL_IRQ_MASK_NONE 0 + +/** }@ defgroup MALIGP2_IRQ*/ + +/** @defgroup MALIGP2_STATUS + * The different Status values to the geometry processor. + * @see MALIGP2_CTRL_REG_STATUS + * @{ + */ +#define MALIGP2_REG_VAL_STATUS_VS_ACTIVE 0x0002 +#define MALIGP2_REG_VAL_STATUS_BUS_STOPPED 0x0004 +#define MALIGP2_REG_VAL_STATUS_PLBU_ACTIVE 0x0008 +#define MALIGP2_REG_VAL_STATUS_BUS_ERROR 0x0040 +#define MALIGP2_REG_VAL_STATUS_WRITE_BOUND_ERR 0x0100 +/** }@ defgroup MALIGP2_STATUS*/ + +#define MALIGP2_REG_VAL_STATUS_MASK_ACTIVE (\ + MALIGP2_REG_VAL_STATUS_VS_ACTIVE|\ + MALIGP2_REG_VAL_STATUS_PLBU_ACTIVE) + + +#define MALIGP2_REG_VAL_STATUS_MASK_ERROR (\ + MALIGP2_REG_VAL_STATUS_BUS_ERROR |\ + MALIGP2_REG_VAL_STATUS_WRITE_BOUND_ERR ) + +/* This should be in the top 16 bit of the version register of gp.*/ +#if defined(USING_MALI200) +#define MALI_GP_PRODUCT_ID 0xA07 +#elif defined(USING_MALI400) +#define MALI300_GP_PRODUCT_ID 0xC07 +#define MALI400_GP_PRODUCT_ID 0xB07 +#define MALI_GP_PRODUCT_ID MALI400_GP_PRODUCT_ID +#else +#error "No supported mali core defined" +#endif + +/** + * The different sources for instrumented on the geometry processor. + * @see MALIGP2_REG_ADDR_MGMT_PERF_CNT_0_SRC + */ + +enum MALIGP2_cont_reg_perf_cnt_src { + MALIGP2_REG_VAL_PERF_CNT1_SRC_NUMBER_OF_VERTICES_PROCESSED = 0x0a, +}; + +#endif diff --git a/drivers/gpu/arm/mali/timestamp-arm11-cc/mali_timestamp.c b/drivers/gpu/arm/mali/timestamp-arm11-cc/mali_timestamp.c new file mode 100644 index 000000000000..242685348218 --- /dev/null +++ b/drivers/gpu/arm/mali/timestamp-arm11-cc/mali_timestamp.c @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_timestamp.h" + +/* This file is intentionally left empty, as all functions are inlined in mali_profiling_sampler.h */ diff --git a/drivers/gpu/arm/mali/timestamp-arm11-cc/mali_timestamp.h b/drivers/gpu/arm/mali/timestamp-arm11-cc/mali_timestamp.h new file mode 100644 index 000000000000..05517260cad6 --- /dev/null +++ b/drivers/gpu/arm/mali/timestamp-arm11-cc/mali_timestamp.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_TIMESTAMP_H__ +#define __MALI_TIMESTAMP_H__ + +#include "mali_osk.h" + +MALI_STATIC_INLINE _mali_osk_errcode_t _mali_timestamp_reset(void) +{ + /* + * reset counters and overflow flags + */ + + u32 mask = (1 << 0) | /* enable all three counters */ + (0 << 1) | /* reset both Count Registers to 0x0 */ + (1 << 2) | /* reset the Cycle Counter Register to 0x0 */ + (0 << 3) | /* 1 = Cycle Counter Register counts every 64th processor clock cycle */ + (0 << 4) | /* Count Register 0 interrupt enable */ + (0 << 5) | /* Count Register 1 interrupt enable */ + (0 << 6) | /* Cycle Counter interrupt enable */ + (0 << 8) | /* Count Register 0 overflow flag (clear or write, flag on read) */ + (0 << 9) | /* Count Register 1 overflow flag (clear or write, flag on read) */ + (1 << 10); /* Cycle Counter Register overflow flag (clear or write, flag on read) */ + + __asm__ __volatile__ ("MCR p15, 0, %0, c15, c12, 0" : : "r" (mask) ); + + return _MALI_OSK_ERR_OK; +} + +MALI_STATIC_INLINE u64 _mali_timestamp_get(void) +{ + u32 result; + + /* this is for the clock cycles */ + __asm__ __volatile__ ("MRC p15, 0, %0, c15, c12, 1" : "=r" (result)); + + return (u64)result; +} + +#endif /* __MALI_TIMESTAMP_H__ */ diff --git a/drivers/gpu/arm/mali/timestamp-default/mali_timestamp.c b/drivers/gpu/arm/mali/timestamp-default/mali_timestamp.c new file mode 100644 index 000000000000..242685348218 --- /dev/null +++ b/drivers/gpu/arm/mali/timestamp-default/mali_timestamp.c @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_timestamp.h" + +/* This file is intentionally left empty, as all functions are inlined in mali_profiling_sampler.h */ diff --git a/drivers/gpu/arm/mali/timestamp-default/mali_timestamp.h b/drivers/gpu/arm/mali/timestamp-default/mali_timestamp.h new file mode 100644 index 000000000000..e6d3f2aaf516 --- /dev/null +++ b/drivers/gpu/arm/mali/timestamp-default/mali_timestamp.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MALI_TIMESTAMP_H__ +#define __MALI_TIMESTAMP_H__ + +#include "mali_osk.h" + +MALI_STATIC_INLINE _mali_osk_errcode_t _mali_timestamp_reset(void) +{ + return _MALI_OSK_ERR_OK; +} + +MALI_STATIC_INLINE u64 _mali_timestamp_get(void) +{ + return _mali_osk_time_get_ns(); +} + +#endif /* __MALI_TIMESTAMP_H__ */ diff --git a/drivers/gpu/arm/ump/Kconfig b/drivers/gpu/arm/ump/Kconfig new file mode 100644 index 000000000000..62f1cec93110 --- /dev/null +++ b/drivers/gpu/arm/ump/Kconfig @@ -0,0 +1,19 @@ +config UMP + bool "Enable UMP(Unified Memory Provider)" + default n + ---help--- + This enables UMP memory provider + +config UMP_MEM_SIZE + int "UMP Memory Size" + depends on UMP + default "64" + ---help--- + This value decide memory size of UMP (unit is MByte). + +config UMP_DEBUG + bool "Enables debug messages" + depends on UMP + default n + ---help--- + This enables UMP driver debug messages diff --git a/drivers/gpu/arm/ump/Makefile b/drivers/gpu/arm/ump/Makefile new file mode 100755 index 000000000000..a560b160da47 --- /dev/null +++ b/drivers/gpu/arm/ump/Makefile @@ -0,0 +1,67 @@ +# +# Copyright (C) 2010-2011 ARM Limited. All rights reserved. +# +# This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# +# A copy of the licence is included with the program, and can also be obtained from Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +ifeq ($(CONFIG_USING_OS_MEMORY),y) +USING_MEMORY_TYPE=1 +endif + +UMP_FILE_PREFIX = +UDD_FILE_PREFIX := drivers/gpu/arm/ump/ + +# For each arch check: CROSS_COMPILE , KDIR , CFLAGS += -DARCH + +## @note Should allow overriding of building UMP for non-debug: + +ifeq (($CONFIG_UMP_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif +EXTRA_CFLAGS += -DMALI_STATE_TRACKING=0 +USING_MEMORY_TYPE ?=0 # 0: dedicated memory 1: OS memory + +EXTRA_CFLAGS += -I$(UDD_FILE_PREFIX) -I$(UDD_FILE_PREFIX)/common -I$(UDD_FILE_PREFIX)/linux -I$(UDD_FILE_PREFIX)/../mali/common -I$(UDD_FILE_PREFIX)/../mali/linux -I$(UDD_FILE_PREFIX)/include + +EXTRA_CFLAGS += -DUMP_MEMORY_TYPE=$(USING_MEMORY_TYPE) +# For customer releases the Linux Device Drivers will be provided as ARM proprietary and GPL releases: +# The ARM proprietary product will only include the license/proprietary directory +# The GPL product will only include the license/gpl directory + +ifeq ($(wildcard $(UDD_FILE_PREFIX)/linux/license/gpl/*),) +EXTRA_CFLAGS += -I$(UDD_FILE_PREFIX)/linux/license/proprietary +else +EXTRA_CFLAGS += -I$(UDD_FILE_PREFIX)/linux/license/gpl +endif + +obj-$(CONFIG_UMP) += ump.o + +ump-y:= $(UMP_FILE_PREFIX)common/ump_kernel_common.o \ + $(UMP_FILE_PREFIX)common/ump_kernel_descriptor_mapping.o \ + $(UMP_FILE_PREFIX)common/ump_kernel_api.o \ + $(UMP_FILE_PREFIX)common/ump_kernel_ref_drv.o \ + $(UMP_FILE_PREFIX)linux/ump_kernel_linux.o \ + $(UMP_FILE_PREFIX)linux/ump_kernel_memory_backend_os.o \ + $(UMP_FILE_PREFIX)linux/ump_kernel_memory_backend_dedicated.o \ + $(UMP_FILE_PREFIX)linux/ump_memory_backend.o \ + $(UMP_FILE_PREFIX)linux/ump_ukk_wrappers.o \ + $(UMP_FILE_PREFIX)linux/ump_ukk_ref_wrappers.o \ + $(UMP_FILE_PREFIX)linux/ump_osk_atomics.o \ + $(UMP_FILE_PREFIX)linux/ump_osk_low_level_mem.o \ + $(UMP_FILE_PREFIX)linux/ump_osk_misc.o \ + $(UMP_FILE_PREFIX)../mali/linux/mali_osk_atomics.o \ + $(UMP_FILE_PREFIX)../mali/linux/mali_osk_locks.o \ + $(UMP_FILE_PREFIX)../mali/linux/mali_osk_memory.o \ + $(UMP_FILE_PREFIX)../mali/linux/mali_osk_math.o \ + $(UMP_FILE_PREFIX)../mali/linux/mali_osk_misc.o + +# Get subversion revision number, fall back to 0000 if no svn info is available +SVN_REV:=$(shell ((svnversion | grep -qvE '(exported|Unversioned)' && echo -n 'Revision: ' && svnversion) || git svn info | sed -e 's/$$$$/M/' | grep '^Revision: ' || echo ${MALI_RELEASE_NAME}) 2>/dev/null | sed -e 's/^Revision: //') + +EXTRA_CFLAGS += -DSVN_REV=$(SVN_REV) +EXTRA_CFLAGS += -DSVN_REV_STRING=\"$(SVN_REV)\" + diff --git a/drivers/gpu/arm/ump/arch/config.h b/drivers/gpu/arm/ump/arch/config.h new file mode 100644 index 000000000000..6efc91e4bc96 --- /dev/null +++ b/drivers/gpu/arm/ump/arch/config.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ARCH_CONFIG_H__ +#define __ARCH_CONFIG_H__ + +#define ARCH_UMP_BACKEND_DEFAULT UMP_MEMORY_TYPE +#define ARCH_UMP_MEMORY_ADDRESS_DEFAULT 0xC8000000 +#define ARCH_UMP_MEMORY_SIZE_DEFAULT CONFIG_UMP_MEM_SIZE * 1024UL * 1024UL + +#endif /* __ARCH_CONFIG_H__ */ diff --git a/drivers/gpu/arm/ump/common/ump_kernel_api.c b/drivers/gpu/arm/ump/common/ump_kernel_api.c new file mode 100644 index 000000000000..c6ea89634b1a --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_kernel_api.c @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_osk.h" +#include "mali_osk_list.h" +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_kernel_interface.h" +#include "ump_kernel_common.h" + + + +/* ---------------- UMP kernel space API functions follows ---------------- */ + + + +UMP_KERNEL_API_EXPORT ump_secure_id ump_dd_secure_id_get(ump_dd_handle memh) +{ + ump_dd_mem * mem = (ump_dd_mem *)memh; + + DEBUG_ASSERT_POINTER(mem); + + DBG_MSG(5, ("Returning secure ID. ID: %u\n", mem->secure_id)); + + return mem->secure_id; +} + + + +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_secure_id(ump_secure_id secure_id) +{ + ump_dd_mem * mem; + + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + DBG_MSG(5, ("Getting handle from secure ID. ID: %u\n", secure_id)); + if (0 != ump_descriptor_mapping_get(device.secure_id_map, (int)secure_id, (void**)&mem)) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(1, ("Secure ID not found. ID: %u\n", secure_id)); + return UMP_DD_HANDLE_INVALID; + } + + ump_dd_reference_add(mem); + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + return (ump_dd_handle)mem; +} + + + +UMP_KERNEL_API_EXPORT unsigned long ump_dd_phys_block_count_get(ump_dd_handle memh) +{ + ump_dd_mem * mem = (ump_dd_mem*) memh; + + DEBUG_ASSERT_POINTER(mem); + + return mem->nr_blocks; +} + + + +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_blocks_get(ump_dd_handle memh, ump_dd_physical_block * blocks, unsigned long num_blocks) +{ + ump_dd_mem * mem = (ump_dd_mem *)memh; + + DEBUG_ASSERT_POINTER(mem); + + if (blocks == NULL) + { + DBG_MSG(1, ("NULL parameter in ump_dd_phys_blocks_get()\n")); + return UMP_DD_INVALID; + } + + if (mem->nr_blocks != num_blocks) + { + DBG_MSG(1, ("Specified number of blocks do not match actual number of blocks\n")); + return UMP_DD_INVALID; + } + + DBG_MSG(5, ("Returning physical block information. ID: %u\n", mem->secure_id)); + + _mali_osk_memcpy(blocks, mem->block_array, sizeof(ump_dd_physical_block) * mem->nr_blocks); + + return UMP_DD_SUCCESS; +} + + + +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_block_get(ump_dd_handle memh, unsigned long index, ump_dd_physical_block * block) +{ + ump_dd_mem * mem = (ump_dd_mem *)memh; + + DEBUG_ASSERT_POINTER(mem); + + if (block == NULL) + { + DBG_MSG(1, ("NULL parameter in ump_dd_phys_block_get()\n")); + return UMP_DD_INVALID; + } + + if (index >= mem->nr_blocks) + { + DBG_MSG(5, ("Invalid index specified in ump_dd_phys_block_get()\n")); + return UMP_DD_INVALID; + } + + DBG_MSG(5, ("Returning physical block information. ID: %u, index: %lu\n", mem->secure_id, index)); + + *block = mem->block_array[index]; + + return UMP_DD_SUCCESS; +} + + + +UMP_KERNEL_API_EXPORT unsigned long ump_dd_size_get(ump_dd_handle memh) +{ + ump_dd_mem * mem = (ump_dd_mem*)memh; + + DEBUG_ASSERT_POINTER(mem); + + DBG_MSG(5, ("Returning size. ID: %u, size: %lu\n", mem->secure_id, mem->size_bytes)); + + return mem->size_bytes; +} + + + +UMP_KERNEL_API_EXPORT void ump_dd_reference_add(ump_dd_handle memh) +{ + ump_dd_mem * mem = (ump_dd_mem*)memh; + int new_ref; + + DEBUG_ASSERT_POINTER(mem); + + new_ref = _ump_osk_atomic_inc_and_read(&mem->ref_count); + + DBG_MSG(4, ("Memory reference incremented. ID: %u, new value: %d\n", mem->secure_id, new_ref)); +} + + + +UMP_KERNEL_API_EXPORT void ump_dd_reference_release(ump_dd_handle memh) +{ + int new_ref; + ump_dd_mem * mem = (ump_dd_mem*)memh; + + DEBUG_ASSERT_POINTER(mem); + + /* We must hold this mutex while doing the atomic_dec_and_read, to protect + that elements in the ump_descriptor_mapping table is always valid. If they + are not, userspace may accidently map in this secure_ids right before its freed + giving a mapped backdoor into unallocated memory.*/ + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + new_ref = _ump_osk_atomic_dec_and_read(&mem->ref_count); + + DBG_MSG(4, ("Memory reference decremented. ID: %u, new value: %d\n", mem->secure_id, new_ref)); + + if (0 == new_ref) + { + DBG_MSG(3, ("Final release of memory. ID: %u\n", mem->secure_id)); + + ump_descriptor_mapping_free(device.secure_id_map, (int)mem->secure_id); + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + mem->release_func(mem->ctx, mem); + _mali_osk_free(mem); + } + else + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + } +} + + + +/* --------------- Handling of user space requests follows --------------- */ + + +_mali_osk_errcode_t _ump_uku_get_api_version( _ump_uk_api_version_s *args ) +{ + ump_session_data * session_data; + + DEBUG_ASSERT_POINTER( args ); + DEBUG_ASSERT_POINTER( args->ctx ); + + session_data = (ump_session_data *)args->ctx; + + /* check compatability */ + if (args->version == UMP_IOCTL_API_VERSION) + { + DBG_MSG(3, ("API version set to newest %d (compatible)\n", GET_VERSION(args->version))); + args->compatible = 1; + session_data->api_version = args->version; + } + else if (args->version == MAKE_VERSION_ID(1)) + { + DBG_MSG(2, ("API version set to depricated: %d (compatible)\n", GET_VERSION(args->version))); + args->compatible = 1; + session_data->api_version = args->version; + } + else + { + DBG_MSG(2, ("API version set to %d (incompatible with client version %d)\n", GET_VERSION(UMP_IOCTL_API_VERSION), GET_VERSION(args->version))); + args->compatible = 0; + args->version = UMP_IOCTL_API_VERSION; /* report our version */ + } + + return _MALI_OSK_ERR_OK; +} + + +_mali_osk_errcode_t _ump_ukk_release( _ump_uk_release_s *release_info ) +{ + ump_session_memory_list_element * session_memory_element; + ump_session_memory_list_element * tmp; + ump_session_data * session_data; + _mali_osk_errcode_t ret = _MALI_OSK_ERR_INVALID_FUNC; + int secure_id; + + DEBUG_ASSERT_POINTER( release_info ); + DEBUG_ASSERT_POINTER( release_info->ctx ); + + /* Retreive the session data */ + session_data = (ump_session_data*)release_info->ctx; + + /* If there are many items in the memory session list we + * could be de-referencing this pointer a lot so keep a local copy + */ + secure_id = release_info->secure_id; + + DBG_MSG(4, ("Releasing memory with IOCTL, ID: %u\n", secure_id)); + + /* Iterate through the memory list looking for the requested secure ID */ + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + _MALI_OSK_LIST_FOREACHENTRY(session_memory_element, tmp, &session_data->list_head_session_memory_list, ump_session_memory_list_element, list) + { + if ( session_memory_element->mem->secure_id == secure_id) + { + ump_dd_mem *release_mem; + + release_mem = session_memory_element->mem; + _mali_osk_list_del(&session_memory_element->list); + ump_dd_reference_release(release_mem); + _mali_osk_free(session_memory_element); + + ret = _MALI_OSK_ERR_OK; + break; + } + } + + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG_IF(1, _MALI_OSK_ERR_OK != ret, ("UMP memory with ID %u does not belong to this session.\n", secure_id)); + + DBG_MSG(4, ("_ump_ukk_release() returning 0x%x\n", ret)); + return ret; +} + +_mali_osk_errcode_t _ump_ukk_size_get( _ump_uk_size_get_s *user_interaction ) +{ + ump_dd_mem * mem; + _mali_osk_errcode_t ret = _MALI_OSK_ERR_FAULT; + + DEBUG_ASSERT_POINTER( user_interaction ); + + /* We lock the mappings so things don't get removed while we are looking for the memory */ + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + if (0 == ump_descriptor_mapping_get(device.secure_id_map, (int)user_interaction->secure_id, (void**)&mem)) + { + user_interaction->size = mem->size_bytes; + DBG_MSG(4, ("Returning size. ID: %u, size: %lu ", (ump_secure_id)user_interaction->secure_id, (unsigned long)user_interaction->size)); + ret = _MALI_OSK_ERR_OK; + } + else + { + user_interaction->size = 0; + DBG_MSG(1, ("Failed to look up mapping in ump_ioctl_size_get(). ID: %u\n", (ump_secure_id)user_interaction->secure_id)); + } + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + return ret; +} + + + +void _ump_ukk_msync( _ump_uk_msync_s *args ) +{ + ump_dd_mem * mem = NULL; + void *virtual = NULL; + u32 size = 0; + u32 offset = 0; + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + ump_descriptor_mapping_get(device.secure_id_map, (int)args->secure_id, (void**)&mem); + + if (NULL == mem) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(1, ("Failed to look up mapping in _ump_ukk_msync(). ID: %u\n", (ump_secure_id)args->secure_id)); + return; + } + /* Ensure the memory doesn't dissapear when we are flushing it. */ + ump_dd_reference_add(mem); + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + /* Returns the cache settings back to Userspace */ + args->is_cached=mem->is_cached; + + /* If this flag is the only one set, we should not do the actual flush, only the readout */ + if ( _UMP_UK_MSYNC_READOUT_CACHE_ENABLED==args->op ) + { + DBG_MSG(3, ("_ump_ukk_msync READOUT ID: %u Enabled: %d\n", (ump_secure_id)args->secure_id, mem->is_cached)); + goto msync_release_and_return; + } + + /* Nothing to do if the memory is not caches */ + if ( 0==mem->is_cached ) + { + DBG_MSG(3, ("_ump_ukk_msync IGNORING ID: %u Enabled: %d OP: %d\n", (ump_secure_id)args->secure_id, mem->is_cached, args->op)); + goto msync_release_and_return; + } + DBG_MSG(3, ("_ump_ukk_msync FLUSHING ID: %u Enabled: %d OP: %d Address: 0x%08x Mapping: 0x%08x\n", + (ump_secure_id)args->secure_id, mem->is_cached, args->op, args->address, args->mapping)); + + if ( args->address ) + { + virtual = (void *)((u32)args->address); + offset = (u32)((args->address) - (args->mapping)); + } else { + /* Flush entire mapping when no address is specified. */ + virtual = args->mapping; + } + if ( args->size ) + { + size = args->size; + } else { + /* Flush entire mapping when no size is specified. */ + size = mem->size_bytes - offset; + } + + if ( (offset + size) > mem->size_bytes ) + { + DBG_MSG(1, ("Trying to flush more than the entire UMP allocation: offset: %u + size: %u > %u\n", offset, size, mem->size_bytes)); + goto msync_release_and_return; + } + + /* The actual cache flush - Implemented for each OS*/ + _ump_osk_msync( mem, virtual, offset, size, args->op); + +msync_release_and_return: + ump_dd_reference_release(mem); + return; +} diff --git a/drivers/gpu/arm/ump/common/ump_kernel_common.c b/drivers/gpu/arm/ump/common/ump_kernel_common.c new file mode 100644 index 000000000000..946286c83c50 --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_kernel_common.c @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_bitops.h" +#include "mali_osk_list.h" +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" +#include "ump_kernel_descriptor_mapping.h" +#include "ump_kernel_memory_backend.h" + + + +/** + * Define the initial and maximum size of number of secure_ids on the system + */ +#define UMP_SECURE_ID_TABLE_ENTRIES_INITIAL (128 ) +#define UMP_SECURE_ID_TABLE_ENTRIES_MAXIMUM (4096 ) + + +/** + * Define the initial and maximum size of the ump_session_data::cookies_map, + * which is a \ref ump_descriptor_mapping. This limits how many secure_ids + * may be mapped into a particular process using _ump_ukk_map_mem(). + */ + +#define UMP_COOKIES_PER_SESSION_INITIAL (UMP_SECURE_ID_TABLE_ENTRIES_INITIAL ) +#define UMP_COOKIES_PER_SESSION_MAXIMUM (UMP_SECURE_ID_TABLE_ENTRIES_MAXIMUM) + +struct ump_dev device; + +_mali_osk_errcode_t ump_kernel_constructor(void) +{ + _mali_osk_errcode_t err; + + /* Perform OS Specific initialization */ + err = _ump_osk_init(); + if( _MALI_OSK_ERR_OK != err ) + { + MSG_ERR(("Failed to initiaze the UMP Device Driver")); + return err; + } + + /* Init the global device */ + _mali_osk_memset(&device, 0, sizeof(device) ); + + /* Create the descriptor map, which will be used for mapping secure ID to ump_dd_mem structs */ + device.secure_id_map_lock = _mali_osk_lock_init(_MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0 , 0); + if (NULL == device.secure_id_map_lock) + { + MSG_ERR(("Failed to create OSK lock for secure id lookup table\n")); + return _MALI_OSK_ERR_NOMEM; + } + + device.secure_id_map = ump_descriptor_mapping_create(UMP_SECURE_ID_TABLE_ENTRIES_INITIAL, UMP_SECURE_ID_TABLE_ENTRIES_MAXIMUM); + if (NULL == device.secure_id_map) + { + _mali_osk_lock_term(device.secure_id_map_lock); + MSG_ERR(("Failed to create secure id lookup table\n")); + return _MALI_OSK_ERR_NOMEM; + } + + /* Init memory backend */ + device.backend = ump_memory_backend_create(); + if (NULL == device.backend) + { + MSG_ERR(("Failed to create memory backend\n")); + _mali_osk_lock_term(device.secure_id_map_lock); + ump_descriptor_mapping_destroy(device.secure_id_map); + return _MALI_OSK_ERR_NOMEM; + } + + return _MALI_OSK_ERR_OK; +} + +void ump_kernel_destructor(void) +{ + DEBUG_ASSERT_POINTER(device.secure_id_map); + DEBUG_ASSERT_POINTER(device.secure_id_map_lock); + + _mali_osk_lock_term(device.secure_id_map_lock); + device.secure_id_map_lock = NULL; + + ump_descriptor_mapping_destroy(device.secure_id_map); + device.secure_id_map = NULL; + + device.backend->shutdown(device.backend); + device.backend = NULL; + + ump_memory_backend_destroy(); + + _ump_osk_term(); +} + +/** Creates a new UMP session + */ +_mali_osk_errcode_t _ump_ukk_open( void** context ) +{ + struct ump_session_data * session_data; + + /* allocated struct to track this session */ + session_data = (struct ump_session_data *)_mali_osk_malloc(sizeof(struct ump_session_data)); + if (NULL == session_data) + { + MSG_ERR(("Failed to allocate ump_session_data in ump_file_open()\n")); + return _MALI_OSK_ERR_NOMEM; + } + + session_data->lock = _mali_osk_lock_init(_MALI_OSK_LOCKFLAG_NONINTERRUPTABLE, 0, 0); + if( NULL == session_data->lock ) + { + MSG_ERR(("Failed to initialize lock for ump_session_data in ump_file_open()\n")); + _mali_osk_free(session_data); + return _MALI_OSK_ERR_NOMEM; + } + + session_data->cookies_map = ump_descriptor_mapping_create( UMP_COOKIES_PER_SESSION_INITIAL, UMP_COOKIES_PER_SESSION_MAXIMUM ); + + if ( NULL == session_data->cookies_map ) + { + MSG_ERR(("Failed to create descriptor mapping for _ump_ukk_map_mem cookies\n")); + + _mali_osk_lock_term( session_data->lock ); + _mali_osk_free( session_data ); + return _MALI_OSK_ERR_NOMEM; + } + + _MALI_OSK_INIT_LIST_HEAD(&session_data->list_head_session_memory_list); + + _MALI_OSK_INIT_LIST_HEAD(&session_data->list_head_session_memory_mappings_list); + + /* Since initial version of the UMP interface did not use the API_VERSION ioctl we have to assume + that it is this version, and not the "latest" one: UMP_IOCTL_API_VERSION + Current and later API versions would do an additional call to this IOCTL and update this variable + to the correct one.*/ + session_data->api_version = MAKE_VERSION_ID(1); + + *context = (void*)session_data; + + DBG_MSG(2, ("New session opened\n")); + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _ump_ukk_close( void** context ) +{ + struct ump_session_data * session_data; + ump_session_memory_list_element * item; + ump_session_memory_list_element * tmp; + + session_data = (struct ump_session_data *)*context; + if (NULL == session_data) + { + MSG_ERR(("Session data is NULL in _ump_ukk_close()\n")); + return _MALI_OSK_ERR_INVALID_ARGS; + } + + /* Unmap any descriptors mapped in. */ + if (0 == _mali_osk_list_empty(&session_data->list_head_session_memory_mappings_list)) + { + ump_memory_allocation *descriptor; + ump_memory_allocation *temp; + + DBG_MSG(1, ("Memory mappings found on session usage list during session termination\n")); + + /* use the 'safe' list iterator, since freeing removes the active block from the list we're iterating */ + _MALI_OSK_LIST_FOREACHENTRY(descriptor, temp, &session_data->list_head_session_memory_mappings_list, ump_memory_allocation, list) + { + _ump_uk_unmap_mem_s unmap_args; + DBG_MSG(4, ("Freeing block with phys address 0x%x size 0x%x mapped in user space at 0x%x\n", + descriptor->phys_addr, descriptor->size, descriptor->mapping)); + unmap_args.ctx = (void*)session_data; + unmap_args.mapping = descriptor->mapping; + unmap_args.size = descriptor->size; + unmap_args._ukk_private = NULL; /* NOTE: unused */ + unmap_args.cookie = descriptor->cookie; + + /* NOTE: This modifies the list_head_session_memory_mappings_list */ + _ump_ukk_unmap_mem( &unmap_args ); + } + } + + /* ASSERT that we really did free everything, because _ump_ukk_unmap_mem() + * can fail silently. */ + DEBUG_ASSERT( _mali_osk_list_empty(&session_data->list_head_session_memory_mappings_list) ); + + _MALI_OSK_LIST_FOREACHENTRY(item, tmp, &session_data->list_head_session_memory_list, ump_session_memory_list_element, list) + { + _mali_osk_list_del(&item->list); + DBG_MSG(2, ("Releasing UMP memory %u as part of file close\n", item->mem->secure_id)); + ump_dd_reference_release(item->mem); + _mali_osk_free(item); + } + + ump_descriptor_mapping_destroy( session_data->cookies_map ); + + _mali_osk_lock_term(session_data->lock); + _mali_osk_free(session_data); + + DBG_MSG(2, ("Session closed\n")); + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _ump_ukk_map_mem( _ump_uk_map_mem_s *args ) +{ + struct ump_session_data * session_data; + ump_memory_allocation * descriptor; /* Describes current mapping of memory */ + _mali_osk_errcode_t err; + unsigned long offset = 0; + unsigned long left; + ump_dd_handle handle; /* The real UMP handle for this memory. Its real datatype is ump_dd_mem* */ + ump_dd_mem * mem; /* The real UMP memory. It is equal to the handle, but with exposed struct */ + u32 block; + int map_id; + + session_data = (ump_session_data *)args->ctx; + if( NULL == session_data ) + { + MSG_ERR(("Session data is NULL in _ump_ukk_map_mem()\n")); + return _MALI_OSK_ERR_INVALID_ARGS; + } + + descriptor = (ump_memory_allocation*) _mali_osk_calloc( 1, sizeof(ump_memory_allocation)); + if (NULL == descriptor) + { + MSG_ERR(("ump_ukk_map_mem: descriptor allocation failed\n")); + return _MALI_OSK_ERR_NOMEM; + } + + handle = ump_dd_handle_create_from_secure_id(args->secure_id); + if ( UMP_DD_HANDLE_INVALID == handle) + { + _mali_osk_free(descriptor); + DBG_MSG(1, ("Trying to map unknown secure ID %u\n", args->secure_id)); + return _MALI_OSK_ERR_FAULT; + } + + mem = (ump_dd_mem*)handle; + DEBUG_ASSERT(mem); + if (mem->size_bytes != args->size) + { + _mali_osk_free(descriptor); + ump_dd_reference_release(handle); + DBG_MSG(1, ("Trying to map too much or little. ID: %u, virtual size=%lu, UMP size: %lu\n", args->secure_id, args->size, mem->size_bytes)); + return _MALI_OSK_ERR_FAULT; + } + + map_id = ump_descriptor_mapping_allocate_mapping( session_data->cookies_map, (void*) descriptor ); + + if (map_id < 0) + { + _mali_osk_free(descriptor); + ump_dd_reference_release(handle); + DBG_MSG(1, ("ump_ukk_map_mem: unable to allocate a descriptor_mapping for return cookie\n")); + + return _MALI_OSK_ERR_NOMEM; + } + + descriptor->size = args->size; + descriptor->handle = handle; + descriptor->phys_addr = args->phys_addr; + descriptor->process_mapping_info = args->_ukk_private; + descriptor->ump_session = session_data; + descriptor->cookie = (u32)map_id; + + if ( mem->is_cached ) + { + descriptor->is_cached = 1; + args->is_cached = 1; + DBG_MSG(3, ("Mapping UMP secure_id: %d as cached.\n", args->secure_id)); + } + else + { + descriptor->is_cached = 0; + args->is_cached = 0; + DBG_MSG(3, ("Mapping UMP secure_id: %d as Uncached.\n", args->secure_id)); + } + + _mali_osk_list_init( &descriptor->list ); + + err = _ump_osk_mem_mapregion_init( descriptor ); + if( _MALI_OSK_ERR_OK != err ) + { + DBG_MSG(1, ("Failed to initialize memory mapping in _ump_ukk_map_mem(). ID: %u\n", args->secure_id)); + ump_descriptor_mapping_free( session_data->cookies_map, map_id ); + _mali_osk_free(descriptor); + ump_dd_reference_release(mem); + return err; + } + + DBG_MSG(4, ("Mapping virtual to physical memory: ID: %u, size:%lu, first physical addr: 0x%08lx, number of regions: %lu\n", + mem->secure_id, + mem->size_bytes, + ((NULL != mem->block_array) ? mem->block_array->addr : 0), + mem->nr_blocks)); + + left = descriptor->size; + /* loop over all blocks and map them in */ + for (block = 0; block < mem->nr_blocks; block++) + { + unsigned long size_to_map; + + if (left > mem->block_array[block].size) + { + size_to_map = mem->block_array[block].size; + } + else + { + size_to_map = left; + } + + if (_MALI_OSK_ERR_OK != _ump_osk_mem_mapregion_map(descriptor, offset, (u32 *)&(mem->block_array[block].addr), size_to_map ) ) + { + DBG_MSG(1, ("WARNING: _ump_ukk_map_mem failed to map memory into userspace\n")); + ump_descriptor_mapping_free( session_data->cookies_map, map_id ); + ump_dd_reference_release(mem); + _ump_osk_mem_mapregion_term( descriptor ); + _mali_osk_free(descriptor); + return _MALI_OSK_ERR_FAULT; + } + left -= size_to_map; + offset += size_to_map; + } + + /* Add to the ump_memory_allocation tracking list */ + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_list_add( &descriptor->list, &session_data->list_head_session_memory_mappings_list ); + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + args->mapping = descriptor->mapping; + args->cookie = descriptor->cookie; + + return _MALI_OSK_ERR_OK; +} + +void _ump_ukk_unmap_mem( _ump_uk_unmap_mem_s *args ) +{ + struct ump_session_data * session_data; + ump_memory_allocation * descriptor; + ump_dd_handle handle; + + session_data = (ump_session_data *)args->ctx; + + if( NULL == session_data ) + { + MSG_ERR(("Session data is NULL in _ump_ukk_map_mem()\n")); + return; + } + + if (0 != ump_descriptor_mapping_get( session_data->cookies_map, (int)args->cookie, (void**)&descriptor) ) + { + MSG_ERR(("_ump_ukk_map_mem: cookie 0x%X not found for this session\n", args->cookie )); + return; + } + + DEBUG_ASSERT_POINTER(descriptor); + + handle = descriptor->handle; + if ( UMP_DD_HANDLE_INVALID == handle) + { + DBG_MSG(1, ("WARNING: Trying to unmap unknown handle: UNKNOWN\n")); + return; + } + + /* Remove the ump_memory_allocation from the list of tracked mappings */ + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_list_del( &descriptor->list ); + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + ump_descriptor_mapping_free( session_data->cookies_map, (int)args->cookie ); + + ump_dd_reference_release(handle); + + _ump_osk_mem_mapregion_term( descriptor ); + _mali_osk_free(descriptor); +} + +u32 _ump_ukk_report_memory_usage( void ) +{ + if(device.backend->stat) + return device.backend->stat(device.backend); + else + return 0; +} diff --git a/drivers/gpu/arm/ump/common/ump_kernel_common.h b/drivers/gpu/arm/ump/common/ump_kernel_common.h new file mode 100644 index 000000000000..3e3636a0401a --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_kernel_common.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_KERNEL_H__ +#define __UMP_KERNEL_H__ + +#include "ump_kernel_types.h" +#include "ump_kernel_interface.h" +#include "ump_kernel_descriptor_mapping.h" +#include "ump_kernel_memory_backend.h" + + +#ifdef DEBUG + extern int ump_debug_level; + #define UMP_DEBUG_PRINT(args) _mali_osk_dbgmsg args + #define UMP_DEBUG_CODE(args) args + #define DBG_MSG(level,args) do { /* args should be in brackets */ \ + ((level) <= ump_debug_level)?\ + UMP_DEBUG_PRINT(("UMP<" #level ">: ")), \ + UMP_DEBUG_PRINT(args):0; \ + } while (0) + + #define DBG_MSG_IF(level,condition,args) /* args should be in brackets */ \ + if((condition)&&((level) <= ump_debug_level)) {\ + UMP_DEBUG_PRINT(("UMP<" #level ">: ")); \ + UMP_DEBUG_PRINT(args); \ + } + + #define DBG_MSG_ELSE(level,args) /* args should be in brackets */ \ + else if((level) <= ump_debug_level) { \ + UMP_DEBUG_PRINT(("UMP<" #level ">: ")); \ + UMP_DEBUG_PRINT(args); \ + } + + #define DEBUG_ASSERT_POINTER(pointer) do {if( (pointer)== NULL) MSG_ERR(("NULL pointer " #pointer)); } while(0) + #define DEBUG_ASSERT(condition) do {if(!(condition)) MSG_ERR(("ASSERT failed: " #condition)); } while(0) +#else /* DEBUG */ + #define UMP_DEBUG_PRINT(args) do {} while(0) + #define UMP_DEBUG_CODE(args) + #define DBG_MSG(level,args) do {} while(0) + #define DBG_MSG_IF(level,condition,args) do {} while(0) + #define DBG_MSG_ELSE(level,args) do {} while(0) + #define DEBUG_ASSERT(condition) do {} while(0) + #define DEBUG_ASSERT_POINTER(pointer) do {} while(0) +#endif /* DEBUG */ + +#define MSG_ERR(args) do{ /* args should be in brackets */ \ + _mali_osk_dbgmsg("UMP: ERR: %s\n" ,__FILE__); \ + _mali_osk_dbgmsg( " %s()%4d\n", __FUNCTION__, __LINE__) ; \ + _mali_osk_dbgmsg args ; \ + _mali_osk_dbgmsg("\n"); \ + } while(0) + +#define MSG(args) do{ /* args should be in brackets */ \ + _mali_osk_dbgmsg("UMP: "); \ + _mali_osk_dbgmsg args; \ + } while (0) + + + +/* + * This struct is used to store per session data. + * A session is created when someone open() the device, and + * closed when someone close() it or the user space application terminates. + */ +typedef struct ump_session_data +{ + _mali_osk_list_t list_head_session_memory_list; /**< List of ump allocations made by the process (elements are ump_session_memory_list_element) */ + _mali_osk_list_t list_head_session_memory_mappings_list; /**< List of ump_memory_allocations mapped in */ + int api_version; + _mali_osk_lock_t * lock; + ump_descriptor_mapping * cookies_map; /**< Secure mapping of cookies from _ump_ukk_map_mem() */ +} ump_session_data; + + + +/* + * This struct is used to track the UMP memory references a session has. + * We need to track this in order to be able to clean up after user space processes + * which don't do it themself (e.g. due to a crash or premature termination). + */ +typedef struct ump_session_memory_list_element +{ + struct ump_dd_mem * mem; + _mali_osk_list_t list; +} ump_session_memory_list_element; + + + +/* + * Device specific data, created when device driver is loaded, and then kept as the global variable device. + */ +typedef struct ump_dev +{ + _mali_osk_lock_t * secure_id_map_lock; + ump_descriptor_mapping * secure_id_map; + ump_memory_backend * backend; +} ump_dev; + + + +extern int ump_debug_level; +extern struct ump_dev device; + +_mali_osk_errcode_t ump_kernel_constructor(void); +void ump_kernel_destructor(void); +int map_errcode( _mali_osk_errcode_t err ); + +/** + * variables from user space cannot be dereferenced from kernel space; tagging them + * with __user allows the GCC compiler to generate a warning. Other compilers may + * not support this so we define it here as an empty macro if the compiler doesn't + * define it. + */ +#ifndef __user +#define __user +#endif + +#endif /* __UMP_KERNEL_H__ */ diff --git a/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.c b/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.c new file mode 100644 index 000000000000..d6b59b74e351 --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_osk_bitops.h" +#include "ump_kernel_common.h" +#include "ump_kernel_descriptor_mapping.h" + +#define MALI_PAD_INT(x) (((x) + (BITS_PER_LONG - 1)) & ~(BITS_PER_LONG - 1)) + +/** + * Allocate a descriptor table capable of holding 'count' mappings + * @param count Number of mappings in the table + * @return Pointer to a new table, NULL on error + */ +static ump_descriptor_table * descriptor_table_alloc(int count); + +/** + * Free a descriptor table + * @param table The table to free + */ +static void descriptor_table_free(ump_descriptor_table * table); + +ump_descriptor_mapping * ump_descriptor_mapping_create(int init_entries, int max_entries) +{ + ump_descriptor_mapping * map = _mali_osk_calloc(1, sizeof(ump_descriptor_mapping) ); + + init_entries = MALI_PAD_INT(init_entries); + max_entries = MALI_PAD_INT(max_entries); + + if (NULL != map) + { + map->table = descriptor_table_alloc(init_entries); + if (NULL != map->table) + { + map->lock = _mali_osk_lock_init(_MALI_OSK_LOCKFLAG_NONINTERRUPTABLE | _MALI_OSK_LOCKFLAG_READERWRITER, 0 , 0); + if ( NULL != map->lock ) + { + _mali_osk_set_nonatomic_bit(0, map->table->usage); /* reserve bit 0 to prevent NULL/zero logic to kick in */ + map->max_nr_mappings_allowed = max_entries; + map->current_nr_mappings = init_entries; + return map; + } + descriptor_table_free(map->table); + } + _mali_osk_free(map); + } + return NULL; +} + +void ump_descriptor_mapping_destroy(ump_descriptor_mapping * map) +{ + descriptor_table_free(map->table); + _mali_osk_lock_term( map->lock ); + _mali_osk_free(map); +} + +int ump_descriptor_mapping_allocate_mapping(ump_descriptor_mapping * map, void * target) +{ + int descriptor = -1;/*-EFAULT;*/ + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RW); + descriptor = _mali_osk_find_first_zero_bit(map->table->usage, map->current_nr_mappings); + if (descriptor == map->current_nr_mappings) + { + int nr_mappings_new; + /* no free descriptor, try to expand the table */ + ump_descriptor_table * new_table; + ump_descriptor_table * old_table = map->table; + nr_mappings_new= map->current_nr_mappings *2; + + if (map->current_nr_mappings >= map->max_nr_mappings_allowed) + { + descriptor = -1; + goto unlock_and_exit; + } + + new_table = descriptor_table_alloc(nr_mappings_new); + if (NULL == new_table) + { + descriptor = -1; + goto unlock_and_exit; + } + + _mali_osk_memcpy(new_table->usage, old_table->usage, (sizeof(unsigned long)*map->current_nr_mappings) / BITS_PER_LONG); + _mali_osk_memcpy(new_table->mappings, old_table->mappings, map->current_nr_mappings * sizeof(void*)); + map->table = new_table; + map->current_nr_mappings = nr_mappings_new; + descriptor_table_free(old_table); + } + + /* we have found a valid descriptor, set the value and usage bit */ + _mali_osk_set_nonatomic_bit(descriptor, map->table->usage); + map->table->mappings[descriptor] = target; + +unlock_and_exit: + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RW); + return descriptor; +} + +int ump_descriptor_mapping_get(ump_descriptor_mapping * map, int descriptor, void** target) +{ + int result = -1;/*-EFAULT;*/ + DEBUG_ASSERT(map); + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) + { + *target = map->table->mappings[descriptor]; + result = 0; + } + else *target = NULL; + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RO); + return result; +} + +int ump_descriptor_mapping_set(ump_descriptor_mapping * map, int descriptor, void * target) +{ + int result = -1;/*-EFAULT;*/ + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RO); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) + { + map->table->mappings[descriptor] = target; + result = 0; + } + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RO); + return result; +} + +void ump_descriptor_mapping_free(ump_descriptor_mapping * map, int descriptor) +{ + _mali_osk_lock_wait(map->lock, _MALI_OSK_LOCKMODE_RW); + if ( (descriptor >= 0) && (descriptor < map->current_nr_mappings) && _mali_osk_test_bit(descriptor, map->table->usage) ) + { + map->table->mappings[descriptor] = NULL; + _mali_osk_clear_nonatomic_bit(descriptor, map->table->usage); + } + _mali_osk_lock_signal(map->lock, _MALI_OSK_LOCKMODE_RW); +} + +static ump_descriptor_table * descriptor_table_alloc(int count) +{ + ump_descriptor_table * table; + + table = _mali_osk_calloc(1, sizeof(ump_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG) + (sizeof(void*) * count) ); + + if (NULL != table) + { + table->usage = (u32*)((u8*)table + sizeof(ump_descriptor_table)); + table->mappings = (void**)((u8*)table + sizeof(ump_descriptor_table) + ((sizeof(unsigned long) * count)/BITS_PER_LONG)); + } + + return table; +} + +static void descriptor_table_free(ump_descriptor_table * table) +{ + _mali_osk_free(table); +} + diff --git a/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.h b/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.h new file mode 100644 index 000000000000..05b39827ce33 --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_kernel_descriptor_mapping.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_descriptor_mapping.h + */ + +#ifndef __UMP_KERNEL_DESCRIPTOR_MAPPING_H__ +#define __UMP_KERNEL_DESCRIPTOR_MAPPING_H__ + +#include "mali_osk.h" + +/** + * The actual descriptor mapping table, never directly accessed by clients + */ +typedef struct ump_descriptor_table +{ + u32 * usage; /**< Pointer to bitpattern indicating if a descriptor is valid/used or not */ + void** mappings; /**< Array of the pointers the descriptors map to */ +} ump_descriptor_table; + +/** + * The descriptor mapping object + * Provides a separate namespace where we can map an integer to a pointer + */ +typedef struct ump_descriptor_mapping +{ + _mali_osk_lock_t *lock; /**< Lock protecting access to the mapping object */ + int max_nr_mappings_allowed; /**< Max number of mappings to support in this namespace */ + int current_nr_mappings; /**< Current number of possible mappings */ + ump_descriptor_table * table; /**< Pointer to the current mapping table */ +} ump_descriptor_mapping; + +/** + * Create a descriptor mapping object + * Create a descriptor mapping capable of holding init_entries growable to max_entries + * @param init_entries Number of entries to preallocate memory for + * @param max_entries Number of entries to max support + * @return Pointer to a descriptor mapping object, NULL on failure + */ +ump_descriptor_mapping * ump_descriptor_mapping_create(int init_entries, int max_entries); + +/** + * Destroy a descriptor mapping object + * @param map The map to free + */ +void ump_descriptor_mapping_destroy(ump_descriptor_mapping * map); + +/** + * Allocate a new mapping entry (descriptor ID) + * Allocates a new entry in the map. + * @param map The map to allocate a new entry in + * @param target The value to map to + * @return The descriptor allocated, a negative value on error + */ +int ump_descriptor_mapping_allocate_mapping(ump_descriptor_mapping * map, void * target); + +/** + * Get the value mapped to by a descriptor ID + * @param map The map to lookup the descriptor id in + * @param descriptor The descriptor ID to lookup + * @param target Pointer to a pointer which will receive the stored value + * @return 0 on successful lookup, negative on error + */ +int ump_descriptor_mapping_get(ump_descriptor_mapping * map, int descriptor, void** target); + +/** + * Set the value mapped to by a descriptor ID + * @param map The map to lookup the descriptor id in + * @param descriptor The descriptor ID to lookup + * @param target Pointer to replace the current value with + * @return 0 on successful lookup, negative on error + */ +int ump_descriptor_mapping_set(ump_descriptor_mapping * map, int descriptor, void * target); + +/** + * Free the descriptor ID + * For the descriptor to be reused it has to be freed + * @param map The map to free the descriptor from + * @param descriptor The descriptor ID to free + */ +void ump_descriptor_mapping_free(ump_descriptor_mapping * map, int descriptor); + +#endif /* __UMP_KERNEL_DESCRIPTOR_MAPPING_H__ */ diff --git a/drivers/gpu/arm/ump/common/ump_kernel_memory_backend.h b/drivers/gpu/arm/ump/common/ump_kernel_memory_backend.h new file mode 100644 index 000000000000..a91ae28672a7 --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_kernel_memory_backend.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_memory_mapping.h + */ + +#ifndef __UMP_KERNEL_MEMORY_BACKEND_H__ +#define __UMP_KERNEL_MEMORY_BACKEND_H__ + +#include "ump_kernel_interface.h" +#include "ump_kernel_types.h" + + +typedef struct ump_memory_allocation +{ + void * phys_addr; + void * mapping; + unsigned long size; + ump_dd_handle handle; + void * process_mapping_info; + u32 cookie; /**< necessary on some U/K interface implementations */ + struct ump_session_data * ump_session; /**< Session that this allocation belongs to */ + _mali_osk_list_t list; /**< List for linking together memory allocations into the session's memory head */ + u32 is_cached; +} ump_memory_allocation; + +typedef struct ump_memory_backend +{ + int (*allocate)(void* ctx, ump_dd_mem * descriptor); + void (*release)(void* ctx, ump_dd_mem * descriptor); + void (*shutdown)(struct ump_memory_backend * backend); + u32 (*stat)(struct ump_memory_backend *backend); + int (*pre_allocate_physical_check)(void *ctx, u32 size); + u32 (*adjust_to_mali_phys)(void *ctx, u32 cpu_phys); + void * ctx; +} ump_memory_backend; + +ump_memory_backend * ump_memory_backend_create ( void ); +void ump_memory_backend_destroy( void ); + +#endif /*__UMP_KERNEL_MEMORY_BACKEND_H__ */ + diff --git a/drivers/gpu/arm/ump/common/ump_kernel_ref_drv.c b/drivers/gpu/arm/ump/common/ump_kernel_ref_drv.c new file mode 100644 index 000000000000..e98f801cb962 --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_kernel_ref_drv.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "mali_osk.h" +#include "mali_osk_list.h" +#include "ump_osk.h" +#include "ump_uk_types.h" + +#include "ump_kernel_interface_ref_drv.h" +#include "ump_kernel_common.h" +#include "ump_kernel_descriptor_mapping.h" + +#define UMP_MINIMUM_SIZE 4096 +#define UMP_MINIMUM_SIZE_MASK (~(UMP_MINIMUM_SIZE-1)) +#define UMP_SIZE_ALIGN(x) (((x)+UMP_MINIMUM_SIZE-1)&UMP_MINIMUM_SIZE_MASK) +#define UMP_ADDR_ALIGN_OFFSET(x) ((x)&(UMP_MINIMUM_SIZE-1)) +static void phys_blocks_release(void * ctx, struct ump_dd_mem * descriptor); + +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_phys_blocks(ump_dd_physical_block * blocks, unsigned long num_blocks) +{ + ump_dd_mem * mem; + unsigned long size_total = 0; + int map_id; + u32 i; + + /* Go through the input blocks and verify that they are sane */ + for (i=0; i < num_blocks; i++) + { + unsigned long addr = blocks[i].addr; + unsigned long size = blocks[i].size; + + DBG_MSG(5, ("Adding physical memory to new handle. Address: 0x%08lx, size: %lu\n", addr, size)); + size_total += blocks[i].size; + + if (0 != UMP_ADDR_ALIGN_OFFSET(addr)) + { + MSG_ERR(("Trying to create UMP memory from unaligned physical address. Address: 0x%08lx\n", addr)); + return UMP_DD_HANDLE_INVALID; + } + + if (0 != UMP_ADDR_ALIGN_OFFSET(size)) + { + MSG_ERR(("Trying to create UMP memory with unaligned size. Size: %lu\n", size)); + return UMP_DD_HANDLE_INVALID; + } + } + + /* Allocate the ump_dd_mem struct for this allocation */ + mem = _mali_osk_malloc(sizeof(*mem)); + if (NULL == mem) + { + DBG_MSG(1, ("Could not allocate ump_dd_mem in ump_dd_handle_create_from_phys_blocks()\n")); + return UMP_DD_HANDLE_INVALID; + } + + /* Find a secure ID for this allocation */ + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + map_id = ump_descriptor_mapping_allocate_mapping(device.secure_id_map, (void*) mem); + + if (map_id < 0) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_free(mem); + DBG_MSG(1, ("Failed to allocate secure ID in ump_dd_handle_create_from_phys_blocks()\n")); + return UMP_DD_HANDLE_INVALID; + } + + /* Now, make a copy of the block information supplied by the user */ + mem->block_array = _mali_osk_malloc(sizeof(ump_dd_physical_block)* num_blocks); + if (NULL == mem->block_array) + { + ump_descriptor_mapping_free(device.secure_id_map, map_id); + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_free(mem); + DBG_MSG(1, ("Could not allocate a mem handle for function ump_dd_handle_create_from_phys_blocks().\n")); + return UMP_DD_HANDLE_INVALID; + } + + _mali_osk_memcpy(mem->block_array, blocks, sizeof(ump_dd_physical_block) * num_blocks); + + /* And setup the rest of the ump_dd_mem struct */ + _mali_osk_atomic_init(&mem->ref_count, 1); + mem->secure_id = (ump_secure_id)map_id; + mem->size_bytes = size_total; + mem->nr_blocks = num_blocks; + mem->backend_info = NULL; + mem->ctx = NULL; + mem->release_func = phys_blocks_release; + /* For now UMP handles created by ump_dd_handle_create_from_phys_blocks() is forced to be Uncached */ + mem->is_cached = 0; + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + DBG_MSG(3, ("UMP memory created. ID: %u, size: %lu\n", mem->secure_id, mem->size_bytes)); + + return (ump_dd_handle)mem; +} + +static void phys_blocks_release(void * ctx, struct ump_dd_mem * descriptor) +{ + _mali_osk_free(descriptor->block_array); + descriptor->block_array = NULL; +} + +_mali_osk_errcode_t _ump_ukk_allocate( _ump_uk_allocate_s *user_interaction ) +{ + ump_session_data * session_data = NULL; + ump_dd_mem *new_allocation = NULL; + ump_session_memory_list_element * session_memory_element = NULL; + int map_id; + + DEBUG_ASSERT_POINTER( user_interaction ); + DEBUG_ASSERT_POINTER( user_interaction->ctx ); + + session_data = (ump_session_data *) user_interaction->ctx; + + session_memory_element = _mali_osk_calloc( 1, sizeof(ump_session_memory_list_element)); + if (NULL == session_memory_element) + { + DBG_MSG(1, ("Failed to allocate ump_session_memory_list_element in ump_ioctl_allocate()\n")); + return _MALI_OSK_ERR_NOMEM; + } + + + new_allocation = _mali_osk_calloc( 1, sizeof(ump_dd_mem)); + if (NULL==new_allocation) + { + _mali_osk_free(session_memory_element); + DBG_MSG(1, ("Failed to allocate ump_dd_mem in _ump_ukk_allocate()\n")); + return _MALI_OSK_ERR_NOMEM; + } + + /* Create a secure ID for this allocation */ + _mali_osk_lock_wait(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + map_id = ump_descriptor_mapping_allocate_mapping(device.secure_id_map, (void*)new_allocation); + + if (map_id < 0) + { + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_free(session_memory_element); + _mali_osk_free(new_allocation); + DBG_MSG(1, ("Failed to allocate secure ID in ump_ioctl_allocate()\n")); + return - _MALI_OSK_ERR_INVALID_FUNC; + } + + /* Initialize the part of the new_allocation that we know so for */ + new_allocation->secure_id = (ump_secure_id)map_id; + _mali_osk_atomic_init(&new_allocation->ref_count,1); + if ( 0==(UMP_REF_DRV_UK_CONSTRAINT_USE_CACHE & user_interaction->constraints) ) + new_allocation->is_cached = 0; + else new_allocation->is_cached = 1; + + /* special case a size of 0, we should try to emulate what malloc does in this case, which is to return a valid pointer that must be freed, but can't be dereferences */ + if (0 == user_interaction->size) + { + user_interaction->size = 1; /* emulate by actually allocating the minimum block size */ + } + + new_allocation->size_bytes = UMP_SIZE_ALIGN(user_interaction->size); /* Page align the size */ + + /* Now, ask the active memory backend to do the actual memory allocation */ + if (!device.backend->allocate( device.backend->ctx, new_allocation ) ) + { + DBG_MSG(3, ("OOM: No more UMP memory left. Failed to allocate memory in ump_ioctl_allocate(). Size: %lu, requested size: %lu\n", new_allocation->size_bytes, (unsigned long)user_interaction->size)); + ump_descriptor_mapping_free(device.secure_id_map, map_id); + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_free(new_allocation); + _mali_osk_free(session_memory_element); + return _MALI_OSK_ERR_INVALID_FUNC; + } + + new_allocation->ctx = device.backend->ctx; + new_allocation->release_func = device.backend->release; + + _mali_osk_lock_signal(device.secure_id_map_lock, _MALI_OSK_LOCKMODE_RW); + + /* Initialize the session_memory_element, and add it to the session object */ + session_memory_element->mem = new_allocation; + _mali_osk_lock_wait(session_data->lock, _MALI_OSK_LOCKMODE_RW); + _mali_osk_list_add(&(session_memory_element->list), &(session_data->list_head_session_memory_list)); + _mali_osk_lock_signal(session_data->lock, _MALI_OSK_LOCKMODE_RW); + + user_interaction->secure_id = new_allocation->secure_id; + user_interaction->size = new_allocation->size_bytes; + DBG_MSG(3, ("UMP memory allocated. ID: %u, size: %lu\n", new_allocation->secure_id, new_allocation->size_bytes)); + + return _MALI_OSK_ERR_OK; +} diff --git a/drivers/gpu/arm/ump/common/ump_kernel_types.h b/drivers/gpu/arm/ump/common/ump_kernel_types.h new file mode 100644 index 000000000000..fa3d9fb8e70a --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_kernel_types.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_KERNEL_TYPES_H__ +#define __UMP_KERNEL_TYPES_H__ + +#include "ump_kernel_interface.h" +#include "mali_osk.h" + +/* + * This struct is what is "behind" a ump_dd_handle + */ +typedef struct ump_dd_mem +{ + ump_secure_id secure_id; + _mali_osk_atomic_t ref_count; + unsigned long size_bytes; + unsigned long nr_blocks; + ump_dd_physical_block * block_array; + void (*release_func)(void * ctx, struct ump_dd_mem * descriptor); + void * ctx; + void * backend_info; + int is_cached; +} ump_dd_mem; + + + +#endif /* __UMP_KERNEL_TYPES_H__ */ diff --git a/drivers/gpu/arm/ump/common/ump_osk.h b/drivers/gpu/arm/ump/common/ump_osk.h new file mode 100644 index 000000000000..56ec07048319 --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_osk.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk.h + * Defines the OS abstraction layer for the UMP kernel device driver (OSK) + */ + +#ifndef __UMP_OSK_H__ +#define __UMP_OSK_H__ + +#include <mali_osk.h> +#include <ump_kernel_memory_backend.h> +#include "ump_uk_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +_mali_osk_errcode_t _ump_osk_init( void ); + +_mali_osk_errcode_t _ump_osk_term( void ); + +int _ump_osk_atomic_inc_and_read( _mali_osk_atomic_t *atom ); + +int _ump_osk_atomic_dec_and_read( _mali_osk_atomic_t *atom ); + +_mali_osk_errcode_t _ump_osk_mem_mapregion_init( ump_memory_allocation *descriptor ); + +_mali_osk_errcode_t _ump_osk_mem_mapregion_map( ump_memory_allocation * descriptor, u32 offset, u32 * phys_addr, unsigned long size ); + +void _ump_osk_mem_mapregion_term( ump_memory_allocation * descriptor ); + +void _ump_osk_msync( ump_dd_mem * mem, void * virt, u32 offset, u32 size, ump_uk_msync_op op ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/drivers/gpu/arm/ump/common/ump_uk_types.h b/drivers/gpu/arm/ump/common/ump_uk_types.h new file mode 100644 index 000000000000..ac0f1ceef41b --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_uk_types.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_uk_types.h + * Defines the types and constants used in the user-kernel interface + */ + +#ifndef __UMP_UK_TYPES_H__ +#define __UMP_UK_TYPES_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Helpers for API version handling */ +#define MAKE_VERSION_ID(x) (((x) << 16UL) | (x)) +#define IS_VERSION_ID(x) (((x) & 0xFFFF) == (((x) >> 16UL) & 0xFFFF)) +#define GET_VERSION(x) (((x) >> 16UL) & 0xFFFF) +#define IS_API_MATCH(x, y) (IS_VERSION_ID((x)) && IS_VERSION_ID((y)) && (GET_VERSION((x)) == GET_VERSION((y)))) + +/** + * API version define. + * Indicates the version of the kernel API + * The version is a 16bit integer incremented on each API change. + * The 16bit integer is stored twice in a 32bit integer + * So for version 1 the value would be 0x00010001 + */ +#define UMP_IOCTL_API_VERSION MAKE_VERSION_ID(2) + +typedef enum +{ + _UMP_IOC_QUERY_API_VERSION = 1, + _UMP_IOC_ALLOCATE, + _UMP_IOC_RELEASE, + _UMP_IOC_SIZE_GET, + _UMP_IOC_MAP_MEM, /* not used in Linux */ + _UMP_IOC_UNMAP_MEM, /* not used in Linux */ + _UMP_IOC_MSYNC, +}_ump_uk_functions; + +typedef enum +{ + UMP_REF_DRV_UK_CONSTRAINT_NONE = 0, + UMP_REF_DRV_UK_CONSTRAINT_PHYSICALLY_LINEAR = 1, + UMP_REF_DRV_UK_CONSTRAINT_USE_CACHE = 4, +} ump_uk_alloc_constraints; + +typedef enum +{ + _UMP_UK_MSYNC_CLEAN = 0, + _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE = 1, + _UMP_UK_MSYNC_READOUT_CACHE_ENABLED = 128, +} ump_uk_msync_op; + +/** + * Get API version ([in,out] u32 api_version, [out] u32 compatible) + */ +typedef struct _ump_uk_api_version_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 version; /**< Set to the user space version on entry, stores the device driver version on exit */ + u32 compatible; /**< Non-null if the device is compatible with the client */ +} _ump_uk_api_version_s; + +/** + * ALLOCATE ([out] u32 secure_id, [in,out] u32 size, [in] contraints) + */ +typedef struct _ump_uk_allocate_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< Return value from DD to Userdriver */ + u32 size; /**< Input and output. Requested size; input. Returned size; output */ + ump_uk_alloc_constraints constraints; /**< Only input to Devicedriver */ +} _ump_uk_allocate_s; + +/** + * SIZE_GET ([in] u32 secure_id, [out]size ) + */ +typedef struct _ump_uk_size_get_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< Input to DD */ + u32 size; /**< Returned size; output */ +} _ump_uk_size_get_s; + +/** + * Release ([in] u32 secure_id) + */ +typedef struct _ump_uk_release_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + u32 secure_id; /**< Input to DD */ +} _ump_uk_release_s; + +typedef struct _ump_uk_map_mem_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; /**< [out] Returns user-space virtual address for the mapping */ + void *phys_addr; /**< [in] physical address */ + unsigned long size; /**< [in] size */ + u32 secure_id; /**< [in] secure_id to assign to mapping */ + void * _ukk_private; /**< Only used inside linux port between kernel frontend and common part to store vma */ + u32 cookie; + u32 is_cached; /**< [in,out] caching of CPU mappings */ +} _ump_uk_map_mem_s; + +typedef struct _ump_uk_unmap_mem_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; + u32 size; + void * _ukk_private; + u32 cookie; +} _ump_uk_unmap_mem_s; + +typedef struct _ump_uk_msync_s +{ + void *ctx; /**< [in,out] user-kernel context (trashed on output) */ + void *mapping; /**< [in] mapping addr */ + void *address; /**< [in] flush start addr */ + u32 size; /**< [in] size to flush */ + ump_uk_msync_op op; /**< [in] flush operation */ + u32 cookie; /**< [in] cookie stored with reference to the kernel mapping internals */ + u32 secure_id; /**< [in] cookie stored with reference to the kernel mapping internals */ + u32 is_cached; /**< [out] caching of CPU mappings */ +} _ump_uk_msync_s; + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_UK_TYPES_H__ */ diff --git a/drivers/gpu/arm/ump/common/ump_ukk.h b/drivers/gpu/arm/ump/common/ump_ukk.h new file mode 100644 index 000000000000..ff5176841980 --- /dev/null +++ b/drivers/gpu/arm/ump/common/ump_ukk.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk.h + * Defines the kernel-side interface of the user-kernel interface + */ + +#ifndef __UMP_UKK_H__ +#define __UMP_UKK_H__ + +#include "mali_osk.h" +#include "ump_uk_types.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +_mali_osk_errcode_t _ump_ukk_open( void** context ); + +_mali_osk_errcode_t _ump_ukk_close( void** context ); + +_mali_osk_errcode_t _ump_ukk_allocate( _ump_uk_allocate_s *user_interaction ); + +_mali_osk_errcode_t _ump_ukk_release( _ump_uk_release_s *release_info ); + +_mali_osk_errcode_t _ump_ukk_size_get( _ump_uk_size_get_s *user_interaction ); + +_mali_osk_errcode_t _ump_ukk_map_mem( _ump_uk_map_mem_s *args ); + +_mali_osk_errcode_t _ump_uku_get_api_version( _ump_uk_api_version_s *args ); + +void _ump_ukk_unmap_mem( _ump_uk_unmap_mem_s *args ); + +void _ump_ukk_msync( _ump_uk_msync_s *args ); + +u32 _ump_ukk_report_memory_usage( void ); + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_UKK_H__ */ diff --git a/drivers/gpu/arm/ump/include/ump_kernel_interface.h b/drivers/gpu/arm/ump/include/ump_kernel_interface.h new file mode 100644 index 000000000000..042c8b1b45bd --- /dev/null +++ b/drivers/gpu/arm/ump/include/ump_kernel_interface.h @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_interface.h + * + * This file contains the kernel space part of the UMP API. + */ + +#ifndef __UMP_KERNEL_INTERFACE_H__ +#define __UMP_KERNEL_INTERFACE_H__ + + +/** @defgroup ump_kernel_space_api UMP Kernel Space API + * @{ */ + + +#include "ump_kernel_platform.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/** + * External representation of a UMP handle in kernel space. + */ +typedef void * ump_dd_handle; + +/** + * Typedef for a secure ID, a system wide identificator for UMP memory buffers. + */ +typedef unsigned int ump_secure_id; + + +/** + * Value to indicate an invalid UMP memory handle. + */ +#define UMP_DD_HANDLE_INVALID ((ump_dd_handle)0) + + +/** + * Value to indicate an invalid secure Id. + */ +#define UMP_INVALID_SECURE_ID ((ump_secure_id)-1) + + +/** + * UMP error codes for kernel space. + */ +typedef enum +{ + UMP_DD_SUCCESS, /**< indicates success */ + UMP_DD_INVALID, /**< indicates failure */ +} ump_dd_status_code; + + +/** + * Struct used to describe a physical block used by UMP memory + */ +typedef struct ump_dd_physical_block +{ + unsigned long addr; /**< The physical address of the block */ + unsigned long size; /**< The length of the block, typically page aligned */ +} ump_dd_physical_block; + + +/** + * Retrieves the secure ID for the specified UMP memory. + * + * This identificator is unique across the entire system, and uniquely identifies + * the specified UMP memory. This identificator can later be used through the + * @ref ump_dd_handle_create_from_secure_id "ump_dd_handle_create_from_secure_id" or + * @ref ump_handle_create_from_secure_id "ump_handle_create_from_secure_id" + * functions in order to access this UMP memory, for instance from another process. + * + * @note There is a user space equivalent function called @ref ump_secure_id_get "ump_secure_id_get" + * + * @see ump_dd_handle_create_from_secure_id + * @see ump_handle_create_from_secure_id + * @see ump_secure_id_get + * + * @param mem Handle to UMP memory. + * + * @return Returns the secure ID for the specified UMP memory. + */ +UMP_KERNEL_API_EXPORT ump_secure_id ump_dd_secure_id_get(ump_dd_handle mem); + + +/** + * Retrieves a handle to allocated UMP memory. + * + * The usage of UMP memory is reference counted, so this will increment the reference + * count by one for the specified UMP memory. + * Use @ref ump_dd_reference_release "ump_dd_reference_release" when there is no longer any + * use for the retrieved handle. + * + * @note There is a user space equivalent function called @ref ump_handle_create_from_secure_id "ump_handle_create_from_secure_id" + * + * @see ump_dd_reference_release + * @see ump_handle_create_from_secure_id + * + * @param secure_id The secure ID of the UMP memory to open, that can be retrieved using the @ref ump_secure_id_get "ump_secure_id_get " function. + * + * @return UMP_INVALID_MEMORY_HANDLE indicates failure, otherwise a valid handle is returned. + */ +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_secure_id(ump_secure_id secure_id); + + +/** + * Retrieves the number of physical blocks used by the specified UMP memory. + * + * This function retrieves the number of @ref ump_dd_physical_block "ump_dd_physical_block" structs needed + * to describe the physical memory layout of the given UMP memory. This can later be used when calling + * the functions @ref ump_dd_phys_blocks_get "ump_dd_phys_blocks_get" and + * @ref ump_dd_phys_block_get "ump_dd_phys_block_get". + * + * @see ump_dd_phys_blocks_get + * @see ump_dd_phys_block_get + * + * @param mem Handle to UMP memory. + * + * @return The number of ump_dd_physical_block structs required to describe the physical memory layout of the specified UMP memory. + */ +UMP_KERNEL_API_EXPORT unsigned long ump_dd_phys_block_count_get(ump_dd_handle mem); + + +/** + * Retrieves all physical memory block information for specified UMP memory. + * + * This function can be used by other device drivers in order to create MMU tables. + * + * @note This function will fail if the num_blocks parameter is either to large or to small. + * + * @see ump_dd_phys_block_get + * + * @param mem Handle to UMP memory. + * @param blocks An array of @ref ump_dd_physical_block "ump_dd_physical_block" structs that will receive the physical description. + * @param num_blocks The number of blocks to return in the blocks array. Use the function + * @ref ump_dd_phys_block_count_get "ump_dd_phys_block_count_get" first to determine the number of blocks required. + * + * @return UMP_DD_SUCCESS indicates success, UMP_DD_INVALID indicates failure. + */ +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_blocks_get(ump_dd_handle mem, ump_dd_physical_block * blocks, unsigned long num_blocks); + + +/** + * Retrieves the physical memory block information for specified block for the specified UMP memory. + * + * This function can be used by other device drivers in order to create MMU tables. + * + * @note This function will return UMP_DD_INVALID if the specified index is out of range. + * + * @see ump_dd_phys_blocks_get + * + * @param mem Handle to UMP memory. + * @param index Which physical info block to retrieve. + * @param block Pointer to a @ref ump_dd_physical_block "ump_dd_physical_block" struct which will receive the requested information. + * + * @return UMP_DD_SUCCESS indicates success, UMP_DD_INVALID indicates failure. + */ +UMP_KERNEL_API_EXPORT ump_dd_status_code ump_dd_phys_block_get(ump_dd_handle mem, unsigned long index, ump_dd_physical_block * block); + + +/** + * Retrieves the actual size of the specified UMP memory. + * + * The size is reported in bytes, and is typically page aligned. + * + * @note There is a user space equivalent function called @ref ump_size_get "ump_size_get" + * + * @see ump_size_get + * + * @param mem Handle to UMP memory. + * + * @return Returns the allocated size of the specified UMP memory, in bytes. + */ +UMP_KERNEL_API_EXPORT unsigned long ump_dd_size_get(ump_dd_handle mem); + + +/** + * Adds an extra reference to the specified UMP memory. + * + * This function adds an extra reference to the specified UMP memory. This function should + * be used every time a UMP memory handle is duplicated, that is, assigned to another ump_dd_handle + * variable. The function @ref ump_dd_reference_release "ump_dd_reference_release" must then be used + * to release each copy of the UMP memory handle. + * + * @note You are not required to call @ref ump_dd_reference_add "ump_dd_reference_add" + * for UMP handles returned from + * @ref ump_dd_handle_create_from_secure_id "ump_dd_handle_create_from_secure_id", + * because these handles are already reference counted by this function. + * + * @note There is a user space equivalent function called @ref ump_reference_add "ump_reference_add" + * + * @see ump_reference_add + * + * @param mem Handle to UMP memory. + */ +UMP_KERNEL_API_EXPORT void ump_dd_reference_add(ump_dd_handle mem); + + +/** + * Releases a reference from the specified UMP memory. + * + * This function should be called once for every reference to the UMP memory handle. + * When the last reference is released, all resources associated with this UMP memory + * handle are freed. + * + * @note There is a user space equivalent function called @ref ump_reference_release "ump_reference_release" + * + * @see ump_reference_release + * + * @param mem Handle to UMP memory. + */ +UMP_KERNEL_API_EXPORT void ump_dd_reference_release(ump_dd_handle mem); + + +#ifdef __cplusplus +} +#endif + + +/** @} */ /* end group ump_kernel_space_api */ + + +#endif /* __UMP_KERNEL_INTERFACE_H__ */ diff --git a/drivers/gpu/arm/ump/include/ump_kernel_interface_ref_drv.h b/drivers/gpu/arm/ump/include/ump_kernel_interface_ref_drv.h new file mode 100644 index 000000000000..c9937461a0ff --- /dev/null +++ b/drivers/gpu/arm/ump/include/ump_kernel_interface_ref_drv.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_interface.h + */ + +#ifndef __UMP_KERNEL_INTERFACE_REF_DRV_H__ +#define __UMP_KERNEL_INTERFACE_REF_DRV_H__ + +#include "ump_kernel_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Turn specified physical memory into UMP memory. */ +UMP_KERNEL_API_EXPORT ump_dd_handle ump_dd_handle_create_from_phys_blocks(ump_dd_physical_block * blocks, unsigned long num_blocks); + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_KERNEL_INTERFACE_REF_DRV_H__ */ diff --git a/drivers/gpu/arm/ump/include/ump_kernel_platform.h b/drivers/gpu/arm/ump/include/ump_kernel_platform.h new file mode 100644 index 000000000000..4349605e8593 --- /dev/null +++ b/drivers/gpu/arm/ump/include/ump_kernel_platform.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_platform.h + * + * This file should define UMP_KERNEL_API_EXPORT, + * which dictates how the UMP kernel API should be exported/imported. + * Modify this file, if needed, to match your platform setup. + */ + +#ifndef __UMP_KERNEL_PLATFORM_H__ +#define __UMP_KERNEL_PLATFORM_H__ + +/** @addtogroup ump_kernel_space_api + * @{ */ + +/** + * A define which controls how UMP kernel space API functions are imported and exported. + * This define should be set by the implementor of the UMP API. + */ + +#if defined(_WIN32) + +#if defined(UMP_BUILDING_UMP_LIBRARY) +#define UMP_KERNEL_API_EXPORT __declspec(dllexport) +#else +#define UMP_KERNEL_API_EXPORT __declspec(dllimport) +#endif + +#else + +#define UMP_KERNEL_API_EXPORT + +#endif + + +/** @} */ /* end group ump_kernel_space_api */ + + +#endif /* __UMP_KERNEL_PLATFORM_H__ */ diff --git a/drivers/gpu/arm/ump/linux/license/gpl/ump_kernel_license.h b/drivers/gpu/arm/ump/linux/license/gpl/ump_kernel_license.h new file mode 100644 index 000000000000..17b930d2c572 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/license/gpl/ump_kernel_license.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_license.h + * Defines for the macro MODULE_LICENSE. + */ + +#ifndef __UMP_KERNEL_LICENSE_H__ +#define __UMP_KERNEL_LICENSE_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define UMP_KERNEL_LINUX_LICENSE "GPL" +#define UMP_LICENSE_IS_GPL 1 + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_KERNEL_LICENSE_H__ */ diff --git a/drivers/gpu/arm/ump/linux/ump_ioctl.h b/drivers/gpu/arm/ump/linux/ump_ioctl.h new file mode 100644 index 000000000000..531de7371378 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_ioctl.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_IOCTL_H__ +#define __UMP_IOCTL_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <linux/types.h> +#include <linux/ioctl.h> + +#include <ump_uk_types.h> + +#ifndef __user +#define __user +#endif + + +/** + * @file UMP_ioctl.h + * This file describes the interface needed to use the Linux device driver. + * The interface is used by the userpace UMP driver. + */ + +#define UMP_IOCTL_NR 0x90 + + +#define UMP_IOC_QUERY_API_VERSION _IOR(UMP_IOCTL_NR, _UMP_IOC_QUERY_API_VERSION, _ump_uk_api_version_s) +#define UMP_IOC_ALLOCATE _IOWR(UMP_IOCTL_NR, _UMP_IOC_ALLOCATE, _ump_uk_allocate_s) +#define UMP_IOC_RELEASE _IOR(UMP_IOCTL_NR, _UMP_IOC_RELEASE, _ump_uk_release_s) +#define UMP_IOC_SIZE_GET _IOWR(UMP_IOCTL_NR, _UMP_IOC_SIZE_GET, _ump_uk_size_get_s) +#define UMP_IOC_MSYNC _IOW(UMP_IOCTL_NR, _UMP_IOC_MSYNC, _ump_uk_size_get_s) + + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_IOCTL_H__ */ diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_linux.c b/drivers/gpu/arm/ump/linux/ump_kernel_linux.c new file mode 100644 index 000000000000..04a55ab6185c --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_kernel_linux.c @@ -0,0 +1,448 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/module.h> /* kernel module definitions */ +#include <linux/fs.h> /* file system operations */ +#include <linux/cdev.h> /* character device definitions */ +#include <linux/ioport.h> /* request_mem_region */ +#include <linux/mm.h> /* memory management functions and types */ +#include <asm/uaccess.h> /* user space access */ +#include <asm/atomic.h> +#include <linux/device.h> +#include <linux/debugfs.h> + +#include <mach/ump/config.h> /* Configuration for current platform. The symlinc for arch is set by Makefile */ +#include "ump_ioctl.h" +#include "ump_kernel_common.h" +#include "ump_kernel_interface.h" +#include "ump_kernel_interface_ref_drv.h" +#include "ump_kernel_descriptor_mapping.h" +#include "ump_kernel_memory_backend.h" +#include "ump_kernel_memory_backend_os.h" +#include "ump_kernel_memory_backend_dedicated.h" +#include "license/gpl/ump_kernel_license.h" + +#include "ump_osk.h" +#include "ump_ukk.h" +#include "ump_uk_types.h" +#include "ump_ukk_wrappers.h" +#include "ump_ukk_ref_wrappers.h" + + +/* Module parameter to control log level */ +int ump_debug_level = 2; +module_param(ump_debug_level, int, S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH); /* rw-rw-r-- */ +MODULE_PARM_DESC(ump_debug_level, "Higher number, more dmesg output"); + +/* By default the module uses any available major, but it's possible to set it at load time to a specific number */ +int ump_major = 243; +module_param(ump_major, int, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_major, "Device major number"); + +/* Name of the UMP device driver */ +static char ump_dev_name[] = "ump"; /* should be const, but the functions we call requires non-cost */ + + +#if UMP_LICENSE_IS_GPL +static struct dentry *ump_debugfs_dir = NULL; +#endif + +/* + * The data which we attached to each virtual memory mapping request we get. + * Each memory mapping has a reference to the UMP memory it maps. + * We release this reference when the last memory mapping is unmapped. + */ +typedef struct ump_vma_usage_tracker +{ + int references; + ump_dd_handle handle; +} ump_vma_usage_tracker; + +struct ump_device +{ + struct cdev cdev; +#if UMP_LICENSE_IS_GPL + struct class * ump_class; +#endif +}; + +/* The global variable containing the global device data */ +static struct ump_device ump_device; + + +/* Forward declare static functions */ +static int ump_file_open(struct inode *inode, struct file *filp); +static int ump_file_release(struct inode *inode, struct file *filp); +#ifdef HAVE_UNLOCKED_IOCTL +static long ump_file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#else +static int ump_file_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +#endif +static int ump_file_mmap(struct file * filp, struct vm_area_struct * vma); + + +/* This variable defines the file operations this UMP device driver offer */ +static struct file_operations ump_fops = +{ + .owner = THIS_MODULE, + .open = ump_file_open, + .release = ump_file_release, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = ump_file_ioctl, +#else + .ioctl = ump_file_ioctl, +#endif + .mmap = ump_file_mmap +}; + + +/* This function is called by Linux to initialize this module. + * All we do is initialize the UMP device driver. + */ +static int ump_initialize_module(void) +{ + _mali_osk_errcode_t err; + + DBG_MSG(2, ("Inserting UMP device driver. Compiled: %s, time: %s\n", __DATE__, __TIME__)); + + err = ump_kernel_constructor(); + if (_MALI_OSK_ERR_OK != err) + { + MSG_ERR(("UMP device driver init failed\n")); + return map_errcode(err); + } + + MSG(("UMP device driver %s loaded\n", SVN_REV_STRING)); + return 0; +} + + + +/* + * This function is called by Linux to unload/terminate/exit/cleanup this module. + * All we do is terminate the UMP device driver. + */ +static void ump_cleanup_module(void) +{ + DBG_MSG(2, ("Unloading UMP device driver\n")); + ump_kernel_destructor(); + DBG_MSG(2, ("Module unloaded\n")); +} + + + +static ssize_t ump_memory_used_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[64]; + size_t r; + u32 mem = _ump_ukk_report_memory_usage(); + + r = snprintf(buf, 64, "%u\n", mem); + return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); +} + +static const struct file_operations ump_memory_usage_fops = { + .owner = THIS_MODULE, + .read = ump_memory_used_read, +}; + +/* + * Initialize the UMP device driver. + */ +int ump_kernel_device_initialize(void) +{ + int err; + dev_t dev = 0; +#if UMP_LICENSE_IS_GPL + ump_debugfs_dir = debugfs_create_dir(ump_dev_name, NULL); + if (ERR_PTR(-ENODEV) == ump_debugfs_dir) + { + ump_debugfs_dir = NULL; + } + else + { + debugfs_create_file("memory_usage", 0400, ump_debugfs_dir, NULL, &ump_memory_usage_fops); + } +#endif + + if (0 == ump_major) + { + /* auto select a major */ + err = alloc_chrdev_region(&dev, 0, 1, ump_dev_name); + ump_major = MAJOR(dev); + } + else + { + /* use load time defined major number */ + dev = MKDEV(ump_major, 0); + err = register_chrdev_region(dev, 1, ump_dev_name); + } + + if (0 == err) + { + memset(&ump_device, 0, sizeof(ump_device)); + + /* initialize our char dev data */ + cdev_init(&ump_device.cdev, &ump_fops); + ump_device.cdev.owner = THIS_MODULE; + ump_device.cdev.ops = &ump_fops; + + /* register char dev with the kernel */ + err = cdev_add(&ump_device.cdev, dev, 1/*count*/); + if (0 == err) + { + +#if UMP_LICENSE_IS_GPL + ump_device.ump_class = class_create(THIS_MODULE, ump_dev_name); + if (IS_ERR(ump_device.ump_class)) + { + err = PTR_ERR(ump_device.ump_class); + } + else + { + struct device * mdev; + mdev = device_create(ump_device.ump_class, NULL, dev, NULL, ump_dev_name); + if (!IS_ERR(mdev)) + { + return 0; + } + + err = PTR_ERR(mdev); + } + cdev_del(&ump_device.cdev); +#else + return 0; +#endif + } + + unregister_chrdev_region(dev, 1); + } + + return err; +} + + + +/* + * Terminate the UMP device driver + */ +void ump_kernel_device_terminate(void) +{ + dev_t dev = MKDEV(ump_major, 0); + +#if UMP_LICENSE_IS_GPL + device_destroy(ump_device.ump_class, dev); + class_destroy(ump_device.ump_class); +#endif + + /* unregister char device */ + cdev_del(&ump_device.cdev); + + /* free major */ + unregister_chrdev_region(dev, 1); + +#if UMP_LICENSE_IS_GPL + if(ump_debugfs_dir) + debugfs_remove_recursive(ump_debugfs_dir); +#endif +} + +/* + * Open a new session. User space has called open() on us. + */ +static int ump_file_open(struct inode *inode, struct file *filp) +{ + struct ump_session_data * session_data; + _mali_osk_errcode_t err; + + /* input validation */ + if (0 != MINOR(inode->i_rdev)) + { + MSG_ERR(("Minor not zero in ump_file_open()\n")); + return -ENODEV; + } + + /* Call the OS-Independent UMP Open function */ + err = _ump_ukk_open((void**) &session_data ); + if( _MALI_OSK_ERR_OK != err ) + { + MSG_ERR(("Ump failed to open a new session\n")); + return map_errcode( err ); + } + + filp->private_data = (void*)session_data; + filp->f_pos = 0; + + return 0; /* success */ +} + + + +/* + * Close a session. User space has called close() or crashed/terminated. + */ +static int ump_file_release(struct inode *inode, struct file *filp) +{ + _mali_osk_errcode_t err; + + err = _ump_ukk_close((void**) &filp->private_data ); + if( _MALI_OSK_ERR_OK != err ) + { + return map_errcode( err ); + } + + return 0; /* success */ +} + + + +/* + * Handle IOCTL requests. + */ +#ifdef HAVE_UNLOCKED_IOCTL +static long ump_file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#else +static int ump_file_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ + int err = -ENOTTY; + void __user * argument; + struct ump_session_data * session_data; + +#ifndef HAVE_UNLOCKED_IOCTL + (void)inode; /* inode not used */ +#endif + + session_data = (struct ump_session_data *)filp->private_data; + if (NULL == session_data) + { + MSG_ERR(("No session data attached to file object\n")); + return -ENOTTY; + } + + /* interpret the argument as a user pointer to something */ + argument = (void __user *)arg; + + switch (cmd) + { + case UMP_IOC_QUERY_API_VERSION: + err = ump_get_api_version_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_ALLOCATE : + err = ump_allocate_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_RELEASE: + err = ump_release_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_SIZE_GET: + err = ump_size_get_wrapper((u32 __user *)argument, session_data); + break; + + case UMP_IOC_MSYNC: + err = ump_msync_wrapper((u32 __user *)argument, session_data); + break; + + default: + DBG_MSG(1, ("No handler for IOCTL. cmd: 0x%08x, arg: 0x%08lx\n", cmd, arg)); + err = -EFAULT; + break; + } + + return err; +} +#ifndef CONFIG_MALI400MP +int map_errcode( _mali_osk_errcode_t err ) +{ + switch(err) + { + case _MALI_OSK_ERR_OK : return 0; + case _MALI_OSK_ERR_FAULT: return -EFAULT; + case _MALI_OSK_ERR_INVALID_FUNC: return -ENOTTY; + case _MALI_OSK_ERR_INVALID_ARGS: return -EINVAL; + case _MALI_OSK_ERR_NOMEM: return -ENOMEM; + case _MALI_OSK_ERR_TIMEOUT: return -ETIMEDOUT; + case _MALI_OSK_ERR_RESTARTSYSCALL: return -ERESTARTSYS; + case _MALI_OSK_ERR_ITEM_NOT_FOUND: return -ENOENT; + default: return -EFAULT; + } +} +#endif + +/* + * Handle from OS to map specified virtual memory to specified UMP memory. + */ +static int ump_file_mmap(struct file * filp, struct vm_area_struct * vma) +{ + _ump_uk_map_mem_s args; + _mali_osk_errcode_t err; + struct ump_session_data * session_data; + + /* Validate the session data */ + session_data = (struct ump_session_data *)filp->private_data; + if (NULL == session_data) + { + MSG_ERR(("mmap() called without any session data available\n")); + return -EFAULT; + } + + /* Re-pack the arguments that mmap() packed for us */ + args.ctx = session_data; + args.phys_addr = 0; + args.size = vma->vm_end - vma->vm_start; + args._ukk_private = vma; + args.secure_id = vma->vm_pgoff; + args.is_cached = 0; + + if (!(vma->vm_flags & VM_SHARED)) + { + args.is_cached = 1; + vma->vm_flags = vma->vm_flags | VM_SHARED | VM_MAYSHARE ; + DBG_MSG(3, ("UMP Map function: Forcing the CPU to use cache\n")); + } + /* By setting this flag, during a process fork; the child process will not have the parent UMP mappings */ + vma->vm_flags |= VM_DONTCOPY; + + DBG_MSG(4, ("UMP vma->flags: %x\n", vma->vm_flags )); + + /* Call the common mmap handler */ + err = _ump_ukk_map_mem( &args ); + if ( _MALI_OSK_ERR_OK != err) + { + MSG_ERR(("_ump_ukk_map_mem() failed in function ump_file_mmap()")); + return map_errcode( err ); + } + + return 0; /* success */ +} + +/* Export UMP kernel space API functions */ +EXPORT_SYMBOL(ump_dd_secure_id_get); +EXPORT_SYMBOL(ump_dd_handle_create_from_secure_id); +EXPORT_SYMBOL(ump_dd_phys_block_count_get); +EXPORT_SYMBOL(ump_dd_phys_block_get); +EXPORT_SYMBOL(ump_dd_phys_blocks_get); +EXPORT_SYMBOL(ump_dd_size_get); +EXPORT_SYMBOL(ump_dd_reference_add); +EXPORT_SYMBOL(ump_dd_reference_release); + +/* Export our own extended kernel space allocator */ +EXPORT_SYMBOL(ump_dd_handle_create_from_phys_blocks); + +/* Setup init and exit functions for this module */ +module_init(ump_initialize_module); +module_exit(ump_cleanup_module); + +/* And some module informatio */ +MODULE_LICENSE(UMP_KERNEL_LINUX_LICENSE); +MODULE_AUTHOR("ARM Ltd."); +MODULE_VERSION(SVN_REV_STRING); diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_linux.h b/drivers/gpu/arm/ump/linux/ump_kernel_linux.h new file mode 100644 index 000000000000..4ec5a4760c1f --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_kernel_linux.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __UMP_KERNEL_H__ +#define __UMP_KERNEL_H__ + +int ump_kernel_device_initialize(void); +void ump_kernel_device_terminate(void); + + +#endif /* __UMP_KERNEL_H__ */ diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.c b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.c new file mode 100644 index 000000000000..463e6097eadf --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.c @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* needed to detect kernel version specific code */ +#include <linux/version.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include <linux/semaphore.h> +#else /* pre 2.6.26 the file was in the arch specific location */ +#include <asm/semaphore.h> +#endif + +#include <linux/mm.h> +#include <linux/slab.h> +#include <asm/atomic.h> +#include <linux/vmalloc.h> +#include "ump_kernel_common.h" +#include "ump_kernel_memory_backend.h" + + + +#define UMP_BLOCK_SIZE (256UL * 1024UL) /* 256kB, remember to keep the ()s */ + + + +typedef struct block_info +{ + struct block_info * next; +} block_info; + + + +typedef struct block_allocator +{ + struct semaphore mutex; + block_info * all_blocks; + block_info * first_free; + u32 base; + u32 num_blocks; + u32 num_free; +} block_allocator; + + +static void block_allocator_shutdown(ump_memory_backend * backend); +static int block_allocator_allocate(void* ctx, ump_dd_mem * mem); +static void block_allocator_release(void * ctx, ump_dd_mem * handle); +static inline u32 get_phys(block_allocator * allocator, block_info * block); +static u32 block_allocator_stat(struct ump_memory_backend *backend); + + + +/* + * Create dedicated memory backend + */ +ump_memory_backend * ump_block_allocator_create(u32 base_address, u32 size) +{ + ump_memory_backend * backend; + block_allocator * allocator; + u32 usable_size; + u32 num_blocks; + + usable_size = (size + UMP_BLOCK_SIZE - 1) & ~(UMP_BLOCK_SIZE - 1); + num_blocks = usable_size / UMP_BLOCK_SIZE; + + if (0 == usable_size) + { + DBG_MSG(1, ("Memory block of size %u is unusable\n", size)); + return NULL; + } + + DBG_MSG(5, ("Creating dedicated UMP memory backend. Base address: 0x%08x, size: 0x%08x\n", base_address, size)); + DBG_MSG(6, ("%u usable bytes which becomes %u blocks\n", usable_size, num_blocks)); + + backend = kzalloc(sizeof(ump_memory_backend), GFP_KERNEL); + if (NULL != backend) + { + allocator = kmalloc(sizeof(block_allocator), GFP_KERNEL); + if (NULL != allocator) + { + allocator->all_blocks = kmalloc(sizeof(block_allocator) * num_blocks, GFP_KERNEL); + if (NULL != allocator->all_blocks) + { + int i; + + allocator->first_free = NULL; + allocator->num_blocks = num_blocks; + allocator->num_free = num_blocks; + allocator->base = base_address; + sema_init(&allocator->mutex, 1); + + for (i = 0; i < num_blocks; i++) + { + allocator->all_blocks[i].next = allocator->first_free; + allocator->first_free = &allocator->all_blocks[i]; + } + + backend->ctx = allocator; + backend->allocate = block_allocator_allocate; + backend->release = block_allocator_release; + backend->shutdown = block_allocator_shutdown; + backend->stat = block_allocator_stat; + backend->pre_allocate_physical_check = NULL; + backend->adjust_to_mali_phys = NULL; + + return backend; + } + kfree(allocator); + } + kfree(backend); + } + + return NULL; +} + + + +/* + * Destroy specified dedicated memory backend + */ +static void block_allocator_shutdown(ump_memory_backend * backend) +{ + block_allocator * allocator; + + BUG_ON(!backend); + BUG_ON(!backend->ctx); + + allocator = (block_allocator*)backend->ctx; + + DBG_MSG_IF(1, allocator->num_free != allocator->num_blocks, ("%u blocks still in use during shutdown\n", allocator->num_blocks - allocator->num_free)); + + kfree(allocator->all_blocks); + kfree(allocator); + kfree(backend); +} + + + +static int block_allocator_allocate(void* ctx, ump_dd_mem * mem) +{ + block_allocator * allocator; + u32 left; + block_info * last_allocated = NULL; + int i = 0; + + BUG_ON(!ctx); + BUG_ON(!mem); + + allocator = (block_allocator*)ctx; + left = mem->size_bytes; + + BUG_ON(!left); + BUG_ON(!&allocator->mutex); + + mem->nr_blocks = ((left + UMP_BLOCK_SIZE - 1) & ~(UMP_BLOCK_SIZE - 1)) / UMP_BLOCK_SIZE; + mem->block_array = (ump_dd_physical_block*)vmalloc(sizeof(ump_dd_physical_block) * mem->nr_blocks); + if (NULL == mem->block_array) + { + MSG_ERR(("Failed to allocate block array\n")); + return 0; + } + + if (down_interruptible(&allocator->mutex)) + { + MSG_ERR(("Could not get mutex to do block_allocate\n")); + return 0; + } + + mem->size_bytes = 0; + + while ((left > 0) && (allocator->first_free)) + { + block_info * block; + + block = allocator->first_free; + allocator->first_free = allocator->first_free->next; + block->next = last_allocated; + last_allocated = block; + allocator->num_free--; + + mem->block_array[i].addr = get_phys(allocator, block); + mem->block_array[i].size = UMP_BLOCK_SIZE; + mem->size_bytes += UMP_BLOCK_SIZE; + + i++; + + if (left < UMP_BLOCK_SIZE) left = 0; + else left -= UMP_BLOCK_SIZE; + } + + if (left) + { + block_info * block; + /* release all memory back to the pool */ + while (last_allocated) + { + block = last_allocated->next; + last_allocated->next = allocator->first_free; + allocator->first_free = last_allocated; + last_allocated = block; + allocator->num_free++; + } + + vfree(mem->block_array); + mem->backend_info = NULL; + mem->block_array = NULL; + + DBG_MSG(4, ("Could not find a mem-block for the allocation.\n")); + up(&allocator->mutex); + + return 0; + } + + mem->backend_info = last_allocated; + + up(&allocator->mutex); + mem->is_cached=0; + + return 1; +} + + + +static void block_allocator_release(void * ctx, ump_dd_mem * handle) +{ + block_allocator * allocator; + block_info * block, * next; + + BUG_ON(!ctx); + BUG_ON(!handle); + + allocator = (block_allocator*)ctx; + block = (block_info*)handle->backend_info; + BUG_ON(!block); + + if (down_interruptible(&allocator->mutex)) + { + MSG_ERR(("Allocator release: Failed to get mutex - memory leak\n")); + return; + } + + while (block) + { + next = block->next; + + BUG_ON( (block < allocator->all_blocks) || (block > (allocator->all_blocks + allocator->num_blocks))); + + block->next = allocator->first_free; + allocator->first_free = block; + allocator->num_free++; + + block = next; + } + DBG_MSG(3, ("%d blocks free after release call\n", allocator->num_free)); + up(&allocator->mutex); + + vfree(handle->block_array); + handle->block_array = NULL; +} + + + +/* + * Helper function for calculating the physical base adderss of a memory block + */ +static inline u32 get_phys(block_allocator * allocator, block_info * block) +{ + return allocator->base + ((block - allocator->all_blocks) * UMP_BLOCK_SIZE); +} + +static u32 block_allocator_stat(struct ump_memory_backend *backend) +{ + block_allocator *allocator; + BUG_ON(!backend); + allocator = (block_allocator*)backend->ctx; + BUG_ON(!allocator); + + return (allocator->num_blocks - allocator->num_free)* UMP_BLOCK_SIZE; +} diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.h b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.h new file mode 100644 index 000000000000..ca8faae847fa --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_dedicated.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_memory_backend_dedicated.h + */ + +#ifndef __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ +#define __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ + +#include "ump_kernel_memory_backend.h" + +ump_memory_backend * ump_block_allocator_create(u32 base_address, u32 size); + +#endif /* __UMP_KERNEL_MEMORY_BACKEND_DEDICATED_H__ */ + diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.c b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.c new file mode 100644 index 000000000000..fc3afa89fd68 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.c @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* needed to detect kernel version specific code */ +#include <linux/version.h> + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include <linux/semaphore.h> +#else /* pre 2.6.26 the file was in the arch specific location */ +#include <asm/semaphore.h> +#endif + +#include <linux/dma-mapping.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <asm/atomic.h> +#include <linux/vmalloc.h> +#include <asm/cacheflush.h> +#include "ump_kernel_common.h" +#include "ump_kernel_memory_backend.h" + + + +typedef struct os_allocator +{ + struct semaphore mutex; + u32 num_pages_max; /**< Maximum number of pages to allocate from the OS */ + u32 num_pages_allocated; /**< Number of pages allocated from the OS */ +} os_allocator; + + + +static void os_free(void* ctx, ump_dd_mem * descriptor); +static int os_allocate(void* ctx, ump_dd_mem * descriptor); +static void os_memory_backend_destroy(ump_memory_backend * backend); +static u32 os_stat(struct ump_memory_backend *backend); + + + +/* + * Create OS memory backend + */ +ump_memory_backend * ump_os_memory_backend_create(const int max_allocation) +{ + ump_memory_backend * backend; + os_allocator * info; + + info = kmalloc(sizeof(os_allocator), GFP_KERNEL); + if (NULL == info) + { + return NULL; + } + + info->num_pages_max = max_allocation >> PAGE_SHIFT; + info->num_pages_allocated = 0; + + sema_init(&info->mutex, 1); + + backend = kmalloc(sizeof(ump_memory_backend), GFP_KERNEL); + if (NULL == backend) + { + kfree(info); + return NULL; + } + + backend->ctx = info; + backend->allocate = os_allocate; + backend->release = os_free; + backend->shutdown = os_memory_backend_destroy; + backend->stat = os_stat; + backend->pre_allocate_physical_check = NULL; + backend->adjust_to_mali_phys = NULL; + + return backend; +} + + + +/* + * Destroy specified OS memory backend + */ +static void os_memory_backend_destroy(ump_memory_backend * backend) +{ + os_allocator * info = (os_allocator*)backend->ctx; + + DBG_MSG_IF(1, 0 != info->num_pages_allocated, ("%d pages still in use during shutdown\n", info->num_pages_allocated)); + + kfree(info); + kfree(backend); +} + + + +/* + * Allocate UMP memory + */ +static int os_allocate(void* ctx, ump_dd_mem * descriptor) +{ + u32 left; + os_allocator * info; + int pages_allocated = 0; + int is_cached; + + BUG_ON(!descriptor); + BUG_ON(!ctx); + + info = (os_allocator*)ctx; + left = descriptor->size_bytes; + is_cached = descriptor->is_cached; + + if (down_interruptible(&info->mutex)) + { + DBG_MSG(1, ("Failed to get mutex in os_free\n")); + return 0; /* failure */ + } + + descriptor->backend_info = NULL; + descriptor->nr_blocks = ((left + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) >> PAGE_SHIFT; + + DBG_MSG(5, ("Allocating page array. Size: %lu\n", descriptor->nr_blocks * sizeof(ump_dd_physical_block))); + + descriptor->block_array = (ump_dd_physical_block *)vmalloc(sizeof(ump_dd_physical_block) * descriptor->nr_blocks); + if (NULL == descriptor->block_array) + { + up(&info->mutex); + DBG_MSG(1, ("Block array could not be allocated\n")); + return 0; /* failure */ + } + + while (left > 0 && ((info->num_pages_allocated + pages_allocated) < info->num_pages_max)) + { + struct page * new_page; + + if (is_cached) + { + new_page = alloc_page(GFP_HIGHUSER | __GFP_ZERO | __GFP_REPEAT | __GFP_NOWARN); + } else + { + new_page = alloc_page(GFP_HIGHUSER | __GFP_ZERO | __GFP_REPEAT | __GFP_NOWARN | __GFP_COLD); + } + if (NULL == new_page) + { + break; + } + + /* Ensure page caches are flushed. */ + if ( is_cached ) + { + descriptor->block_array[pages_allocated].addr = page_to_phys(new_page); + descriptor->block_array[pages_allocated].size = PAGE_SIZE; + } else + { + descriptor->block_array[pages_allocated].addr = dma_map_page(NULL, new_page, 0, PAGE_SIZE, DMA_BIDIRECTIONAL ); + descriptor->block_array[pages_allocated].size = PAGE_SIZE; + } + + DBG_MSG(5, ("Allocated page 0x%08lx cached: %d\n", descriptor->block_array[pages_allocated].addr, is_cached)); + + if (left < PAGE_SIZE) + { + left = 0; + } + else + { + left -= PAGE_SIZE; + } + + pages_allocated++; + } + + DBG_MSG(5, ("Alloce for ID:%2d got %d pages, cached: %d\n", descriptor->secure_id, pages_allocated)); + + if (left) + { + DBG_MSG(1, ("Failed to allocate needed pages\n")); + + while(pages_allocated) + { + pages_allocated--; + if ( !is_cached ) + { + dma_unmap_page(NULL, descriptor->block_array[pages_allocated].addr, PAGE_SIZE, DMA_BIDIRECTIONAL); + } + __free_page(pfn_to_page(descriptor->block_array[pages_allocated].addr >> PAGE_SHIFT) ); + } + + up(&info->mutex); + + return 0; /* failure */ + } + + info->num_pages_allocated += pages_allocated; + + DBG_MSG(6, ("%d out of %d pages now allocated\n", info->num_pages_allocated, info->num_pages_max)); + + up(&info->mutex); + + return 1; /* success*/ +} + + +/* + * Free specified UMP memory + */ +static void os_free(void* ctx, ump_dd_mem * descriptor) +{ + os_allocator * info; + int i; + + BUG_ON(!ctx); + BUG_ON(!descriptor); + + info = (os_allocator*)ctx; + + BUG_ON(descriptor->nr_blocks > info->num_pages_allocated); + + if (down_interruptible(&info->mutex)) + { + DBG_MSG(1, ("Failed to get mutex in os_free\n")); + return; + } + + DBG_MSG(5, ("Releasing %lu OS pages\n", descriptor->nr_blocks)); + + info->num_pages_allocated -= descriptor->nr_blocks; + + up(&info->mutex); + + for ( i = 0; i < descriptor->nr_blocks; i++) + { + DBG_MSG(6, ("Freeing physical page. Address: 0x%08lx\n", descriptor->block_array[i].addr)); + if ( ! descriptor->is_cached) + { + dma_unmap_page(NULL, descriptor->block_array[i].addr, PAGE_SIZE, DMA_BIDIRECTIONAL); + } + __free_page(pfn_to_page(descriptor->block_array[i].addr>>PAGE_SHIFT) ); + } + + vfree(descriptor->block_array); +} + + +static u32 os_stat(struct ump_memory_backend *backend) +{ + os_allocator *info; + info = (os_allocator*)backend->ctx; + return info->num_pages_allocated * _MALI_OSK_MALI_PAGE_SIZE; +} diff --git a/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.h b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.h new file mode 100644 index 000000000000..6f7e610324ff --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_kernel_memory_backend_os.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_kernel_memory_backend_os.h + */ + +#ifndef __UMP_KERNEL_MEMORY_BACKEND_OS_H__ +#define __UMP_KERNEL_MEMORY_BACKEND_OS_H__ + +#include "ump_kernel_memory_backend.h" + +ump_memory_backend * ump_os_memory_backend_create(const int max_allocation); + +#endif /* __UMP_KERNEL_MEMORY_BACKEND_OS_H__ */ + diff --git a/drivers/gpu/arm/ump/linux/ump_memory_backend.c b/drivers/gpu/arm/ump/linux/ump_memory_backend.c new file mode 100644 index 000000000000..d4444cad7e14 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_memory_backend.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/module.h> /* kernel module definitions */ +#include <linux/ioport.h> /* request_mem_region */ + +#include <mach/ump/config.h> /* Configuration for current platform. The symlink for arch is set by Makefile */ + +#include "ump_osk.h" +#include "ump_kernel_common.h" +#include "ump_kernel_memory_backend_os.h" +#include "ump_kernel_memory_backend_dedicated.h" + +/* Configure which dynamic memory allocator to use */ +int ump_backend = ARCH_UMP_BACKEND_DEFAULT; +module_param(ump_backend, int, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_backend, "0 = dedicated memory backend (default), 1 = OS memory backend"); + +/* The base address of the memory block for the dedicated memory backend */ +unsigned int ump_memory_address = ARCH_UMP_MEMORY_ADDRESS_DEFAULT; +module_param(ump_memory_address, uint, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_memory_address, "The physical address to map for the dedicated memory backend"); + +/* The size of the memory block for the dedicated memory backend */ +unsigned int ump_memory_size = ARCH_UMP_MEMORY_SIZE_DEFAULT; +module_param(ump_memory_size, uint, S_IRUGO); /* r--r--r-- */ +MODULE_PARM_DESC(ump_memory_size, "The size of fixed memory to map in the dedicated memory backend"); + +ump_memory_backend* ump_memory_backend_create ( void ) +{ + ump_memory_backend * backend = NULL; + + /* Create the dynamic memory allocator backend */ + if (0 == ump_backend) + { + DBG_MSG(2, ("Using dedicated memory backend\n")); + + DBG_MSG(2, ("Requesting dedicated memory: 0x%08x, size: %u\n", ump_memory_address, ump_memory_size)); + /* Ask the OS if we can use the specified physical memory */ + if (NULL == request_mem_region(ump_memory_address, ump_memory_size, "UMP Memory")) + { + MSG_ERR(("Failed to request memory region (0x%08X - 0x%08X). Is Mali DD already loaded?\n", ump_memory_address, ump_memory_address + ump_memory_size - 1)); + return NULL; + } + backend = ump_block_allocator_create(ump_memory_address, ump_memory_size); + } + else if (1 == ump_backend) + { + DBG_MSG(2, ("Using OS memory backend, allocation limit: %d\n", ump_memory_size)); + backend = ump_os_memory_backend_create(ump_memory_size); + } + + return backend; +} + +void ump_memory_backend_destroy( void ) +{ + if (0 == ump_backend) + { + DBG_MSG(2, ("Releasing dedicated memory: 0x%08x\n", ump_memory_address)); + release_mem_region(ump_memory_address, ump_memory_size); + } +} diff --git a/drivers/gpu/arm/ump/linux/ump_osk_atomics.c b/drivers/gpu/arm/ump/linux/ump_osk_atomics.c new file mode 100644 index 000000000000..b117d99bd77b --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_osk_atomics.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk_atomics.c + * Implementation of the OS abstraction layer for the UMP kernel device driver + */ + +#include "ump_osk.h" +#include <asm/atomic.h> + +int _ump_osk_atomic_dec_and_read( _mali_osk_atomic_t *atom ) +{ + return atomic_dec_return((atomic_t *)&atom->u.val); +} + +int _ump_osk_atomic_inc_and_read( _mali_osk_atomic_t *atom ) +{ + return atomic_inc_return((atomic_t *)&atom->u.val); +} diff --git a/drivers/gpu/arm/ump/linux/ump_osk_low_level_mem.c b/drivers/gpu/arm/ump/linux/ump_osk_low_level_mem.c new file mode 100644 index 000000000000..fd0d7e523e0f --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_osk_low_level_mem.c @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk_memory.c + * Implementation of the OS abstraction layer for the kernel device driver + */ + +/* needed to detect kernel version specific code */ +#include <linux/version.h> + +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" +#include <linux/module.h> /* kernel module definitions */ +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/slab.h> + +#include <asm/memory.h> +#include <asm/uaccess.h> /* to verify pointers from user space */ +#include <asm/cacheflush.h> +#include <linux/dma-mapping.h> + +typedef struct ump_vma_usage_tracker +{ + atomic_t references; + ump_memory_allocation *descriptor; +} ump_vma_usage_tracker; + +static void ump_vma_open(struct vm_area_struct * vma); +static void ump_vma_close(struct vm_area_struct * vma); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +static int ump_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf); +#else +static unsigned long ump_cpu_page_fault_handler(struct vm_area_struct * vma, unsigned long address); +#endif + +static struct vm_operations_struct ump_vm_ops = +{ + .open = ump_vma_open, + .close = ump_vma_close, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + .fault = ump_cpu_page_fault_handler +#else + .nopfn = ump_cpu_page_fault_handler +#endif +}; + +/* + * Page fault for VMA region + * This should never happen since we always map in the entire virtual memory range. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +static int ump_cpu_page_fault_handler(struct vm_area_struct *vma, struct vm_fault *vmf) +#else +static unsigned long ump_cpu_page_fault_handler(struct vm_area_struct * vma, unsigned long address) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + void __user * address; + address = vmf->virtual_address; +#endif + MSG_ERR(("Page-fault in UMP memory region caused by the CPU\n")); + MSG_ERR(("VMA: 0x%08lx, virtual address: 0x%08lx\n", (unsigned long)vma, address)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + return VM_FAULT_SIGBUS; +#else + return NOPFN_SIGBUS; +#endif +} + +static void ump_vma_open(struct vm_area_struct * vma) +{ + ump_vma_usage_tracker * vma_usage_tracker; + int new_val; + + vma_usage_tracker = (ump_vma_usage_tracker*)vma->vm_private_data; + BUG_ON(NULL == vma_usage_tracker); + + new_val = atomic_inc_return(&vma_usage_tracker->references); + + DBG_MSG(4, ("VMA open, VMA reference count incremented. VMA: 0x%08lx, reference count: %d\n", (unsigned long)vma, new_val)); +} + +static void ump_vma_close(struct vm_area_struct * vma) +{ + ump_vma_usage_tracker * vma_usage_tracker; + _ump_uk_unmap_mem_s args; + int new_val; + + vma_usage_tracker = (ump_vma_usage_tracker*)vma->vm_private_data; + BUG_ON(NULL == vma_usage_tracker); + + new_val = atomic_dec_return(&vma_usage_tracker->references); + + DBG_MSG(4, ("VMA close, VMA reference count decremented. VMA: 0x%08lx, reference count: %d\n", (unsigned long)vma, new_val)); + + if (0 == new_val) + { + ump_memory_allocation * descriptor; + + descriptor = vma_usage_tracker->descriptor; + + args.ctx = descriptor->ump_session; + args.cookie = descriptor->cookie; + args.mapping = descriptor->mapping; + args.size = descriptor->size; + + args._ukk_private = NULL; /** @note unused */ + + DBG_MSG(4, ("No more VMA references left, releasing UMP memory\n")); + _ump_ukk_unmap_mem( & args ); + + /* vma_usage_tracker is free()d by _ump_osk_mem_mapregion_term() */ + } +} + +_mali_osk_errcode_t _ump_osk_mem_mapregion_init( ump_memory_allocation * descriptor ) +{ + ump_vma_usage_tracker * vma_usage_tracker; + struct vm_area_struct *vma; + + if (NULL == descriptor) return _MALI_OSK_ERR_FAULT; + + vma_usage_tracker = kmalloc(sizeof(ump_vma_usage_tracker), GFP_KERNEL); + if (NULL == vma_usage_tracker) + { + DBG_MSG(1, ("Failed to allocate memory for ump_vma_usage_tracker in _mali_osk_mem_mapregion_init\n")); + return -_MALI_OSK_ERR_FAULT; + } + + vma = (struct vm_area_struct*)descriptor->process_mapping_info; + if (NULL == vma ) + { + kfree(vma_usage_tracker); + return _MALI_OSK_ERR_FAULT; + } + + vma->vm_private_data = vma_usage_tracker; + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + + if (0==descriptor->is_cached) + { + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + } + DBG_MSG(3, ("Mapping with page_prot: 0x%x\n", vma->vm_page_prot )); + + /* Setup the functions which handle further VMA handling */ + vma->vm_ops = &ump_vm_ops; + + /* Do the va range allocation - in this case, it was done earlier, so we copy in that information */ + descriptor->mapping = (void __user*)vma->vm_start; + + atomic_set(&vma_usage_tracker->references, 1); /*this can later be increased if process is forked, see ump_vma_open() */ + vma_usage_tracker->descriptor = descriptor; + + return _MALI_OSK_ERR_OK; +} + +void _ump_osk_mem_mapregion_term( ump_memory_allocation * descriptor ) +{ + struct vm_area_struct* vma; + ump_vma_usage_tracker * vma_usage_tracker; + + if (NULL == descriptor) return; + + /* Linux does the right thing as part of munmap to remove the mapping + * All that remains is that we remove the vma_usage_tracker setup in init() */ + vma = (struct vm_area_struct*)descriptor->process_mapping_info; + + vma_usage_tracker = vma->vm_private_data; + + /* We only get called if mem_mapregion_init succeeded */ + kfree(vma_usage_tracker); + return; +} + +_mali_osk_errcode_t _ump_osk_mem_mapregion_map( ump_memory_allocation * descriptor, u32 offset, u32 * phys_addr, unsigned long size ) +{ + struct vm_area_struct *vma; + _mali_osk_errcode_t retval; + + if (NULL == descriptor) return _MALI_OSK_ERR_FAULT; + + vma = (struct vm_area_struct*)descriptor->process_mapping_info; + + if (NULL == vma ) return _MALI_OSK_ERR_FAULT; + + retval = remap_pfn_range( vma, ((u32)descriptor->mapping) + offset, (*phys_addr) >> PAGE_SHIFT, size, vma->vm_page_prot) ? _MALI_OSK_ERR_FAULT : _MALI_OSK_ERR_OK;; + + DBG_MSG(4, ("Mapping virtual to physical memory. ID: %u, vma: 0x%08lx, virtual addr:0x%08lx, physical addr: 0x%08lx, size:%lu, prot:0x%x, vm_flags:0x%x RETVAL: 0x%x\n", + ump_dd_secure_id_get(descriptor->handle), + (unsigned long)vma, + (unsigned long)(vma->vm_start + offset), + (unsigned long)*phys_addr, + size, + (unsigned int)vma->vm_page_prot, vma->vm_flags, retval)); + + return retval; +} + + +void _ump_osk_msync( ump_dd_mem * mem, void * virt, u32 offset, u32 size, ump_uk_msync_op op ) +{ + int i; + const void *start_v, *end_v; + + DBG_MSG(3, ("Flushing nr of blocks: %u, size: %u. First: paddr: 0x%08x vaddr: 0x%08x offset: 0x%08x size:%uB\n", + mem->nr_blocks, mem->size_bytes, mem->block_array[0].addr, virt, offset, size)); + + /* Flush L1 using virtual address, the entire range in one go. + * Only flush if user space process has a valid write mapping on given address. */ + if(access_ok(VERIFY_WRITE, virt, size)) + { + start_v = (void *)virt; + end_v = (void *)(start_v + size - 1); + /* There is no dmac_clean_range, so the L1 is always flushed, + * also for UMP_MSYNC_CLEAN. */ + dmac_flush_range(start_v, end_v); + } + else + { + DBG_MSG(1, ("Attempt to flush %d@%x as part of ID %u rejected: there is no valid mapping.\n", + size, virt, mem->secure_id)); + return; + } + + /* Flush L2 using physical addresses, block for block. */ + for (i=0 ; i < mem->nr_blocks; i++) + { + u32 start_p, end_p; + ump_dd_physical_block *block; + block = &mem->block_array[i]; + + if(offset >= block->size) + { + offset -= block->size; + continue; + } + + if(offset) + { + start_p = (u32)block->addr + offset; + /* We'll zero the offset later, after using it to calculate end_p. */ + } + else + { + start_p = (u32)block->addr; + } + + if(size < block->size - offset) + { + end_p = start_p + size - 1; + size = 0; + } + else + { + if(offset) + { + end_p = start_p + (block->size - offset - 1); + size -= block->size - offset; + offset = 0; + } + else + { + end_p = start_p + block->size - 1; + size -= block->size; + } + } + + switch(op) + { + case _UMP_UK_MSYNC_CLEAN: + outer_clean_range(start_p, end_p); + break; + case _UMP_UK_MSYNC_CLEAN_AND_INVALIDATE: + outer_flush_range(start_p, end_p); + break; + default: + break; + } + + if(0 == size) + { + /* Nothing left to flush. */ + break; + } + } + + return; +} diff --git a/drivers/gpu/arm/ump/linux/ump_osk_misc.c b/drivers/gpu/arm/ump/linux/ump_osk_misc.c new file mode 100644 index 000000000000..3be6fedcb0b9 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_osk_misc.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_osk_misc.c + * Implementation of the OS abstraction layer for the UMP kernel device driver + */ + + +#include "ump_osk.h" + +#include <linux/kernel.h> +#include "ump_kernel_linux.h" + +/* is called from ump_kernel_constructor in common code */ +_mali_osk_errcode_t _ump_osk_init( void ) +{ + if (0 != ump_kernel_device_initialize()) + { + return _MALI_OSK_ERR_FAULT; + } + + return _MALI_OSK_ERR_OK; +} + +_mali_osk_errcode_t _ump_osk_term( void ) +{ + ump_kernel_device_terminate(); + return _MALI_OSK_ERR_OK; +} diff --git a/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.c b/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.c new file mode 100644 index 000000000000..6a2791e28c2d --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.c + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls for the reference implementation + */ + + +#include <asm/uaccess.h> /* user space access */ + +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" + +/* + * IOCTL operation; Allocate UMP memory + */ +int ump_allocate_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_allocate_s user_interaction; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_allocate()\n")); + return -ENOTTY; + } + + /* Copy the user space memory to kernel space (so we safely can read it) */ + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_allocate()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + err = _ump_ukk_allocate( &user_interaction ); + if( _MALI_OSK_ERR_OK != err ) + { + DBG_MSG(1, ("_ump_ukk_allocate() failed in ump_ioctl_allocate()\n")); + return map_errcode(err); + } + user_interaction.ctx = NULL; + + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) + { + /* If the copy fails then we should release the memory. We can use the IOCTL release to accomplish this */ + _ump_uk_release_s release_args; + + MSG_ERR(("copy_to_user() failed in ump_ioctl_allocate()\n")); + + release_args.ctx = (void *) session_data; + release_args.secure_id = user_interaction.secure_id; + + err = _ump_ukk_release( &release_args ); + if(_MALI_OSK_ERR_OK != err) + { + MSG_ERR(("_ump_ukk_release() also failed when trying to release newly allocated memory in ump_ioctl_allocate()\n")); + } + + return -EFAULT; + } + + return 0; /* success */ +} diff --git a/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.h b/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.h new file mode 100644 index 000000000000..a3e3cb0c2f64 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_ukk_ref_wrappers.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.h + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls for the reference implementation + */ + +#ifndef __UMP_UKK_REF_WRAPPERS_H__ +#define __UMP_UKK_REF_WRAPPERS_H__ + +#include <linux/kernel.h> +#include "ump_kernel_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +int ump_allocate_wrapper(u32 __user * argument, struct ump_session_data * session_data); + + +#ifdef __cplusplus +} +#endif + +#endif /* __UMP_UKK_REF_WRAPPERS_H__ */ diff --git a/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.c b/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.c new file mode 100644 index 000000000000..7fa805993b8e --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2010-2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.c + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls + */ + +#include <asm/uaccess.h> /* user space access */ + +#include "ump_osk.h" +#include "ump_uk_types.h" +#include "ump_ukk.h" +#include "ump_kernel_common.h" + +/* + * IOCTL operation; Negotiate version of IOCTL API + */ +int ump_get_api_version_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_api_version_s version_info; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_get_api_version()\n")); + return -ENOTTY; + } + + /* Copy the user space memory to kernel space (so we safely can read it) */ + if (0 != copy_from_user(&version_info, argument, sizeof(version_info))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_get_api_version()\n")); + return -EFAULT; + } + + version_info.ctx = (void*) session_data; + err = _ump_uku_get_api_version( &version_info ); + if( _MALI_OSK_ERR_OK != err ) + { + MSG_ERR(("_ump_uku_get_api_version() failed in ump_ioctl_get_api_version()\n")); + return map_errcode(err); + } + + version_info.ctx = NULL; + + /* Copy ouput data back to user space */ + if (0 != copy_to_user(argument, &version_info, sizeof(version_info))) + { + MSG_ERR(("copy_to_user() failed in ump_ioctl_get_api_version()\n")); + return -EFAULT; + } + + return 0; /* success */ +} + + +/* + * IOCTL operation; Release reference to specified UMP memory. + */ +int ump_release_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_release_s release_args; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_release()\n")); + return -ENOTTY; + } + + /* Copy the user space memory to kernel space (so we safely can read it) */ + if (0 != copy_from_user(&release_args, argument, sizeof(release_args))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_get_api_version()\n")); + return -EFAULT; + } + + release_args.ctx = (void*) session_data; + err = _ump_ukk_release( &release_args ); + if( _MALI_OSK_ERR_OK != err ) + { + MSG_ERR(("_ump_ukk_release() failed in ump_ioctl_release()\n")); + return map_errcode(err); + } + + + return 0; /* success */ +} + +/* + * IOCTL operation; Return size for specified UMP memory. + */ +int ump_size_get_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_size_get_s user_interaction; + _mali_osk_errcode_t err; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_size_get()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + err = _ump_ukk_size_get( &user_interaction ); + if( _MALI_OSK_ERR_OK != err ) + { + MSG_ERR(("_ump_ukk_size_get() failed in ump_ioctl_size_get()\n")); + return map_errcode(err); + } + + user_interaction.ctx = NULL; + + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) + { + MSG_ERR(("copy_to_user() failed in ump_ioctl_size_get()\n")); + return -EFAULT; + } + + return 0; /* success */ +} + +/* + * IOCTL operation; Do cache maintenance on specified UMP memory. + */ +int ump_msync_wrapper(u32 __user * argument, struct ump_session_data * session_data) +{ + _ump_uk_msync_s user_interaction; + + /* Sanity check input parameters */ + if (NULL == argument || NULL == session_data) + { + MSG_ERR(("NULL parameter in ump_ioctl_size_get()\n")); + return -ENOTTY; + } + + if (0 != copy_from_user(&user_interaction, argument, sizeof(user_interaction))) + { + MSG_ERR(("copy_from_user() in ump_ioctl_msync()\n")); + return -EFAULT; + } + + user_interaction.ctx = (void *) session_data; + + _ump_ukk_msync( &user_interaction ); + + user_interaction.ctx = NULL; + + if (0 != copy_to_user(argument, &user_interaction, sizeof(user_interaction))) + { + MSG_ERR(("copy_to_user() failed in ump_ioctl_msync()\n")); + return -EFAULT; + } + + return 0; /* success */ +} diff --git a/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.h b/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.h new file mode 100644 index 000000000000..b04445a2c7a3 --- /dev/null +++ b/drivers/gpu/arm/ump/linux/ump_ukk_wrappers.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010, 2012 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file ump_ukk_wrappers.h + * Defines the wrapper functions which turn Linux IOCTL calls into _ukk_ calls + */ + +#ifndef __UMP_UKK_WRAPPERS_H__ +#define __UMP_UKK_WRAPPERS_H__ + +#include <linux/kernel.h> +#include "ump_kernel_common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +int ump_get_api_version_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_release_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_size_get_wrapper(u32 __user * argument, struct ump_session_data * session_data); +int ump_msync_wrapper(u32 __user * argument, struct ump_session_data * session_data); + + +#ifdef __cplusplus +} +#endif + + + +#endif /* __UMP_UKK_WRAPPERS_H__ */ diff --git a/drivers/gpu/arm/ump/readme.txt b/drivers/gpu/arm/ump/readme.txt new file mode 100644 index 000000000000..c238cf0f2b1f --- /dev/null +++ b/drivers/gpu/arm/ump/readme.txt @@ -0,0 +1,28 @@ +Building the UMP Device Driver for Linux +---------------------------------------- + +Build the UMP Device Driver for Linux by running the following make command: + +KDIR=<kdir_path> CONFIG=<your_config> BUILD=<build_option> make + +where + kdir_path: Path to your Linux Kernel directory + your_config: Name of the sub-folder to find the required config.h file + ("arch-" will be prepended) + build_option: debug or release. Debug is default. + +The config.h contains following configuration parameters: + +ARCH_UMP_BACKEND_DEFAULT + 0 specifies the dedicated memory allocator. + 1 specifies the OS memory allocator. +ARCH_UMP_MEMORY_ADDRESS_DEFAULT + This is only required for the dedicated memory allocator, and specifies + the physical start address of the memory block reserved for UMP. +ARCH_UMP_MEMORY_SIZE_DEFAULT + This specified the size of the memory block reserved for UMP, or the + maximum limit for allocations from the OS. + +The result will be a ump.ko file, which can be loaded into the Linux kernel +by using the insmod command. The driver can also be built as a part of the +kernel itself. diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 18321b68b880..4e2440a22dc6 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -195,6 +195,13 @@ config DRM_SAVAGE Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister chipset. If M is selected the module will be called savage. +config DRM_MALI + tristate "Mali-200, Mali-400 GPU" + depends on DRM && UMP + help + Choose this option if you have a Mali-200, Mali-400 or compatible GPU + chipset. If M is selected the module will be called mali. + source "drivers/gpu/drm/exynos/Kconfig" source "drivers/gpu/drm/vmwgfx/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 2ff5cefe9ead..aa0e94d019ea 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -48,4 +48,5 @@ obj-$(CONFIG_DRM_GMA500) += gma500/ obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_AST) += ast/ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ +obj-$(CONFIG_DRM_MALI) +=mali/ obj-y += i2c/ diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c index 118c117b3226..95bd2a789d23 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c @@ -25,6 +25,7 @@ #include <drm/drmP.h> #include <drm/exynos_drm.h> +#include <linux/module.h> #include "exynos_drm_drv.h" #include "exynos_drm_gem.h" diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c index 0f68a2872673..3b01eacccfee 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c @@ -27,6 +27,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <linux/module.h> #include <drm/exynos_drm.h> #include "exynos_drm_drv.h" diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c index 94026ad76a77..307d41c7886d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_core.c +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -27,6 +27,7 @@ */ #include <drm/drmP.h> +#include <linux/module.h> #include "exynos_drm_drv.h" #include "exynos_drm_encoder.h" #include "exynos_drm_connector.h" diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index fce245f64c4f..1cc5641f1252 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -28,6 +28,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <linux/module.h> #include "exynos_drm_drv.h" #include "exynos_drm_encoder.h" diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 1de7baafddd0..e7886247ed55 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -27,6 +27,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <linux/module.h> #include <drm/exynos_drm.h> diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c index 241ad1eeec64..7cfec770490a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c @@ -28,6 +28,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <linux/module.h> #include "exynos_drm_drv.h" #include "exynos_drm_encoder.h" diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 4ef4cd3f9936..7117370e97f1 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -30,6 +30,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_helper.h> +#include <linux/module.h> #include "exynos_drm_drv.h" #include "exynos_drm_fb.h" diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 67eb6ba56edf..fd571e51d24e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -30,6 +30,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_fb_helper.h> #include <drm/drm_crtc_helper.h> +#include <linux/module.h> #include "exynos_drm_drv.h" #include "exynos_drm_fb.h" diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index d2545560664f..b4e68408a605 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -24,6 +24,7 @@ */ #include <drm/drmP.h> +#include <linux/module.h> #include <linux/shmem_fs.h> #include <drm/exynos_drm.h> diff --git a/drivers/gpu/drm/mali/Makefile b/drivers/gpu/drm/mali/Makefile new file mode 100644 index 000000000000..68ee68c03c5c --- /dev/null +++ b/drivers/gpu/drm/mali/Makefile @@ -0,0 +1,20 @@ +# +# * Copyright (C) 2010 ARM Limited. All rights reserved. +# * +# * This program is free software and is provided to you under the terms of the GNU General Public License version 2 +# * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. +# * +# * A copy of the licence is included with the program, and can also be obtained from Free Software +# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +# +# Makefile for the Mali drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. + +ccflags-y = -Iinclude/drm +mali_drm-y := mali_drv.o + +obj-$(CONFIG_DRM_MALI) += mali_drm.o + + diff --git a/drivers/gpu/drm/mali/mali_drv.c b/drivers/gpu/drm/mali/mali_drv.c new file mode 100644 index 000000000000..ec6fdcacb7df --- /dev/null +++ b/drivers/gpu/drm/mali/mali_drv.c @@ -0,0 +1,177 @@ +/** + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * @file mali_drv.c + * Implementation of the Linux device driver entrypoints for Mali DRM + */ +#include <linux/module.h> +#include <linux/version.h> +#include <linux/vermagic.h> +#include "drmP.h" +#include "mali_drv.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) +static struct pci_device_id pciidlist[] = { + /*need to fill this with right id lists*/ +}; + +static struct pci_driver mali_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, +}; +#endif + +void mali_drm_preclose(struct drm_device *dev, struct drm_file *file_priv) +{ +} + +void mali_drm_lastclose(struct drm_device *dev) +{ +} + +static int mali_drm_suspend(struct drm_device *dev, pm_message_t state) +{ + return 0; +} + +static int mali_drm_resume(struct drm_device *dev) +{ + return 0; +} + +static int mali_drm_load(struct drm_device *dev, unsigned long chipset) +{ + return 0; +} + +static int mali_drm_unload(struct drm_device *dev) +{ + return 0; +} + +static const struct file_operations drm_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = drm_mmap, + .poll = drm_poll, + .fasync = drm_fasync, +}; + +static struct drm_driver driver = +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) + .driver_features = DRIVER_USE_PLATFORM_DEVICE, +#endif + .load = mali_drm_load, + .unload = mali_drm_unload, + .context_dtor = NULL, + .reclaim_buffers = NULL, + .reclaim_buffers_idlelocked = NULL, + .preclose = mali_drm_preclose, + .lastclose = mali_drm_lastclose, + .suspend = mali_drm_suspend, + .resume = mali_drm_resume, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) + .get_map_ofs = drm_core_get_map_ofs, + .get_reg_ofs = drm_core_get_reg_ofs, +#endif + .ioctls = NULL, + .fops = &drm_fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; + +int mali_drm_init(struct platform_device *dev) +{ + printk(KERN_INFO "Mali DRM initialize, driver name: %s, version %d.%d\n", DRIVER_NAME, DRIVER_MAJOR, DRIVER_MINOR); + driver.num_ioctls = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) + driver.platform_device = dev; +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) + return drm_init(&driver); +#else + return drm_pci_init(&driver, &mali_pci_driver); +#endif +} + +void mali_drm_exit(void) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) + drm_exit(&driver); +#else + drm_pci_exit(&driver, &mali_pci_driver); +#endif +} + +static int __devinit mali_platform_drm_probe(struct platform_device *dev) +{ + return mali_drm_init(dev); +} + +static int mali_platform_drm_remove(struct platform_device *dev) +{ + mali_drm_exit(); + + return 0; +} + +static int mali_platform_drm_suspend(struct platform_device *dev, pm_message_t state) +{ + return 0; +} + +static int mali_platform_drm_resume(struct platform_device *dev) +{ + return 0; +} + + +static struct platform_driver platform_drm_driver = { + .probe = mali_platform_drm_probe, + .remove = __devexit_p(mali_platform_drm_remove), + .suspend = mali_platform_drm_suspend, + .resume = mali_platform_drm_resume, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, +}; + +static int __init mali_platform_drm_init(void) +{ + return platform_driver_register( &platform_drm_driver ); +} + +static void __exit mali_platform_drm_exit(void) +{ + platform_driver_unregister( &platform_drm_driver ); +} + +#ifdef MODULE +module_init(mali_platform_drm_init); +#else +late_initcall(mali_platform_drm_init); +#endif +module_exit(mali_platform_drm_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE(DRIVER_LICENCE); +MODULE_ALIAS(DRIVER_ALIAS); +MODULE_INFO(vermagic, VERMAGIC_STRING); diff --git a/drivers/gpu/drm/mali/mali_drv.h b/drivers/gpu/drm/mali/mali_drv.h new file mode 100644 index 000000000000..aed5fd356b73 --- /dev/null +++ b/drivers/gpu/drm/mali/mali_drv.h @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2010 ARM Limited. All rights reserved. + * + * This program is free software and is provided to you under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. + * + * A copy of the licence is included with the program, and can also be obtained from Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _MALI_DRV_H_ +#define _MALI_DRV_H_ + +#define DRIVER_AUTHOR "ARM Ltd." +#define DRIVER_NAME "mali_drm" +#define DRIVER_DESC "DRM module for Mali-200, Mali-400" +#define DRIVER_LICENSE "GPLv2" +#define DRIVER_ALIAS "platform:mali_drm" +#define DRIVER_DATE "20101111" +#define DRIVER_VERSION "0.2" +#define DRIVER_MAJOR 2 +#define DRIVER_MINOR 1 +#define DRIVER_PATCHLEVEL 1 + +#endif /* _MALI_DRV_H_ */ diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c index 1002ec0d52de..baab41042ce2 100644 --- a/drivers/gpu/ion/ion.c +++ b/drivers/gpu/ion/ion.c @@ -191,7 +191,7 @@ static struct ion_handle *ion_handle_create(struct ion_client *client, if (!handle) return ERR_PTR(-ENOMEM); kref_init(&handle->ref); - rb_init_node(&handle->node); + RB_CLEAR_NODE(&handle->node); handle->client = client; ion_buffer_get(buffer); handle->buffer = buffer; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index f342aa88019d..c38ea43c6b2a 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -906,4 +906,11 @@ config TOUCHSCREEN_TPS6507X To compile this driver as a module, choose M here: the module will be called tps6507x_ts. +config TOUCHSCREEN_UNIDISPLAY_TS + tristate "Pixcir capacitive touchscreen driver" + depends on I2C + help + Say Y here if you have a Pixcir capacitive based touchscreen + controller. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index d6bac0bfc3f4..8e903cf8e1a7 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -74,3 +74,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o +obj-$(CONFIG_TOUCHSCREEN_UNIDISPLAY_TS) += unidisplay_ts.o diff --git a/drivers/input/touchscreen/unidisplay_ts.c b/drivers/input/touchscreen/unidisplay_ts.c new file mode 100644 index 000000000000..4f71bd78fdf4 --- /dev/null +++ b/drivers/input/touchscreen/unidisplay_ts.c @@ -0,0 +1,406 @@ +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/of.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/gpio.h> + +#include <mach/regs-gpio.h> + +#include <plat/gpio-cfg.h> + +/* 20 ms */ +#define TOUCH_READ_TIME msecs_to_jiffies(20) + +#define TOUCH_INT_PIN EXYNOS4_GPX3(1) +#define TOUCH_INT_PIN_SHIFT 1 +#define TOUCH_RST_PIN EXYNOS4_GPE3(5) + +#define TOUCHSCREEN_MINX 0 +#define TOUCHSCREEN_MAXX 3968 +#define TOUCHSCREEN_MINY 0 +#define TOUCHSCREEN_MAXY 2304 +#define TOUCH_DEBUG +#ifdef TOUCH_DEBUG +#define DEBUG_PRINT(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_PRINT(fmt, args...) +#endif + +#define INPUT_REPORT(x, y, p, val1, val2) \ + { \ + input_report_abs(tsdata->input, ABS_MT_POSITION_X, x); \ + input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y); \ + input_report_abs(tsdata->input, ABS_MT_TOUCH_MAJOR, p); \ + input_report_abs(tsdata->input, ABS_PRESSURE, val1); \ + input_report_key(tsdata->input, BTN_TOUCH, val2); \ + input_mt_sync(tsdata->input); \ + } + +struct unidisplay_ts_data { + struct i2c_client *client; + struct input_dev *input; + struct task_struct *kidle_task; + wait_queue_head_t idle_wait; + struct delayed_work work; + int irq; + unsigned int irq_pending; +}; + + +static irqreturn_t unidisplay_ts_isr(int irq, void *dev_id); + +static void unidisplay_ts_config(void) +{ + s3c_gpio_cfgpin(TOUCH_INT_PIN, S3C_GPIO_SFN(0x0F)); + s3c_gpio_setpull(TOUCH_INT_PIN, S3C_GPIO_PULL_UP); + + if (gpio_request(TOUCH_INT_PIN, "TOUCH_INT_PIN")) { + pr_err("%s : gpio request failed.\n", __func__); + return; + } + gpio_direction_input(TOUCH_INT_PIN); + gpio_free(TOUCH_INT_PIN); + + s3c_gpio_setpull(TOUCH_RST_PIN, S3C_GPIO_PULL_NONE); + s3c_gpio_cfgpin(TOUCH_RST_PIN, S3C_GPIO_OUTPUT); + + if (gpio_request(TOUCH_RST_PIN, "TOUCH_RST_PIN")) { + pr_err("%s : gpio request failed.\n", __func__); + return; + } + gpio_direction_output(TOUCH_RST_PIN, 1); + gpio_free(TOUCH_RST_PIN); +} + +static void unidisplay_ts_start(void) +{ + if (gpio_request(TOUCH_RST_PIN, "TOUCH_RST_PIN")) { + pr_err("%s : gpio request failed.\n", __func__); + return; + } + gpio_set_value(TOUCH_RST_PIN, 0); + gpio_free(TOUCH_RST_PIN); +} + +static void unidisplay_ts_stop(void) +{ + if (gpio_request(TOUCH_RST_PIN, "TOUCH_RST_PIN")) { + pr_err("%s : gpio request failed.\n", __func__); + return; + } + gpio_set_value(TOUCH_RST_PIN, 1); + gpio_free(TOUCH_RST_PIN); +} + +static void unidisplay_ts_reset(void) +{ + unidisplay_ts_stop(); + udelay(100); + unidisplay_ts_start(); +} + +static int unidisplay_ts_pen_up(void) +{ + return (gpio_get_value(TOUCH_INT_PIN) & 0x1); +} + +static irqreturn_t unidisplay_ts_isr(int irq, void *dev_id) +{ + struct unidisplay_ts_data *tsdata = dev_id; + if (irq == tsdata->irq) { + disable_irq_nosync(tsdata->irq); + tsdata->irq_pending = 1; + wake_up(&tsdata->idle_wait); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static int unidisplay_ts_thread(void *kthread) +{ + struct unidisplay_ts_data *tsdata = kthread; + struct task_struct *tsk = current; + int ret = 0; + struct sched_param param = { .sched_priority = 1 }; + sched_setscheduler(tsk, SCHED_FIFO, ¶m); + set_freezable(); + while (!kthread_should_stop()) { + int x1 = 0, y1 = 0; + u8 buf[9]; + u8 type = 0; + unsigned int pendown = 0; + long timeout = 0; + if (tsdata->irq_pending) { + tsdata->irq_pending = 0; + enable_irq(tsdata->irq); + } + pendown = !unidisplay_ts_pen_up(); + if (pendown) { + u8 addr = 0x10; + memset(buf, 0, sizeof(buf)); + ret = i2c_master_send(tsdata->client, &addr, 1); + if (ret != 1) { + dev_err(&tsdata->client->dev,\ + "Unable to write to i2c touchscreen\n"); + ret = -EIO; + timeout = MAX_SCHEDULE_TIMEOUT; + goto wait_event; + } + ret = i2c_master_recv(tsdata->client, buf, 9); + if (ret != 9) { + dev_err(&tsdata->client->dev,\ + "Unable to read to i2c touchscreen!\n"); + ret = -EIO; + timeout = MAX_SCHEDULE_TIMEOUT; + goto wait_event; + } + /* mark everything ok now */ + ret = 0; + type = buf[0]; + if (type & 0x1) { + x1 = buf[2]; + x1 <<= 8; + x1 |= buf[1]; + y1 = buf[4]; + y1 <<= 8; + y1 |= buf[3]; + INPUT_REPORT(x1, y1, 1, 255, 1); + } + if (type & 0x2) { + x1 = buf[6]; + x1 <<= 8; + x1 |= buf[5]; + y1 = buf[8]; + y1 <<= 8; + y1 |= buf[7]; + INPUT_REPORT(x1, y1, 2, 255, 1); + } + input_sync(tsdata->input); + timeout = msecs_to_jiffies(20); + } else { + INPUT_REPORT(0, 0, 0, 0 ,0); + INPUT_REPORT(0, 0, 0, 0, 0); + input_sync(tsdata->input); + timeout = MAX_SCHEDULE_TIMEOUT; + } +wait_event: + wait_event_freezable_timeout(tsdata->idle_wait, \ + tsdata->irq_pending || kthread_should_stop(), \ + timeout); + } + return ret; +} + +static int unidisplay_ts_open(struct input_dev *dev) +{ + struct unidisplay_ts_data *tsdata = input_get_drvdata(dev); + int ret = 0; + u8 addr = 0x10; + BUG_ON(tsdata->kidle_task); + + ret = i2c_master_send(tsdata->client, &addr, 1); + + if (ret != 1) { + dev_err(&tsdata->client->dev, "Unable to open touchscreen device\n"); + return -ENODEV; + } + + tsdata->kidle_task = kthread_run(unidisplay_ts_thread, tsdata, \ + "unidisplay_ts"); + if (IS_ERR(tsdata->kidle_task)) { + ret = PTR_ERR(tsdata->kidle_task); + tsdata->kidle_task = NULL; + return ret; + } + enable_irq(tsdata->irq); + + return 0; +} + +static void unidisplay_ts_close(struct input_dev *dev) +{ + struct unidisplay_ts_data *tsdata = input_get_drvdata(dev); + + if (tsdata->kidle_task) { + kthread_stop(tsdata->kidle_task); + tsdata->kidle_task = NULL; + } + + disable_irq(tsdata->irq); +} + +static int unidisplay_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct unidisplay_ts_data *tsdata; + int err; + + unidisplay_ts_config(); + unidisplay_ts_reset(); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "i2c func not supported\n"); + err = -EIO; + goto end; + } + + tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL); + if (!tsdata) { + dev_err(&client->dev, "failed to allocate driver data!\n"); + err = -ENOMEM; + goto fail1; + } + + dev_set_drvdata(&client->dev, tsdata); + + tsdata->input = input_allocate_device(); + if (!tsdata->input) { + dev_err(&client->dev, "failed to allocate input device!\n"); + err = -ENOMEM; + goto fail2; + } + + tsdata->input->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) |\ + BIT_MASK(EV_ABS); + set_bit(EV_SYN, tsdata->input->evbit); + set_bit(EV_KEY, tsdata->input->evbit); + set_bit(EV_ABS, tsdata->input->evbit); + + tsdata->input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + set_bit(0, tsdata->input->absbit); + set_bit(1, tsdata->input->absbit); + set_bit(2, tsdata->input->absbit); + + input_set_abs_params(tsdata->input, ABS_X, TOUCHSCREEN_MINX,\ + TOUCHSCREEN_MAXX, 0, 0); + input_set_abs_params(tsdata->input, ABS_Y, TOUCHSCREEN_MINY,\ + TOUCHSCREEN_MAXY, 0, 0); + input_set_abs_params(tsdata->input, ABS_HAT0X, TOUCHSCREEN_MINX,\ + TOUCHSCREEN_MAXX, 0, 0); + input_set_abs_params(tsdata->input, ABS_HAT0Y, TOUCHSCREEN_MINY,\ + TOUCHSCREEN_MAXY, 0, 0); + input_set_abs_params(tsdata->input, ABS_MT_POSITION_X,\ + TOUCHSCREEN_MINX, TOUCHSCREEN_MAXX, 0, 0); + input_set_abs_params(tsdata->input, ABS_MT_POSITION_Y, \ + TOUCHSCREEN_MINY, TOUCHSCREEN_MAXY, 0, 0); + input_set_abs_params(tsdata->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(tsdata->input, ABS_MT_WIDTH_MAJOR, 0, 25, 0, 0); + + tsdata->input->name = client->name; + tsdata->input->id.bustype = BUS_I2C; + tsdata->input->dev.parent = &client->dev; + + tsdata->input->open = unidisplay_ts_open; + tsdata->input->close = unidisplay_ts_close; + + input_set_drvdata(tsdata->input, tsdata); + + tsdata->client = client; + tsdata->irq = client->irq; + + err = input_register_device(tsdata->input); + if (err) + goto fail2; + + device_init_wakeup(&client->dev, 1); + init_waitqueue_head(&tsdata->idle_wait); + + err = request_irq(tsdata->irq, unidisplay_ts_isr,\ + IRQF_TRIGGER_FALLING, client->name, tsdata); + if (err != 0) { + dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); + goto fail3; + } + /* disable irq for now, will be enabled when device is opened */ + disable_irq(tsdata->irq); + pr_info("Unidisplay touch driver registered successfully\n"); + return err; +fail3: + input_unregister_device(tsdata->input); +fail2: + input_free_device(tsdata->input); + kfree(tsdata); +fail1: + dev_set_drvdata(&client->dev, NULL); +end: + return err; +} + + +static int unidisplay_ts_remove(struct i2c_client *client) +{ + struct unidisplay_ts_data *tsdata = dev_get_drvdata(&client->dev); + disable_irq(tsdata->irq); + free_irq(tsdata->irq, tsdata); + input_unregister_device(tsdata->input); + kfree(tsdata); + dev_set_drvdata(&client->dev, NULL); + return 0; +} +#ifdef CONFIG_PM +static int unidisplay_ts_suspend(struct device *dev) +{ + struct unidisplay_ts_data *tsdata = dev_get_drvdata(dev); + disable_irq(tsdata->irq); + return 0; +} + +static int unidisplay_ts_resume(struct device *dev) +{ + struct unidisplay_ts_data *tsdata = dev_get_drvdata(dev); + enable_irq(tsdata->irq); + return 0; +} +static const struct dev_pm_ops unidisplay_ts_pm = { + .suspend = unidisplay_ts_suspend, + .resume = unidisplay_ts_resume, +}; +#endif + +static const struct i2c_device_id unidisplay_ts_i2c_id[] = { + { "unidisplay_ts", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, unidisplay_ts_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id unidisplay_ts_of_match[] = { + { .compatible = "pixcir,unidisplay-ts", }, + { } +}; +MODULE_DEVICE_TABLE(of, unidisplay_ts_of_match); +#endif + +static struct i2c_driver unidisplay_ts_i2c_driver = { + .driver = { + .name = "Unidisplay Touch Driver", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &unidisplay_ts_pm, +#endif + .of_match_table = of_match_ptr(unidisplay_ts_of_match), + }, + .probe = unidisplay_ts_probe, + .remove = unidisplay_ts_remove, + .id_table = unidisplay_ts_i2c_id, +}; + +module_i2c_driver(unidisplay_ts_i2c_driver); + +MODULE_AUTHOR("JHKIM"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); +MODULE_DESCRIPTION("unidisplay Touch-screen Driver"); + diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index eb6a70b0f821..912dc0510193 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -196,6 +196,16 @@ static struct mfc_control controls[] = { .default_value = 1, .is_volatile = 1, }, + { + .id = V4L2_CID_CODEC_FRAME_TAG, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Frame Tag", + .minimum = 0, + .maximum = INT_MAX, + .step = 1, + .default_value = 0, + .is_volatile = 1, + }, }; #define NUM_CTRLS ARRAY_SIZE(controls) @@ -711,6 +721,9 @@ static int s5p_mfc_dec_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE: ctx->slice_interface = ctrl->val; break; + case V4L2_CID_CODEC_FRAME_TAG: + ctx->frame_tag = ctrl->val; + break; default: mfc_err("Invalid control 0x%08x\n", ctrl->id); return -EINVAL; @@ -745,6 +758,9 @@ static int s5p_mfc_dec_g_v_ctrl(struct v4l2_ctrl *ctrl) return -EINVAL; } break; + case V4L2_CID_CODEC_FRAME_TAG: + ctrl->val = s5p_mfc_hw_call(dev->mfc_ops, get_frame_tag, ctx); + break; } return 0; } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 2af6d522f4ac..30697b97d865 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -1542,7 +1542,7 @@ int vidioc_encoder_cmd(struct file *file, void *priv, } static int vidioc_subscribe_event(struct v4l2_fh *fh, - struct v4l2_event_subscription *sub) + const struct v4l2_event_subscription *sub) { switch (sub->type) { case V4L2_EVENT_EOS: diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h index 420abecafec0..312ce0fe4395 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h @@ -77,6 +77,7 @@ struct s5p_mfc_hw_ops { unsigned int (*get_pic_type_bot)(struct s5p_mfc_ctx *ctx); unsigned int (*get_crop_info_h)(struct s5p_mfc_ctx *ctx); unsigned int (*get_crop_info_v)(struct s5p_mfc_ctx *ctx); + unsigned int (*get_frame_tag)(struct s5p_mfc_ctx *ctx); }; void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index bf7d010a4107..8ed95bf67f0a 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -1135,6 +1135,9 @@ static void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush) dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) & ~(S5P_FIMV_DPB_FLUSH_MASK << S5P_FIMV_DPB_FLUSH_SHIFT); mfc_write(dev, dpb, S5P_FIMV_SI_CH0_DPB_CONF_CTRL); + + s5p_mfc_write_info_v5(ctx, ctx->frame_tag, + S5P_FIMV_SHARED_SET_FRAME_TAG); } /* Decode a single frame */ @@ -1733,6 +1736,11 @@ unsigned int s5p_mfc_get_crop_info_v_v5(struct s5p_mfc_ctx *ctx) return s5p_mfc_read_info_v5(ctx, CROP_INFO_V); } +unsigned int s5p_mfc_get_frame_tag(struct s5p_mfc_ctx *ctx) +{ + return s5p_mfc_read_info_v5(ctx, GET_FRAME_TAG_TOP); +} + /* Initialize opr function pointers for MFC v5 */ static struct s5p_mfc_hw_ops s5p_mfc_ops_v5 = { .alloc_dec_temp_buffers = s5p_mfc_alloc_dec_temp_buffers_v5, @@ -1786,6 +1794,7 @@ static struct s5p_mfc_hw_ops s5p_mfc_ops_v5 = { .get_pic_type_bot = s5p_mfc_get_pic_type_bot_v5, .get_crop_info_h = s5p_mfc_get_crop_info_h_v5, .get_crop_info_v = s5p_mfc_get_crop_info_v_v5, + .get_frame_tag = s5p_mfc_get_frame_tag, }; struct s5p_mfc_hw_ops *s5p_mfc_init_hw_ops_v5(void) diff --git a/drivers/media/platform/s5p-tv/Kconfig b/drivers/media/platform/s5p-tv/Kconfig index ea11a513033f..d6dd6b88d4f9 100644 --- a/drivers/media/platform/s5p-tv/Kconfig +++ b/drivers/media/platform/s5p-tv/Kconfig @@ -72,6 +72,7 @@ config VIDEO_SAMSUNG_S5P_MIXER depends on VIDEO_DEV && VIDEO_V4L2 depends on VIDEO_SAMSUNG_S5P_TV select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_FB help Say Y here if you want support for the Mixer in Samsung S5P SoCs. This device produce image data to one of output interfaces. diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 8a9cf43018f6..9cc2e0edd118 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -31,6 +31,8 @@ #include <linux/pm_runtime.h> #include <linux/clk.h> #include <linux/regulator/consumer.h> +#include <linux/of.h> +#include <linux/of_i2c.h> #include <media/s5p_hdmi.h> #include <media/v4l2-common.h> @@ -44,7 +46,7 @@ MODULE_DESCRIPTION("Samsung HDMI"); MODULE_LICENSE("GPL"); /* default preset configured on probe */ -#define HDMI_DEFAULT_PRESET V4L2_DV_480P59_94 +#define HDMI_DEFAULT_PRESET V4L2_DV_1080P60 struct hdmi_pulse { u32 beg; @@ -176,16 +178,175 @@ static irqreturn_t hdmi_irq_handler(int irq, void *dev_data) return IRQ_HANDLED; } +/* Audio related changes */ +static void hdmi_set_acr(u32 freq, u8 *acr) +{ + u32 n, cts; + + switch (freq) { + case 32000: + n = 4096; + cts = 27000; + break; + case 44100: + n = 6272; + cts = 30000; + break; + case 88200: + n = 12544; + cts = 30000; + break; + case 176400: + n = 25088; + cts = 30000; + break; + case 48000: + n = 6144; + cts = 27000; + break; + case 96000: + n = 12288; + cts = 27000; + break; + case 192000: + n = 24576; + cts = 27000; + break; + default: + n = 0; + cts = 0; + break; + } + + acr[1] = cts >> 16; + acr[2] = cts >> 8 & 0xff; + acr[3] = cts & 0xff; + + acr[4] = n >> 16; + acr[5] = n >> 8 & 0xff; + acr[6] = n & 0xff; +} + +static void hdmi_reg_acr(struct hdmi_device *hdev, u8 *acr) +{ + hdmi_writeb(hdev, HDMI_ACR_N0, acr[6]); + hdmi_writeb(hdev, HDMI_ACR_N1, acr[5]); + hdmi_writeb(hdev, HDMI_ACR_N2, acr[4]); + hdmi_writeb(hdev, HDMI_ACR_MCTS0, acr[3]); + hdmi_writeb(hdev, HDMI_ACR_MCTS1, acr[2]); + hdmi_writeb(hdev, HDMI_ACR_MCTS2, acr[1]); + hdmi_writeb(hdev, HDMI_ACR_CTS0, acr[3]); + hdmi_writeb(hdev, HDMI_ACR_CTS1, acr[2]); + hdmi_writeb(hdev, HDMI_ACR_CTS2, acr[1]); + + hdmi_writeb(hdev, HDMI_ACR_CON, 4); +} + +static void hdmi_audio_init(struct hdmi_device *hdev) +{ + u32 sample_rate, bits_per_sample, frame_size_code; + u32 data_num, bit_ch, sample_frq; + u32 val; + u8 acr[7]; + + sample_rate = 44100; + bits_per_sample = 16; + frame_size_code = 0; + + switch (bits_per_sample) { + case 20: + data_num = 2; + bit_ch = 1; + break; + case 24: + data_num = 3; + bit_ch = 1; + break; + default: + data_num = 1; + bit_ch = 0; + break; + } + + hdmi_set_acr(sample_rate, acr); + hdmi_reg_acr(hdev, acr); + + hdmi_writeb(hdev, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE + | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE + | HDMI_I2S_MUX_ENABLE); + + hdmi_writeb(hdev, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN + | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN); + + hdmi_writeb(hdev, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN); + + sample_frq = (sample_rate == 44100) ? 0 : + (sample_rate == 48000) ? 2 : + (sample_rate == 32000) ? 3 : + (sample_rate == 96000) ? 0xa : 0x0; + + hdmi_writeb(hdev, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS); + hdmi_writeb(hdev, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN); + + val = hdmi_read(hdev, HDMI_I2S_DSD_CON) | 0x01; + hdmi_writeb(hdev, HDMI_I2S_DSD_CON, val); + + /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */ + hdmi_writeb(hdev, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5) + | HDMI_I2S_SEL_LRCK(6)); + hdmi_writeb(hdev, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(3) + | HDMI_I2S_SEL_SDATA2(4)); + hdmi_writeb(hdev, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1) + | HDMI_I2S_SEL_SDATA2(2)); + hdmi_writeb(hdev, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0)); + + /* I2S_CON_1 & 2 */ + hdmi_writeb(hdev, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE + | HDMI_I2S_L_CH_LOW_POL); + hdmi_writeb(hdev, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE + | HDMI_I2S_SET_BIT_CH(bit_ch) + | HDMI_I2S_SET_SDATA_BIT(data_num) + | HDMI_I2S_BASIC_FORMAT); + + /* Configure register related to CUV information */ + hdmi_writeb(hdev, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0 + | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH + | HDMI_I2S_COPYRIGHT + | HDMI_I2S_LINEAR_PCM + | HDMI_I2S_CONSUMER_FORMAT); + hdmi_writeb(hdev, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER); + hdmi_writeb(hdev, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0)); + hdmi_writeb(hdev, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2 + | HDMI_I2S_SET_SMP_FREQ(sample_frq)); + hdmi_writeb(hdev, HDMI_I2S_CH_ST_4, + HDMI_I2S_ORG_SMP_FREQ_44_1 + | HDMI_I2S_WORD_LEN_MAX24_24BITS + | HDMI_I2S_WORD_LEN_MAX_24BITS); + + hdmi_writeb(hdev, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD); +} + +static void hdmi_audio_control(struct hdmi_device *hdev, bool onoff) +{ + u32 mod; + + mod = hdmi_read(hdev, HDMI_MODE_SEL); + if (mod & HDMI_MODE_DVI_EN) + return; + + hdmi_writeb(hdev, HDMI_AUI_CON, onoff ? 2 : 0); + hdmi_write_mask(hdev, HDMI_CON_0, onoff ? + HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK); +} + static void hdmi_reg_init(struct hdmi_device *hdev) { /* enable HPD interrupts */ hdmi_write_mask(hdev, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL | HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG); - /* choose DVI mode */ + /* choose HDMI mode */ hdmi_write_mask(hdev, HDMI_MODE_SEL, - HDMI_MODE_DVI_EN, HDMI_MODE_MASK); - hdmi_write_mask(hdev, HDMI_CON_2, ~0, - HDMI_DVI_PERAMBLE_EN | HDMI_DVI_BAND_EN); + HDMI_MODE_HDMI_EN, HDMI_MODE_MASK); /* disable bluescreen */ hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN); /* choose bluescreen (fecal) color */ @@ -282,9 +443,11 @@ static int hdmi_conf_apply(struct hdmi_device *hdmi_dev) mdelay(10); hdmi_reg_init(hdmi_dev); + hdmi_audio_init(hdmi_dev); /* setting core registers */ hdmi_timing_apply(hdmi_dev, conf); + hdmi_audio_control(hdmi_dev, true); hdmi_dev->cur_conf_dirty = 0; @@ -679,6 +842,56 @@ static int hdmi_enum_dv_presets(struct v4l2_subdev *sd, preset); } +#ifdef CONFIG_OF +/* Heavily based[1] on v4l2_i2c_new_subdev_board() + * + * [1] Copy-pasted, that is + */ +struct v4l2_subdev *hdmi_of_get_i2c_subdev(struct v4l2_device *v4l2_dev, + struct device_node *np, const char *propname) +{ + struct v4l2_subdev *sd = NULL; + struct i2c_client *client; + struct device_node *cnp; + + BUG_ON(!v4l2_dev); + + cnp = of_parse_phandle(np, propname, 0); + if (!cnp) { + dev_err(v4l2_dev->dev, "Can't find subdev %s\n", propname); + goto err; + } + + client = of_find_i2c_device_by_node(cnp); + if (!client) { + dev_err(v4l2_dev->dev, "subdev %s doesn't reference correct node\n", + propname); + goto err; + } + + if (client == NULL || client->driver == NULL) + goto err; + + /* Lock the module so we can safely get the v4l2_subdev pointer */ + if (!try_module_get(client->driver->driver.owner)) + goto err; + sd = i2c_get_clientdata(client); + + /* Register with the v4l2_device which increases the module's + use count as well. */ + if (v4l2_device_register_subdev(v4l2_dev, sd)) { + printk(KERN_ERR "%s: failed to register subdev\n", __func__); + sd = NULL; + } + /* Decrease the module use count to match the first try_module_get. */ + module_put(client->driver->driver.owner); +err: + of_node_put(cnp); + + return sd; +} +#endif + static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = { .s_power = hdmi_s_power, }; @@ -702,8 +915,15 @@ static int hdmi_runtime_suspend(struct device *dev) struct hdmi_device *hdev = sd_to_hdmi_dev(sd); dev_dbg(dev, "%s\n", __func__); +#if 0 + /* + * Currently we are getting a system-hang during soft-reboot and + * suspend-resume here. Commenting temporarily to fix that issue. + * Also HDMI is not working after resume. + */ v4l2_subdev_call(hdev->mhl_sd, core, s_power, 0); hdmi_resource_poweroff(&hdev->res); +#endif /* flag that device context is lost */ hdev->cur_conf_dirty = 1; return 0; @@ -830,11 +1050,53 @@ fail: return -ENODEV; } +static struct v4l2_subdev *hdmi_get_subdev( + struct hdmi_device *hdmi_dev, + struct i2c_board_info *bdinfo, + int bus, + const char *propname) +{ + struct v4l2_subdev *sd = NULL; + struct i2c_adapter *adapter; + struct device *dev = hdmi_dev->dev; + +#ifdef CONFIG_OF + if (dev->of_node) + return hdmi_of_get_i2c_subdev(&hdmi_dev->v4l2_dev, + dev->of_node, propname); +#endif + + if (!bdinfo) { + dev_err(dev, "%s info is missing in platform data\n", + propname); + return ERR_PTR(-ENXIO); + } + + adapter = i2c_get_adapter(bus); + if (adapter == NULL) { + dev_err(dev, "%s adapter request failed, name\n", + propname); + return ERR_PTR(-ENXIO); + } + + sd = v4l2_i2c_new_subdev_board(&hdmi_dev->v4l2_dev, + adapter, bdinfo, NULL); + + /* on failure or not adapter is no longer useful */ + i2c_put_adapter(adapter); + + if (sd == NULL) { + dev_err(dev, "missing subdev for %s\n", propname); + return ERR_PTR(-ENODEV); + } + + return sd; +} + static int __devinit hdmi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res; - struct i2c_adapter *adapter; struct v4l2_subdev *sd; struct hdmi_device *hdmi_dev = NULL; struct s5p_hdmi_platform_data *pdata = dev->platform_data; @@ -842,7 +1104,7 @@ static int __devinit hdmi_probe(struct platform_device *pdev) dev_dbg(dev, "probe start\n"); - if (!pdata) { + if (!pdata && !dev->of_node) { dev_err(dev, "platform data is missing\n"); ret = -ENODEV; goto fail; @@ -902,47 +1164,22 @@ static int __devinit hdmi_probe(struct platform_device *pdev) goto fail_init; } - /* testing if hdmiphy info is present */ - if (!pdata->hdmiphy_info) { - dev_err(dev, "hdmiphy info is missing in platform data\n"); - ret = -ENXIO; - goto fail_vdev; - } - - adapter = i2c_get_adapter(pdata->hdmiphy_bus); - if (adapter == NULL) { - dev_err(dev, "hdmiphy adapter request failed\n"); - ret = -ENXIO; + hdmi_dev->phy_sd = hdmi_get_subdev(hdmi_dev, + pdata ? pdata->hdmiphy_info : NULL, + pdata ? pdata->hdmiphy_bus : -1, + "phy"); + if (IS_ERR_OR_NULL(hdmi_dev->phy_sd)) { + ret = PTR_ERR(hdmi_dev->phy_sd); goto fail_vdev; } - hdmi_dev->phy_sd = v4l2_i2c_new_subdev_board(&hdmi_dev->v4l2_dev, - adapter, pdata->hdmiphy_info, NULL); - /* on failure or not adapter is no longer useful */ - i2c_put_adapter(adapter); - if (hdmi_dev->phy_sd == NULL) { - dev_err(dev, "missing subdev for hdmiphy\n"); - ret = -ENODEV; - goto fail_vdev; - } - - /* initialization of MHL interface if present */ - if (pdata->mhl_info) { - adapter = i2c_get_adapter(pdata->mhl_bus); - if (adapter == NULL) { - dev_err(dev, "MHL adapter request failed\n"); - ret = -ENXIO; - goto fail_vdev; - } - - hdmi_dev->mhl_sd = v4l2_i2c_new_subdev_board( - &hdmi_dev->v4l2_dev, adapter, - pdata->mhl_info, NULL); - /* on failure or not adapter is no longer useful */ - i2c_put_adapter(adapter); - if (hdmi_dev->mhl_sd == NULL) { - dev_err(dev, "missing subdev for MHL\n"); - ret = -ENODEV; + if (pdata && pdata->mhl_info) { + hdmi_dev->mhl_sd = hdmi_get_subdev(hdmi_dev, + pdata ? pdata->mhl_info : NULL , + pdata ? pdata->mhl_bus : -1, + "mhl"); + if (IS_ERR_OR_NULL(hdmi_dev->mhl_sd)) { + ret = PTR_ERR(hdmi_dev->mhl_sd); goto fail_vdev; } } @@ -995,6 +1232,13 @@ static int __devexit hdmi_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static struct of_device_id hdmi_dt_match[] = { + { .compatible = "samsung,s5pv210-hdmi" }, + { }, +}; +#endif + static struct platform_driver hdmi_driver __refdata = { .probe = hdmi_probe, .remove = __devexit_p(hdmi_remove), @@ -1003,6 +1247,7 @@ static struct platform_driver hdmi_driver __refdata = { .name = "s5p-hdmi", .owner = THIS_MODULE, .pm = &hdmi_pm_ops, + .of_match_table = of_match_ptr(hdmi_dt_match), } }; diff --git a/drivers/media/platform/s5p-tv/hdmiphy_drv.c b/drivers/media/platform/s5p-tv/hdmiphy_drv.c index f67b38631801..cf30a87d5688 100644 --- a/drivers/media/platform/s5p-tv/hdmiphy_drv.c +++ b/drivers/media/platform/s5p-tv/hdmiphy_drv.c @@ -18,6 +18,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/err.h> +#include <linux/of.h> #include <media/v4l2-subdev.h> @@ -316,10 +317,22 @@ static const struct i2c_device_id hdmiphy_id[] = { }; MODULE_DEVICE_TABLE(i2c, hdmiphy_id); +#ifdef CONFIG_OF +static struct of_device_id hdmiphy_dt_match[] = { + { .compatible = "samsung,hdmiphy-s5pv210" }, + { .compatible = "samsung,hdmiphy-exynos4210" }, + { .compatible = "samsung,hdmiphy-exynos4212" }, + { .compatible = "samsung,hdmiphy-exynos4412" }, + { }, +}; +MODULE_DEVICE_TABLE(of, hdmiphy_dt_match); +#endif + static struct i2c_driver hdmiphy_driver = { .driver = { .name = "s5p-hdmiphy", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(hdmiphy_dt_match), }, .probe = hdmiphy_probe, .remove = __devexit_p(hdmiphy_remove), diff --git a/drivers/media/platform/s5p-tv/mixer.h b/drivers/media/platform/s5p-tv/mixer.h index ddb422e23550..f5b772eb5e40 100644 --- a/drivers/media/platform/s5p-tv/mixer.h +++ b/drivers/media/platform/s5p-tv/mixer.h @@ -152,6 +152,8 @@ struct mxr_layer_ops { struct mxr_layer { /** parent mixer device */ struct mxr_device *mdev; + /** frame buffer emulator */ + void *fb; /** layer index (unique identifier) */ int idx; /** callbacks for layer methods */ diff --git a/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c index ca0f29717448..8f93d2258c8c 100644 --- a/drivers/media/platform/s5p-tv/mixer_drv.c +++ b/drivers/media/platform/s5p-tv/mixer_drv.c @@ -448,6 +448,14 @@ static int __devexit mxr_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id mxr_dt_match[] = { + { .compatible = "samsung,s5pv210-tvmixer" }, + { }, +}; +MODULE_DEVICE_TABLE(of, mxr_dt_match); +#endif + static struct platform_driver mxr_driver __refdata = { .probe = mxr_probe, .remove = __devexit_p(mxr_remove), @@ -455,6 +463,7 @@ static struct platform_driver mxr_driver __refdata = { .name = MXR_DRIVER_NAME, .owner = THIS_MODULE, .pm = &mxr_pm_ops, + .of_match_table = of_match_ptr(mxr_dt_match), } }; diff --git a/drivers/media/platform/s5p-tv/mixer_reg.c b/drivers/media/platform/s5p-tv/mixer_reg.c index 3b1670a045f4..8991c175c91e 100644 --- a/drivers/media/platform/s5p-tv/mixer_reg.c +++ b/drivers/media/platform/s5p-tv/mixer_reg.c @@ -115,7 +115,13 @@ void mxr_reg_reset(struct mxr_device *mdev) /* setting graphical layers */ val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */ +#if 0 + /* + * Without this change, the HDMI display in Android had a green tint. + * Yet to find the exact reason for the same. + */ val |= MXR_GRP_CFG_BLEND_PRE_MUL; /* premul mode */ +#endif val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */ /* the same configuration for both layers */ diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 0c1cd895ff66..d1fa0c139ca3 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -17,6 +17,7 @@ #include <media/v4l2-ioctl.h> #include <linux/videodev2.h> +#include <media/videobuf2-fb.h> #include <linux/mm.h> #include <linux/module.h> #include <linux/version.h> @@ -1033,11 +1034,18 @@ int mxr_base_layer_register(struct mxr_layer *layer) else mxr_info(mdev, "registered layer %s as /dev/video%d\n", layer->vfd.name, layer->vfd.num); + + layer->fb = vb2_fb_register(&layer->vb_queue, &layer->vfd); + if (PTR_ERR(layer->fb)) + layer->fb = NULL; + return ret; } void mxr_base_layer_unregister(struct mxr_layer *layer) { + if (layer->fb) + vb2_fb_unregister(layer->fb); video_unregister_device(&layer->vfd); } diff --git a/drivers/media/platform/s5p-tv/regs-hdmi.h b/drivers/media/platform/s5p-tv/regs-hdmi.h index a889d1f57f28..5d028368120a 100644 --- a/drivers/media/platform/s5p-tv/regs-hdmi.h +++ b/drivers/media/platform/s5p-tv/regs-hdmi.h @@ -19,6 +19,7 @@ #define HDMI_CTRL_BASE(x) ((x) + 0x00000000) #define HDMI_CORE_BASE(x) ((x) + 0x00010000) +#define HDMI_I2S_BASE(x) ((x) + 0x00040000) #define HDMI_TG_BASE(x) ((x) + 0x00050000) /* Control registers */ @@ -73,6 +74,20 @@ #define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4) #define HDMI_HPD_GEN HDMI_CORE_BASE(0x05C8) +/* Audio related registers */ +#define HDMI_ACR_CON HDMI_CORE_BASE(0x0180) +#define HDMI_ACR_MCTS0 HDMI_CORE_BASE(0x0184) +#define HDMI_ACR_MCTS1 HDMI_CORE_BASE(0x0188) +#define HDMI_ACR_MCTS2 HDMI_CORE_BASE(0x018C) +#define HDMI_ACR_CTS0 HDMI_CORE_BASE(0x0190) +#define HDMI_ACR_CTS1 HDMI_CORE_BASE(0x0194) +#define HDMI_ACR_CTS2 HDMI_CORE_BASE(0x0198) +#define HDMI_ACR_N0 HDMI_CORE_BASE(0x01A0) +#define HDMI_ACR_N1 HDMI_CORE_BASE(0x01A4) +#define HDMI_ACR_N2 HDMI_CORE_BASE(0x01A8) + +#define HDMI_AUI_CON HDMI_CORE_BASE(0x0360) + /* Timing generator registers */ #define HDMI_TG_CMD HDMI_TG_BASE(0x0000) #define HDMI_TG_H_FSZ_L HDMI_TG_BASE(0x0018) @@ -125,6 +140,9 @@ /* HDMI_CON_0 */ #define HDMI_BLUE_SCR_EN (1 << 5) +#define HDMI_ASP_EN (1 << 2) +#define HDMI_ASP_DIS (0 << 2) +#define HDMI_ASP_MASK (1 << 2) #define HDMI_EN (1 << 0) /* HDMI_CON_2 */ @@ -139,6 +157,95 @@ #define HDMI_MODE_DVI_EN (1 << 0) #define HDMI_MODE_MASK (3 << 0) +/* HDMI I2S register */ +#define HDMI_I2S_CLK_CON HDMI_I2S_BASE(0x000) +#define HDMI_I2S_CON_1 HDMI_I2S_BASE(0x004) +#define HDMI_I2S_CON_2 HDMI_I2S_BASE(0x008) +#define HDMI_I2S_PIN_SEL_0 HDMI_I2S_BASE(0x00c) +#define HDMI_I2S_PIN_SEL_1 HDMI_I2S_BASE(0x010) +#define HDMI_I2S_PIN_SEL_2 HDMI_I2S_BASE(0x014) +#define HDMI_I2S_PIN_SEL_3 HDMI_I2S_BASE(0x018) +#define HDMI_I2S_DSD_CON HDMI_I2S_BASE(0x01c) +#define HDMI_I2S_MUX_CON HDMI_I2S_BASE(0x020) +#define HDMI_I2S_CH_ST_CON HDMI_I2S_BASE(0x024) +#define HDMI_I2S_CH_ST_0 HDMI_I2S_BASE(0x028) +#define HDMI_I2S_CH_ST_1 HDMI_I2S_BASE(0x02c) +#define HDMI_I2S_CH_ST_2 HDMI_I2S_BASE(0x030) +#define HDMI_I2S_CH_ST_3 HDMI_I2S_BASE(0x034) +#define HDMI_I2S_CH_ST_4 HDMI_I2S_BASE(0x038) +#define HDMI_I2S_MUX_CH HDMI_I2S_BASE(0x054) +#define HDMI_I2S_MUX_CUV HDMI_I2S_BASE(0x058) + +/* I2S bit definition */ + +/* I2S_CLK_CON */ +#define HDMI_I2S_CLK_DIS (0) +#define HDMI_I2S_CLK_EN (1) + +/* I2S_CON_1 */ +#define HDMI_I2S_SCLK_FALLING_EDGE (0 << 1) +#define HDMI_I2S_L_CH_LOW_POL (0) + +/* I2S_CON_2 */ +#define HDMI_I2S_MSB_FIRST_MODE (0 << 6) +#define HDMI_I2S_BASIC_FORMAT (0) +#define HDMI_I2S_SET_BIT_CH(x) (((x) & 0x7) << 4) +#define HDMI_I2S_SET_SDATA_BIT(x) (((x) & 0x7) << 2) + +/* I2S_PIN_SEL_0 */ +#define HDMI_I2S_SEL_SCLK(x) (((x) & 0x7) << 4) +#define HDMI_I2S_SEL_LRCK(x) ((x) & 0x7) + +/* I2S_PIN_SEL_1 */ +#define HDMI_I2S_SEL_SDATA1(x) (((x) & 0x7) << 4) +#define HDMI_I2S_SEL_SDATA2(x) ((x) & 0x7) + +/* I2S_PIN_SEL_2 */ +#define HDMI_I2S_SEL_SDATA3(x) (((x) & 0x7) << 4) +#define HDMI_I2S_SEL_SDATA4(x) ((x) & 0x7) + +/* I2S_PIN_SEL_3 */ +#define HDMI_I2S_SEL_DSD(x) ((x) & 0x7) + +/* I2S_MUX_CON */ +#define HDMI_I2S_IN_DISABLE (1 << 4) +#define HDMI_I2S_AUD_I2S (1 << 2) +#define HDMI_I2S_CUV_I2S_ENABLE (1 << 1) +#define HDMI_I2S_MUX_ENABLE (1) + +/* I2S_CH_ST_CON */ +#define HDMI_I2S_CH_STATUS_RELOAD (1) + +/* I2S_CH_ST_0 / I2S_CH_ST_SH_0 */ +#define HDMI_I2S_CH_STATUS_MODE_0 (0 << 6) +#define HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH (0 << 3) +#define HDMI_I2S_COPYRIGHT (0 << 2) +#define HDMI_I2S_LINEAR_PCM (0 << 1) +#define HDMI_I2S_CONSUMER_FORMAT (0) + +/* I2S_CH_ST_1 / I2S_CH_ST_SH_1 */ +#define HDMI_I2S_CD_PLAYER (0x00) + +/* I2S_CH_ST_2 / I2S_CH_ST_SH_2 */ +#define HDMI_I2S_SET_SOURCE_NUM(x) ((x) & (0xF)) + +/* I2S_CH_ST_3 / I2S_CH_ST_SH_3 */ +#define HDMI_I2S_CLK_ACCUR_LEVEL_2 (0 << 4) +#define HDMI_I2S_SET_SMP_FREQ(x) ((x) & (0xF)) + +/* I2S_CH_ST_4 / I2S_CH_ST_SH_4 */ +#define HDMI_I2S_ORG_SMP_FREQ_44_1 (0xF << 4) +#define HDMI_I2S_WORD_LEN_MAX24_24BITS (0x5 << 1) +#define HDMI_I2S_WORD_LEN_MAX_24BITS (1) + +/* I2S_MUX_CH */ +#define HDMI_I2S_CH2_EN (3 << 4) +#define HDMI_I2S_CH1_EN (3 << 2) +#define HDMI_I2S_CH0_EN (3) + +/* I2S_MUX_CUV */ +#define HDMI_I2S_CUV_RL_EN (0x03) + /* HDMI_TG_CMD */ #define HDMI_TG_FIELD_EN (1 << 1) #define HDMI_TG_EN (1 << 0) diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c index 716d4846f8bd..26ba39f11557 100644 --- a/drivers/media/platform/s5p-tv/sii9234_drv.c +++ b/drivers/media/platform/s5p-tv/sii9234_drv.c @@ -22,6 +22,8 @@ #include <linux/pm_runtime.h> #include <linux/regulator/machine.h> #include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_gpio.h> #include <mach/gpio.h> #include <plat/gpio-cfg.h> @@ -337,7 +339,16 @@ static int __devinit sii9234_probe(struct i2c_client *client, return PTR_ERR(ctx->power); } - ctx->gpio_n_reset = pdata->gpio_n_reset; + if (dev->of_node) { + ctx->gpio_n_reset = of_get_named_gpio(dev->of_node, "gpio-reset", 0); + if (!gpio_is_valid(ctx->gpio_n_reset)) { + ret = -ENODEV; + goto fail_power; + } + } else { + ctx->gpio_n_reset = pdata->gpio_n_reset; + } + ret = gpio_request(ctx->gpio_n_reset, "MHL_RST"); if (ret) { dev_err(dev, "failed to acquire MHL_RST gpio\n"); @@ -392,6 +403,13 @@ static int __devexit sii9234_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id sii9234_dt_match[] = { + { .compatible = "sil,mhl-9234" }, + { }, +}; +MODULE_DEVICE_TABLE(of, sii9234_dt_match); +#endif static const struct i2c_device_id sii9234_id[] = { { "SII9234", 0 }, @@ -404,6 +422,7 @@ static struct i2c_driver sii9234_driver = { .name = "sii9234", .owner = THIS_MODULE, .pm = &sii9234_pm_ops, + .of_match_table = of_match_ptr(sii9234_dt_match), }, .probe = sii9234_probe, .remove = __devexit_p(sii9234_remove), diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 0c54e19d9944..5ceb067a9987 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -64,6 +64,13 @@ config VIDEOBUF2_CORE config VIDEOBUF2_MEMOPS tristate +config VIDEOBUF2_FB + depends on VIDEOBUF2_CORE + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + tristate + config VIDEOBUF2_DMA_CONTIG tristate select VIDEOBUF2_CORE diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index c2d61d4f03d1..a4cf3c170acc 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-core.o obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o +obj-$(CONFIG_VIDEOBUF2_FB) += videobuf2-fb.o obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG) += videobuf2-dma-contig.o obj-$(CONFIG_VIDEOBUF2_DMA_SG) += videobuf2-dma-sg.o diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index f6ee201d9347..a92de5f6d8d7 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -697,6 +697,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_DEC_PTS: return "Video Decoder PTS"; case V4L2_CID_MPEG_VIDEO_DEC_FRAME: return "Video Decoder Frame Count"; case V4L2_CID_MPEG_VIDEO_VBV_DELAY: return "Initial Delay for VBV Control"; + case V4L2_CID_CODEC_FRAME_TAG: return "Video Decoder Frame Tag"; /* CAMERA controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -964,6 +965,12 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *flags |= V4L2_CTRL_FLAG_READ_ONLY; *min = *max = *step = *def = 0; break; + case V4L2_CID_CODEC_FRAME_TAG: + *type = V4L2_CTRL_TYPE_INTEGER; + *step = 1; + *min = 0; + *max = INT_MAX; + break; default: *type = V4L2_CTRL_TYPE_INTEGER; break; diff --git a/drivers/media/v4l2-core/videobuf2-fb.c b/drivers/media/v4l2-core/videobuf2-fb.c new file mode 100644 index 000000000000..c48e45076790 --- /dev/null +++ b/drivers/media/v4l2-core/videobuf2-fb.c @@ -0,0 +1,565 @@ +/* + * videobuf2-fb.c - FrameBuffer API emulator on top of Videobuf2 framework + * + * Copyright (C) 2011 Samsung Electronics + * + * Author: Marek Szyprowski <m.szyprowski@samsung.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 + * the Free Software Foundation. + */ + +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/fb.h> + +#include <linux/videodev2.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-fb.h> + +static int debug = 1; +module_param(debug, int, 0644); + +#define dprintk(level, fmt, arg...) \ + do { \ + if (debug >= level) \ + printk(KERN_DEBUG "vb2: " fmt, ## arg); \ + } while (0) + +struct vb2_fb_data { + struct video_device *vfd; + struct vb2_queue *q; + struct device *dev; + struct v4l2_requestbuffers req; + struct v4l2_buffer b; + struct v4l2_plane p; + void *vaddr; + unsigned int size; + int refcount; + int blank; + int streaming; + + struct file fake_file; + struct dentry fake_dentry; + struct inode fake_inode; +}; + +static int vb2_fb_stop(struct fb_info *info); + +struct fmt_desc { + __u32 fourcc; + __u32 bits_per_pixel; + struct fb_bitfield red; + struct fb_bitfield green; + struct fb_bitfield blue; + struct fb_bitfield transp; +}; + +static struct fmt_desc fmt_conv_table[] = { + { + .fourcc = V4L2_PIX_FMT_RGB565, + .bits_per_pixel = 16, + .red = { .offset = 11, .length = 5, }, + .green = { .offset = 5, .length = 6, }, + .blue = { .offset = 0, .length = 5, }, + }, { + .fourcc = V4L2_PIX_FMT_RGB555, + .bits_per_pixel = 16, + .red = { .offset = 11, .length = 5, }, + .green = { .offset = 5, .length = 5, }, + .blue = { .offset = 0, .length = 5, }, + }, { + .fourcc = V4L2_PIX_FMT_RGB444, + .bits_per_pixel = 16, + .red = { .offset = 8, .length = 4, }, + .green = { .offset = 4, .length = 4, }, + .blue = { .offset = 0, .length = 4, }, + .transp = { .offset = 12, .length = 4, }, + }, { + .fourcc = V4L2_PIX_FMT_BGR32, + .bits_per_pixel = 32, + .red = { .offset = 16, .length = 4, }, + .green = { .offset = 8, .length = 8, }, + .blue = { .offset = 0, .length = 8, }, + .transp = { .offset = 24, .length = 8, }, + }, + /* TODO: add more format descriptors */ +}; + +/** + * vb2_drv_lock() - a shortcut to call driver specific lock() + * @q: videobuf2 queue + */ +static inline void vb2_drv_lock(struct vb2_queue *q) +{ + q->ops->wait_finish(q); +} + +/** + * vb2_drv_unlock() - a shortcut to call driver specific unlock() + * @q: videobuf2 queue + */ +static inline void vb2_drv_unlock(struct vb2_queue *q) +{ + q->ops->wait_prepare(q); +} + +/** + * vb2_fb_activate() - activate framebuffer emulator + * @info: framebuffer vb2 emulator data + * This function activates framebuffer emulator. The pixel format + * is acquired from video node, memory is allocated and framebuffer + * structures are filled with valid data. + */ +static int vb2_fb_activate(struct fb_info *info) +{ + struct vb2_fb_data *data = info->par; + struct vb2_queue *q = data->q; + struct fb_var_screeninfo *var; + struct v4l2_format fmt; + struct fmt_desc *conv = NULL; + int width, height, fourcc, bpl, size; + int i, ret = 0; + int (*g_fmt)(struct file *file, void *fh, struct v4l2_format *f); + + /* + * Check if streaming api has not been already activated. + */ + if (q->streaming || q->num_buffers > 0) + return -EBUSY; + + dprintk(3, "setting up framebuffer\n"); + + /* + * Open video node. + */ + ret = data->vfd->fops->open(&data->fake_file); + if (ret) + return ret; + + /* + * Get format from the video node. + */ + memset(&fmt, 0, sizeof(fmt)); + fmt.type = q->type; + if (data->vfd->ioctl_ops->vidioc_g_fmt_vid_out) { + g_fmt = data->vfd->ioctl_ops->vidioc_g_fmt_vid_out; + ret = g_fmt(&data->fake_file, data->fake_file.private_data, &fmt); + if (ret) + goto err; + width = fmt.fmt.pix.width; + height = fmt.fmt.pix.height; + fourcc = fmt.fmt.pix.pixelformat; + bpl = fmt.fmt.pix.bytesperline; + size = fmt.fmt.pix.sizeimage; + } else if (data->vfd->ioctl_ops->vidioc_g_fmt_vid_out_mplane) { + g_fmt = data->vfd->ioctl_ops->vidioc_g_fmt_vid_out_mplane; + ret = g_fmt(&data->fake_file, data->fake_file.private_data, &fmt); + if (ret) + goto err; + width = fmt.fmt.pix_mp.width; + height = fmt.fmt.pix_mp.height; + fourcc = fmt.fmt.pix_mp.pixelformat; + bpl = fmt.fmt.pix_mp.plane_fmt[0].bytesperline; + size = fmt.fmt.pix_mp.plane_fmt[0].sizeimage; + } else { + ret = -EINVAL; + goto err; + } + + dprintk(3, "fb emu: width %d height %d fourcc %08x size %d bpl %d\n", + width, height, fourcc, size, bpl); + + /* + * Find format mapping with fourcc returned by g_fmt(). + */ + for (i = 0; i < ARRAY_SIZE(fmt_conv_table); i++) { + if (fmt_conv_table[i].fourcc == fourcc) { + conv = &fmt_conv_table[i]; + break; + } + } + + if (conv == NULL) { + ret = -EBUSY; + goto err; + } + + /* + * Request buffers and use MMAP type to force driver + * to allocate buffers by itself. + */ + data->req.count = 1; + data->req.memory = V4L2_MEMORY_MMAP; + data->req.type = q->type; + ret = vb2_reqbufs(q, &data->req); + if (ret) + goto err; + + /* + * Check if plane_count is correct, + * multiplane buffers are not supported. + */ + if (q->bufs[0]->num_planes != 1) { + data->req.count = 0; + ret = -EBUSY; + goto err; + } + + /* + * Get kernel address of the buffer. + */ + data->vaddr = vb2_plane_vaddr(q->bufs[0], 0); + if (data->vaddr == NULL) { + ret = -EINVAL; + goto err; + } + data->size = size = vb2_plane_size(q->bufs[0], 0); + + /* + * Clear the buffer + */ + memset(data->vaddr, 0, size); + + /* + * Setup framebuffer parameters + */ + info->screen_base = data->vaddr; + info->screen_size = size; + info->fix.line_length = bpl; + info->fix.smem_len = info->fix.mmio_len = size; + + var = &info->var; + var->xres = var->xres_virtual = var->width = width; + var->yres = var->yres_virtual = var->height = height; + var->bits_per_pixel = conv->bits_per_pixel; + var->red = conv->red; + var->green = conv->green; + var->blue = conv->blue; + var->transp = conv->transp; + + return 0; + +err: + data->vfd->fops->release(&data->fake_file); + return ret; +} + +/** + * vb2_fb_deactivate() - deactivate framebuffer emulator + * @info: framebuffer vb2 emulator data + * Stop displaying video data and close framebuffer emulator. + */ +static int vb2_fb_deactivate(struct fb_info *info) +{ + struct vb2_fb_data *data = info->par; + + info->screen_base = NULL; + info->screen_size = 0; + data->blank = 1; + data->streaming = 0; + + vb2_fb_stop(info); + return data->vfd->fops->release(&data->fake_file); +} + +/** + * vb2_fb_start() - start displaying the video buffer + * @info: framebuffer vb2 emulator data + * This function queues video buffer to the driver and starts streaming. + */ +static int vb2_fb_start(struct fb_info *info) +{ + struct vb2_fb_data *data = info->par; + struct v4l2_buffer *b = &data->b; + struct v4l2_plane *p = &data->p; + struct vb2_queue *q = data->q; + int ret; + + if (data->streaming) + return 0; + + /* + * Prepare the buffer and queue it. + */ + memset(b, 0, sizeof(*b)); + b->type = q->type; + b->memory = q->memory; + b->index = 0; + + if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + b->bytesused = data->size; + b->length = data->size; + } else if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + memset(p, 0, sizeof(*p)); + b->m.planes = p; + b->length = 1; + p->bytesused = data->size; + p->length = data->size; + } + ret = vb2_qbuf(q, b); + if (ret) + return ret; + + /* + * Start streaming. + */ + ret = vb2_streamon(q, q->type); + if (ret == 0) { + data->streaming = 1; + dprintk(3, "fb emu: enabled streaming\n"); + } + return ret; +} + +/** + * vb2_fb_start() - stop displaying video buffer + * @info: framebuffer vb2 emulator data + * This function stops streaming on the video driver. + */ +static int vb2_fb_stop(struct fb_info *info) +{ + struct vb2_fb_data *data = info->par; + struct vb2_queue *q = data->q; + int ret = 0; + + if (data->streaming) { + ret = vb2_streamoff(q, q->type); + data->streaming = 0; + dprintk(3, "fb emu: disabled streaming\n"); + } + + return ret; +} + +/** + * vb2_fb_open() - open method for emulated framebuffer + * @info: framebuffer vb2 emulator data + * @user: client type (0 means kernel, 1 mean userspace) + */ +static int vb2_fb_open(struct fb_info *info, int user) +{ + struct vb2_fb_data *data = info->par; + int ret = 0; + dprintk(3, "fb emu: open()\n"); + + /* + * Reject open() call from fb console. + */ + if (user == 0) + return -ENODEV; + + vb2_drv_lock(data->q); + + /* + * Activate emulation on the first open. + */ + if (data->refcount == 0) + ret = vb2_fb_activate(info); + + if (ret == 0) + data->refcount++; + + vb2_drv_unlock(data->q); + + return ret; +} + +/** + * vb2_fb_release() - release method for emulated framebuffer + * @info: framebuffer vb2 emulator data + * @user: client type (0 means kernel, 1 mean userspace) + */ +static int vb2_fb_release(struct fb_info *info, int user) +{ + struct vb2_fb_data *data = info->par; + int ret = 0; + + dprintk(3, "fb emu: release()\n"); + + vb2_drv_lock(data->q); + + if (--data->refcount == 0) + ret = vb2_fb_deactivate(info); + + vb2_drv_unlock(data->q); + + return ret; +} + +/** + * vb2_fb_mmap() - mmap method for emulated framebuffer + * @info: framebuffer vb2 emulator data + * @vma: memory area to map + */ +static int vb2_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct vb2_fb_data *data = info->par; + int ret = 0; + + dprintk(3, "fb emu: mmap offset %ld\n", vma->vm_pgoff); + + /* + * Add flags required by v4l2/vb2 + */ + vma->vm_flags |= VM_SHARED; + + /* + * Only the most common case (mapping the whole framebuffer) is + * supported for now. + */ + if (vma->vm_pgoff != 0 || (vma->vm_end - vma->vm_start) < data->size) + return -EINVAL; + + vb2_drv_lock(data->q); + ret = vb2_mmap(data->q, vma); + vb2_drv_unlock(data->q); + + return ret; +} + +/** + * vb2_fb_blank() - blank method for emulated framebuffer + * @blank_mode: requested blank method + * @info: framebuffer vb2 emulator data + */ +static int vb2_fb_blank(int blank_mode, struct fb_info *info) +{ + struct vb2_fb_data *data = info->par; + int ret = -EBUSY; + + dprintk(3, "fb emu: blank mode %d, blank %d, streaming %d\n", + blank_mode, data->blank, data->streaming); + + /* + * If no blank mode change then return immediately + */ + if ((data->blank && blank_mode != FB_BLANK_UNBLANK) || + (!data->blank && blank_mode == FB_BLANK_UNBLANK)) + return 0; + + /* + * Currently blank works only if device has been opened first. + */ + if (!data->refcount) + return -EBUSY; + + vb2_drv_lock(data->q); + + /* + * Start emulation if user requested mode == FB_BLANK_UNBLANK. + */ + if (blank_mode == FB_BLANK_UNBLANK && data->blank) { + ret = vb2_fb_start(info); + if (ret == 0) + data->blank = 0; + } + + /* + * Stop emulation if user requested mode != FB_BLANK_UNBLANK. + */ + if (blank_mode != FB_BLANK_UNBLANK && !data->blank) { + ret = vb2_fb_stop(info); + if (ret == 0) + data->blank = 1; + } + + vb2_drv_unlock(data->q); + + return ret; +} + +static struct fb_ops vb2_fb_ops = { + .owner = THIS_MODULE, + .fb_open = vb2_fb_open, + .fb_release = vb2_fb_release, + .fb_mmap = vb2_fb_mmap, + .fb_blank = vb2_fb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +/** + * vb2_fb_reqister() - register framebuffer emulation + * @q: videobuf2 queue + * @vfd: video node + * This function registers framebuffer emulation for specified + * videobuf2 queue and video node. It returns a pointer to the registered + * framebuffer device. + */ +void *vb2_fb_register(struct vb2_queue *q, struct video_device *vfd) +{ + struct vb2_fb_data *data; + struct fb_info *info; + int ret; + + BUG_ON(q->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + q->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + BUG_ON(!q->mem_ops->vaddr); + BUG_ON(!q->ops->wait_prepare || !q->ops->wait_finish); + BUG_ON(!vfd->ioctl_ops || !vfd->fops); + + if (!try_module_get(vfd->fops->owner)) + return ERR_PTR(-ENODEV); + + info = framebuffer_alloc(sizeof(struct vb2_fb_data), &vfd->dev); + if (!info) + return ERR_PTR(-ENOMEM); + + data = info->par; + + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.accel = FB_ACCEL_NONE; + info->fix.visual = FB_VISUAL_TRUECOLOR, + info->var.activate = FB_ACTIVATE_NOW; + info->var.vmode = FB_VMODE_NONINTERLACED; + info->fbops = &vb2_fb_ops; + info->flags = FBINFO_FLAG_DEFAULT; + info->screen_base = NULL; + + ret = register_framebuffer(info); + if (ret) + return ERR_PTR(ret); + + printk(KERN_INFO "fb%d: registered frame buffer emulation for /dev/%s\n", + info->node, dev_name(&vfd->dev)); + + data->blank = 1; + data->vfd = vfd; + data->q = q; + data->fake_file.f_path.dentry = &data->fake_dentry; + data->fake_dentry.d_inode = &data->fake_inode; + data->fake_inode.i_rdev = vfd->cdev->dev; + + return info; +} +EXPORT_SYMBOL_GPL(vb2_fb_register); + +/** + * vb2_fb_unreqister() - unregister framebuffer emulation + * @fb_emu: emulated framebuffer device + */ +int vb2_fb_unregister(void *fb_emu) +{ + struct fb_info *info = fb_emu; + struct vb2_fb_data *data = info->par; + struct module *owner = data->vfd->fops->owner; + + unregister_framebuffer(info); + module_put(owner); + return 0; +} +EXPORT_SYMBOL_GPL(vb2_fb_unregister); + +MODULE_DESCRIPTION("FrameBuffer emulator for Videobuf2 and Video for Linux 2"); +MODULE_AUTHOR("Marek Szyprowski"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index f123517065ec..abd5c80c7cf5 100644 --- a/drivers/mfd/max8997.c +++ b/drivers/mfd/max8997.c @@ -21,8 +21,10 @@ * This driver is based on max8998.c */ +#include <linux/err.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/of_irq.h> #include <linux/interrupt.h> #include <linux/pm_runtime.h> #include <linux/module.h> @@ -47,6 +49,13 @@ static struct mfd_cell max8997_devs[] = { { .name = "max8997-led", .id = 2 }, }; +#ifdef CONFIG_OF +static struct of_device_id __devinitdata max8997_pmic_dt_match[] = { + { .compatible = "maxim,max8997-pmic", .data = TYPE_MAX8997 }, + {}, +}; +#endif + int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest) { struct max8997_dev *max8997 = i2c_get_clientdata(i2c); @@ -123,6 +132,58 @@ int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask) } EXPORT_SYMBOL_GPL(max8997_update_reg); +#ifdef CONFIG_OF +/* + * Only the common platform data elements for max8997 are parsed here from the + * device tree. Other sub-modules of max8997 such as pmic, rtc and others have + * to parse their own platform data elements from device tree. + * + * The max8997 platform data structure is instantiated here and the drivers for + * the sub-modules need not instantiate another instance while parsing their + * platform data. + */ +static struct max8997_platform_data *max8997_i2c_parse_dt_pdata( + struct device *dev) +{ + struct max8997_platform_data *pd; + + pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); + if (!pd) { + dev_err(dev, "could not allocate memory for pdata\n"); + return ERR_PTR(-ENOMEM); + } + + pd->ono = irq_of_parse_and_map(dev->of_node, 1); + + /* + * ToDo: the 'wakeup' member in the platform data is more of a linux + * specfic information. Hence, there is no binding for that yet and + * not parsed here. + */ + + return pd; +} +#else +static struct max8997_platform_data *max8997_i2c_parse_dt_pdata( + struct device *dev) +{ + return 0; +} +#endif + +static inline int max8997_i2c_get_driver_data(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ +#ifdef CONFIG_OF + if (i2c->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(max8997_pmic_dt_match, i2c->dev.of_node); + return (int)match->data; + } +#endif + return (int)id->driver_data; +} + static int max8997_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -137,12 +198,21 @@ static int max8997_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, max8997); max8997->dev = &i2c->dev; max8997->i2c = i2c; - max8997->type = id->driver_data; + max8997->type = max8997_i2c_get_driver_data(i2c, id); max8997->irq = i2c->irq; + if (max8997->dev->of_node) { + pdata = max8997_i2c_parse_dt_pdata(max8997->dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err; + } + } + if (!pdata) goto err; + max8997->pdata = pdata; max8997->ono = pdata->ono; mutex_init(&max8997->iolock); @@ -434,6 +504,7 @@ static struct i2c_driver max8997_i2c_driver = { .name = "max8997", .owner = THIS_MODULE, .pm = &max8997_pm, + .of_match_table = of_match_ptr(max8997_pmic_dt_match), }, .probe = max8997_i2c_probe, .remove = max8997_i2c_remove, diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index 3d8ceb4084de..6c1e63a72117 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -46,7 +46,7 @@ static int process_sdio_pending_irqs(struct mmc_host *host) return 1; } - ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending); + ret = mmc_io_rw_direct_irq(card, &pending); if (ret) { pr_debug("%s: error %d reading SDIO_CCCR_INTx\n", mmc_card_id(card), ret); diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c index d29e20630eed..2fcb53e678cc 100644 --- a/drivers/mmc/core/sdio_ops.c +++ b/drivers/mmc/core/sdio_ops.c @@ -111,6 +111,46 @@ static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn, return 0; } +static struct mmc_command sdio_intx_cmd = { + .opcode = SD_IO_RW_DIRECT, + .arg = SDIO_CCCR_INTx << 9, + .flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC, +}; + +int mmc_io_rw_direct_irq(struct mmc_card *card, u8 *out) +{ + int err; + struct mmc_host *host ; + + BUG_ON(!card); + host = card->host; + BUG_ON(!host); + + err = mmc_wait_for_cmd(host, &sdio_intx_cmd, 0); + if (err) + return err; + + if (mmc_host_is_spi(host)) { + /* host driver already reported errors */ + } else { + if (sdio_intx_cmd.resp[0] & R5_ERROR) + return -EIO; + if (sdio_intx_cmd.resp[0] & R5_FUNCTION_NUMBER) + return -EINVAL; + if (sdio_intx_cmd.resp[0] & R5_OUT_OF_RANGE) + return -ERANGE; + } + + if (out) { + if (mmc_host_is_spi(host)) + *out = (sdio_intx_cmd.resp[0] >> 8) & 0xFF; + else + *out = sdio_intx_cmd.resp[0] & 0xFF; + } + + return 0; +} + int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, unsigned addr, u8 in, u8 *out) { diff --git a/drivers/mmc/core/sdio_ops.h b/drivers/mmc/core/sdio_ops.h index 12a4d3ab174c..5fe8ad667452 100644 --- a/drivers/mmc/core/sdio_ops.h +++ b/drivers/mmc/core/sdio_ops.h @@ -15,6 +15,7 @@ int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, unsigned addr, u8 in, u8* out); +int mmc_io_rw_direct_irq(struct mmc_card *card, u8 *out); int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz); int sdio_reset(struct mmc_host *host); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index df3ec76c9cde..47c4f36dbd96 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2812,7 +2812,11 @@ int sdhci_add_host(struct sdhci_host *host) mmc->max_discard_to = (1 << 27) / host->timeout_clk; +#if defined(CONFIG_MACH_ORIGEN) && defined(CONFIG_ATH6KL_POLL) + mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23; +#else mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; +#endif if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) host->flags |= SDHCI_AUTO_CMD12; diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index d716b748e574..fc2851e2b7c3 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_ATH5K) += ath5k/ obj-$(CONFIG_ATH9K_HW) += ath9k/ obj-$(CONFIG_CARL9170) += carl9170/ obj-$(CONFIG_ATH6KL) += ath6kl/ - +obj-$(CONFIG_ATH6KL_PLATFORM_DATA) += platform_data.o obj-$(CONFIG_ATH_COMMON) += ath.o ath-objs := main.o \ diff --git a/drivers/net/wireless/ath/ath6kl/Kconfig b/drivers/net/wireless/ath/ath6kl/Kconfig index d755a5e7ed20..734a6ef3d08c 100644 --- a/drivers/net/wireless/ath/ath6kl/Kconfig +++ b/drivers/net/wireless/ath/ath6kl/Kconfig @@ -30,3 +30,15 @@ config ATH6KL_DEBUG depends on ATH6KL ---help--- Enables debug support + +config ATH6KL_PLATFORM_DATA + bool "Atheros ath6kl Platform data support" + depends on ATH6KL + ---help--- + Enables ath6kl platform data + +config ATH6KL_POLL + bool "Atheros ath6kl Polling support" + depends on ATH6KL + ---help--- + Enables workaround for ar6003 hw2.0 diff --git a/drivers/net/wireless/ath/ath6kl/Makefile b/drivers/net/wireless/ath/ath6kl/Makefile index 8cae8886f17d..ef3ba6a58da5 100644 --- a/drivers/net/wireless/ath/ath6kl/Makefile +++ b/drivers/net/wireless/ath/ath6kl/Makefile @@ -22,22 +22,22 @@ # Author(s): ="Atheros" #------------------------------------------------------------------------------ -obj-$(CONFIG_ATH6KL) += ath6kl_core.o -ath6kl_core-y += debug.o -ath6kl_core-y += hif.o -ath6kl_core-y += htc_mbox.o -ath6kl_core-y += htc_pipe.o -ath6kl_core-y += bmi.o -ath6kl_core-y += cfg80211.o -ath6kl_core-y += init.o -ath6kl_core-y += main.o -ath6kl_core-y += txrx.o -ath6kl_core-y += wmi.o -ath6kl_core-y += core.o -ath6kl_core-$(CONFIG_NL80211_TESTMODE) += testmode.o +obj-$(CONFIG_ATH6KL) += ath6kl.o +ath6kl-y += debug.o +ath6kl-y += hif.o +ath6kl-y += htc_mbox.o +ath6kl-y += htc_pipe.o +ath6kl-y += bmi.o +ath6kl-y += cfg80211.o +ath6kl-y += init.o +ath6kl-y += main.o +ath6kl-y += txrx.o +ath6kl-y += wmi.o +ath6kl-y += core.o +ath6kl-$(CONFIG_NL80211_TESTMODE) += testmode.o obj-$(CONFIG_ATH6KL_SDIO) += ath6kl_sdio.o -ath6kl_sdio-y += sdio.o +ath6kl-y += sdio.o obj-$(CONFIG_ATH6KL_USB) += ath6kl_usb.o ath6kl_usb-y += usb.o diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 05b95405f7b5..0a80af3ba109 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -23,6 +23,7 @@ #include <linux/mmc/sdio_ids.h> #include <linux/mmc/sdio.h> #include <linux/mmc/sd.h> +#include <linux/ath6kl.h> #include "hif.h" #include "hif-ops.h" #include "target.h" @@ -1423,7 +1424,11 @@ static struct sdio_driver ath6kl_sdio_driver = { static int __init ath6kl_sdio_init(void) { int ret; + const struct ath6kl_platform_data *wlan_data; + wlan_data = ath6kl_get_platform_data(); + if (!IS_ERR(wlan_data)) + wlan_data->setup_power(true); ret = sdio_register_driver(&ath6kl_sdio_driver); if (ret) ath6kl_err("sdio driver registration failed: %d\n", ret); @@ -1433,7 +1438,13 @@ static int __init ath6kl_sdio_init(void) static void __exit ath6kl_sdio_exit(void) { + const struct ath6kl_platform_data *wlan_data; + sdio_unregister_driver(&ath6kl_sdio_driver); + + wlan_data = ath6kl_get_platform_data(); + if (!IS_ERR(wlan_data)) + wlan_data->setup_power(false); } module_init(ath6kl_sdio_init); diff --git a/drivers/net/wireless/ath/platform_data.c b/drivers/net/wireless/ath/platform_data.c new file mode 100644 index 000000000000..dddffe409b09 --- /dev/null +++ b/drivers/net/wireless/ath/platform_data.c @@ -0,0 +1,26 @@ +#include <linux/module.h> +#include <linux/err.h> +#include <linux/ath6kl.h> + +static const struct ath6kl_platform_data *platform_data; + +int __init ath6kl_set_platform_data(const struct ath6kl_platform_data *data) +{ + if (platform_data) + return -EBUSY; + if (!data) + return -EINVAL; + + platform_data = kmemdup(data, sizeof(*data), GFP_KERNEL); + if (!platform_data) + return -ENOMEM; + return 0; +} + +const struct ath6kl_platform_data *ath6kl_get_platform_data(void) +{ + if (!platform_data) + return ERR_PTR(-ENODEV); + return platform_data; +} +EXPORT_SYMBOL(ath6kl_get_platform_data); diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index 023a3bee76e7..9c6f49e4ff7d 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -26,6 +26,8 @@ #include <plat/regs-timer.h> +#define NUM_PWM 4 + struct s3c_chip { struct platform_device *pdev; @@ -37,7 +39,6 @@ struct s3c_chip { unsigned int duty_ns; unsigned char tcon_base; - unsigned char pwm_id; struct pwm_chip chip; }; @@ -133,8 +134,8 @@ static int s3c_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, /* The TCMP and TCNT can be read without a lock, they're not * shared between the timers. */ - tcmp = __raw_readl(S3C2410_TCMPB(s3c->pwm_id)); - tcnt = __raw_readl(S3C2410_TCNTB(s3c->pwm_id)); + tcmp = __raw_readl(S3C2410_TCMPB(pwm->hwpwm)); + tcnt = __raw_readl(S3C2410_TCNTB(pwm->hwpwm)); period = NS_IN_HZ / period_ns; @@ -177,8 +178,8 @@ static int s3c_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, local_irq_save(flags); - __raw_writel(tcmp, S3C2410_TCMPB(s3c->pwm_id)); - __raw_writel(tcnt, S3C2410_TCNTB(s3c->pwm_id)); + __raw_writel(tcmp, S3C2410_TCMPB(pwm->hwpwm)); + __raw_writel(tcnt, S3C2410_TCNTB(pwm->hwpwm)); tcon = __raw_readl(S3C2410_TCON); tcon |= pwm_tcon_manulupdate(s3c); @@ -225,7 +226,7 @@ static int s3c_pwm_probe(struct platform_device *pdev) s3c->chip.dev = &pdev->dev; s3c->chip.ops = &s3c_pwm_ops; s3c->chip.base = -1; - s3c->chip.npwm = 1; + s3c->chip.npwm = NUM_PWM; s3c->clk = devm_clk_get(dev, "pwm-tin"); if (IS_ERR(s3c->clk)) { diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c index e39a0c7260dc..b7639aed13b1 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997.c @@ -24,6 +24,7 @@ #include <linux/bug.h> #include <linux/err.h> #include <linux/gpio.h> +#include <linux/of_gpio.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -31,6 +32,7 @@ #include <linux/regulator/machine.h> #include <linux/mfd/max8997.h> #include <linux/mfd/max8997-private.h> +#include <linux/regulator/of_regulator.h> struct max8997_data { struct device *dev; @@ -933,10 +935,145 @@ static struct regulator_desc regulators[] = { max8997_charger_fixedstate_ops), }; +#ifdef CONFIG_OF +static int max8997_pmic_dt_parse_dvs_gpio(struct max8997_dev *iodev, + struct max8997_platform_data *pdata, + struct device_node *pmic_np) +{ + int i, gpio; + + for (i = 0; i < 3; i++) { + gpio = of_get_named_gpio(pmic_np, + "max8997,pmic-buck125-dvs-gpios", i); + if (!gpio_is_valid(gpio)) { + dev_err(iodev->dev, "invalid gpio[%d]: %d\n", i, gpio); + return -EINVAL; + } + pdata->buck125_gpios[i] = gpio; + } + return 0; +} + +static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev, + struct max8997_platform_data *pdata) +{ + struct device_node *pmic_np, *regulators_np, *reg_np; + struct max8997_regulator_data *rdata; + unsigned int i, dvs_voltage_nr = 1, ret; + + pmic_np = iodev->dev->of_node; + if (!pmic_np) { + dev_err(iodev->dev, "could not find pmic sub-node\n"); + return -ENODEV; + } + + regulators_np = of_find_node_by_name(pmic_np, "regulators"); + if (!regulators_np) { + dev_err(iodev->dev, "could not find regulators sub-node\n"); + return -EINVAL; + } + + /* count the number of regulators to be supported in pmic */ + pdata->num_regulators = 0; + for_each_child_of_node(regulators_np, reg_np) + pdata->num_regulators++; + + rdata = devm_kzalloc(iodev->dev, sizeof(*rdata) * + pdata->num_regulators, GFP_KERNEL); + if (!rdata) { + dev_err(iodev->dev, "could not allocate memory for " + "regulator data\n"); + return -ENOMEM; + } + + pdata->regulators = rdata; + for_each_child_of_node(regulators_np, reg_np) { + for (i = 0; i < ARRAY_SIZE(regulators); i++) + if (!of_node_cmp(reg_np->name, regulators[i].name)) + break; + + if (i == ARRAY_SIZE(regulators)) { + dev_warn(iodev->dev, "don't know how to configure " + "regulator %s\n", reg_np->name); + continue; + } + + rdata->id = i; + rdata->initdata = of_get_regulator_init_data( + iodev->dev, reg_np); + rdata->reg_node = reg_np; + rdata++; + } + + if (of_get_property(pmic_np, "max8997,pmic-buck1-uses-gpio-dvs", NULL)) + pdata->buck1_gpiodvs = true; + + if (of_get_property(pmic_np, "max8997,pmic-buck2-uses-gpio-dvs", NULL)) + pdata->buck2_gpiodvs = true; + + if (of_get_property(pmic_np, "max8997,pmic-buck5-uses-gpio-dvs", NULL)) + pdata->buck5_gpiodvs = true; + + if (pdata->buck1_gpiodvs || pdata->buck2_gpiodvs || + pdata->buck5_gpiodvs) { + ret = max8997_pmic_dt_parse_dvs_gpio(iodev, pdata, pmic_np); + if (ret) + return -EINVAL; + } + + if (of_property_read_u32(pmic_np, + "max8997,pmic-buck125-default-dvs-idx", + &pdata->buck125_default_idx)) { + pdata->buck125_default_idx = 0; + } else { + if (pdata->buck125_default_idx >= 8) { + pdata->buck125_default_idx = 0; + dev_info(iodev->dev, "invalid value for " + "default dvs index, using 0 instead\n"); + } + } + + if (of_get_property(pmic_np, + "max8997,pmic-ignore-gpiodvs-side-effect", NULL)) + pdata->ignore_gpiodvs_side_effect = true; + + dvs_voltage_nr = 8; + + if (of_property_read_u32_array(pmic_np, + "max8997,pmic-buck1-dvs-voltage", + pdata->buck1_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck1 voltages not specified\n"); + return -EINVAL; + } + + if (of_property_read_u32_array(pmic_np, + "max8997,pmic-buck2-dvs-voltage", + pdata->buck2_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck2 voltages not specified\n"); + return -EINVAL; + } + + if (of_property_read_u32_array(pmic_np, + "max8997,pmic-buck5-dvs-voltage", + pdata->buck5_voltage, dvs_voltage_nr)) { + dev_err(iodev->dev, "buck5 voltages not specified\n"); + return -EINVAL; + } + + return 0; +} +#else +static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev, + struct max8997_platform_data *pdata) +{ + return 0; +} +#endif /* CONFIG_OF */ + static __devinit int max8997_pmic_probe(struct platform_device *pdev) { struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev); + struct max8997_platform_data *pdata = iodev->pdata; struct regulator_config config = { }; struct regulator_dev **rdev; struct max8997_data *max8997; @@ -944,11 +1081,17 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev) int i, ret, size; u8 max_buck1 = 0, max_buck2 = 0, max_buck5 = 0; - if (!pdata) { + if (IS_ERR_OR_NULL(pdata)) { dev_err(pdev->dev.parent, "No platform init data supplied.\n"); return -ENODEV; } + if (iodev->dev->of_node) { + ret = max8997_pmic_dt_parse_pdata(iodev, pdata); + if (ret) + return ret; + } + max8997 = devm_kzalloc(&pdev->dev, sizeof(struct max8997_data), GFP_KERNEL); if (!max8997) @@ -1101,6 +1244,7 @@ static __devinit int max8997_pmic_probe(struct platform_device *pdev) config.dev = max8997->dev; config.init_data = pdata->regulators[i].initdata; config.driver_data = max8997; + config.of_node = pdata->regulators[i].reg_node; rdev[i] = regulator_register(®ulators[id], &config); if (IS_ERR(rdev[i])) { diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index d805eef11915..2abc4a71f99c 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -144,4 +144,6 @@ source "drivers/staging/imx-drm/Kconfig" source "drivers/staging/dgrp/Kconfig" +source "drivers/staging/alc5625/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 76e2ebd596ff..300ab1c11cc1 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -64,3 +64,4 @@ obj-$(CONFIG_NET_VENDOR_SILICOM) += silicom/ obj-$(CONFIG_CED1401) += ced1401/ obj-$(CONFIG_DRM_IMX) += imx-drm/ obj-$(CONFIG_DGRP) += dgrp/ +obj-$(CONFIG_SND_SOC_ORIGEN_ALC5625) += alc5625/ diff --git a/drivers/staging/alc5625/Kconfig b/drivers/staging/alc5625/Kconfig new file mode 100644 index 000000000000..b35a4695497d --- /dev/null +++ b/drivers/staging/alc5625/Kconfig @@ -0,0 +1,13 @@ +# Staging the origen board audio codec +config SND_SOC_ALC5625 + tristate + +config SND_SOC_ORIGEN_ALC5625 + tristate "SoC I2S Audio support for ALC5625 on ORIGEN board" + depends on SND_SOC_SAMSUNG && MACH_ORIGEN + select SND_SOC_ALC5625 + select SND_SAMSUNG_I2S + select fool + help + Say Y if you want to add support for SoC audio + on origen board using ALC5625 codec diff --git a/drivers/staging/alc5625/Makefile b/drivers/staging/alc5625/Makefile new file mode 100644 index 000000000000..ae4d85e7738b --- /dev/null +++ b/drivers/staging/alc5625/Makefile @@ -0,0 +1,3 @@ +# Staging the origen board audio codec +obj-$(CONFIG_SND_SOC_ALC5625) += alc5625.o +obj-$(CONFIG_SND_SOC_ORIGEN_ALC5625) += origen_alc5625.o diff --git a/drivers/staging/alc5625/alc5625.c b/drivers/staging/alc5625/alc5625.c new file mode 100644 index 000000000000..6c63b41be0b7 --- /dev/null +++ b/drivers/staging/alc5625/alc5625.c @@ -0,0 +1,2278 @@ +/* + * alc5625.c -- ALC5625 ALSA SoC Audio driver + * + * Copyright (C) 2011 Insignal Co., Ltd. + * + * Author: Pan<pan@insginal.co.kr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "alc5625.h" + +struct alc5625_priv { + unsigned int stereo_sysclk; + unsigned int voice_sysclk; + enum snd_soc_control_type control_type; + void *control_data; + struct snd_soc_codec *codec; + struct regmap *regmap; +}; + +struct alc5625_init_reg { + u8 reg_index; + u16 reg_value; +}; + +static struct alc5625_init_reg alc5625_init_list[] = { + + {ALC5625_HP_OUT_VOL, 0x9090}, /* default is -12db */ + {ALC5625_SPK_OUT_VOL, 0x8080}, /* default is 0db */ + {ALC5625_DAC_AND_MIC_CTRL, 0xee03}, /* DAC to hpmixer */ + {ALC5625_OUTPUT_MIXER_CTRL, 0x0748}, /* all output from hpmixer */ + {ALC5625_MIC_CTRL, 0x0500}, /* mic boost 20db */ + {ALC5625_ADC_REC_MIXER, 0x3f3f}, /* record source from mic1 */ + {ALC5625_GEN_CTRL_REG1, 0x0c0a}, /* speaker vdd ratio is 1 */ + + /* gain 15db of ADC by default */ + {ALC5625_ADC_REC_GAIN, 0xd5d5}, + + /* Audio Record settings */ + {ALC5625_LINE_IN_VOL, 0xff1f}, + {ALC5625_PD_CTRL_STAT, 0x00c0}, + {ALC5625_PWR_MANAG_ADD3, 0x80c2}, +}; + +#define ALC5625_INIT_REG_NUM ARRAY_SIZE(alc5625_init_list) + +/* + * bit[0] for linein playback switch + * bit[1] phone + * bit[2] mic1 + * bit[3] mic2 + * bit[4] vopcm + * + */ +#define HPL_MIXER 0x80 +#define HPR_MIXER 0x82 +static unsigned int reg80, reg82; + +/* + * bit[0][1][2] use for aec control + * bit[3] for none + * bit[4] for SPKL pga + * bit[5] for SPKR pga + * bit[6] for hpl pga + * bit[7] for hpr pga + * bit[8] for dump dsp + */ +#define virtual_reg_FOR_MISC_FUNC 0x84 +static unsigned int reg84; + +static const u16 alc5625_reg[] = { + 0x59b4, 0x8080, 0x9090, 0x8080, /* reg00-reg06 */ + 0xc800, 0xff1f, 0x1010, 0x0808, /* reg08-reg0e */ + 0xe0ef, 0xd5d5, 0x3f3f, 0x0000, /* reg10-reg16 */ + 0xe010, 0x0000, 0x0748, 0x2007, /* reg18-reg1e */ + 0x0000, 0x0500, 0x00c0, 0x00c0, /* reg20-reg26 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* reg28-reg2e */ + 0x0000, 0x0000, 0x0000, 0x0000, /* reg30-reg36 */ + 0x0000, 0x0000, 0x0000, 0x80c2, /* reg38-reg3e */ + 0x0c0a, 0x0000, 0x0000, 0x0000, /* reg40-reg46 */ + 0x0029, 0x0000, 0xbe3e, 0x3e3e, /* reg48-reg4e */ + 0x0000, 0x0000, 0x803a, 0x0000, /* reg50-reg56 */ + 0x0000, 0x0009, 0x0000, 0x3000, /* reg58-reg5e */ + 0x3075, 0x1010, 0x3110, 0x0000, /* reg60-reg66 */ + 0x0553, 0x0000, 0x0000, 0x0000, /* reg68-reg6e */ + 0x0000, 0x0000, 0x0000, 0x0000, /* reg70-reg76 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* reg78-reg7e */ +}; + +struct voice_dsp_reg vodsp_aec_init_value[] = { + {0x232c, 0x0025}, + {0x230b, 0x0001}, + {0x2308, 0x007f}, + {0x23f8, 0x4003}, + {0x2301, 0x0002}, + {0x2328, 0x0001}, + {0x2304, 0x00fa}, + {0x2305, 0x0500}, + {0x2306, 0x4000}, + {0x230d, 0x0900}, + {0x230e, 0x0280}, + {0x2312, 0x00b1}, + {0x2314, 0xc000}, + {0x2316, 0x0041}, + {0x2317, 0x2200}, + {0x2318, 0x0c00}, + {0x231d, 0x0050}, + {0x231f, 0x4000}, + {0x2330, 0x0008}, + {0x2335, 0x000a}, + {0x2336, 0x0004}, + {0x2337, 0x5000}, + {0x233a, 0x0300}, + {0x233b, 0x0030}, + {0x2341, 0x0008}, + {0x2343, 0x0800}, + {0x2352, 0x7fff}, + {0x237f, 0x0400}, + {0x23a7, 0x2800}, + {0x22ce, 0x0400}, + {0x22d3, 0x1500}, + {0x22d4, 0x2800}, + {0x22d5, 0x3000}, + {0x2399, 0x2800}, + {0x230c, 0x0000}, /* to enable VODSP AEC function */ +}; + +#define SET_VODSP_REG_INIT_NUM ARRAY_SIZE(vodsp_aec_init_value) + +static inline unsigned int alc5625_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + + if (reg > 0x7e) + return 0; + return cache[reg / 2]; +} + +static unsigned int alc5625_read_hw_reg(struct snd_soc_codec *codec, + unsigned int reg) +{ + unsigned int value = 0x0; + + if (regmap_read(codec->control_data, reg, &value) < 0) { + printk(KERN_DEBUG "%s failed\n", __func__); + return -EIO; + } + return value; +} + + +static unsigned int alc5625_read(struct snd_soc_codec *codec, unsigned int reg) +{ + if ((reg == 0x80) || (reg == 0x82) || (reg == 0x84)) + return (reg == 0x80) ? reg80 : + ((reg == 0x82) ? reg82 : reg84); + + return alc5625_read_hw_reg(codec, reg); +} + +static inline void alc5625_write_reg_cache(struct snd_soc_codec *codec, + unsigned int reg, + unsigned int value) +{ + u16 *cache = codec->reg_cache; + if (reg > 0x7E) + return; + cache[reg / 2] = value; +} + +static int alc5625_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + unsigned int *regvalue = NULL; + + if ((reg == 0x80) || (reg == 0x82) || (reg == 0x84)) { + regvalue = ((reg == 0x80) ? ®80 : + ((reg == 0x82) ? ®82 : ®84)); + *regvalue = value; + return 0; + } + alc5625_write_reg_cache(codec, reg, value); + + if (!regmap_write(codec->control_data, reg, value)) { + return 0; + } else { + printk(KERN_ERR "alc5625_write fail\n"); + return -EIO; + } +} + +#define alc5625_write_mask(c, reg, value, mask) snd_soc_update_bits(c,\ + reg, mask, value) + +#define alc5625_reset(c) alc5625_write(c, ALC5625_RESET, 0) + +/* read/write dsp reg */ +static int alc5625_wait_vodsp_i2c_done(struct snd_soc_codec *codec) +{ + unsigned int checkcount = 0; + unsigned int vodsp_data; + + vodsp_data = alc5625_read(codec, ALC5625_VODSP_REG_CMD); + while (vodsp_data & VODSP_BUSY) { + if (checkcount > 10) + return -EBUSY; + vodsp_data = alc5625_read(codec, ALC5625_VODSP_REG_CMD); + checkcount++; + } + return 0; +} + +static int alc5625_write_vodsp_reg(struct snd_soc_codec *codec, + unsigned int vodspreg, unsigned int value) +{ + if (alc5625_wait_vodsp_i2c_done(codec)) + return -EBUSY; + + alc5625_write(codec, ALC5625_VODSP_REG_ADDR, vodspreg); + alc5625_write(codec, ALC5625_VODSP_REG_DATA, value); + alc5625_write(codec, ALC5625_VODSP_REG_CMD, + VODSP_WRITE_ENABLE | VODSP_CMD_MW); + mdelay(10); + return 0; + +} + +static unsigned int alc5625_read_vodsp_reg(struct snd_soc_codec *codec, + unsigned int vodspreg) +{ + unsigned int ndata_h, ndata_l; + unsigned int value; + + if (alc5625_wait_vodsp_i2c_done(codec)) + return -EBUSY; + + alc5625_write(codec, ALC5625_VODSP_REG_ADDR, vodspreg); + alc5625_write(codec, ALC5625_VODSP_REG_CMD, + VODSP_READ_ENABLE | VODSP_CMD_MR); + + if (alc5625_wait_vodsp_i2c_done(codec)) + return -EBUSY; + + alc5625_write(codec, ALC5625_VODSP_REG_ADDR, 0x26); + alc5625_write(codec, ALC5625_VODSP_REG_CMD, + VODSP_READ_ENABLE | VODSP_CMD_RR); + + if (alc5625_wait_vodsp_i2c_done(codec)) + return -EBUSY; + + ndata_h = alc5625_read(codec, ALC5625_VODSP_REG_DATA); + alc5625_write(codec, ALC5625_VODSP_REG_ADDR, 0x25); + alc5625_write(codec, ALC5625_VODSP_REG_CMD, + VODSP_READ_ENABLE | VODSP_CMD_RR); + + if (alc5625_wait_vodsp_i2c_done(codec)) + return -EBUSY; + + ndata_l = alc5625_read(codec, ALC5625_VODSP_REG_DATA); + value = ((ndata_h & 0xff) << 8) | (ndata_l & 0xff); + + return value; +} + +static int alc5625_reg_init(struct snd_soc_codec *codec) +{ + int i; + + for (i = 0; i < ALC5625_INIT_REG_NUM; i++) + alc5625_write(codec, alc5625_init_list[i].reg_index, + alc5625_init_list[i].reg_value); + + return 0; +} + +static const char *const alc5625_aec_path_sel[] = { + "aec func disable", "aec func for pcm in/out", + "aec func for iis in/out", "aec func for analog in/out" +}; /* 0 */ +static const char *const alc5625_spk_out_sel[] = { + "Class AB", "Class D" +}; /* 1 */ +static const char *const alc5625_spk_l_source_sel[] = { + "LPRN", "LPRP", "LPLN", "MM" +}; /* 2 */ +static const char *const alc5625_spkmux_source_sel[] = { + "VMID", "HP Mixer", "SPK Mixer", "Mono Mixer" +}; /* 3 */ +static const char *const alc5625_hplmux_source_sel[] = { + "VMID", "HPL Mixer" +}; /* 4 */ +static const char *const alc5625_hprmux_source_sel[] = { + "VMID", "HPR Mixer" +}; /* 5 */ +static const char *const alc5625_auxmux_source_sel[] = { + "VMID", "HP Mixer", "SPK Mixer", "Mono Mixer" +}; /* 6 */ +static const char *const alc5625_spkamp_ratio_sel[] = { + "2.25 Vdd", "2.00 Vdd", "1.75 Vdd", + "1.50 Vdd", "1.25 Vdd", "1.00 Vdd" +}; /* 7 */ +static const char *const alc5625_mic1_boost_sel[] = { + "Bypass", "+20db", "+30db", "+40db" +}; /* 8 */ +static const char *const alc5625_mic2_boost_sel[] = { + "Bypass", "+20db", "+30db", "+40db" +}; /* 9 */ +static const char *const alc5625_dmic_boost_sel[] = { + "Bypass", "+6db", "+12db", "+18db", + "+24db", "+30db", "+36db", "+42db" +}; /* 10 */ +static const char *const alc5625_adcr_func_sel[] = { + "Stereo ADC", "Voice ADC", + "VoDSP Interface", "PDM Slave Interface" +}; /* 11 */ + +static const struct soc_enum alc5625_enum[] = { + SOC_ENUM_SINGLE(virtual_reg_FOR_MISC_FUNC, 0, 4, + alc5625_aec_path_sel), /* 0 */ + SOC_ENUM_SINGLE(ALC5625_OUTPUT_MIXER_CTRL, 13, 2, + alc5625_spk_out_sel), /* 1 */ + SOC_ENUM_SINGLE(ALC5625_OUTPUT_MIXER_CTRL, 14, 4, + alc5625_spk_l_source_sel), /* 2 */ + SOC_ENUM_SINGLE(ALC5625_OUTPUT_MIXER_CTRL, 10, 4, + alc5625_spkmux_source_sel), /* 3 */ + SOC_ENUM_SINGLE(ALC5625_OUTPUT_MIXER_CTRL, 9, 2, + alc5625_hplmux_source_sel), /* 4 */ + SOC_ENUM_SINGLE(ALC5625_OUTPUT_MIXER_CTRL, 8, 2, + alc5625_hprmux_source_sel), /* 5 */ + SOC_ENUM_SINGLE(ALC5625_OUTPUT_MIXER_CTRL, 6, 4, + alc5625_auxmux_source_sel), /* 6 */ + SOC_ENUM_SINGLE(ALC5625_GEN_CTRL_REG1, 1, 6, + alc5625_spkamp_ratio_sel), /* 7 */ + SOC_ENUM_SINGLE(ALC5625_MIC_CTRL, 10, 4, + alc5625_mic1_boost_sel), /* 8 */ + SOC_ENUM_SINGLE(ALC5625_MIC_CTRL, 8, 4, + alc5625_mic2_boost_sel), /* 9 */ + SOC_ENUM_SINGLE(ALC5625_DMIC_CTRL, 0, 8, + alc5625_dmic_boost_sel), /* 10 */ + SOC_ENUM_SINGLE(ALC5625_DAC_ADC_VODAC_FUN_SEL, 4, 4, + alc5625_adcr_func_sel), /* 11 */ +}; + +/* function: Enable the Voice PCM interface Path */ +static int config_pcm_voice_path(struct snd_soc_codec *codec, + unsigned int enable_voice_path, + unsigned int mode) +{ + if (enable_voice_path) { + /* Power on DAC reference */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, + PWR_DAC_REF | PWR_VOICE_DF2SE, + PWR_DAC_REF | PWR_VOICE_DF2SE); + /* Power on Voice DAC/ADC */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD2, + PWR_VOICE_CLOCK, + PWR_VOICE_CLOCK); + /* routing voice to HPMixer */ + alc5625_write_mask(codec, ALC5625_VOICE_DAC_OUT_VOL, 0, + M_V_DAC_TO_HP_MIXER); + + switch (mode) { + case PCM_SLAVE_MODE_B: + /* + * 8kHz sampling rate,16 bits PCM mode and Slave mode, + * PCM mode is B,MCLK=24.576MHz from Oscillator. + * CSR PSKEY_PCM_CONFIG32 (HEX) = 0x08C00000, + * PSKEY_FORMAT=0x0060 + * + * Set LRCK voice select divide 32 + * set voice blck select divide 6 and 8 + * voice filter clock divide 3 and 16 + * the register 0x64 value's should is 0x5524 + */ + alc5625_write(codec, ALC5625_VOICE_DAC_PCMCLK_CTRL1, + 0x5524); + + break; + + case PCM_SLAVE_MODE_A: + /* + * 8kHz sampling rate,16 bits PCM and Slave mode, + * PCM mode is A,MCLK=24.576MHz from Oscillator. + * CSR PSKEY_PCM_CONFIG32 (HEX) = 0x08C00004, + * PSKEY_FORMAT=0x0060 + * + * Enable GPIO 1,3,4,5 to voice interface + * Set I2S to Slave mode + * Voice I2S SYSCLK Source select Main SYSCLK + * Set voice i2s VBCLK Polarity to Invert + * Set Data length to 16 bit + * set Data Fomrat to PCM mode A + * the register 0x36 value's should is 0xC082 + */ + alc5625_write(codec, ALC5625_EXTEND_SDP_CTRL, 0xC082); + + /* + * Set LRCK voice select divide 64 + * set voice blck select divide 6 and 8 + * voice filter clock divide 3 and 16 + * the register 0x64 value's should is 0x5524 + */ + alc5625_write(codec, ALC5625_VOICE_DAC_PCMCLK_CTRL1, + 0x5524); + + break; + + case PCM_MASTER_MODE_B: + /* + * 8kHz sampling rate,16 bits PCM and Master mode, + * PCM mode is B,Clock from PLL OUT + * CSR PSKEY_PCM_CONFIG32 (HEX) = 0x08000002, + * PSKEY_FORMAT=0x0060 + * Enable GPIO 1,3,4,5 to voice interface + * Set I2S to master mode + * Set voice i2s VBCLK Polarity to Invert + * Set Data length to 16 bit + * set Data Fomrat to PCM mode B + * the register 0x36 value's should is 0x8083 + */ + alc5625_write(codec, ALC5625_EXTEND_SDP_CTRL, 0x8083); + + /* + * Set LRCK voice select divide 64 + * set voice blck select divide 6 and 8 + * voice filter clock divide 3 and 16 + * the register 0x64 value's should is 0x5524 + */ + alc5625_write(codec, ALC5625_VOICE_DAC_PCMCLK_CTRL1, + 0x5524); + break; + + default: + /* do nothing */ + break; + } + } else { + /* Power down Voice Different to sing-end power */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, 0, + PWR_VOICE_DF2SE); + /* Power down Voice DAC/ADC */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD2, 0, + PWR_VOICE_CLOCK); + /* Disable Voice PCM interface */ + alc5625_write_mask(codec, ALC5625_EXTEND_SDP_CTRL, 0, + EXT_I2S_FUNC_ENABLE); + } + + return 0; +} + +static int init_vodsp_aec(struct snd_soc_codec *codec) +{ + int i; + int ret = 0; + + /* Disable LDO power */ + alc5625_write_mask(codec, ALC5625_LDO_CTRL, 0, LDO_ENABLE); + mdelay(20); + alc5625_write_mask(codec, ALC5625_VODSP_CTL, + VODSP_NO_PD_MODE_ENA, VODSP_NO_PD_MODE_ENA); + /* Enable LDO power and set output voltage to 1.2V */ + alc5625_write_mask(codec, ALC5625_LDO_CTRL, + LDO_ENABLE | LDO_OUT_VOL_CTRL_1_20V, + LDO_ENABLE | LDO_OUT_VOL_CTRL_MASK); + mdelay(20); + /* Enable power of VODSP I2C interface */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD3, + PWR_VODSP_INTERFACE | PWR_I2C_FOR_VODSP, + PWR_VODSP_INTERFACE | PWR_I2C_FOR_VODSP); + mdelay(1); + /* Reset VODSP */ + alc5625_write_mask(codec, ALC5625_VODSP_CTL, + 0, VODSP_NO_RST_MODE_ENA); + mdelay(1); + /* Set VODSP to non-reset status */ + alc5625_write_mask(codec, ALC5625_VODSP_CTL, + VODSP_NO_RST_MODE_ENA, VODSP_NO_RST_MODE_ENA); + mdelay(20); + + /*initize AEC paramter*/ + for (i = 0; i < SET_VODSP_REG_INIT_NUM; i++) { + ret = alc5625_write_vodsp_reg(codec, + vodsp_aec_init_value[i].index, + vodsp_aec_init_value[i].val); + if (ret) + return -EIO; + } + + schedule_timeout_uninterruptible(msecs_to_jiffies(10)); + + return 0; +} + +/* + * Enable/Disable the VODSP interface Path + * + * For system clock only support specific clock, realtek suggests customer to + * use 24.576Mhz or 22.5792Mhz clock for MCLK (MCLK=48k*512 or 44.1k*512Mhz) + */ +static int set_vodsp_aec_path(struct snd_soc_codec *codec, unsigned int mode) +{ + switch (mode) { + case PCM_IN_PCM_OUT: + /* set PCM format */ + config_pcm_voice_path(codec, 1, PCM_MASTER_MODE_B); + /* set AEC path */ + alc5625_write_mask(codec, ALC5625_PD_CTRL_STAT, + 0x0300, 0x0300); + alc5625_write_mask(codec, ALC5625_VODSP_PDM_CTL, + VODSP_RXDP_PWR | + VODSP_RXDP_S_SEL_VOICE | + VOICE_PCM_S_SEL_AEC_TXDP, + VODSP_RXDP_PWR | + VODSP_RXDP_S_SEL_MASK | + VOICE_PCM_S_SEL_MASK); + alc5625_write_mask(codec, ALC5625_DAC_ADC_VODAC_FUN_SEL, + ADCR_FUNC_SEL_PDM | + VODAC_SOUR_SEL_VODSP_TXDC, + ADCR_FUNC_SEL_MASK | + VODAC_SOUR_SEL_MASK); + alc5625_write_mask(codec, ALC5625_VODSP_CTL, + VODSP_LRCK_SEL_8K, + VODSP_LRCK_SEL_MASK); + alc5625_write_mask(codec, ALC5625_PD_CTRL_STAT, + 0x0000, 0x0300); + + /* Set input&output path and power + * Power on related bit + * + * I2S DAI Enable | spk amp enable | + * Dac2Mixer pwr on | MICBIAS1 Enable | + * MICBIAS2 Enable | Main Bias Pwr | + * DAC ref voltage pwr on + */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, + 0x0c8f, 0x0c8f); + + /* Pwr on Pll1 | over temperature sensor pwr on | + * pwr voice DAC on | Left and Right ADC on | + * Spk mixer pwr on | ADC mixer left/right pwr on + */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD2, + 0xa4cb, 0xa4cb); + + /* power spk left/right vol | pwr vodsp interface | + * power on microphone1 boost + */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD3, + 0x3302, 0xf302); + + /* Mute DAC to hpmixer */ + alc5625_write(codec, ALC5625_DAC_AND_MIC_CTRL, 0xee0f); + + /* Set Mic1 to differential mode */ + alc5625_write(codec, ALC5625_MIC_VOL, 0x8808); + + /* Mic boost 0db */ + alc5625_write(codec, ALC5625_MIC_CTRL, 0x0000); + + /* ADC_Mixer_R boost 10.5 db */ + alc5625_write(codec, ALC5625_ADC_REC_GAIN, 0xcbd3); + + /* Mic1->ADCMixer_R */ + alc5625_write(codec, ALC5625_ADC_REC_MIXER, 0x7f3f); + + /* VoDAC to speakerMixer,0db */ + alc5625_write(codec, ALC5625_VOICE_DAC_OUT_VOL, 0xa010); + + /* Speaker source from speakermixer */ + alc5625_write(codec, ALC5625_OUTPUT_MIXER_CTRL, 0x8808); + + /* Unmute speaker */ + alc5625_write_mask(codec, ALC5625_SPK_OUT_VOL, 0x0000, 0x8080); + + break; + + case ANALOG_IN_ANALOG_OUT: + alc5625_write_mask(codec, ALC5625_PD_CTRL_STAT, 0x0300, 0x0300); + alc5625_write_mask(codec, ALC5625_VODSP_PDM_CTL, + VODSP_RXDP_PWR | + VODSP_RXDP_S_SEL_ADCL | + VOICE_PCM_S_SEL_AEC_TXDP, + VODSP_RXDP_PWR | VODSP_RXDP_S_SEL_MASK | + VOICE_PCM_S_SEL_MASK); + alc5625_write_mask(codec, ALC5625_DAC_ADC_VODAC_FUN_SEL, + ADCR_FUNC_SEL_PDM | + VODAC_SOUR_SEL_VODSP_TXDC | + DAC_FUNC_SEL_VODSP_TXDP| + ADCL_FUNC_SEL_VODSP, + ADCR_FUNC_SEL_MASK | + VODAC_SOUR_SEL_MASK | + DAC_FUNC_SEL_MASK | + ADCL_FUNC_SEL_MASK); + alc5625_write_mask(codec, ALC5625_VODSP_CTL, + VODSP_LRCK_SEL_16K, + VODSP_LRCK_SEL_MASK); + alc5625_write_mask(codec, ALC5625_PD_CTRL_STAT, 0x0000, 0x0300); + + /* Set input&output path and power */ + /* Power on related bit */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, + 0xcc8f, 0xcc8f); + + /* Power on related bit */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD2, + 0xa7cf, 0xa7cf); + + /* Power on related bit */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD3, + 0xf312, 0xf312); + + /* Set Mic1 to differential mode */ + alc5625_write(codec, ALC5625_MIC_VOL, 0x8808); + + /* Set phone in to differential mode */ + alc5625_write(codec, ALC5625_PHONEIN_VOL, 0xe800); + + /* Mic boost 0db */ + alc5625_write(codec, ALC5625_MIC_CTRL, 0x0000); + + /* Mic1->ADCMixer_R,phone in-->ADCMixer_L */ + alc5625_write(codec, ALC5625_ADC_REC_MIXER, 0x773f); + + /* ADC_Mixer_R boost 10.5 db */ + alc5625_write(codec, ALC5625_ADC_REC_GAIN, 0xCBD3); + + /* Speaker from spkmixer,monoOut from monoMixer */ + alc5625_write(codec, ALC5625_OUTPUT_MIXER_CTRL, 0x88c8); + + /* Unmute VoDAC to spkmixer */ + alc5625_write(codec, ALC5625_VOICE_DAC_OUT_VOL, 0xA010); + + /* Unmute DAC to monoMixer */ + alc5625_write(codec, ALC5625_DAC_AND_MIC_CTRL, 0xee0e); + alc5625_write(codec, ALC5625_STEREO_DAC_CLK_CTRL2, 0x2222); + alc5625_write(codec, ALC5625_VOICE_DAC_PCMCLK_CTRL1, 0x3122); + + /* Unmute speaker */ + alc5625_write_mask(codec, ALC5625_SPK_OUT_VOL, 0x0000, 0x8080); + + /* Unmute auxout */ + alc5625_write_mask(codec, ALC5625_AUX_OUT_VOL, 0x0000, 0x8080); + break; + + case DAC_IN_ADC_OUT: + alc5625_write_mask(codec, ALC5625_PD_CTRL_STAT, 0x0300, 0x0300); + alc5625_write_mask(codec, ALC5625_DAC_ADC_VODAC_FUN_SEL, + ADCR_FUNC_SEL_PDM | + DAC_FUNC_SEL_VODSP_TXDC, + ADCR_FUNC_SEL_MASK | + DAC_FUNC_SEL_MASK); + alc5625_write_mask(codec, ALC5625_VODSP_PDM_CTL, + VODSP_SRC1_PWR | + VODSP_SRC2_PWR | + VODSP_RXDP_PWR | + VODSP_RXDP_S_SEL_SRC1 | + REC_S_SEL_SRC2, + VODSP_SRC1_PWR | + VODSP_SRC2_PWR | + VODSP_RXDP_PWR | + VODSP_RXDP_S_SEL_MASK | + REC_S_SEL_MASK); + alc5625_write_mask(codec, ALC5625_VODSP_CTL, + VODSP_LRCK_SEL_16K, + VODSP_LRCK_SEL_MASK); + alc5625_write_mask(codec, ALC5625_PD_CTRL_STAT, 0x0000, 0x0300); + + /* Set input&output path and power */ + /* Power on related bit */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, + 0xcc0f, 0xcc0f); + + /* Power on related bit */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD2, + 0xa7cb, 0xa7cb); + + /* Power on related bit */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD3, + 0x3302, 0x3302); + + /* Set Mic1 to differential mode */ + alc5625_write(codec, ALC5625_MIC_VOL, 0x8808); + + /*Mic boost 0db */ + alc5625_write(codec, ALC5625_MIC_CTRL, 0x0000); + + /*Mic1->ADCMixer_R */ + alc5625_write(codec, ALC5625_ADC_REC_MIXER, 0x7f3f); + + /*ADC_Mixer_R boost 10.5 db */ + alc5625_write(codec, ALC5625_ADC_REC_GAIN, 0xCBD3); + + /* Speaker out from spkMixer */ + alc5625_write(codec, ALC5625_OUTPUT_MIXER_CTRL, 0x8808); + + /* Unmute DAC to spkMixer */ + alc5625_write(codec, ALC5625_DAC_AND_MIC_CTRL, 0xee0d); + alc5625_write(codec, ALC5625_STEREO_DAC_CLK_CTRL1, 0x3075); + alc5625_write(codec, ALC5625_STEREO_DAC_CLK_CTRL2, 0x1010); + + /* Unmute speaker */ + alc5625_write_mask(codec, ALC5625_SPK_OUT_VOL, 0x0000, 0x8080); + + break; + + case VODSP_AEC_DISABLE: + default: + /* Mute speaker out */ + alc5625_write_mask(codec, ALC5625_SPK_OUT_VOL, 0x8080, 0x8080); + + /* Mute auxout */ + alc5625_write_mask(codec, ALC5625_AUX_OUT_VOL, 0x8080, 0x8080); + + /* Mic boost 20db by default */ + alc5625_write(codec, ALC5625_MIC_CTRL, 0x0500); + + /* Record from Mic1 by default */ + alc5625_write(codec, ALC5625_ADC_REC_MIXER, 0x3f3f); + + /* ADC_Mixer_R boost 15 db by default */ + alc5625_write(codec, ALC5625_ADC_REC_GAIN, 0xD5D5); + + /* All output from HPmixer by default */ + alc5625_write(codec, ALC5625_OUTPUT_MIXER_CTRL, 0x0748); + + /* DAC to HPmixer by default */ + alc5625_write(codec, ALC5625_DAC_AND_MIC_CTRL, 0xee03); + + /* Mute VoDAC to mixer by default */ + alc5625_write(codec, ALC5625_VOICE_DAC_OUT_VOL, 0xe010); + + alc5625_write_mask(codec, ALC5625_PD_CTRL_STAT, 0x0000, 0x0300); + + /* Set stereo DAC&Voice DAC&Stereo ADC function + * select to default + */ + alc5625_write(codec, ALC5625_DAC_ADC_VODAC_FUN_SEL, 0); + + /* Set VODSP&PDM Control to default */ + alc5625_write(codec, ALC5625_VODSP_PDM_CTL, 0); + + alc5625_write_mask(codec, ALC5625_PD_CTRL_STAT, 0x0000, 0x0300); + + /* Power down related bit */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD3, + 0x0000, 0xf312); + + /* Power down related bit */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, + 0x0000, 0xcc8d); + + /* Power down related bit */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD2, + 0x0000, 0x07cf); + break; + } + return 0; +} + +static int enable_vodsp_aec(struct snd_soc_codec *codec, + unsigned int enable_vodspAEC, + unsigned int aec_mode) +{ + int ret = 0; + + if (enable_vodspAEC != 0) { + /* enable power of VODSP I2C interface & VODSP interface */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD3, + PWR_VODSP_INTERFACE | + PWR_I2C_FOR_VODSP, + PWR_VODSP_INTERFACE | + PWR_I2C_FOR_VODSP); + /* enable power of VODSP I2S interface */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, + PWR_I2S_INTERFACE, + PWR_I2S_INTERFACE); + /* select input/output of VODSP AEC */ + set_vodsp_aec_path(codec, aec_mode); + } else { + /* disable VODSP AEC path */ + set_vodsp_aec_path(codec, VODSP_AEC_DISABLE); + /* set VODSP AEC to power down mode */ + alc5625_write_mask(codec, ALC5625_VODSP_CTL, 0, + VODSP_NO_PD_MODE_ENA); + /* disable power of VODSP I2C interface & VODSP interface */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD3, 0, + PWR_VODSP_INTERFACE | + PWR_I2C_FOR_VODSP); + } + + return ret; +} + +static void alc5625_aec_config(struct snd_soc_codec *codec, unsigned int mode) +{ + if (mode == VODSP_AEC_DISABLE) { + enable_vodsp_aec(codec, 0, mode); + /* disable LDO power */ + alc5625_write_mask(codec, ALC5625_LDO_CTRL, 0, LDO_ENABLE); + } else { + init_vodsp_aec(codec); + enable_vodsp_aec(codec, 1, mode); + } +} + +/* function:disable alc5625's function */ +static int alc5625_func_aec_disable(struct snd_soc_codec *codec, int mode) +{ + switch (mode) { + case ALC5625_AEC_PCM_IN_OUT: + case ALC5625_AEC_IIS_IN_OUT: + case ALC5625_AEC_ANALOG_IN_OUT: + /* disable AEC function and path */ + alc5625_aec_config(codec, VODSP_AEC_DISABLE); + break; + default: + break; + } + return 0; +} + +static int alc5625_get_dsp_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + /* cause we choose bit[0][1] to store the mode type */ + int mode = (alc5625_read(codec, virtual_reg_FOR_MISC_FUNC)) & 0x03; + + ucontrol->value.integer.value[0] = mode; + return 0; +} + +static int alc5625_set_dsp_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + u16 virtual_reg = alc5625_read(codec, virtual_reg_FOR_MISC_FUNC); + int alc5625_mode = (virtual_reg) & 0x03; + + if (alc5625_mode == ucontrol->value.integer.value[0]) + return 0; + + switch (ucontrol->value.integer.value[0]) { + case ALC5625_AEC_PCM_IN_OUT: + /* enable AEC PCM in/out function and path */ + alc5625_aec_config(codec, PCM_IN_PCM_OUT); + break; + + case ALC5625_AEC_IIS_IN_OUT: + /* enable AEC IIS in/out function and path */ + alc5625_aec_config(codec, DAC_IN_ADC_OUT); + break; + + case ALC5625_AEC_ANALOG_IN_OUT: + /* enable AEC analog in/out function and path */ + alc5625_aec_config(codec, ANALOG_IN_ANALOG_OUT); + break; + + case ALC5625_AEC_DISABLE: + /* disable previous select function */ + alc5625_func_aec_disable(codec, alc5625_mode); + break; + + default: + break; + } + + virtual_reg &= 0xfffc; + virtual_reg |= (ucontrol->value.integer.value[0]); + alc5625_write(codec, virtual_reg_FOR_MISC_FUNC, virtual_reg); + + return 0; +} + +static int alc5625_dump_dsp_reg(struct snd_soc_codec *codec) +{ + int i; + + alc5625_write_mask(codec, ALC5625_VODSP_CTL, + VODSP_NO_PD_MODE_ENA, + VODSP_NO_PD_MODE_ENA); + for (i = 0; i < SET_VODSP_REG_INIT_NUM; i++) + alc5625_read_vodsp_reg(codec, + vodsp_aec_init_value[i].index); + + return 0; +} + +static int alc5625_dump_dsp_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int mode = alc5625_read(codec, virtual_reg_FOR_MISC_FUNC); + + mode &= ~(0x01 << 8); + mode |= (ucontrol->value.integer.value[0] << 8); + alc5625_write(codec, virtual_reg_FOR_MISC_FUNC, mode); + alc5625_dump_dsp_reg(codec); + + return 0; +} + +static const struct snd_kcontrol_new alc5625_snd_ctrls[] = { + SOC_ENUM_EXT("alc5625 aec mode sel", alc5625_enum[0], + alc5625_get_dsp_mode, alc5625_set_dsp_mode), + SOC_ENUM("SPK Amp Type", alc5625_enum[1]), + SOC_ENUM("Left SPK Source", alc5625_enum[2]), + SOC_ENUM("SPK Amp Ratio", alc5625_enum[7]), + SOC_ENUM("Mic1 Boost", alc5625_enum[8]), + SOC_ENUM("Mic2 Boost", alc5625_enum[9]), + SOC_ENUM("Dmic Boost", alc5625_enum[10]), + SOC_ENUM("ADCR Func", alc5625_enum[11]), + SOC_DOUBLE("PCM Playback Volume", ALC5625_STEREO_DAC_VOL, 8, 0, 63, 1), + SOC_DOUBLE("LineIn Playback Volume", ALC5625_LINE_IN_VOL, 8, 0, 31, 1), + SOC_SINGLE("Phone Playback Volume", ALC5625_PHONEIN_VOL, 8, 31, 1), + SOC_SINGLE("Mic1 Playback Volume", ALC5625_MIC_VOL, 8, 31, 1), + SOC_SINGLE("Mic2 Playback Volume", ALC5625_MIC_VOL, 0, 31, 1), + SOC_DOUBLE("PCM Capture Volume", ALC5625_ADC_REC_GAIN, 8, 0, 31, 1), + SOC_DOUBLE("SPKOUT Playback Volume", ALC5625_SPK_OUT_VOL, 8, 0, 31, 1), + SOC_DOUBLE("SPKOUT Playback Switch", ALC5625_SPK_OUT_VOL, 15, 7, 1, 1), + SOC_DOUBLE("HPOUT Playback Volume", ALC5625_HP_OUT_VOL, 8, 0, 31, 1), + SOC_DOUBLE("HPOUT Playback Switch", ALC5625_HP_OUT_VOL, 15, 7, 1, 1), + SOC_DOUBLE("AUXOUT Playback Volume", ALC5625_AUX_OUT_VOL, 8, 0, 31, 1), + SOC_DOUBLE("AUXOUT Playback Switch", ALC5625_AUX_OUT_VOL, 15, 7, 1, 1), + SOC_DOUBLE("ADC Record Gain", ALC5625_ADC_REC_GAIN, 8, 0, 31, 0), + SOC_SINGLE_EXT("VoDSP Dump", virtual_reg_FOR_MISC_FUNC, 8, 1, 0, + snd_soc_get_volsw, alc5625_dump_dsp_put), +}; + +static void hp_depop_mode2(struct snd_soc_codec *codec) +{ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, + PWR_SOFTGEN_EN, + PWR_SOFTGEN_EN); + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD3, + PWR_HP_R_OUT_VOL | PWR_HP_L_OUT_VOL, + PWR_HP_R_OUT_VOL | PWR_HP_L_OUT_VOL); + alc5625_write(codec, ALC5625_MISC_CTRL, HP_DEPOP_MODE2_EN); + schedule_timeout_uninterruptible(msecs_to_jiffies(500)); + + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, + PWR_HP_OUT_AMP | PWR_HP_OUT_ENH_AMP, + PWR_HP_OUT_AMP | PWR_HP_OUT_ENH_AMP); + +} + +/* enable depop function for mute/unmute */ +static void hp_mute_unmute_depop(struct snd_soc_codec *codec, int mute) +{ + if (mute) { + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, + PWR_SOFTGEN_EN, + PWR_SOFTGEN_EN); + alc5625_write(codec, ALC5625_MISC_CTRL, + M_UM_DEPOP_EN | HP_R_M_UM_DEPOP_EN | + HP_L_M_UM_DEPOP_EN); + /* Mute headphone right/left channel */ + alc5625_write_mask(codec, ALC5625_HP_OUT_VOL, + ALC_L_MUTE|ALC_R_MUTE, + ALC_L_MUTE|ALC_R_MUTE); + mdelay(50); + } else { + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, + PWR_SOFTGEN_EN, + PWR_SOFTGEN_EN); + alc5625_write(codec, ALC5625_MISC_CTRL, + M_UM_DEPOP_EN | HP_R_M_UM_DEPOP_EN | + HP_L_M_UM_DEPOP_EN); + /* unMute headphone right/left channel */ + alc5625_write_mask(codec, ALC5625_HP_OUT_VOL, 0, + ALC_L_MUTE|ALC_R_MUTE); + mdelay(50); + } +} + +/* + * _DAPM_ Controls + */ +/* Left ADC Rec mixer */ +static const struct snd_kcontrol_new alc5625_ctrl_adc_l[] = { + SOC_DAPM_SINGLE("Mic1 Capture Switch", + ALC5625_ADC_REC_MIXER, 14, 1, 1), + SOC_DAPM_SINGLE("Mic2 Capture Switch", + ALC5625_ADC_REC_MIXER, 13, 1, 1), + SOC_DAPM_SINGLE("LineIn Capture Switch", + ALC5625_ADC_REC_MIXER, 12, 1, 1), + SOC_DAPM_SINGLE("Phone Capture Switch", + ALC5625_ADC_REC_MIXER, 11, 1, 1), + SOC_DAPM_SINGLE("HP Mixer Capture Switch", + ALC5625_ADC_REC_MIXER, 10, 1, 1), + SOC_DAPM_SINGLE("SPK Mixer Capture Switch", + ALC5625_ADC_REC_MIXER, 9, 1, 1), + SOC_DAPM_SINGLE("MoNo Mixer Capture Switch", + ALC5625_ADC_REC_MIXER, 8, 1, 1), +}; + +/* Left ADC Rec mixer */ +static const struct snd_kcontrol_new alc5625_ctrl_adc_r[] = { + SOC_DAPM_SINGLE("Mic1 Capture Switch", + ALC5625_ADC_REC_MIXER, 6, 1, 1), + SOC_DAPM_SINGLE("Mic2 Capture Switch", + ALC5625_ADC_REC_MIXER, 5, 1, 1), + SOC_DAPM_SINGLE("LineIn Capture Switch", + ALC5625_ADC_REC_MIXER, 4, 1, 1), + SOC_DAPM_SINGLE("Phone Capture Switch", + ALC5625_ADC_REC_MIXER, 3, 1, 1), + SOC_DAPM_SINGLE("HP Mixer Capture Switch", + ALC5625_ADC_REC_MIXER, 2, 1, 1), + SOC_DAPM_SINGLE("SPK Mixer Capture Switch", + ALC5625_ADC_REC_MIXER, 1, 1, 1), + SOC_DAPM_SINGLE("MoNo Mixer Capture Switch", + ALC5625_ADC_REC_MIXER, 0, 1, 1), +}; + +/* Left hpmixer mixer */ +static const struct snd_kcontrol_new alc5625_ctrl_hp_l[] = { + SOC_DAPM_SINGLE("ADC Playback Switch", + ALC5625_ADC_REC_GAIN, 15, 1, 1), + SOC_DAPM_SINGLE("LineIn Playback Switch", + HPL_MIXER, 0, 1, 0), + SOC_DAPM_SINGLE("Phone Playback Switch", + HPL_MIXER, 1, 1, 0), + SOC_DAPM_SINGLE("Mic1 Playback Switch", + HPL_MIXER, 2, 1, 0), + SOC_DAPM_SINGLE("Mic2 Playback Switch", + HPL_MIXER, 3, 1, 0), + SOC_DAPM_SINGLE("Voice DAC Playback Switch", + HPL_MIXER, 4, 1, 0), + SOC_DAPM_SINGLE("HIFI DAC Playback Switch", + ALC5625_DAC_AND_MIC_CTRL, 3, 1, 1), +}; + +/* Right hpmixer mixer */ +static const struct snd_kcontrol_new alc5625_ctrl_hp_r[] = { + SOC_DAPM_SINGLE("ADC Playback Switch", + ALC5625_ADC_REC_GAIN, 7, 1, 1), + SOC_DAPM_SINGLE("LineIn Playback Switch", + HPR_MIXER, 0, 1, 0), + SOC_DAPM_SINGLE("Phone Playback Switch", + HPR_MIXER, 1, 1, 0), + SOC_DAPM_SINGLE("Mic1 Playback Switch", + HPR_MIXER, 2, 1, 0), + SOC_DAPM_SINGLE("Mic2 Playback Switch", + HPR_MIXER, 3, 1, 0), + SOC_DAPM_SINGLE("Voice DAC Playback Switch", + HPR_MIXER, 4, 1, 0), + SOC_DAPM_SINGLE("HIFI DAC Playback Switch", + ALC5625_DAC_AND_MIC_CTRL, 2, 1, 1), +}; + +/* mono mixer */ +static const struct snd_kcontrol_new alc5625_ctrl_mono[] = { + SOC_DAPM_SINGLE("ADCL Playback Switch", + ALC5625_ADC_REC_GAIN, 14, 1, 1), + SOC_DAPM_SINGLE("ADCR Playback Switch", + ALC5625_ADC_REC_GAIN, 6, 1, 1), + SOC_DAPM_SINGLE("Line Mixer Playback Switch", + ALC5625_LINE_IN_VOL, 13, 1, 1), + SOC_DAPM_SINGLE("Mic1 Playback Switch", + ALC5625_DAC_AND_MIC_CTRL, 13, 1, 1), + SOC_DAPM_SINGLE("Mic2 Playback Switch", + ALC5625_DAC_AND_MIC_CTRL, 9, 1, 1), + SOC_DAPM_SINGLE("DAC Mixer Playback Switch", + ALC5625_DAC_AND_MIC_CTRL, 0, 1, 1), + SOC_DAPM_SINGLE("Voice DAC Playback Switch", + ALC5625_VOICE_DAC_OUT_VOL, 13, 1, 1), +}; + +/* speaker mixer */ +static const struct snd_kcontrol_new alc5625_ctrl_spk[] = { + SOC_DAPM_SINGLE("Line Mixer Playback Switch", + ALC5625_LINE_IN_VOL, 14, 1, 1), + SOC_DAPM_SINGLE("Phone Playback Switch", + ALC5625_PHONEIN_VOL, 14, 1, 1), + SOC_DAPM_SINGLE("Mic1 Playback Switch", + ALC5625_DAC_AND_MIC_CTRL, 14, 1, 1), + SOC_DAPM_SINGLE("Mic2 Playback Switch", + ALC5625_DAC_AND_MIC_CTRL, 10, 1, 1), + SOC_DAPM_SINGLE("DAC Mixer Playback Switch", + ALC5625_DAC_AND_MIC_CTRL, 1, 1, 1), + SOC_DAPM_SINGLE("Voice DAC Playback Switch", + ALC5625_VOICE_DAC_OUT_VOL, 14, 1, 1), +}; + +static int mixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = w->codec; + unsigned int l, r; + + l = alc5625_read(codec, HPL_MIXER); + r = alc5625_read(codec, HPR_MIXER); + + /* Mute/Unmute vol output to hp mixer */ + if ((l & 0x1) || (r & 0x1)) + alc5625_write_mask(codec, ALC5625_LINE_IN_VOL, + 0x0000, 0x8000); + else + alc5625_write_mask(codec, ALC5625_LINE_IN_VOL, + 0x8000, 0x8000); + + /* Mute/Unmute phone input to hp mixer */ + if ((l & 0x2) || (r & 0x2)) + alc5625_write_mask(codec, ALC5625_PHONEIN_VOL, + 0x0000, 0x8000); + else + alc5625_write_mask(codec, ALC5625_PHONEIN_VOL, + 0x8000, 0x8000); + + /* Mute/Unmute Mic1 vol output to hp mixer */ + if ((l & 0x4) || (r & 0x4)) + alc5625_write_mask(codec, ALC5625_DAC_AND_MIC_CTRL, + 0x0000, 0x8000); + else + alc5625_write_mask(codec, ALC5625_DAC_AND_MIC_CTRL, + 0x8000, 0x8000); + + /* Mute/Unmute Mic2 vol output to hp mixer */ + if ((l & 0x8) || (r & 0x8)) + alc5625_write_mask(codec, ALC5625_DAC_AND_MIC_CTRL, + 0x0000, 0x0800); + else + alc5625_write_mask(codec, ALC5625_DAC_AND_MIC_CTRL, + 0x0800, 0x0800); + + /* Mute/Unmute voice DAC vol to hp mixer */ + if ((l & 0x10) || (r & 0x10)) + alc5625_write_mask(codec, ALC5625_VOICE_DAC_OUT_VOL, + 0x0000, 0x8000); + else + alc5625_write_mask(codec, ALC5625_VOICE_DAC_OUT_VOL, + 0x8000, 0x8000); + + return 0; +} + +/* + * bit[0][1] use for aec control + * bit[2][3] for ADCR func + * bit[4] for SPKL pga + * bit[5] for SPKR pga + * bit[6] for hpl pga + * bit[7] for hpr pga + */ +static int spk_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, + int event) +{ + struct snd_soc_codec *codec = w->codec; + int reg; + + reg = alc5625_read(codec, virtual_reg_FOR_MISC_FUNC) & (0x3 << 4); + if (reg && (reg >> 4) != 0x3) + return 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD3, + 0x3000, 0x3000); + alc5625_write_mask(codec, ALC5625_SPK_OUT_VOL, + 0x0000, 0x8080); + /* power on spk amp */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, + 0x0400, 0x0400); + break; + case SND_SOC_DAPM_POST_PMD: + /* power off spk amp */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, + 0x0000, 0x0400); + alc5625_write_mask(codec, ALC5625_SPK_OUT_VOL, + 0x8080, 0x8080); + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD3, + 0x0000, 0x3000); + break; + default: + return 0; + } + return 0; +} + +static int hp_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = w->codec; + int reg; + + reg = alc5625_read(codec, virtual_reg_FOR_MISC_FUNC) & (0x3 << 6); + if (reg && (reg >> 6) != 0x3) + return 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + printk(KERN_DEBUG "ALC5625: Powering down.\n"); + hp_mute_unmute_depop(codec, 1); /* mute hp */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, + 0x0000, 0x0300); + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD3, + 0x0000, 0x0c00); + break; + + case SND_SOC_DAPM_POST_PMU: + printk(KERN_DEBUG "ALC5625: Powering on.\n"); + hp_depop_mode2(codec); + hp_mute_unmute_depop(codec, 0); /* unmute hp */ + break; + + default: + return 0; + } + + return 0; +} + +static int aux_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + return 0; +} + +/* SPKOUT Mux */ +static const struct snd_kcontrol_new alc5625_ctrl_spkmux = + SOC_DAPM_ENUM("Route", alc5625_enum[3]); + +/* HPLOUT MUX */ +static const struct snd_kcontrol_new alc5625_ctrl_hplmux = + SOC_DAPM_ENUM("Route", alc5625_enum[4]); + +/* HPROUT MUX */ +static const struct snd_kcontrol_new alc5625_ctrl_hprmux = + SOC_DAPM_ENUM("Route", alc5625_enum[5]); +/* AUXOUT MUX */ +static const struct snd_kcontrol_new alc5625_ctrl_auxmux = + SOC_DAPM_ENUM("Route", alc5625_enum[6]); + +static const struct snd_soc_dapm_widget alc5625_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("Left LineIn"), + SND_SOC_DAPM_INPUT("Right LineIn"), + SND_SOC_DAPM_INPUT("Phone"), + SND_SOC_DAPM_INPUT("Mic1"), + SND_SOC_DAPM_INPUT("Mic2"), + + SND_SOC_DAPM_PGA("Mic1 Boost", + ALC5625_PWR_MANAG_ADD3, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mic2 Boost", + ALC5625_PWR_MANAG_ADD3, 0, 0, NULL, 0), + + SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback DAC", + ALC5625_PWR_MANAG_ADD2, 9, 0), + SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback DAC", + ALC5625_PWR_MANAG_ADD2, 8, 0), + SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback DAC", + ALC5625_PWR_MANAG_ADD2, 10, 0), + SND_SOC_DAPM_PGA("Left LineIn PGA", + ALC5625_PWR_MANAG_ADD3, + 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right LineIn PGA", + ALC5625_PWR_MANAG_ADD3, 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("Phone PGA", + ALC5625_PWR_MANAG_ADD3, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mic1 PGA", + ALC5625_PWR_MANAG_ADD3, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mic2 PGA", + ALC5625_PWR_MANAG_ADD3, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("VoDAC PGA", + ALC5625_PWR_MANAG_ADD1, 7, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Left Rec Mixer", + ALC5625_PWR_MANAG_ADD2, 1, 0, + &alc5625_ctrl_adc_l[0], + ARRAY_SIZE(alc5625_ctrl_adc_l)), + SND_SOC_DAPM_MIXER("Right Rec Mixer", + ALC5625_PWR_MANAG_ADD2, 0, 0, + &alc5625_ctrl_adc_r[0], + ARRAY_SIZE(alc5625_ctrl_adc_r)), + SND_SOC_DAPM_MIXER_E("Left HP Mixer", + ALC5625_PWR_MANAG_ADD2, 5, 0, + &alc5625_ctrl_hp_l[0], + ARRAY_SIZE(alc5625_ctrl_hp_l), + mixer_event, SND_SOC_DAPM_POST_REG), + SND_SOC_DAPM_MIXER_E("Right HP Mixer", + ALC5625_PWR_MANAG_ADD2, 4, 0, + &alc5625_ctrl_hp_r[0], + ARRAY_SIZE(alc5625_ctrl_hp_r), + mixer_event, SND_SOC_DAPM_POST_REG), + SND_SOC_DAPM_MIXER("MoNo Mixer", + ALC5625_PWR_MANAG_ADD2, 2, 0, + &alc5625_ctrl_mono[0], + ARRAY_SIZE(alc5625_ctrl_mono)), + SND_SOC_DAPM_MIXER("SPK Mixer", + ALC5625_PWR_MANAG_ADD2, 3, 0, + &alc5625_ctrl_spk[0], + ARRAY_SIZE(alc5625_ctrl_spk)), + SND_SOC_DAPM_MIXER("HP Mixer", + SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("DAC Mixer", + SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Line Mixer", + SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX("SPKOUT Mux", + SND_SOC_NOPM, 0, 0, + &alc5625_ctrl_spkmux), + SND_SOC_DAPM_MUX("HPLOUT Mux", + SND_SOC_NOPM, 0, 0, + &alc5625_ctrl_hplmux), + SND_SOC_DAPM_MUX("HPROUT Mux", + SND_SOC_NOPM, 0, 0, + &alc5625_ctrl_hprmux), + SND_SOC_DAPM_MUX("AUXOUT Mux", + SND_SOC_NOPM, 0, 0, + &alc5625_ctrl_auxmux), + SND_SOC_DAPM_PGA_E("SPKL Out PGA", + virtual_reg_FOR_MISC_FUNC, 4, 0, + NULL, 0, spk_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("SPKR Out PGA", + virtual_reg_FOR_MISC_FUNC, 5, 0, + NULL, 0, spk_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPL Out PGA", + virtual_reg_FOR_MISC_FUNC, 6, 0, + NULL, 0, hp_pga_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("HPR Out PGA", + virtual_reg_FOR_MISC_FUNC, 7, 0, + NULL, 0, hp_pga_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("AUX Out PGA", + ALC5625_PWR_MANAG_ADD3, 14, 0, + NULL, 0, aux_pga_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_ADC("Left ADC", "Left ADC HiFi Capture", + ALC5625_PWR_MANAG_ADD2, 7, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right ADC HiFi Capture", + ALC5625_PWR_MANAG_ADD2, 6, 0), + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("SPKR"), + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("AUX"), + SND_SOC_DAPM_MICBIAS("Mic1 Bias", + ALC5625_PWR_MANAG_ADD1, 3, 0), + SND_SOC_DAPM_MICBIAS("Mic2 Bias", + ALC5625_PWR_MANAG_ADD1, 2, 0), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + + /* Input PGA */ + {"Left LineIn PGA", NULL, "Left LineIn"}, + {"Right LineIn PGA", NULL, "Right LineIn"}, + {"Phone PGA", NULL, "Phone"}, + {"Mic1 Boost", NULL, "Mic1"}, + {"Mic2 Boost", NULL, "Mic2"}, + {"Mic1 PGA", NULL, "Mic1"}, + {"Mic2 PGA", NULL, "Mic2"}, + {"VoDAC PGA", NULL, "Voice DAC"}, + + /* Left ADC mixer */ + {"Left Rec Mixer", "LineIn Capture Switch", "Left LineIn"}, + {"Left Rec Mixer", "Phone Capture Switch", "Phone"}, + {"Left Rec Mixer", "Mic1 Capture Switch", "Mic1 Boost"}, + {"Left Rec Mixer", "Mic2 Capture Switch", "Mic2 Boost"}, + {"Left Rec Mixer", "HP Mixer Capture Switch", "Left HP Mixer"}, + {"Left Rec Mixer", "SPK Mixer Capture Switch", "SPK Mixer"}, + {"Left Rec Mixer", "MoNo Mixer Capture Switch", "MoNo Mixer"}, + + /* Right ADC Mixer */ + {"Right Rec Mixer", "LineIn Capture Switch", "Right LineIn"}, + {"Right Rec Mixer", "Phone Capture Switch", "Phone"}, + {"Right Rec Mixer", "Mic1 Capture Switch", "Mic1 Boost"}, + {"Right Rec Mixer", "Mic2 Capture Switch", "Mic2 Boost"}, + {"Right Rec Mixer", "HP Mixer Capture Switch", "Right HP Mixer"}, + {"Right Rec Mixer", "SPK Mixer Capture Switch", "SPK Mixer"}, + {"Right Rec Mixer", "MoNo Mixer Capture Switch", "MoNo Mixer"}, + + /* HPL mixer */ + {"Left HP Mixer", "ADC Playback Switch", "Left Rec Mixer"}, + {"Left HP Mixer", "LineIn Playback Switch", "Left LineIn PGA"}, + {"Left HP Mixer", "Phone Playback Switch", "Phone PGA"}, + {"Left HP Mixer", "Mic1 Playback Switch", "Mic1 PGA"}, + {"Left HP Mixer", "Mic2 Playback Switch", "Mic2 PGA"}, + {"Left HP Mixer", "HIFI DAC Playback Switch", "Left DAC"}, + {"Left HP Mixer", "Voice DAC Playback Switch", "VoDAC PGA"}, + + /* HPR mixer */ + {"Right HP Mixer", "ADC Playback Switch", "Right Rec Mixer"}, + {"Right HP Mixer", "LineIn Playback Switch", "Right LineIn PGA"}, + {"Right HP Mixer", "HIFI DAC Playback Switch", "Right DAC"}, + {"Right HP Mixer", "Phone Playback Switch", "Phone PGA"}, + {"Right HP Mixer", "Mic1 Playback Switch", "Mic1 PGA"}, + {"Right HP Mixer", "Mic2 Playback Switch", "Mic2 PGA"}, + {"Right HP Mixer", "Voice DAC Playback Switch", "VoDAC PGA"}, + + /* DAC Mixer */ + {"DAC Mixer", NULL, "Left DAC"}, + {"DAC Mixer", NULL, "Right DAC"}, + + /* line mixer */ + {"Line Mixer", NULL, "Left LineIn PGA"}, + {"Line Mixer", NULL, "Right LineIn PGA"}, + + /* spk mixer */ + {"SPK Mixer", "Line Mixer Playback Switch", "Line Mixer"}, + {"SPK Mixer", "Phone Playback Switch", "Phone PGA"}, + {"SPK Mixer", "Mic1 Playback Switch", "Mic1 PGA"}, + {"SPK Mixer", "Mic2 Playback Switch", "Mic2 PGA"}, + {"SPK Mixer", "DAC Mixer Playback Switch", "DAC Mixer"}, + {"SPK Mixer", "Voice DAC Playback Switch", "VoDAC PGA"}, + + /* mono mixer */ + {"MoNo Mixer", "Line Mixer Playback Switch", "Line Mixer"}, + {"MoNo Mixer", "ADCL Playback Switch", "Left Rec Mixer"}, + {"MoNo Mixer", "ADCR Playback Switch", "Right Rec Mixer"}, + {"MoNo Mixer", "Mic1 Playback Switch", "Mic1 PGA"}, + {"MoNo Mixer", "Mic2 Playback Switch", "Mic2 PGA"}, + {"MoNo Mixer", "DAC Mixer Playback Switch", "DAC Mixer"}, + {"MoNo Mixer", "Voice DAC Playback Switch", "VoDAC PGA"}, + + /* hp mixer */ + {"HP Mixer", NULL, "Left HP Mixer"}, + {"HP Mixer", NULL, "Right HP Mixer"}, + + /* spkout mux */ + {"SPKOUT Mux", "HP Mixer", "HP Mixer"}, + {"SPKOUT Mux", "SPK Mixer", "SPK Mixer"}, + {"SPKOUT Mux", "Mono Mixer", "MoNo Mixer"}, + + /* hpl out mux */ + {"HPLOUT Mux", "HPL Mixer", "Left HP Mixer"}, + + /* hpr out mux */ + {"HPROUT Mux", "HPR Mixer", "Right HP Mixer"}, + + /* aux out mux */ + {"AUXOUT Mux", "HP Mixer", "HP Mixer"}, + {"AUXOUT Mux", "SPK Mixer", "SPK Mixer"}, + {"SPKOUT Mux", "Mono Mixer", "MoNo Mixer"}, + + /* spkl out pga */ + {"SPKL Out PGA", NULL, "SPKOUT Mux"}, + + /* spkr out pga */ + {"SPKR Out PGA", NULL, "SPKOUT Mux"}, + + /* hpl out pga */ + {"HPL Out PGA", NULL, "HPLOUT Mux"}, + + /* hpr out pga */ + {"HPR Out PGA", NULL, "HPROUT Mux"}, + + /* aux out pga */ + {"AUX Out PGA", NULL, "AUXOUT Mux"}, + + /* left adc */ + {"Left ADC", NULL, "Left Rec Mixer"}, + + /* right adc */ + {"Right ADC", NULL, "Right Rec Mixer"}, + + /* output */ + {"SPKL", NULL, "SPKL Out PGA"}, + {"SPKR", NULL, "SPKR Out PGA"}, + {"HPL", NULL, "HPL Out PGA"}, + {"HPR", NULL, "HPR Out PGA"}, + {"AUX", NULL, "AUX Out PGA"}, +}; + +static int alc5625_add_widgets(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + ret = snd_soc_dapm_new_controls(dapm, alc5625_dapm_widgets, + ARRAY_SIZE(alc5625_dapm_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); + if (ret) + return ret; + + return 0; +} + +struct _pll_div { + u32 pll_in; + u32 pll_out; + u16 regvalue; +}; + +/* + * watch out! + * our codec support you to select different source as pll input, + * but if you use both of the I2S audio interface and pcm interface + * instantially. The two DAI must have the same pll setting params, + * so you have to offer the same pll input, and set our codec's sysclk + * the same one, we suggest 24576000. + */ +static const struct _pll_div codec_master_pll1_div[] = { + { 2048000, 8192000, 0x0ea0}, + { 3686400, 8192000, 0x4e27}, + {12000000, 8192000, 0x456b}, + {13000000, 8192000, 0x495f}, + {13100000, 8192000, 0x0320}, + { 2048000, 11289600, 0xf637}, + { 3686400, 11289600, 0x2f22}, + {12000000, 11289600, 0x3e2f}, + {13000000, 11289600, 0x4d5b}, + {13100000, 11289600, 0x363b}, + { 2048000, 16384000, 0x1ea0}, + { 3686400, 16384000, 0x9e27}, + {12000000, 16384000, 0x452b}, + {13000000, 16384000, 0x542f}, + {13100000, 16384000, 0x03a0}, + { 2048000, 16934400, 0xe625}, + { 3686400, 16934400, 0x9126}, + {12000000, 16934400, 0x4d2c}, + {13000000, 16934400, 0x742f}, + {13100000, 16934400, 0x3c27}, + { 2048000, 22579200, 0x2aa0}, + { 3686400, 22579200, 0x2f20}, + {12000000, 22579200, 0x7e2f}, + {13000000, 22579200, 0x742f}, + {13100000, 22579200, 0x3c27}, + { 2048000, 24576000, 0x2ea0}, + { 3686400, 24576000, 0xee27}, + {12000000, 24576000, 0x2915}, + {13000000, 24576000, 0x772e}, + {13100000, 24576000, 0x0d20}, + {26000000, 24576000, 0x2027}, + {26000000, 22579200, 0x392f}, + {24576000, 22579200, 0x0921}, + {24576000, 24576000, 0x02a0}, +}; + +static const struct _pll_div codec_bclk_pll1_div[] = { + { 256000, 4096000, 0x3ea0}, + { 352800, 5644800, 0x3ea0}, + { 512000, 8192000, 0x3ea0}, + { 705600, 11289600, 0x3ea0}, + {1024000, 16384000, 0x3ea0}, + {1411200, 22579200, 0x3ea0}, + {1536000, 24576000, 0x3ea0}, + {2048000, 16384000, 0x1ea0}, + {2822400, 22579200, 0x1ea0}, + {3072000, 24576000, 0x1ea0}, + { 705600, 11289600, 0x3ea0}, + { 705600, 8467200, 0x3ab0}, +}; + +static const struct _pll_div codec_vbclk_pll1_div[] = { + { 256000, 4096000, 0x3ea0}, + { 352800, 5644800, 0x3ea0}, + { 512000, 8192000, 0x3ea0}, + { 705600, 11289600, 0x3ea0}, + {1024000, 16384000, 0x3ea0}, + {1411200, 22579200, 0x3ea0}, + {1536000, 24576000, 0x3ea0}, + {2048000, 16384000, 0x1ea0}, + {2822400, 22579200, 0x1ea0}, + {3072000, 24576000, 0x1ea0}, + { 705600, 11289600, 0x3ea0}, + { 705600, 8467200, 0x3ab0}, +}; + +struct _coeff_div_stereo { + unsigned int mclk; + unsigned int rate; + unsigned int reg60; + unsigned int reg62; +}; + +struct _coeff_div_voice { + unsigned int mclk; + unsigned int rate; + unsigned int reg64; +}; + +static const struct _coeff_div_stereo coeff_div_stereo[] = { + /* + * bclk is config to 32fs, if codec is choose to + * be slave mode , input bclk should be 32*fs + */ + {24576000, 48000, 0x3174, 0x1010}, + {12288000, 48000, 0x1174, 0x0000}, + {18432000, 48000, 0x2174, 0x1111}, + {36864000, 48000, 0x2274, 0x2020}, + {49152000, 48000, 0xf074, 0x3030}, + {24576000, 48000, 0x3172, 0x1010}, + {24576000, 8000, 0xB274, 0x2424}, + {24576000, 16000, 0xB174, 0x2222}, + {24576000, 32000, 0xB074, 0x2121}, + {22579200, 11025, 0X3374, 0x1414}, + {22579200, 22050, 0X3274, 0x1212}, + {22579200, 44100, 0X3174, 0x1010}, + {0, 0, 0, 0}, +}; + +static const struct _coeff_div_voice coeff_div_voice[] = { + /* + * bclk is config to 32fs, if codec is choose to be slave mode, + * input bclk should be 32*fs + */ + {24576000, 16000, 0x2622}, + {24576000, 8000, 0x2824}, + {0, 0, 0}, +}; + +static int get_coeff(unsigned int mclk, unsigned int rate, int mode) +{ + int i; + + if (!mode) { + for (i = 0; i < ARRAY_SIZE(coeff_div_stereo); i++) { + if ((coeff_div_stereo[i].rate == rate) && + (coeff_div_stereo[i].mclk == mclk)) + return i; + } + } else { + for (i = 0; i < ARRAY_SIZE(coeff_div_voice); i++) { + if ((coeff_div_voice[i].rate == rate) && + (coeff_div_voice[i].mclk == mclk)) + return i; + } + } + + return -EINVAL; + printk(KERN_ERR "can't find a matched mclk and rate in %s\n", + (mode ? "coeff_div_voice[]" : "coeff_div_audio[]")); +} + +static int alc5625_codec_set_dai_pll(struct snd_soc_dai *codec_dai, + int pll_id, int source, + unsigned int freq_in, + unsigned int freq_out) +{ + int i; + int pll_src_regval = 0; + struct snd_soc_codec *codec = codec_dai->codec; + const struct _pll_div *codec_pll_div = NULL; + int pll_div_count = 0; + + if (pll_id < ALC5625_PLL1_FROM_MCLK || pll_id > ALC5625_PLL1_FROM_VBCLK) + return -EINVAL; + + if (!freq_in || !freq_out) + return 0; + + switch (pll_id) { + case ALC5625_PLL1_FROM_MCLK: + codec_pll_div = codec_master_pll1_div; + pll_div_count = ARRAY_SIZE(codec_master_pll1_div); + break; + case ALC5625_PLL1_FROM_BCLK: + codec_pll_div = codec_bclk_pll1_div; + pll_div_count = ARRAY_SIZE(codec_bclk_pll1_div); + pll_src_regval = 0x2000; + break; + case ALC5625_PLL1_FROM_VBCLK: + codec_pll_div = codec_vbclk_pll1_div; + pll_div_count = ARRAY_SIZE(codec_vbclk_pll1_div); + pll_src_regval = 0x3000; + default: + return -EINVAL; + } + + for (i = 0; i < pll_div_count; i++) + if ((freq_in == codec_pll_div[i].pll_in) && + (freq_out == codec_pll_div[i].pll_out)) { + alc5625_write(codec, + ALC5625_GEN_CTRL_REG2, + pll_src_regval); + + /* set pll code */ + alc5625_write(codec, + ALC5625_PLL_CTRL, + codec_pll_div[i].regvalue); + + /* enable pll power */ + alc5625_write_mask(codec, + ALC5625_PWR_MANAG_ADD2, + 0x8000, 0x8000); + + alc5625_write_mask(codec, + ALC5625_GEN_CTRL_REG1, + 0x8000, 0x8000); + + return 0; + } + + return -EINVAL; +} + +static int alc5625_hifi_codec_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, + int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct alc5625_priv *alc5625 = snd_soc_codec_get_drvdata(codec); + + if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) { + alc5625->stereo_sysclk = freq; + return 0; + } + + printk(KERN_ERR "unsupported sysclk freq %u for audio i2s\n", freq); + alc5625->stereo_sysclk = DEFAULT_SYSCLK; + + return 0; +} + +static int alc5625_voice_codec_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, + int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct alc5625_priv *alc5625 = snd_soc_codec_get_drvdata(codec); + + if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) { + alc5625->voice_sysclk = freq; + return 0; + } + + printk(KERN_ERR "unsupported sysclk freq %u for voice pcm\n", freq); + alc5625->voice_sysclk = DEFAULT_SYSCLK; + + return 0; +} + +static int alc5625_hifi_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct alc5625_priv *alc5625 = snd_soc_codec_get_drvdata(codec); + unsigned int iface; + + int rate = params_rate(params); + int coeff = get_coeff(alc5625->stereo_sysclk, rate, 0); + + iface = alc5625_read(codec, ALC5625_MAIN_SDP_CTRL) & 0xfff3; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + /* Nothing to be done */ + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface |= 0x0004; + break; + case SNDRV_PCM_FORMAT_S24_LE: + iface |= 0x0008; + break; + case SNDRV_PCM_FORMAT_S8: + iface |= 0x000c; + } + + alc5625_write(codec, ALC5625_MAIN_SDP_CTRL, iface); + + /* power i2s and dac ref */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, + 0xc801, 0xc801); + if (coeff >= 0) { + alc5625_write(codec, ALC5625_STEREO_DAC_CLK_CTRL1, + coeff_div_stereo[coeff].reg60); + alc5625_write(codec, ALC5625_STEREO_DAC_CLK_CTRL2, + coeff_div_stereo[coeff].reg62); + } + + return 0; +} + +static int alc5625_voice_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct alc5625_priv *alc5625 = snd_soc_codec_get_drvdata(codec); + unsigned int iface; + int rate = params_rate(params); + int coeff = get_coeff(alc5625->voice_sysclk, rate, 1); + + iface = alc5625_read(codec, ALC5625_EXTEND_SDP_CTRL) & 0xfff3; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + /* Nothing to be done */ + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface |= 0x0004; + break; + case SNDRV_PCM_FORMAT_S24_LE: + iface |= 0x0008; + break; + case SNDRV_PCM_FORMAT_S8: + iface |= 0x000c; + } + + /* power i2s and dac ref */ + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, + 0x0801, 0x0801); + alc5625_write(codec, ALC5625_EXTEND_SDP_CTRL, iface); + if (coeff >= 0) + alc5625_write(codec, ALC5625_VOICE_DAC_PCMCLK_CTRL1, + coeff_div_voice[coeff].reg64); + + return 0; +} + +static int alc5625_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + + struct snd_soc_codec *codec = codec_dai->codec; + u16 iface = 0; + + /* set master/slave interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface = 0x0000; + break; + case SND_SOC_DAIFMT_CBS_CFS: + iface = 0x8000; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + /* Nothing to be done */ + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x0003; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + /* Nothing to be done */ + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0080; + break; + default: + return -EINVAL; + } + + alc5625_write(codec, ALC5625_MAIN_SDP_CTRL, iface); + return 0; +} + +static int alc5625_voice_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + int iface; + + /*set slave/master mode*/ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface = 0x0000; + break; + case SND_SOC_DAIFMT_CBS_CFS: + iface = 0x4000; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + /* Nothing to be done */ + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x0003; + break; + default: + return -EINVAL; + } + + /*clock inversion*/ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + /* Nothing to be done */ + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0080; + break; + default: + return -EINVAL; + } + + iface |= 0x8000; /* enable vopcm */ + alc5625_write(codec, ALC5625_EXTEND_SDP_CTRL, iface); + return 0; +} + +static int alc5625_hifi_codec_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + + if (mute) + alc5625_write_mask(codec, ALC5625_STEREO_DAC_VOL, + 0x8080, 0x8080); + else + alc5625_write_mask(codec, ALC5625_STEREO_DAC_VOL, + 0x0000, 0x8080); + return 0; +} + +static int alc5625_voice_codec_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + + if (mute) + alc5625_write_mask(codec, ALC5625_VOICE_DAC_OUT_VOL, + 0x1000, 0x1000); + else + alc5625_write_mask(codec, ALC5625_VOICE_DAC_OUT_VOL, + 0x0000, 0x1000); + return 0; +} + +static int alc5625_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + alc5625_write(codec, ALC5625_PD_CTRL_STAT, 0x0000); + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD2, + 0x2000, 0x2000); + alc5625_write_mask(codec, ALC5625_PWR_MANAG_ADD1, + 0x000e, 0x000e); + break; + case SND_SOC_BIAS_STANDBY: + break; + case SND_SOC_BIAS_OFF: + alc5625_write_mask(codec, + ALC5625_HP_OUT_VOL, 0x8080, 0x8080); /* mute hp */ + alc5625_write_mask(codec, ALC5625_SPK_OUT_VOL, + 0x8080, 0x8080); /* mute spk */ + alc5625_write(codec, ALC5625_PWR_MANAG_ADD3, + 0x0000); /* power off all bit */ + alc5625_write(codec, ALC5625_PWR_MANAG_ADD1, + 0x0000); /* power off all bit */ + alc5625_write(codec, ALC5625_PWR_MANAG_ADD2, + 0x0000); /* power off all bit */ + break; + } + codec->dapm.bias_level = level; + return 0; +} + + +#define ALC5625_STEREO_RATES SNDRV_PCM_RATE_8000_48000 + +#define ALC5626_VOICE_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000) + +#define ALC5625_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S8) + +static struct snd_soc_dai_ops alc5625_dai_ops_hifi = { + + .hw_params = alc5625_hifi_pcm_hw_params, + .set_fmt = alc5625_hifi_codec_set_dai_fmt, + .set_pll = alc5625_codec_set_dai_pll, + .set_sysclk = alc5625_hifi_codec_set_dai_sysclk, + .digital_mute = alc5625_hifi_codec_mute, +}; + +static struct snd_soc_dai_ops alc5625_dai_ops_voice = { + + .hw_params = alc5625_voice_pcm_hw_params, + .set_fmt = alc5625_voice_codec_set_dai_fmt, + .set_pll = alc5625_codec_set_dai_pll, + .set_sysclk = alc5625_voice_codec_set_dai_sysclk, + .digital_mute = alc5625_voice_codec_mute, +}; + +static struct snd_soc_dai_driver alc5625_dai[] = { + { + .name = "alc5625-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 2, + .channels_max = 2, + .rates = ALC5625_STEREO_RATES, + .formats = ALC5625_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = ALC5625_STEREO_RATES, + .formats = ALC5625_FORMATS, + }, + .ops = &alc5625_dai_ops_hifi, + }, + + /* voice codec dai */ + { + .name = "ALC5625 Voice", + .id = 1, + .playback = { + .stream_name = "Voice Playback", + .channels_min = 1, + .channels_max = 1, + .rates = ALC5626_VOICE_RATES, + .formats = ALC5625_FORMATS, + }, + .capture = { + .stream_name = "Voice Capture", + .channels_min = 1, + .channels_max = 1, + .rates = ALC5626_VOICE_RATES, + .formats = ALC5625_FORMATS, + }, + + .ops = &alc5625_dai_ops_voice, + + }, +}; + +static void alc5625_work(struct work_struct *work) +{ + struct snd_soc_codec *codec = + container_of(work, struct snd_soc_codec,\ + dapm.delayed_work.work); + alc5625_set_bias_level(codec, codec->dapm.bias_level); +} + + +static int alc5625_codec_init(struct snd_soc_codec *codec) +{ + + int ret = 0; + + codec->read = alc5625_read; + codec->write = alc5625_write; + codec->hw_write = (hw_write_t)i2c_master_send; + codec->num_dai = 2; + codec->reg_cache = kmemdup(alc5625_reg, sizeof(alc5625_reg), + GFP_KERNEL); + if (codec->reg_cache == NULL) + return -ENOMEM; + + alc5625_reset(codec); + + alc5625_write(codec, ALC5625_PD_CTRL_STAT, 0); + alc5625_write(codec, ALC5625_PWR_MANAG_ADD1, PWR_MAIN_BIAS); + alc5625_write(codec, ALC5625_PWR_MANAG_ADD2, PWR_MIXER_VREF); + alc5625_reg_init(codec); + alc5625_set_bias_level(codec, SND_SOC_BIAS_PREPARE); + codec->dapm.bias_level = SND_SOC_BIAS_STANDBY; + schedule_delayed_work(&codec->dapm.delayed_work, msecs_to_jiffies(80)); + + ret = snd_soc_add_codec_controls(codec, alc5625_snd_ctrls, + ARRAY_SIZE(alc5625_snd_ctrls)); + if (ret) + return ret; + alc5625_add_widgets(codec); + if (ret) + return ret; + + return 0; +} + +#ifdef CONFIG_PM +static int alc5625_suspend(struct snd_soc_codec *codec) +{ + alc5625_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int alc5625_resume(struct snd_soc_codec *codec) +{ + alc5625_reset(codec); + alc5625_write(codec, ALC5625_PD_CTRL_STAT, 0); + alc5625_write(codec, ALC5625_PWR_MANAG_ADD1, PWR_MAIN_BIAS); + alc5625_write(codec, ALC5625_PWR_MANAG_ADD2, PWR_MIXER_VREF); + alc5625_reg_init(codec); + + /* charge alc5625 caps */ + if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) { + alc5625_set_bias_level(codec, SND_SOC_BIAS_PREPARE); + codec->dapm.bias_level = SND_SOC_BIAS_ON; + schedule_delayed_work(&codec->dapm.delayed_work, + msecs_to_jiffies(100)); + } + + return 0; +} +#else +#define alc5625_suspend NULL +#define alc5625_resume NULL +#endif + +static int alc5625_probe(struct snd_soc_codec *codec) +{ + struct alc5625_priv *alc5625 = snd_soc_codec_get_drvdata(codec); + int ret; + + codec->control_data = alc5625->regmap; + ret = snd_soc_codec_set_cache_io(codec, 8, 16, alc5625->control_type); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + mutex_init(&codec->mutex); + INIT_DELAYED_WORK(&codec->dapm.delayed_work, alc5625_work); + + ret = alc5625_codec_init(codec); + + return ret; +} + +static int run_delayed_work(struct delayed_work *dwork) +{ + int ret; + + /* cancel any work waiting to be queued. */ + ret = cancel_delayed_work(dwork); + + /* if there was any work waiting then we run it now and + * wait for it's completion */ + if (ret) { + schedule_delayed_work(dwork, 0); + flush_scheduled_work(); + } + return ret; +} + +static int alc5625_remove(struct snd_soc_codec *codec) +{ + if (codec->control_data) + alc5625_set_bias_level(codec, SND_SOC_BIAS_OFF); + run_delayed_work(&codec->dapm.delayed_work); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_alc5625 = { + .probe = alc5625_probe, + .remove = alc5625_remove, + .suspend = alc5625_suspend, + .resume = alc5625_resume, + .read = alc5625_read, + .write = alc5625_write, + .set_bias_level = alc5625_set_bias_level, + .reg_cache_size = ARRAY_SIZE(alc5625_reg)*2, + .reg_cache_default = alc5625_reg, + .reg_word_size = 2, +}; + +static const struct regmap_config alc5625_i2c_regmap_config = { + .val_bits = 16, + .reg_bits = 8, +}; + + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static __devinit int alc5625_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct alc5625_priv *alc5625; + int ret; + + alc5625 = kzalloc(sizeof(struct alc5625_priv), GFP_KERNEL); + if (alc5625 == NULL) + return -ENOMEM; + + alc5625->regmap = regmap_init_i2c(i2c, &alc5625_i2c_regmap_config); + if (IS_ERR(alc5625->regmap)) { + ret = PTR_ERR(alc5625->regmap); + goto err_free; + } + + i2c_set_clientdata(i2c, alc5625); + alc5625->control_data = i2c; + alc5625->control_type = SND_SOC_REGMAP; + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_alc5625, + alc5625_dai, ARRAY_SIZE(alc5625_dai)); + + if (ret < 0) + goto err_regmap; + + return ret; + +err_regmap: + regmap_exit(alc5625->regmap); +err_free: + if (ret < 0) + kfree(alc5625); + return ret; +} + +static __devexit int alc5625_i2c_remove(struct i2c_client *client) +{ + struct alc5625_priv *alc5625 = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&client->dev); + regmap_exit(alc5625->regmap); + kfree(i2c_get_clientdata(client)); + return 0; +} + +static const struct i2c_device_id alc5625_i2c_id[] = { + { "alc5625", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, alc5625_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id alc5625_of_match[] = { + { .compatible = "realtek,alc5625", }, + { } +}; +MODULE_DEVICE_TABLE(of, alc5625_of_match); +#endif + +static struct i2c_driver alc5625_i2c_driver = { + .driver = { + .name = "alc5625-codec", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(alc5625_of_match), + }, + .probe = alc5625_i2c_probe, + .remove = __devexit_p(alc5625_i2c_remove), + .id_table = alc5625_i2c_id, +}; +#endif + +module_i2c_driver(alc5625_i2c_driver); + +MODULE_DESCRIPTION("ASoC ALC5625 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/alc5625/alc5625.h b/drivers/staging/alc5625/alc5625.h new file mode 100644 index 000000000000..5e02a36d91ed --- /dev/null +++ b/drivers/staging/alc5625/alc5625.h @@ -0,0 +1,865 @@ +/* + * alc5625.h -- Header file for ALC5625 ALSA SoC Audio driver + * + * Copyright (C) 2011 Insignal Co., Ltd. + * + * Author: Pan<pan@insginal.co.kr> + * + * 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 _ALC5625_H +#define _ALC5625_H + +#define ALC5625_RESET 0X00 /* RESET CODEC TO DEFAULT */ +#define ALC5625_SPK_OUT_VOL 0X02 /* SPEAKER OUT VOLUME */ +#define ALC5625_HP_OUT_VOL 0X04 /* HEADPHONE OUTPUT VOLUME */ +#define ALC5625_AUX_OUT_VOL 0X06 /* AUXOUT VOLUME */ +#define ALC5625_PHONEIN_VOL 0X08 /* PHONE INPUT VOLUME */ +#define ALC5625_LINE_IN_VOL 0X0A /* LINE IN VOLUME */ +#define ALC5625_STEREO_DAC_VOL 0X0C /* STEREO DAC VOLUME */ +#define ALC5625_MIC_VOL 0X0E /* MICROPHONE VOLUME */ + +/* STEREO DAC AND MIC ROUTING CONTROL */ +#define ALC5625_DAC_AND_MIC_CTRL 0X10 +#define ALC5625_ADC_REC_GAIN 0X12 /* ADC RECORD GAIN */ +#define ALC5625_ADC_REC_MIXER 0X14 /* ADC RECORD MIXER CONTROL */ +#define ALC5625_VOICE_DAC_OUT_VOL 0X18 /* VOICE DAC OUTPUT VOLUME */ +#define ALC5625_VODSP_PDM_CTL 0X1A /* VODSP & PDM CONTROL */ +#define ALC5625_OUTPUT_MIXER_CTRL 0X1C /* OUTPUT MIXER CONTROL */ +#define ALC5625_VODSP_CTL 0X1E /* VODSP CONTROL */ +#define ALC5625_MIC_CTRL 0X22 /* MICROPHONE CONTROL */ +#define ALC5625_DMIC_CTRL 0x24 +#define ALC5625_PD_CTRL_STAT 0X26 /* POWER DOWN CONTROL/STATUS */ + +/* STEREO DAC,VOICE DAC,STEREO ADC FUNCTION SELECT */ +#define ALC5625_DAC_ADC_VODAC_FUN_SEL 0X2E + +/* MAIN SERIAL DATA PORT CONTROL(STEREO I2S) */ +#define ALC5625_MAIN_SDP_CTRL 0X34 + +/* EXTEND SERIAL DATA PORT CONTROL(VOICE I2S/PCM) */ +#define ALC5625_EXTEND_SDP_CTRL 0X36 +#define ALC5625_PWR_MANAG_ADD1 0X3A /* POWER MANAGMENT ADDITION 1 */ +#define ALC5625_PWR_MANAG_ADD2 0X3C /* POWER MANAGMENT ADDITION 2 */ +#define ALC5625_PWR_MANAG_ADD3 0X3E /* POWER MANAGMENT ADDITION 3 */ + +/* GENERAL PURPOSE CONTROL REGISTER 1 */ +#define ALC5625_GEN_CTRL_REG1 0X40 + +/* GENERAL PURPOSE CONTROL REGISTER 2 */ +#define ALC5625_GEN_CTRL_REG2 0X42 +#define ALC5625_PLL_CTRL 0X44 /* PLL1 CONTROL */ +#define ALC5625_PLL2_CTRL 0X46 /* PLL2 CONTROL */ +#define ALC5625_LDO_CTRL 0X48 /* LDO CONTROL */ +#define ALC5625_GPIO_PIN_CONFIG 0X4C /* GPIO PIN CONFIGURATION */ +#define ALC5625_GPIO_PIN_POLARITY 0X4E /* GPIO PIN POLARITY */ +#define ALC5625_GPIO_PIN_STICKY 0X50 /* GPIO PIN STICKY */ +#define ALC5625_GPIO_PIN_WAKEUP 0X52 /* GPIO PIN WAKE UP */ +#define ALC5625_GPIO_PIN_STATUS 0X54 /* GPIO PIN STATUS */ +#define ALC5625_GPIO_PIN_SHARING 0X56 /* GPIO PIN SHARING */ + +/* OVER TEMPERATURE AND CURRENT STATUS */ +#define ALC5625_OVER_TEMP_CURR_STATUS 0X58 + +/* SOFT VOLUME CONTROL SETTING */ +#define ALC5625_SOFT_VOL_CTRL 0X5A +#define ALC5625_GPIO_OUT_CTRL 0X5C /* GPIO OUTPUT PIN CONTRL */ +#define ALC5625_MISC_CTRL 0X5E /* MISC CONTROL */ +#define ALC5625_STEREO_DAC_CLK_CTRL1 0X60 /* STEREO DAC CLOCK CONTROL 1 */ +#define ALC5625_STEREO_DAC_CLK_CTRL2 0X62 /* STEREO DAC CLOCK CONTROL 2 */ + +/* VOICE/PCM DAC CLOCK CONTROL 1 */ +#define ALC5625_VOICE_DAC_PCMCLK_CTRL1 0X64 + +/* PSEDUEO STEREO /SPATIAL EFFECT BLOCK CONTROL */ +#define ALC5625_PSEDUEO_SPATIAL_CTRL 0X68 +#define ALC5625_PRIV_ADDR 0X6A /* PRIVATE ADDRESS */ +#define ALC5625_PRIV_DATA 0X6C /* PRIVATE DATA */ + +/* EQ CONTROL AND STATUS /ADC HPF CONTROL */ +#define ALC5625_EQ_CTRL_ADC_HPF 0X6E +#define ALC5625_VODSP_REG_ADDR 0x70 /* VODSP REGISTER ADDRESS */ +#define ALC5625_VODSP_REG_DATA 0x72 /* VODSP REGISTER DATA */ +#define ALC5625_VODSP_REG_CMD 0x74 /* VODSP REGISTER COMMAND */ + +/* Bit define of Codec Register */ +#define ALC_L_MUTE (0x1<<15) /* Mute Left Control */ + +/* Mute Left Zero-Cross Detector Control */ +#define ALC_L_ZC (0x1<<14) +#define ALC_R_MUTE (0x1<<7) /* Mute Right Control */ + +/* Mute Right Zero-Cross Detector Control */ +#define ALC_R_ZC (0x1<<6) + +/* Mute source to HP Mixer */ +#define ALC_M_HP_MIXER (0x1<<15) + +/* Mute source to Speaker Mixer */ +#define ALC_M_SPK_MIXER (0x1<<14) + +/* Mute source to Mono Mixer */ +#define ALC_M_MONO_MIXER (0x1<<13) + +/* Phone Input Volume(0x08) */ +/* Mute Phone In volume to HP mixer */ +#define M_PHONEIN_TO_HP_MIXER (0x1<<15) + +/* Mute Phone In volume to speaker mixer */ +#define M_PHONEIN_TO_SPK_MIXER (0x1<<14) + +/* Mic Routing Control(0x10) */ +/* Mute MIC1 to HP mixer */ +#define M_MIC1_TO_HP_MIXER (0x1<<15) + +/* Mute MiC1 to SPK mixer */ +#define M_MIC1_TO_SPK_MIXER (0x1<<14) + +/* Mute MIC1 to MONO mixer */ +#define M_MIC1_TO_MONO_MIXER (0x1<<13) + +/* Mute MIC2 to HP mixer */ +#define M_MIC2_TO_HP_MIXER (0x1<<11) + +/* Mute MiC2 to SPK mixer */ +#define M_MIC2_TO_SPK_MIXER (0x1<<10) + +/* Mute MIC2 to MONO mixer */ +#define M_MIC2_TO_MONO_MIXER (0x1<<9) + +/* Mute DAC to HP left mixer */ +#define M_DAC_TO_HPL_MIXER (0x1<<3) + +/* Mute DAC to HP right mixer */ +#define M_DAC_TO_HPR_MIXER (0x1<<2) + +/* Mute DAC to SPK mixer */ +#define M_DAC_TO_SPK_MIXER (0x1<<1) + +/* Mute DAC to MONO mixer */ +#define M_DAC_TO_MONO_MIXER (0x1<<0) + +/* ADC Record Gain(0x12) */ +/* Mute left of ADC to HP Mixer */ +#define M_ADC_L_TO_HP_MIXER (0x1<<15) + +/* Mute left of ADC to MONO Mixer */ +#define M_ADC_L_TO_MONO_MIXER (0x1<<14) + +/* ADC Zero-Cross Detector Control */ +#define ADC_L_ZC_DET (0x1<<13) + + /* ADC Record Gain Left channel Mask */ +#define ADC_L_GAIN_MASK (0x1f<<8) + +/* Mute right of ADC to HP Mixer */ +#define M_ADC_R_TO_HP_MIXER (0x1<<7) + +/* Mute right of ADC to MONO Mixer */ +#define M_ADC_R_TO_MONO_MIXER (0x1<<6) + +/* ADC Zero-Cross Detector Control */ +#define ADC_R_ZC_DET (0x1<<5) + +/* ADC Record Gain Right channel Mask */ +#define ADC_R_GAIN_MASK (0x1f<<0) + +/* Voice DAC Output Volume(0x18) */ +#define M_V_DAC_TO_HP_MIXER (0x1<<15) +#define M_V_DAC_TO_SPK_MIXER (0x1<<14) +#define M_V_DAC_TO_MONO_MIXER (0x1<<13) + +/* AEC & PDM Control(0x1A) */ +#define VODSP_SRC1_PWR (0x1<<15) /* Enable SRC1 Power */ +#define VODSP_SRC2_PWR (0x1<<13) /* Enable SRC2 Power */ + +#define VODSP_SRC2_S_SEL_MASK (0x1<<12) + +/* SRC2 source select AEC_TXDP */ +#define VODSP_SRC2_S_SEL_TXDP (0x0<<12) + +/* SRC2 source select AEC_TXDC */ +#define VODSP_SRC2_S_SEL_TXDC (0x1<<12) + +/* Enable AEC RXDP Power */ +#define VODSP_RXDP_PWR (0x1<<11) + +#define VODSP_RXDP_S_SEL_MASK (0x3<<9) + +/* AEC RxDP source select SRC1 Output */ +#define VODSP_RXDP_S_SEL_SRC1 (0x0<<9) + +/* AEC RxDP source select ADC Left to AEC Digital Path */ +#define VODSP_RXDP_S_SEL_ADCL (0x1<<9) + +/* AEC RxDP source select Voice to Stereo Digital Path */ +#define VODSP_RXDP_S_SEL_VOICE (0x2<<9) + +/* AEC RxDP source select ADC Right to AEC Digital Path */ +#define VODSP_RXDP_S_SEL_ADCR (0x3<<9) + +/* Enable AEC RXDC Power */ +#define VODSP_RXDC_PWR (0x1<<8) + +#define VOICE_PCM_S_SEL_MASK (0x1<<7) + +/* VSADC PCM interface source select ADC R */ +#define VOICE_PCM_S_SEL_ADC_R (0x0<<7) + +/* VSADC PCM interface source select AEC_TXDP */ +#define VOICE_PCM_S_SEL_AEC_TXDP (0x1<<7) + +#define REC_S_SEL_MASK (0x3<<4) + +/* Main Stereo Record I2S source select ADC L/R */ +#define REC_S_SEL_ADC (0x0<<4) + +/* Main Stereo Record I2S source select Voice to Stereo Digital Path */ +#define REC_S_SEL_VOICE (0x1<<4) + +/* Main Stereo Record I2S source select SRC2 */ +#define REC_S_SEL_SRC2 (0x2<<4) + +/*Output Mixer Control(0x1C) */ +#define SPKOUT_N_SOUR_MASK (0x3<<14) +#define SPKOUT_N_SOUR_LN (0x2<<14) +#define SPKOUT_N_SOUR_RP (0x1<<14) +#define SPKOUT_N_SOUR_RN (0x0<<14) + +#define SPKOUT_SEL_CLASS_D (0x1<<13) +#define SPKOUT_SEL_CLASS_AB (0x0<<13) + +#define SPK_CLASS_AB_S_AMP (0x0<<12) +#define SPK_CALSS_AB_W_AMP (0x1<<12) + +#define SPKOUT_INPUT_SEL_MASK (0x3<<10) +#define SPKOUT_INPUT_SEL_MONOMIXER (0x3<<10) +#define SPKOUT_INPUT_SEL_SPKMIXER (0x2<<10) +#define SPKOUT_INPUT_SEL_HPMIXER (0x1<<10) +#define SPKOUT_INPUT_SEL_VMID (0x0<<10) + +#define HPL_INPUT_SEL_HPLMIXER (0x1<<9) +#define HPR_INPUT_SEL_HPRMIXER (0x1<<8) + +#define AUXOUT_INPUT_SEL_MASK (0x3<<6) +#define AUXOUT_INPUT_SEL_MONOMIXER (0x3<<6) +#define AUXOUT_INPUT_SEL_SPKMIXER (0x2<<6) +#define AUXOUT_INPUT_SEL_HPMIXER (0x1<<6) +#define AUXOUT_INPUT_SEL_VMID (0x0<<6) + +/* Voice DSP Control(0x1E) */ +#define VODSP_SYSCLK_S_SEL_MASK (0x1<<15) +#define VODSP_SYSCLK_S_SEL_M_CLK (0x0<<15) +#define VODSP_SYSCLK_S_SEL_V_CLK (0x1<<15) + +#define VODSP_LRCK_SEL_MASK (0x1<<13) +#define VODSP_LRCK_SEL_8K (0x0<<13) +#define VODSP_LRCK_SEL_16K (0x1<<13) +#define VODSP_TEST_MODE_ENA (0x1<<3) +#define VODSP_NO_BP_MODE_ENA (0x1<<2) +#define VODSP_NO_PD_MODE_ENA (0x1<<1) +#define VODSP_NO_RST_MODE_ENA (0x1<<0) + +/* Micphone Control define(0x22) */ +#define MIC1 1 +#define MIC2 2 +#define MIC_BIAS_90_PRECNET_AVDD 1 +#define MIC_BIAS_75_PRECNET_AVDD 2 + +#define MIC1_BOOST_CONTROL_MASK (0x3<<10) +#define MIC1_BOOST_CONTROL_BYPASS (0x0<<10) +#define MIC1_BOOST_CONTROL_20DB (0x1<<10) +#define MIC1_BOOST_CONTROL_30DB (0x2<<10) +#define MIC1_BOOST_CONTROL_40DB (0x3<<10) + +#define MIC2_BOOST_CONTROL_MASK (0x3<<8) +#define MIC2_BOOST_CONTROL_BYPASS (0x0<<8) +#define MIC2_BOOST_CONTROL_20DB (0x1<<8) +#define MIC2_BOOST_CONTROL_30DB (0x2<<8) +#define MIC2_BOOST_CONTROL_40DB (0x3<<8) + +#define MIC1_BIAS_VOLT_CTRL_MASK (0x1<<5) +#define MIC1_BIAS_VOLT_CTRL_90P (0x0<<5) +#define MIC1_BIAS_VOLT_CTRL_75P (0x1<<5) + +#define MIC2_BIAS_VOLT_CTRL_MASK (0x1<<4) +#define MIC2_BIAS_VOLT_CTRL_90P (0x0<<4) +#define MIC2_BIAS_VOLT_CTRL_75P (0x1<<4) + +/* PowerDown control of register(0x26) */ +/* power management bits */ +/* write this bit to power down the Speaker Amplifier */ +#define ALC_PWR_PR7 (0x1<<15) + +/* write this bit to power down the Headphone Out and MonoOut */ +#define ALC_PWR_PR6 (0x1<<14) + +/* write this bit to power down the internal clock(without PLL) */ +#define ALC_PWR_PR5 (0x1<<13) + +/* write this bit to power down the mixer(vref/vrefout out off) */ +#define ALC_PWR_PR3 (0x1<<11) + +/* write this bit to power down the mixer(vref/vrefout still on) */ +#define ALC_PWR_PR2 (0x1<<10) + +/* write this bit to power down the dac */ +#define ALC_PWR_PR1 (0x1<<9) + +/* write this bit to power down the adc */ +#define ALC_PWR_PR0 (0x1<<8) + + +#define ALC_PWR_REF (0x1<<3) /* read only */ +#define ALC_PWR_ANL (0x1<<2) /* read only */ +#define ALC_PWR_DAC (0x1<<1) /* read only */ +#define ALC_PWR_ADC (0x1) /* read only */ + +/* Stereo DAC/Voice DAC/Stereo ADC function(0x2E) */ +#define DAC_FUNC_SEL_MASK (0x3<<12) +#define DAC_FUNC_SEL_DAC (0x0<<12) +#define DAC_FUNC_SEL_SRC2 (0x1<<12) +#define DAC_FUNC_SEL_VODSP_TXDP (0x2<<12) +#define DAC_FUNC_SEL_VODSP_TXDC (0x3<<12) + +#define VODAC_SOUR_SEL_MASK (0x7<<8) +#define VODAC_SOUR_SEL_VOICE (0x0<<8) +#define VODAC_SOUR_SEL_SRC2 (0x1<<8) +#define VODAC_SOUR_SEL_VODSP_TXDP (0x2<<8) +#define VODAC_SOUR_SEL_VODSP_TXDC (0x3<<8) + +#define ADCR_FUNC_SEL_MASK (0x3<<4) +#define ADCR_FUNC_SEL_ADC (0x0<<4) +#define ADCR_FUNC_SEL_VOADC (0x1<<4) +#define ADCR_FUNC_SEL_VODSP (0x2<<4) +#define ADCR_FUNC_SEL_PDM (0x3<<4) + +#define ADCL_FUNC_SEL_MASK (0x3<<0) +#define ADCL_FUNC_SEL_ADC (0x0<<0) +#define ADCL_FUNC_SEL_VODSP (0x1<<0) + +/* + * Main Serial Data Port Control(0x34) + * 0:Master mode 1:Slave mode + */ +#define MAIN_I2S_MODE_SEL (0x1<<15) + +/* 0:Disable,ADC and DAC use the same fs,1:Enable */ +#define MAIN_I2S_SADLRCK_CTRL (0x1<<14) + +/* 0:Normal SADLRCK/SDALRCK,1:Invert SADLRCK/SDALRCK */ +#define MAIN_I2S_PCM_MODE (0x1<<6) + +/* Data Length Selection */ +/* main i2s Data Length mask */ +#define MAIN_I2S_DL_MASK (0x3<<2) +#define MAIN_I2S_DL_16 (0x0<<2) /* 16 bits */ +#define MAIN_I2S_DL_20 (0x1<<2) /* 20 bits */ +#define MAIN_I2S_DL_24 (0x2<<2) /* 24 bits */ +#define MAIN_I2S_DL_32 (0x3<<2) /* 8 bits */ + +/* PCM Data Format Selection */ +/* main i2s Data Format mask */ +#define MAIN_I2S_DF_MASK (0x3) + +/* I2S FORMAT */ +#define MAIN_I2S_DF_I2S (0x0) + +/* LEFT JUSTIFIED format */ +#define MAIN_I2S_DF_LEFT (0x1) + +/* PCM Mode A */ +#define MAIN_I2S_DF_PCM_A (0x2) + +/* PCM Mode B */ +#define MAIN_I2S_DF_PCM_B (0x3) + +/* + * Extend Serial Data Port Control(0x36) + * Enable PCM interface on GPIO 1,3,4,5 + * 0:GPIO function, + * 1:Voice PCM interface + */ +#define EXT_I2S_FUNC_ENABLE (0x1<<15) + +/* 0:Master, 1:Slave */ +#define EXT_I2S_MODE_SEL (0x1<<14) + +/* 0:Disable, 1:Enable */ +#define EXT_I2S_AUTO_CLK_CTRL (0x1<<13) + +/* 0:Normal 1:Invert */ +#define EXT_I2S_BCLK_POLARITY (0x1<<7) + +/* 0:Normal VSLRCK,1:Invert VSLRCK */ +#define EXT_I2S_PCM_MODE (0x1<<6) +/* Data Length Slection */ +/* Extend i2s Data Length mask */ +#define EXT_I2S_DL_MASK (0x3<<2) +#define EXT_I2S_DL_32 (0x3<<2) /* 8 bits */ +#define EXT_I2S_DL_24 (0x2<<2) /* 24 bits */ +#define EXT_I2S_DL_20 (0x1<<2) /* 20 bits */ +#define EXT_I2S_DL_16 (0x0<<2) /* 16 bits */ + +/* Voice Data Format */ +/* Extend i2s Data Format mask */ +#define EXT_I2S_DF_MASK (0x3) + +/* I2S FORMAT */ +#define EXT_I2S_DF_I2S (0x0) + +/* LEFT JUSTIFIED format */ +#define EXT_I2S_DF_LEFT (0x1) + +/* PCM Mode A */ +#define EXT_I2S_DF_PCM_A (0x2) + +/* PCM Mode B */ +#define EXT_I2S_DF_PCM_B (0x3) + +/* Power managment addition 1 (0x3A),0:Disable,1:Enable */ +#define PWR_DAC_DF2SE_L (0x1<<15) +#define PWR_DAC_DF2SE_R (0x1<<14) +#define PWR_ZC_DET_PD (0x1<<13) +#define PWR_I2S_INTERFACE (0x1<<11) +#define PWR_AMP_POWER (0x1<<10) +#define PWR_HP_OUT_AMP (0x1<<9) +#define PWR_HP_OUT_ENH_AMP (0x1<<8) +#define PWR_VOICE_DF2SE (0x1<<7) +#define PWR_SOFTGEN_EN (0x1<<6) +#define PWR_MIC_BIAS1_DET (0x1<<5) +#define PWR_MIC_BIAS2_DET (0x1<<4) +#define PWR_MIC_BIAS1 (0x1<<3) +#define PWR_MIC_BIAS2 (0x1<<2) +#define PWR_MAIN_BIAS (0x1<<1) +#define PWR_DAC_REF (0x1) + +/* Power managment addition 2(0x3C),0:Disable,1:Enable */ +#define PWR_PLL1 (0x1<<15) +#define PWR_PLL2 (0x1<<14) +#define PWR_MIXER_VREF (0x1<<13) +#define PWR_TEMP_SENSOR (0x1<<12) +#define PWR_AUX_ADC (0x1<<11) +#define PWR_VOICE_CLOCK (0x1<<10) +#define PWR_L_DAC_CLK (0x1<<9) +#define PWR_R_DAC_CLK (0x1<<8) +#define PWR_L_ADC_CLK (0x1<<7) +#define PWR_R_ADC_CLK (0x1<<6) +#define PWR_L_HP_MIXER (0x1<<5) +#define PWR_R_HP_MIXER (0x1<<4) +#define PWR_SPK_MIXER (0x1<<3) +#define PWR_MONO_MIXER (0x1<<2) +#define PWR_L_ADC_REC_MIXER (0x1<<1) +#define PWR_R_ADC_REC_MIXER (0x1) + +/* Power managment addition 3(0x3E),0:Disable,1:Enable */ +#define PWR_OSC_EN (0x1<<15) +#define PWR_AUXOUT_VOL (0x1<<14) +#define PWR_SPK_OUT (0x1<<13) +#define PWR_SPK_OUT_N (0x1<<12) +#define PWR_HP_L_OUT_VOL (0x1<<11) +#define PWR_HP_R_OUT_VOL (0x1<<10) +#define PWR_VODSP_INTERFACE (0x1<<9) +#define PWR_I2C_FOR_VODSP (0x1<<8) +#define PWR_LINE_IN_L (0x1<<7) +#define PWR_LINE_IN_R (0x1<<6) +#define PWR_PHONE_VOL (0x1<<5) +#define PWR_PHONE_ADMIXER (0x1<<4) +#define PWR_MIC1_VOL_CTRL (0x1<<3) +#define PWR_MIC2_VOL_CTRL (0x1<<2) +#define PWR_MIC1_BOOST (0x1<<1) +#define PWR_MIC2_BOOST (0x1) + +/* General Purpose Control Register 1(0x40) */ +#define GP_CLK_FROM_PLL (0x1<<15) +#define GP_CLK_FROM_MCLK (0x0<<15) + +/* Enable DAC High Pass Filter */ +#define GP_DAC_HI_PA_ENA (0x1<<10) + +#define GP_EXTCLK_S_SEL_PLL2 (0x1<<6) +#define GP_EXTCLK_S_SEL_PLL1 (0x0<<6) + +#define GP_EXTCLK_DIR_SEL_OUTPUT (0x1<<5) +#define GP_EXTCLK_DIR_SEL_INTPUT (0x0<<5) + +#define GP_VOSYS_S_SEL_PLL2 (0x0<<4) +#define GP_VOSYS_S_SEL_EXTCLK (0x1<<4) + +#define GP_SPK_AMP_CTRL_MASK (0x7<<1) +#define GP_SPK_AMP_CTRL_RATIO_225 (0x0<<1) /* 2.25 Vdd */ +#define GP_SPK_AMP_CTRL_RATIO_200 (0x1<<1) /* 2.00 Vdd */ +#define GP_SPK_AMP_CTRL_RATIO_175 (0x2<<1) /* 1.75 Vdd */ +#define GP_SPK_AMP_CTRL_RATIO_150 (0x3<<1) /* 1.50 Vdd */ +#define GP_SPK_AMP_CTRL_RATIO_125 (0x4<<1) /* 1.25 Vdd */ +#define GP_SPK_AMP_CTRL_RATIO_100 (0x5<<1) /* 1.00 Vdd */ + +/* General Purpose Control Register 2(0x42) */ +#define GP2_PLL1_SOUR_SEL_MASK (0x3<<12) +#define GP2_PLL1_SOUR_SEL_MCLK (0x0<<12) +#define GP2_PLL1_SOUR_SEL_BCLK (0x2<<12) +#define GP2_PLL1_SOUR_SEL_VBCLK (0x3<<12) + +/* PLL Control(0x44) */ +#define PLL_M_CODE_MASK 0xF /* PLL M code mask */ +#define PLL_K_CODE_MASK (0x7<<4) /* PLL K code mask */ +#define PLL_BYPASS_N (0x1<<7) /* bypass PLL N code */ +#define PLL_N_CODE_MASK (0xFF<<8) /* PLL N code mask */ + +#define PLL_CTRL_M_VAL(m) ((m)&0xf) +#define PLL_CTRL_K_VAL(k) (((k)&0x7)<<4) +#define PLL_CTRL_N_VAL(n) (((n)&0xff)<<8) + +/* PLL2 CONTROL */ +#define PLL2_ENA (0x1<<15) +#define PLL_2_RATIO_8X (0x0) +#define PLL_2_RATIO_16X (0x1) + +/* LDO Control(0x48) */ +#define LDO_ENABLE (0x1<<15) + +#define LDO_OUT_VOL_CTRL_MASK (0xf<<0) +#define LDO_OUT_VOL_CTRL_1_55V (0xf<<0) +#define LDO_OUT_VOL_CTRL_1_50V (0xe<<0) +#define LDO_OUT_VOL_CTRL_1_45V (0xd<<0) +#define LDO_OUT_VOL_CTRL_1_40V (0xc<<0) +#define LDO_OUT_VOL_CTRL_1_35V (0xb<<0) +#define LDO_OUT_VOL_CTRL_1_30V (0xa<<0) +#define LDO_OUT_VOL_CTRL_1_25V (0x9<<0) +#define LDO_OUT_VOL_CTRL_1_20V (0x8<<0) +#define LDO_OUT_VOL_CTRL_1_15V (0x7<<0) +#define LDO_OUT_VOL_CTRL_1_05V (0x6<<0) +#define LDO_OUT_VOL_CTRL_1_00V (0x5<<0) +#define LDO_OUT_VOL_CTRL_0_95V (0x4<<0) +#define LDO_OUT_VOL_CTRL_0_90V (0x3<<0) +#define LDO_OUT_VOL_CTRL_0_85V (0x2<<0) +#define LDO_OUT_VOL_CTRL_0_80V (0x1<<0) +#define LDO_OUT_VOL_CTRL_0_75V (0x0<<0) + +/* GPIO Pin Configuration(0x4C) */ +#define GPIO_1 (0x1<<1) +#define GPIO_2 (0x1<<2) +#define GPIO_3 (0x1<<3) +#define GPIO_4 (0x1<<4) +#define GPIO_5 (0x1<<5) + +/* INTERRUPT CONTROL(0x5E) */ +#define DISABLE_FAST_VREG (0x1<<15) + +#define AVC_TARTGET_SEL_MASK (0x3<<12) +#define AVC_TARTGET_SEL_NONE (0x0<<12) +#define AVC_TARTGET_SEL_R (0x1<<12) +#define AVC_TARTGET_SEL_L (0x2<<12) +#define AVC_TARTGET_SEL_BOTH (0x3<<12) + +#define HP_DEPOP_MODE2_EN (0x1<<8) +#define HP_DEPOP_MODE1_EN (0x1<<9) +#define HP_L_M_UM_DEPOP_EN (0x1<<7) +#define HP_R_M_UM_DEPOP_EN (0x1<<6) +#define M_UM_DEPOP_EN (0x1<<5) + +/* Stereo DAC Clock Control 1(0x60) */ +#define STEREO_BCLK_DIV1_MASK (0xF<<12) +#define STEREO_BCLK_DIV1_1 (0x0<<12) +#define STEREO_BCLK_DIV1_2 (0x1<<12) +#define STEREO_BCLK_DIV1_3 (0x2<<12) +#define STEREO_BCLK_DIV1_4 (0x3<<12) +#define STEREO_BCLK_DIV1_5 (0x4<<12) +#define STEREO_BCLK_DIV1_6 (0x5<<12) +#define STEREO_BCLK_DIV1_7 (0x6<<12) +#define STEREO_BCLK_DIV1_8 (0x7<<12) +#define STEREO_BCLK_DIV1_9 (0x8<<12) +#define STEREO_BCLK_DIV1_10 (0x9<<12) +#define STEREO_BCLK_DIV1_11 (0xA<<12) +#define STEREO_BCLK_DIV1_12 (0xB<<12) +#define STEREO_BCLK_DIV1_13 (0xC<<12) +#define STEREO_BCLK_DIV1_14 (0xD<<12) +#define STEREO_BCLK_DIV1_15 (0xE<<12) +#define STEREO_BCLK_DIV1_16 (0xF<<12) + +#define STEREO_BCLK_DIV2_MASK (0x7<<8) +#define STEREO_BCLK_DIV2_2 (0x0<<8) +#define STEREO_BCLK_DIV2_4 (0x1<<8) +#define STEREO_BCLK_DIV2_8 (0x2<<8) +#define STEREO_BCLK_DIV2_16 (0x3<<8) +#define STEREO_BCLK_DIV2_32 (0x4<<8) + +#define STEREO_AD_LRCK_DIV1_MASK (0xF<<4) +#define STEREO_AD_LRCK_DIV1_1 (0x0<<4) +#define STEREO_AD_LRCK_DIV1_2 (0x1<<4) +#define STEREO_AD_LRCK_DIV1_3 (0x2<<4) +#define STEREO_AD_LRCK_DIV1_4 (0x3<<4) +#define STEREO_AD_LRCK_DIV1_5 (0x4<<4) +#define STEREO_AD_LRCK_DIV1_6 (0x5<<4) +#define STEREO_AD_LRCK_DIV1_7 (0x6<<4) +#define STEREO_AD_LRCK_DIV1_8 (0x7<<4) +#define STEREO_AD_LRCK_DIV1_9 (0x8<<4) +#define STEREO_AD_LRCK_DIV1_10 (0x9<<4) +#define STEREO_AD_LRCK_DIV1_11 (0xA<<4) +#define STEREO_AD_LRCK_DIV1_12 (0xB<<4) +#define STEREO_AD_LRCK_DIV1_13 (0xC<<4) +#define STEREO_AD_LRCK_DIV1_14 (0xD<<4) +#define STEREO_AD_LRCK_DIV1_15 (0xE<<4) +#define STEREO_AD_LRCK_DIV1_16 (0xF<<4) + +#define STEREO_AD_LRCK_DIV2_MASK (0x7<<1) +#define STEREO_AD_LRCK_DIV2_2 (0x0<<1) +#define STEREO_AD_LRCK_DIV2_4 (0x1<<1) +#define STEREO_AD_LRCK_DIV2_8 (0x2<<1) +#define STEREO_AD_LRCK_DIV2_16 (0x3<<1) +#define STEREO_AD_LRCK_DIV2_32 (0x4<<1) + +#define STEREO_DA_LRCK_DIV_MASK (1) +#define STEREO_DA_LRCK_DIV_32 (0) +#define STEREO_DA_LRCK_DIV_64 (1) + +/* Stereo DAC Clock Control 2(0x62) */ +#define STEREO_DA_FILTER_DIV1_MASK (0xF<<12) +#define STEREO_DA_FILTER_DIV1_1 (0x0<<12) +#define STEREO_DA_FILTER_DIV1_2 (0x1<<12) +#define STEREO_DA_FILTER_DIV1_3 (0x2<<12) +#define STEREO_DA_FILTER_DIV1_4 (0x3<<12) +#define STEREO_DA_FILTER_DIV1_5 (0x4<<12) +#define STEREO_DA_FILTER_DIV1_6 (0x5<<12) +#define STEREO_DA_FILTER_DIV1_7 (0x6<<12) +#define STEREO_DA_FILTER_DIV1_8 (0x7<<12) +#define STEREO_DA_FILTER_DIV1_9 (0x8<<12) +#define STEREO_DA_FILTER_DIV1_10 (0x9<<12) +#define STEREO_DA_FILTER_DIV1_11 (0xA<<12) +#define STEREO_DA_FILTER_DIV1_12 (0xB<<12) +#define STEREO_DA_FILTER_DIV1_13 (0xC<<12) +#define STEREO_DA_FILTER_DIV1_14 (0xD<<12) +#define STEREO_DA_FILTER_DIV1_15 (0xE<<12) +#define STEREO_DA_FILTER_DIV1_16 (0xF<<12) + +#define STEREO_DA_FILTER_DIV2_MASK (0x7<<9) +#define STEREO_DA_FILTER_DIV2_2 (0x0<<9) +#define STEREO_DA_FILTER_DIV2_4 (0x1<<9) +#define STEREO_DA_FILTER_DIV2_8 (0x2<<9) +#define STEREO_DA_FILTER_DIV2_16 (0x3<<9) +#define STEREO_DA_FILTER_DIV2_32 (0x4<<9) + +#define STEREO_AD_FILTER_DIV1_MASK (0xF<<4) +#define STEREO_AD_FILTER_DIV1_1 (0x0<<4) +#define STEREO_AD_FILTER_DIV1_2 (0x1<<4) +#define STEREO_AD_FILTER_DIV1_3 (0x2<<4) +#define STEREO_AD_FILTER_DIV1_4 (0x3<<4) +#define STEREO_AD_FILTER_DIV1_5 (0x4<<4) +#define STEREO_AD_FILTER_DIV1_6 (0x5<<4) +#define STEREO_AD_FILTER_DIV1_7 (0x6<<4) +#define STEREO_AD_FILTER_DIV1_8 (0x7<<4) +#define STEREO_AD_FILTER_DIV1_9 (0x8<<4) +#define STEREO_AD_FILTER_DIV1_10 (0x9<<4) +#define STEREO_AD_FILTER_DIV1_11 (0xA<<4) +#define STEREO_AD_FILTER_DIV1_12 (0xB<<4) +#define STEREO_AD_FILTER_DIV1_13 (0xC<<4) +#define STEREO_AD_FILTER_DIV1_14 (0xD<<4) +#define STEREO_AD_FILTER_DIV1_15 (0xE<<4) +#define STEREO_AD_FILTER_DIV1_16 (0xF<<4) + +#define STEREO_AD_FILTER_DIV2_MASK (0x7<<1) +#define STEREO_AD_FILTER_DIV2_1 (0x0<<1) +#define STEREO_AD_FILTER_DIV2_2 (0x1<<1) +#define STEREO_AD_FILTER_DIV2_4 (0x2<<1) +#define STEREO_AD_FILTER_DIV2_8 (0x3<<1) +#define STEREO_AD_FILTER_DIV2_16 (0x4<<1) +#define STEREO_AD_FILTER_DIV2_32 (0x5<<1) + +/* Voice DAC PCM Clock Control 1(0x64) */ +#define VOICE_BCLK_DIV1_MASK (0xF<<12) +#define VOICE_BCLK_DIV1_1 (0x0<<12) +#define VOICE_BCLK_DIV1_2 (0x1<<12) +#define VOICE_BCLK_DIV1_3 (0x2<<12) +#define VOICE_BCLK_DIV1_4 (0x3<<12) +#define VOICE_BCLK_DIV1_5 (0x4<<12) +#define VOICE_BCLK_DIV1_6 (0x5<<12) +#define VOICE_BCLK_DIV1_7 (0x6<<12) +#define VOICE_BCLK_DIV1_8 (0x7<<12) +#define VOICE_BCLK_DIV1_9 (0x8<<12) +#define VOICE_BCLK_DIV1_10 (0x9<<12) +#define VOICE_BCLK_DIV1_11 (0xA<<12) +#define VOICE_BCLK_DIV1_12 (0xB<<12) +#define VOICE_BCLK_DIV1_13 (0xC<<12) +#define VOICE_BCLK_DIV1_14 (0xD<<12) +#define VOICE_BCLK_DIV1_15 (0xE<<12) +#define VOICE_BCLK_DIV1_16 (0xF<<12) + +#define VOICE_BCLK_DIV2_MASK (0x7<<8) +#define VOICE_BCLK_DIV2_2 (0x0<<8) +#define VOICE_BCLK_DIV2_4 (0x1<<8) +#define VOICE_BCLK_DIV2_8 (0x2<<8) +#define VOICE_BCLK_DIV2_16 (0x3<<8) +#define VOICE_BCLK_DIV2_32 (0x4<<8) + +#define VOICE_AD_LRCK_DIV1_MASK (0xF<<4) +#define VOICE_AD_LRCK_DIV1_1 (0x0<<4) +#define VOICE_AD_LRCK_DIV1_2 (0x1<<4) +#define VOICE_AD_LRCK_DIV1_3 (0x2<<4) +#define VOICE_AD_LRCK_DIV1_4 (0x3<<4) +#define VOICE_AD_LRCK_DIV1_5 (0x4<<4) +#define VOICE_AD_LRCK_DIV1_6 (0x5<<4) +#define VOICE_AD_LRCK_DIV1_7 (0x6<<4) +#define VOICE_AD_LRCK_DIV1_8 (0x7<<4) +#define VOICE_AD_LRCK_DIV1_9 (0x8<<4) +#define VOICE_AD_LRCK_DIV1_10 (0x9<<4) +#define VOICE_AD_LRCK_DIV1_11 (0xA<<4) +#define VOICE_AD_LRCK_DIV1_12 (0xB<<4) +#define VOICE_AD_LRCK_DIV1_13 (0xC<<4) +#define VOICE_AD_LRCK_DIV1_14 (0xD<<4) +#define VOICE_AD_LRCK_DIV1_15 (0xE<<4) +#define VOICE_AD_LRCK_DIV1_16 (0xF<<4) + +#define VOICE_AD_LRCK_DIV2_MASK (0x7<<1) +#define VOICE_AD_LRCK_DIV2_2 (0x0<<1) +#define VOICE_AD_LRCK_DIV2_4 (0x1<<1) +#define VOICE_AD_LRCK_DIV2_8 (0x2<<1) +#define VOICE_AD_LRCK_DIV2_16 (0x3<<1) +#define VOICE_AD_LRCK_DIV2_32 (0x4<<1) + +#define VOICE_DA_LRCK_DIV_MASK (1) +#define VOICE_DA_LRCK_DIV_32 (0) +#define VOICE_DA_LRCK_DIV_64 (1) + + +/* Psedueo Stereo & Spatial Effect Block Control(0x68) */ +#define SPATIAL_CTRL_EN (0x1<<15) +#define ALL_PASS_FILTER_EN (0x1<<14) +#define PSEUDO_STEREO_EN (0x1<<13) +#define STEREO_EXPENSION_EN (0x1<<12) + +#define SPATIAL_3D_GAIN1_MASK (0x3<<10) +#define SPATIAL_3D_GAIN1_1_0 (0x0<<10) +#define SPATIAL_3D_GAIN1_1_5 (0x1<<10) +#define SPATIAL_3D_GAIN1_2_0 (0x2<<10) + +#define SPATIAL_3D_RATIO1_MASK (0x3<<8) +#define SPATIAL_3D_RATIO1_0_0 (0x0<<8) +#define SPATIAL_3D_RATIO1_0_66 (0x1<<8) +#define SPATIAL_3D_RATIO1_1_0 (0x2<<8) + +#define SPATIAL_3D_GAIN2_MASK (0x3<<6) +#define SPATIAL_3D_GAIN2_1_0 (0x0<<6) +#define SPATIAL_3D_GAIN2_1_5 (0x1<<6) +#define SPATIAL_3D_GAIN2_2_0 (0x2<<6) + +#define SPATIAL_3D_RATIO2_MASK (0x3<<4) +#define SPATIAL_3D_RATIO2_0_0 (0x0<<4) +#define SPATIAL_3D_RATIO2_0_66 (0x1<<4) +#define SPATIAL_3D_RATIO2_1_0 (0x2<<4) + +#define APF_MASK (0x3) +#define APF_FOR_48K (0x3) +#define APF_FOR_44_1K (0x2) +#define APF_FOR_32K (0x1) + +/* EQ Control and Status /ADC HPF Control(0x6E) */ +/* HW EQ block control */ +#define EN_HW_EQ_BLK (0x1<<15) + +#define EQ_SOUR_SEL_DAC (0x0<<14) +#define EQ_SOUR_SEL_ADC (0x1<<14) + +/* EQ parameter update control */ +#define EQ_CHANGE_EN (0x1<<7) + +/* EQ High Pass Filter Control */ +#define EN_HW_EQ_HPF (0x1<<4) + +/* EQ Band-3 Control */ +#define EN_HW_EQ_BP3 (0x1<<3) + +/* EQ Band-2 Control */ +#define EN_HW_EQ_BP2 (0x1<<2) + +/* EQ Band-1 Control */ +#define EN_HW_EQ_BP1 (0x1<<1) + +/* EQ Low Pass Filter Control */ +#define EN_HW_EQ_LPF (0x1<<0) + +/* AEC register command(0x74) */ +/* VODSP I2C busy flag */ +#define VODSP_BUSY (0x1<<15) + +#define VODSP_S_FROM_VODSP_RD (0x0<<14) +#define VODSP_S_FROM_MX72 (0x1<<14) + +/* VODSP CLK select Mask */ +#define VODSP_CLK_SEL_MASK (0x3<<12) + +/* VODSP CLK select 12.288Mhz */ +#define VODSP_CLK_SEL_12_288M (0x0<<12) + +/* VODSP CLK select 6.144Mhz */ +#define VODSP_CLK_SEL_6_144M (0x1<<12) + +/* VODSP CLK select 3.072Mhz */ +#define VODSP_CLK_SEL_3_072M (0x2<<12) + +/* VODSP CLK select 2.0488Mhz */ +#define VODSP_CLK_SEL_2_048M (0x3<<12) + +/* VODSP Read Enable */ +#define VODSP_READ_ENABLE (0x1<<9) + +/* VODSP Write Enable */ +#define VODSP_WRITE_ENABLE (0x1<<8) + +#define VODSP_CMD_MASK (0xFF<<0) +#define VODSP_CMD_MW (0x3B<<0) /* Memory Write */ +#define VODSP_CMD_MR (0x37<<0) /* Memory Read */ +#define VODSP_CMD_RR (0x60<<0) /* Register Read */ +#define VODSP_CMD_RW (0x68<<0) /* Register Write */ + +/* + * Index register of codec + * Index(0x20) for Auto Volume Control + */ +#define AVC_CH_SEL_MASK (0x1<<7) +#define AVC_CH_SEL_L_CH (0x0<<7) +#define AVC_CH_SEL_R_CH (0x1<<7) +#define ENABLE_AVC_GAIN_CTRL (0x1<<15) + +#define DEFAULT_SYSCLK 24576000 + +enum ALC5625_PLL_SEL { + ALC5625_PLL1_FROM_MCLK = 0, + ALC5625_PLL1_FROM_BCLK, + ALC5625_PLL1_FROM_VBCLK, +}; + +enum ALC5625_AEC_MODE { + PCM_IN_PCM_OUT = 0, + ANALOG_IN_ANALOG_OUT, + DAC_IN_ADC_OUT, + VODSP_AEC_DISABLE +}; + +enum ALC5625_PCM_MODE { + PCM_MASTER_MODE_A = 0, + PCM_MASTER_MODE_B, + PCM_SLAVE_MODE_A, + PCM_SLAVE_MODE_B, +}; + +enum ALC5625_FUNC_SEL { + ALC5625_AEC_DISABLE = 0, + ALC5625_AEC_PCM_IN_OUT, + ALC5625_AEC_IIS_IN_OUT, + ALC5625_AEC_ANALOG_IN_OUT, +}; + +struct alc5625_setup_data { + int i2c_bus; + int i2c_address; +}; + +struct voice_dsp_reg { + u16 index; + u16 val; +}; + +#endif diff --git a/drivers/staging/alc5625/origen_alc5625.c b/drivers/staging/alc5625/origen_alc5625.c new file mode 100644 index 000000000000..c352325b6df2 --- /dev/null +++ b/drivers/staging/alc5625/origen_alc5625.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2011 Insignal Co., Ltd. + * + * Author: Pan <pan@insginal.co.kr> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/module.h> + +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> + +#include "../../../sound/soc/samsung/i2s.h" + +static int set_epll_rate(unsigned long rate) +{ + struct clk *fout_epll; + + fout_epll = clk_get(NULL, "fout_epll"); + if (IS_ERR(fout_epll)) { + printk(KERN_ERR "%s: failed to get fout_epll\n", __func__); + return -ENOENT; + } + + if (rate == clk_get_rate(fout_epll)) + goto out; + + clk_set_rate(fout_epll, rate); +out: + clk_put(fout_epll); + + return 0; +} + +static int origen_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int bfs, psr, rfs, ret; + unsigned long rclk; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_U24: + case SNDRV_PCM_FORMAT_S24: + bfs = 48; + break; + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_S16_LE: + bfs = 32; + break; + default: + return -EINVAL; + } + + switch (params_rate(params)) { + case 16000: + case 22050: + case 24000: + case 32000: + case 44100: + case 48000: + case 88200: + case 96000: + rfs = (bfs == 48) ? 384 : 256; + break; + case 64000: + rfs = 384; + break; + case 8000: + case 11025: + case 12000: + rfs = (bfs == 48) ? 768 : 512; + break; + default: + return -EINVAL; + } + + rclk = params_rate(params) * rfs; + + switch (rclk) { + case 4096000: + case 5644800: + case 6144000: + case 8467200: + case 9216000: + psr = 8; + break; + case 8192000: + case 11289600: + case 12288000: + case 16934400: + case 18432000: + psr = 4; + break; + case 22579200: + case 24576000: + case 33868800: + case 36864000: + psr = 2; + break; + case 67737600: + case 73728000: + psr = 1; + break; + default: + printk(KERN_ERR "Not yet supported!\n"); + return -EINVAL; + } + + set_epll_rate(rclk * psr); + + /* Set the Codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* Set the AP DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK, rfs, + SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(cpu_dai, SAMSUNG_I2S_DIV_BCLK, bfs); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops origen_ops = { + .hw_params = origen_hw_params, +}; + +static int origen_wm8994_init_paiftx(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_dapm_sync(dapm); + + return 0; +} + +static struct snd_soc_dai_link origen_dai[] = { + { /* Primary DAI i/f */ + .name = "ALC5625 PAIF", + .stream_name = "Pri_Dai", + .cpu_dai_name = "samsung-i2s.0", + .codec_dai_name = "alc5625-aif1", + .platform_name = "samsung-audio", + .codec_name = "alc5625-codec.1-001e", + .init = origen_wm8994_init_paiftx, + .ops = &origen_ops, + }, +}; + +static struct snd_soc_card origen = { + .name = "ORIGEN-I2S", + .dai_link = origen_dai, + .num_links = ARRAY_SIZE(origen_dai), +}; + +static struct platform_device *origen_snd_device; + +static int __init origen_audio_init(void) +{ + int ret; + + origen_snd_device = platform_device_alloc("soc-audio", -1); + if (!origen_snd_device) + return -ENOMEM; + + platform_set_drvdata(origen_snd_device, &origen); + + ret = platform_device_add(origen_snd_device); + + if (ret) + platform_device_put(origen_snd_device); + + return ret; +} +late_initcall(origen_audio_init); + +static void __exit origen_audio_exit(void) +{ + platform_device_unregister(origen_snd_device); +} +module_exit(origen_audio_exit); + +MODULE_AUTHOR("Pan, <pan@insignal.co.kr>"); +MODULE_DESCRIPTION("ALSA SoC ORIGEN+ALC5625"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 7f04717176aa..56843963eebf 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1004,6 +1004,9 @@ static void s3c24xx_serial_resetport(struct uart_port *port, wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); wr_regl(port, S3C2410_UFCON, cfg->ufcon); + wr_regl(port, S3C64XX_UINTM, 0xf); + wr_regl(port, S3C64XX_UINTP, 0xf); + /* some delay is required after fifo reset */ udelay(1); } diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index a4b4e003b01f..132b79d42c8f 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -833,6 +833,13 @@ config USB_G_ANDROID Each function can be configured and enabled/disabled dynamically from userspace through a sysfs interface. +config USB_G_RNDIS_DWORD_ALIGNED + bool "RNDIS Dword Aligned support" + depends on USB_G_ANDROID + default y + help + Provides dword aligned for DMA controller. + config USB_CDC_COMPOSITE tristate "CDC Composite Device (Ethernet and ACM)" depends on NET diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index d2c3393237b6..2b110552955e 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -1118,7 +1118,7 @@ static int android_bind(struct usb_composite_dev *cdev) { struct android_dev *dev = _android_dev; struct usb_gadget *gadget = cdev->gadget; - int gcnum, id, ret; + int id, ret; /* * Start disconnected. Userspace will connect the gadget once @@ -1156,15 +1156,6 @@ static int android_bind(struct usb_composite_dev *cdev) strings_dev[STRING_SERIAL_IDX].id = id; device_desc.iSerialNumber = id; - gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) - device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); - else { - pr_warning("%s: controller '%s' not recognized\n", - longname, gadget->name); - device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); - } - usb_gadget_set_selfpowered(gadget); dev->cdev = cdev; @@ -1184,6 +1175,7 @@ static struct usb_composite_driver android_usb_driver = { .name = "android_usb", .dev = &device_desc, .strings = dev_strings, + .bind = android_bind, .unbind = android_usb_unbind, .max_speed = USB_SPEED_HIGH, }; @@ -1300,10 +1292,10 @@ static int __init init(void) _android_dev = dev; /* Override composite driver functions */ - composite_driver.setup = android_setup; - composite_driver.disconnect = android_disconnect; + composite_driver_template.setup = android_setup; + composite_driver_template.disconnect = android_disconnect; - return usb_composite_probe(&android_usb_driver, android_bind); + return usb_composite_probe(&android_usb_driver); } module_init(init); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 957f973dd96a..c4460a54f917 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1528,8 +1528,11 @@ composite_resume(struct usb_gadget *gadget) } /*-------------------------------------------------------------------------*/ - +#if IS_ENABLED(CONFIG_USB_G_ANDROID) +static struct usb_gadget_driver composite_driver_template = { +#else static const struct usb_gadget_driver composite_driver_template = { +#endif .bind = composite_bind, .unbind = composite_unbind, diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 6f696ee8b817..5e8e98f63d6e 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -28,6 +28,7 @@ #include <linux/io.h> #include <linux/slab.h> #include <linux/clk.h> +#include <linux/of.h> #include <linux/regulator/consumer.h> #include <linux/usb/ch9.h> @@ -3485,6 +3486,8 @@ static void s3c_hsotg_release(struct device *dev) kfree(hsotg); } +static u64 s3c_hsotg_dma_mask = DMA_BIT_MASK(32); + /** * s3c_hsotg_probe - probe function for hsotg driver * @pdev: The platform information for the driver @@ -3507,6 +3510,14 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev) return -EINVAL; } + /* + * Right now device-tree probed devices don't get dma_mask set. + */ + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &s3c_hsotg_dma_mask; + if (!pdev->dev.coherent_dma_mask) + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL); if (!hsotg) { dev_err(dev, "cannot get memory\n"); @@ -3702,10 +3713,19 @@ static int __devexit s3c_hsotg_remove(struct platform_device *pdev) #define s3c_hsotg_resume NULL #endif +#ifdef CONFIG_OF +static const struct of_device_id exynos_hsotg_match[] = { + { .compatible = "samsung,exynos-hsotg" }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_hsotg_match); +#endif + static struct platform_driver s3c_hsotg_driver = { .driver = { .name = "s3c-hsotg", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(exynos_hsotg_match), }, .probe = s3c_hsotg_probe, .remove = __devexit_p(s3c_hsotg_remove), diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 4ec3c0d7a18b..13ceb2c201f1 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -235,13 +235,15 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) DBG(dev, "no rx skb\n"); goto enomem; } - +#ifndef USB_G_RNDIS_DWORD_ALIGNED /* Some platforms perform better when IP packets are aligned, * but on at least one, checksumming fails otherwise. Note: * RNDIS headers involve variable numbers of LE32 values. */ skb_reserve(skb, NET_IP_ALIGN); +#endif + req->buf = skb->data; req->length = size; req->complete = rx_complete; @@ -471,6 +473,10 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req) spin_unlock(&dev->req_lock); dev_kfree_skb_any(skb); +#ifdef USB_G_RNDIS_DWORD_ALIGNED + if (req->buf != skb->data) + kfree(req->buf); +#endif atomic_dec(&dev->tx_qlen); if (netif_carrier_ok(dev->net)) netif_wake_queue(dev->net); @@ -564,7 +570,18 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, length = skb->len; } +#ifdef USB_G_RNDIS_DWORD_ALIGNED +if ((int)skb->data & 3) { + req->buf = kmalloc(skb->len, GFP_ATOMIC); + if (!req->buf) + goto drop; + memcpy((void *)req->buf, (void *)skb->data, skb->len); + } else { + req->buf = skb->data; + } +#else req->buf = skb->data; +#endif req->context = skb; req->complete = tx_complete; @@ -606,6 +623,10 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, dev_kfree_skb_any(skb); drop: dev->net->stats.tx_dropped++; +#ifdef USB_G_RNDIS_DWORD_ALIGNED + if (req->buf != skb->data) + kfree(req->buf); +#endif spin_lock_irqsave(&dev->req_lock, flags); if (list_empty(&dev->tx_reqs)) netif_start_queue(net); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 4b2ca30aa12e..8f7331258966 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -25,6 +25,8 @@ source "drivers/gpu/stub/Kconfig" source "drivers/gpu/ion/Kconfig" +source "drivers/gpu/arm/Kconfig" + config VGASTATE tristate default n diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 765a945f8ea1..371f53106642 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -87,6 +87,13 @@ config LCD_PLATFORM This driver provides a platform-device registered LCD power control interface. +config LCD_PWRCTRL + tristate "LCD panel power control" + help + Say y here, if you have a lcd panel that allows reset and vcc to be + controlled by the host system, and which does not use a serial command + interface (such as i2c or spi) or memory-mapped-io interface. + config LCD_TOSA tristate "Sharp SL-6000 LCD Driver" depends on I2C && SPI && MACH_TOSA diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index e7ce7291635d..03963a500344 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_LCD_LMS283GF05) += lms283gf05.o obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o obj-$(CONFIG_LCD_ILI9320) += ili9320.o obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o +obj-$(CONFIG_LCD_PWRCTRL) += lcd_pwrctrl.o obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o obj-$(CONFIG_LCD_TDO24M) += tdo24m.o obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o diff --git a/drivers/video/backlight/lcd_pwrctrl.c b/drivers/video/backlight/lcd_pwrctrl.c new file mode 100644 index 000000000000..917d842523c8 --- /dev/null +++ b/drivers/video/backlight/lcd_pwrctrl.c @@ -0,0 +1,226 @@ +/* + * Simple lcd panel power control driver. + * + * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd. + * Copyright (c) 2011-2012 Linaro Ltd. + * + * This driver is for controlling power for raster type lcd panels that requires + * its nRESET interface line to be connected and controlled by a GPIO of the + * host system and the Vcc line controlled by a voltage regulator. This + * excludes support for lcd panels that use a serial command interface or direct + * memory mapped IO interface. + * + * The nRESET interface line of the panel should be connected to a gpio of the + * host system. The Vcc pin is controlled using a external volatage regulator. + * Panel backlight is not controlled by this driver. + * + * This driver is derived from platform-lcd.c which was written by + * Ben Dooks <ben@simtec.co.uk> + * + * 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/platform_device.h> +#include <linux/fb.h> +#include <linux/lcd.h> +#include <linux/gpio.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> +#include <video/lcd_pwrctrl.h> + +struct lcd_pwrctrl { + struct device *dev; + struct lcd_device *lcd; + struct lcd_pwrctrl_data *pdata; + struct regulator *regulator; + unsigned int power; + bool suspended; + bool pwr_en; +}; + +static int lcd_pwrctrl_get_power(struct lcd_device *lcd) +{ + struct lcd_pwrctrl *lp = lcd_get_data(lcd); + return lp->power; +} + +static int lcd_pwrctrl_set_power(struct lcd_device *lcd, int power) +{ + struct lcd_pwrctrl *lp = lcd_get_data(lcd); + struct lcd_pwrctrl_data *pd = lp->pdata; + bool lcd_enable; + int lcd_reset, ret = 0; + + lcd_enable = (power == FB_BLANK_POWERDOWN || lp->suspended) ? 0 : 1; + lcd_reset = (pd->invert) ? !lcd_enable : lcd_enable; + + if (lp->pwr_en == lcd_enable) + return 0; + + if (!IS_ERR_OR_NULL(lp->regulator)) { + if (lcd_enable) { + if (regulator_enable(lp->regulator)) { + dev_info(lp->dev, "regulator enable failed\n"); + ret = -EPERM; + } + } else { + if (regulator_disable(lp->regulator)) { + dev_info(lp->dev, "regulator disable failed\n"); + ret = -EPERM; + } + } + } + + gpio_direction_output(lp->pdata->gpio, lcd_reset); + lp->power = power; + lp->pwr_en = lcd_enable; + return ret; +} + +static int lcd_pwrctrl_check_fb(struct lcd_device *lcd, struct fb_info *info) +{ + struct lcd_pwrctrl *lp = lcd_get_data(lcd); + return lp->dev->parent == info->device; +} + +static struct lcd_ops lcd_pwrctrl_ops = { + .get_power = lcd_pwrctrl_get_power, + .set_power = lcd_pwrctrl_set_power, + .check_fb = lcd_pwrctrl_check_fb, +}; + +#ifdef CONFIG_OF +static void __devinit lcd_pwrctrl_parse_dt(struct device *dev, + struct lcd_pwrctrl_data *pdata) +{ + struct device_node *np = dev->of_node; + + pdata->gpio = of_get_named_gpio(np, "lcd-reset-gpio", 0); + if (of_get_property(np, "lcd-reset-active-high", NULL)) + pdata->invert = true; +} +#endif + +static int __devinit lcd_pwrctrl_probe(struct platform_device *pdev) +{ + struct lcd_pwrctrl *lp; + struct lcd_pwrctrl_data *pdata = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + int err; + +#ifdef CONFIG_OF + if (dev->of_node) { + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "memory allocation for pdata failed\n"); + return -ENOMEM; + } + lcd_pwrctrl_parse_dt(dev, pdata); + } +#endif + + if (!pdata) { + dev_err(dev, "platform data not available\n"); + return -EINVAL; + } + + lp = devm_kzalloc(dev, sizeof(struct lcd_pwrctrl), GFP_KERNEL); + if (!lp) { + dev_err(dev, "memory allocation failed for private data\n"); + return -ENOMEM; + } + + err = gpio_request(pdata->gpio, "LCD-nRESET"); + if (err) { + dev_err(dev, "gpio [%d] request failed\n", pdata->gpio); + return err; + } + + /* + * If power to lcd and/or lcd interface is controlled using a regulator, + * get the handle to the regulator for later use during power switching. + */ + lp->regulator = devm_regulator_get(dev, "vcc-lcd"); + if (IS_ERR(lp->regulator)) + dev_info(dev, "could not find regulator\n"); + + lp->dev = dev; + lp->pdata = pdata; + lp->lcd = lcd_device_register(dev_name(dev), dev, lp, &lcd_pwrctrl_ops); + if (IS_ERR(lp->lcd)) { + dev_err(dev, "cannot register lcd device\n"); + gpio_free(pdata->gpio); + return PTR_ERR(lp->lcd); + } + + platform_set_drvdata(pdev, lp); + lcd_pwrctrl_set_power(lp->lcd, FB_BLANK_NORMAL); + return 0; +} + +static int __devexit lcd_pwrctrl_remove(struct platform_device *pdev) +{ + struct lcd_pwrctrl *lp = platform_get_drvdata(pdev); + lcd_device_unregister(lp->lcd); + gpio_free(lp->pdata->gpio); + return 0; +} + +#ifdef CONFIG_PM +static int lcd_pwrctrl_suspend(struct device *dev) +{ + struct lcd_pwrctrl *lp = dev_get_drvdata(dev); + + lp->suspended = true; + lcd_pwrctrl_set_power(lp->lcd, FB_BLANK_POWERDOWN); + return 0; +} + +static int lcd_pwrctrl_resume(struct device *dev) +{ + struct lcd_pwrctrl *lp = dev_get_drvdata(dev); + + lp->suspended = false; + lcd_pwrctrl_set_power(lp->lcd, FB_BLANK_UNBLANK); + return 0; +} + +static const struct dev_pm_ops lcd_pwrctrl_dev_pm_ops = { + .suspend = lcd_pwrctrl_suspend, + .resume = lcd_pwrctrl_resume, +}; + +#define LCD_PWRCTRL_DEV_PM_OPS (&lcd_pwrctrl_dev_pm_ops) +#else +#define LCD_PWRCTRL_DEV_PM_OPS NULL +#endif /* CONFIG_PM */ + +#ifdef CONFIG_OF +static const struct of_device_id lcd_pwrctrl_match[] = { + { .compatible = "lcd-powercontrol", }, + {}, +}; +MODULE_DEVICE_TABLE(of, lcd_pwrctrl_match); +#endif + +static struct platform_driver lcd_pwrctrl_driver = { + .driver = { + .name = "lcd-pwrctrl", + .owner = THIS_MODULE, + .pm = LCD_PWRCTRL_DEV_PM_OPS, + .of_match_table = of_match_ptr(lcd_pwrctrl_match), + }, + .probe = lcd_pwrctrl_probe, + .remove = lcd_pwrctrl_remove, +}; + +module_platform_driver(lcd_pwrctrl_driver); + +MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:lcd-pwrctrl"); diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 069983ca49ff..743624e32add 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -274,6 +274,15 @@ static int pwm_backlight_remove(struct platform_device *pdev) return 0; } +static void pwm_backlight_shutdown(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); + + pwm_config(pb->pwm, 0, pb->period); + pwm_disable(pb->pwm); +} + #ifdef CONFIG_PM static int pwm_backlight_suspend(struct device *dev) { @@ -313,6 +322,7 @@ static struct platform_driver pwm_backlight_driver = { }, .probe = pwm_backlight_probe, .remove = pwm_backlight_remove, + .shutdown = pwm_backlight_shutdown, }; module_platform_driver(pwm_backlight_driver); diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index 2ed7b633bbd9..79140f6d7dd8 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -24,6 +24,8 @@ #include <linux/uaccess.h> #include <linux/interrupt.h> #include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/of_gpio.h> #include <video/samsung_fimd.h> #include <mach/map.h> @@ -67,6 +69,8 @@ struct s3c_fb; #define VIDOSD_C(win, variant) (OSD_BASE(win, variant) + 0x08) #define VIDOSD_D(win, variant) (OSD_BASE(win, variant) + 0x0C) +#define S3CFB_WIN_SET_PIXEL_ALPHA _IOW('F', 204, __u32) + /** * struct s3c_fb_variant - fb variant information * @is_2443: Set if S3C2443/S3C2416 style hardware. @@ -220,6 +224,7 @@ struct s3c_fb { int irq_no; unsigned long irq_flags; struct s3c_fb_vsync vsync_info; + int *gpios; }; /** @@ -1013,6 +1018,33 @@ static int s3c_fb_wait_for_vsync(struct s3c_fb *sfb, u32 crtc) return 0; } +int s3cfb_set_alpha_blending(struct s3c_fb *ctrl, int id) +{ + u32 avalue = 0, cfg; + + if (id == 0) { + dev_err(ctrl->dev, "[fb%d] does not support alpha blending\n", + id); + return -EINVAL; + } + + cfg = readl(ctrl->regs + S3C_WINCON(id)); + cfg |= (1 << 0); + writel(cfg, ctrl->regs + S3C_WINCON(id)); + cfg = readl(ctrl->regs + S3C_WINSHMAP); + cfg |= S3C_WINSHMAP_CH_ENABLE(id); + writel(cfg, ctrl->regs + S3C_WINSHMAP); + cfg = readl(ctrl->regs + S3C_WINCON(id)); + cfg &= ~(S3C_WINCON_BLD_MASK | S3C_WINCON_ALPHA_SEL_MASK); + + cfg |= (S3C_WINCON_BLD_PIXEL | S3C_WINCON_ALPHA1_SEL); + writel(cfg, ctrl->regs + S3C_WINCON(id)); + writel(avalue, ctrl->regs + S3C_VIDOSD_C(id)); + + cfg = readl(ctrl->regs + S3C_WINCON(id)); + return 0; +} + static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { @@ -1030,6 +1062,9 @@ static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd, ret = s3c_fb_wait_for_vsync(sfb, crtc); break; + case S3CFB_WIN_SET_PIXEL_ALPHA: + ret = s3cfb_set_alpha_blending(sfb, win->index); + break; default: ret = -ENOTTY; } @@ -1037,8 +1072,30 @@ static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd, return ret; } +int s3c_fb_open(struct fb_info *info, int user) +{ + s3c_fb_set_par(info); + return 0; +} + +int s3c_fb_release(struct fb_info *info, int user) +{ + struct s3c_fb_win *win = info->par; + struct s3c_fb *sfb = win->parent; + void __iomem *regs = sfb->regs; + int win_no = win->index; + + if (win_no != 2) { + printk(KERN_DEBUG"Releasing window %d\n", win_no); + writel(0, regs + WINCON(win_no)); + } + return 0; +} + static struct fb_ops s3c_fb_ops = { .owner = THIS_MODULE, + .fb_open = s3c_fb_open, + .fb_release = s3c_fb_release, .fb_check_var = s3c_fb_check_var, .fb_set_par = s3c_fb_set_par, .fb_blank = s3c_fb_blank, @@ -1268,8 +1325,6 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, else dev_err(sfb->dev, "failed to allocate fb cmap\n"); - s3c_fb_set_par(fbinfo); - dev_dbg(sfb->dev, "about to register framebuffer\n"); /* run the check_var and set_par on our configuration. */ @@ -1358,27 +1413,214 @@ static void s3c_fb_clear_win(struct s3c_fb *sfb, int win) } } +#ifdef CONFIG_OF +static int s3c_fb_dt_parse_gpios(struct device *dev, struct s3c_fb *sfb, + bool request) +{ + int nr_gpios, idx, gpio, ret; + + nr_gpios = sfb->pdata->win[0]->max_bpp + 4; + sfb->gpios = devm_kzalloc(dev, sizeof(int) * nr_gpios, GFP_KERNEL); + if (!sfb->gpios) { + dev_err(dev, "unable to allocate private data for gpio\n"); + return -ENOMEM; + } + + for (idx = 0; idx < nr_gpios; idx++) { + gpio = of_get_gpio(dev->of_node, idx); + if (!gpio_is_valid(gpio)) { + dev_err(dev, "invalid gpio[%d]: %d\n", idx, gpio); + return -EINVAL; + } + + if (!request) + continue; + + ret = gpio_request(gpio, "fimd"); + if (ret) { + dev_err(dev, "gpio [%d] request failed\n", gpio); + goto gpio_free; + } + sfb->gpios[idx] = gpio; + } + return 0; + +gpio_free: + while (--idx >= 0) + gpio_free(sfb->gpios[idx]); + return ret; +} + +static void s3c_fb_dt_free_gpios(struct s3c_fb *sfb) +{ + unsigned int idx, nr_gpio; + + nr_gpio = sfb->pdata->win[0]->max_bpp + 4; + for (idx = 0; idx < nr_gpio; idx++) + gpio_free(sfb->gpios[idx]); +} + +static struct s3c_fb_platdata *s3c_fb_dt_parse_pdata(struct device *dev) +{ + struct device_node *np = dev->of_node, *win_np; + struct device_node *disp_np; + struct s3c_fb_platdata *pd; + struct s3c_fb_pd_win *win; + u32 wnum = 0, data[4]; + + pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); + if (!pd) { + dev_err(dev, "memory allocation for pdata failed\n"); + return ERR_PTR(-ENOMEM); + } + + pd->vtiming = devm_kzalloc(dev, sizeof(*pd->vtiming), GFP_KERNEL); + if (!pd->vtiming) { + dev_err(dev, "memory allocation for vtiming failed\n"); + return ERR_PTR(-ENOMEM); + } + + if (of_get_property(np, "samsung,fimd-vidout-rgb", NULL)) + pd->vidcon0 |= VIDCON0_VIDOUT_RGB; + if (of_get_property(np, "samsung,fimd-vidout-tv", NULL)) + pd->vidcon0 |= VIDCON0_VIDOUT_TV; + if (of_get_property(np, "samsung,fimd-inv-hsync", NULL)) + pd->vidcon1 |= VIDCON1_INV_HSYNC; + if (of_get_property(np, "samsung,fimd-inv-vsync", NULL)) + pd->vidcon1 |= VIDCON1_INV_VSYNC; + if (of_get_property(np, "samsung,fimd-inv-vclk", NULL)) + pd->vidcon1 |= VIDCON1_INV_VCLK; + if (of_get_property(np, "samsung,fimd-inv-vden", NULL)) + pd->vidcon1 |= VIDCON1_INV_VDEN; + + disp_np = of_parse_phandle(np, "samsung,fimd-display", 0); + if (!disp_np) { + dev_err(dev, "unable to find display panel info\n"); + return ERR_PTR(-EINVAL); + } + + if (of_property_read_u32_array(disp_np, "lcd-htiming", data, 4)) { + dev_err(dev, "invalid horizontal timing\n"); + return ERR_PTR(-EINVAL); + } + pd->vtiming->left_margin = data[0]; + pd->vtiming->right_margin = data[1]; + pd->vtiming->hsync_len = data[2]; + pd->vtiming->xres = data[3]; + + if (of_property_read_u32_array(disp_np, "lcd-vtiming", data, 4)) { + dev_err(dev, "invalid vertical timing\n"); + return ERR_PTR(-EINVAL); + } + pd->vtiming->upper_margin = data[0]; + pd->vtiming->lower_margin = data[1]; + pd->vtiming->vsync_len = data[2]; + pd->vtiming->yres = data[3]; + + of_property_read_u32_array(np, "samsung,fimd-frame-rate", + &pd->vtiming->refresh, 1); + + for_each_child_of_node(np, win_np) { + if (of_property_read_u32_array(win_np, "samsung,fimd-win-id", + &wnum, 1)) { + dev_err(dev, "window id not specified\n"); + return ERR_PTR(-EINVAL); + } + + win = devm_kzalloc(dev, sizeof(*win), GFP_KERNEL); + if (!win) { + dev_err(dev, "no memory for window[%d] data\n", wnum); + return ERR_PTR(-ENOMEM); + } + pd->win[wnum] = win; + + if (of_property_read_u32_array(win_np, "samsung,fimd-win-bpp", + data, 2)) { + dev_err(dev, "invalid window bpp\n"); + return ERR_PTR(-EINVAL); + } + win->default_bpp = data[0]; + win->max_bpp = data[1]; + + if (of_property_read_u32_array(win_np, "samsung,fimd-win-res", + data, 2)) { + dev_info(dev, "window [%d] resolution not specified. " + "Using lcd resolution X[%d] and Y[%d]", wnum, + pd->vtiming->xres, pd->vtiming->yres); + win->xres = pd->vtiming->xres; + win->yres = pd->vtiming->yres; + } else { + win->xres = data[0]; + win->yres = data[1]; + } + + if (!of_property_read_u32_array(win_np, + "samsung,fimd-win-virtres", data, 2)) { + win->virtual_x = data[0]; + win->virtual_y = data[1]; + } + } + + return pd; +} +#else +static int s3c_fb_dt_parse_gpios(struct device *dev, struct s3c_fb *sfb, + bool request) +{ + return 0; +} + +static void s3c_fb_dt_free_gpios(struct s3c_fb *sfb) +{ + return 0; +} + +static int s3c_fb_dt_parse_pdata(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static const struct of_device_id s3c_fb_dt_match[]; + +static inline struct s3c_fb_driverdata *s3c_fb_get_driver_data( + struct platform_device *pdev) +{ +#ifdef CONFIG_OF + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(s3c_fb_dt_match, pdev->dev.of_node); + return (struct s3c_fb_driverdata *)match->data; + } +#endif + return (struct s3c_fb_driverdata *) + platform_get_device_id(pdev)->driver_data; +} + static int __devinit s3c_fb_probe(struct platform_device *pdev) { - const struct platform_device_id *platid; struct s3c_fb_driverdata *fbdrv; struct device *dev = &pdev->dev; - struct s3c_fb_platdata *pd; + struct s3c_fb_platdata *pd = pdev->dev.platform_data; struct s3c_fb *sfb; struct resource *res; int win; int ret = 0; u32 reg; - platid = platform_get_device_id(pdev); - fbdrv = (struct s3c_fb_driverdata *)platid->driver_data; + fbdrv = s3c_fb_get_driver_data(pdev); if (fbdrv->variant.nr_windows > S3C_FB_MAX_WIN) { dev_err(dev, "too many windows, cannot attach\n"); return -EINVAL; } - pd = pdev->dev.platform_data; + if (pdev->dev.of_node) { + pd = s3c_fb_dt_parse_pdata(&pdev->dev); + if (IS_ERR(pd)) + return PTR_ERR(pd); + } + if (!pd) { dev_err(dev, "no platform data specified\n"); return -EINVAL; @@ -1448,7 +1690,12 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) /* setup gpio and output polarity controls */ - pd->setup_gpio(); + if (dev->of_node) { + if (s3c_fb_dt_parse_gpios(dev, sfb, true)) + goto err_lcd_clk; + } else { + pd->setup_gpio(); + } writel(pd->vidcon1, sfb->regs + VIDCON1); @@ -1498,6 +1745,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) return 0; err_pm_runtime: + s3c_fb_dt_free_gpios(sfb); pm_runtime_put_sync(sfb->dev); err_lcd_clk: @@ -1537,6 +1785,7 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) pm_runtime_put_sync(sfb->dev); pm_runtime_disable(sfb->dev); + s3c_fb_dt_free_gpios(sfb); return 0; } @@ -1587,7 +1836,10 @@ static int s3c_fb_resume(struct device *dev) clk_prepare_enable(sfb->lcd_clk); /* setup gpio and output polarity controls */ - pd->setup_gpio(); + if (dev->of_node) + s3c_fb_dt_parse_gpios(dev, sfb, false); + else + pd->setup_gpio(); writel(pd->vidcon1, sfb->regs + VIDCON1); /* set video clock running at under-run */ @@ -1659,7 +1911,10 @@ static int s3c_fb_runtime_resume(struct device *dev) clk_prepare_enable(sfb->lcd_clk); /* setup gpio and output polarity controls */ - pd->setup_gpio(); + if (dev->of_node) + s3c_fb_dt_parse_gpios(dev, sfb, false); + else + pd->setup_gpio(); writel(pd->vidcon1, sfb->regs + VIDCON1); return 0; @@ -2029,6 +2284,15 @@ static struct platform_device_id s3c_fb_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids); +#ifdef CONFIG_OF +static const struct of_device_id s3c_fb_dt_match[] = { + { .compatible = "samsung,exynos4210-fimd", + .data = (void *)&s3c_fb_data_exynos4 }, + {}, +}; +MODULE_DEVICE_TABLE(of, s3c_fb_dt_match); +#endif + static const struct dev_pm_ops s3cfb_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(s3c_fb_suspend, s3c_fb_resume) SET_RUNTIME_PM_OPS(s3c_fb_runtime_suspend, s3c_fb_runtime_resume, @@ -2043,6 +2307,7 @@ static struct platform_driver s3c_fb_driver = { .name = "s3c-fb", .owner = THIS_MODULE, .pm = &s3cfb_pm_ops, + .of_match_table = of_match_ptr(s3c_fb_dt_match), }, }; diff --git a/include/linux/ath6kl.h b/include/linux/ath6kl.h new file mode 100644 index 000000000000..0e0ff495481f --- /dev/null +++ b/include/linux/ath6kl.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org> + * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LINUX_ATH6KL_PLATFORM_H +#define _LINUX_ATH6KL_PLATFORM_H + +struct ath6kl_platform_data { + int (*setup_power)(bool); +}; + +#ifdef CONFIG_ATH6KL_PLATFORM_DATA +int ath6kl_set_platform_data(const struct ath6kl_platform_data *data); +#else +static inline +int ath6kl_set_platform_data(const struct ath6kl_platform_data *data) +{ + return -ENOSYS; +} +#endif + +const struct ath6kl_platform_data *ath6kl_get_platform_data(void); + +#endif /* _LINUX_ATH6KL_PLATFORM_H */ diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h index 830152cfae33..6ae21bf47d64 100644 --- a/include/linux/mfd/max8997-private.h +++ b/include/linux/mfd/max8997-private.h @@ -316,6 +316,7 @@ enum max8997_irq { #define MAX8997_NUM_GPIO 12 struct max8997_dev { struct device *dev; + struct max8997_platform_data *pdata; struct i2c_client *i2c; /* 0xcc / PMIC, Battery Control, and FLASH */ struct i2c_client *rtc; /* slave addr 0x0c */ struct i2c_client *haptic; /* slave addr 0x90 */ diff --git a/include/linux/mfd/max8997.h b/include/linux/mfd/max8997.h index 328d8e24b533..1d4a4fe6ac33 100644 --- a/include/linux/mfd/max8997.h +++ b/include/linux/mfd/max8997.h @@ -75,6 +75,7 @@ enum max8998_regulators { struct max8997_regulator_data { int id; struct regulator_init_data *initdata; + struct device_node *reg_node; }; enum max8997_muic_usb_type { diff --git a/include/linux/printk.h b/include/linux/printk.h index 9afc01e5a0a6..a2560f6391ed 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -6,6 +6,7 @@ extern const char linux_banner[]; extern const char linux_proc_banner[]; +extern const char linux_scm_version_banner[]; static inline int printk_get_level(const char *buffer) { diff --git a/include/media/videobuf2-fb.h b/include/media/videobuf2-fb.h new file mode 100644 index 000000000000..fea16b655a94 --- /dev/null +++ b/include/media/videobuf2-fb.h @@ -0,0 +1,22 @@ +/* + * videobuf2-fb.h - FrameBuffer API emulator on top of Videobuf2 framework + * + * Copyright (C) 2011 Samsung Electronics + * + * Author: Marek Szyprowski <m.szyprowski@samsung.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 + * the Free Software Foundation. + */ + +#ifndef _MEDIA_VIDEOBUF2_FB_H +#define _MEDIA_VIDEOBUF2_FB_H + +#include <media/v4l2-dev.h> +#include <media/videobuf2-core.h> + +void *vb2_fb_register(struct vb2_queue *q, struct video_device *vfd); +int vb2_fb_unregister(void *fb_emu); + +#endif diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index f56c945cecd4..3dae014932c2 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -141,8 +141,10 @@ enum v4l2_colorfx { #define V4L2_CID_ALPHA_COMPONENT (V4L2_CID_BASE+41) #define V4L2_CID_COLORFX_CBCR (V4L2_CID_BASE+42) +#define V4L2_CID_CODEC_FRAME_TAG (V4L2_CID_BASE+43) + /* last CID + 1 */ -#define V4L2_CID_LASTP1 (V4L2_CID_BASE+43) +#define V4L2_CID_LASTP1 (V4L2_CID_BASE+44) /* MPEG-class control IDs */ diff --git a/include/video/lcd_pwrctrl.h b/include/video/lcd_pwrctrl.h new file mode 100644 index 000000000000..924bfd222340 --- /dev/null +++ b/include/video/lcd_pwrctrl.h @@ -0,0 +1,24 @@ +/* + * Simple lcd panel power control driver. + * + * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd. + * Copyright (c) 2011-2012 Linaro Ltd. + * + * This driver is derived from platform-lcd.h which was written by + * Ben Dooks <ben@simtec.co.uk> + * + * 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. + * +*/ + +/** + * struct lcd_pwrctrl_data - platform data for lcd_pwrctrl driver. + * @gpio: GPIO number of the host system that connects to nRESET line. + * @invert: True, if output of gpio connected to nRESET should be inverted. + */ +struct lcd_pwrctrl_data { + int gpio; + bool invert; +}; diff --git a/include/video/samsung_fimd.h b/include/video/samsung_fimd.h index 7ae6c07f2ef8..f9ad4b8b2e32 100644 --- a/include/video/samsung_fimd.h +++ b/include/video/samsung_fimd.h @@ -393,6 +393,20 @@ #define BLENDCON_NEW_8BIT_ALPHA_VALUE (1 << 0) #define BLENDCON_NEW_4BIT_ALPHA_VALUE (0 << 0) +/* Blending control registers */ +#define S3C_WINCON_BLD_PIXEL (1 << 6) +#define S3C_WINCON_BLD_MASK (1 << 6) +#define S3C_WINCON_ALPHA1_SEL (1 << 1) +#define S3C_WINCON_ALPHA_SEL_MASK (1 << 1) +#define S3C_WINCON(x) (0x0020 + (x * 0x04)) +#define S3C_VIDOSD_C(x) (0x0048 + (x * 0x10)) +#define S3C_WINSHMAP (0x0034) +#define S3C_WINSHMAP_CH_ENABLE(x) (1 << (x)) +#define S3C_WINCON_BLD_PLANE (0 << 6) +#define S3C_WINCON_ALPHA0_SEL (0 << 1) +#define S3C_VIDOSD_ALPHA0_SHIFT (12) +#define S3C_VIDOSD_ALPHA1_SHIFT (0) + #define S3C_FB_MAX_WIN (5) /* number of hardware windows available. */ #define VIDCON1_FSTATUS_EVEN (1 << 15) diff --git a/init/Kconfig b/init/Kconfig index bebbed60fc7f..a306ec77ebf4 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1487,7 +1487,7 @@ config PROFILING # dynamically changed for a probe function. # config TRACEPOINTS - bool + bool "Tracepoints support" source "arch/Kconfig" diff --git a/init/main.c b/init/main.c index e33e09df3cbc..5c002596fe86 100644 --- a/init/main.c +++ b/init/main.c @@ -496,6 +496,9 @@ asmlinkage void __init start_kernel(void) boot_cpu_init(); page_address_init(); printk(KERN_NOTICE "%s", linux_banner); +#if !IS_ENABLED(CONFIG_LOCALVERSION_AUTO) + printk(KERN_NOTICE "%s", linux_scm_version_banner); +#endif setup_arch(&command_line); mm_init_owner(&init_mm, &init_task); mm_init_cpumask(&init_mm); diff --git a/init/version.c b/init/version.c index 86fe0ccb997a..e86cce9a1855 100644 --- a/init/version.c +++ b/init/version.c @@ -46,3 +46,6 @@ const char linux_proc_banner[] = "%s version %s" " (" LINUX_COMPILE_BY "@" LINUX_COMPILE_HOST ")" " (" LINUX_COMPILER ") %s\n"; + +const char linux_scm_version_banner [] = + "Detailed version Linux "UTS_RELEASE "" KERNEL_VERSION_LOCAL "\n"; diff --git a/linaro/configs/origen.conf b/linaro/configs/origen.conf new file mode 100644 index 000000000000..27b0ed0ee13a --- /dev/null +++ b/linaro/configs/origen.conf @@ -0,0 +1,125 @@ +CONFIG_ARCH_EXYNOS=y +CONFIG_S3C_LOWLEVEL_UART_PORT=2 +CONFIG_S3C24XX_PWM=y +CONFIG_MACH_SMDKC210=y +CONFIG_MACH_ARMLEX4210=y +CONFIG_MACH_UNIVERSAL_C210=y +CONFIG_MACH_NURI=y +CONFIG_MACH_ORIGEN=y +CONFIG_MACH_SMDK4412=y +CONFIG_MACH_EXYNOS4_DT=y +CONFIG_NR_CPUS=2 +CONFIG_AEABI=y +CONFIG_CMDLINE="root=/dev/mmcblk0p1 rw rootwait console=ttySAC2,115200" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_PM_RUNTIME=y +CONFIG_RFKILL=y +CONFIG_RFKILL_GPIO=y +CONFIG_CMA=y +CONFIG_CMA_SIZE_MBYTES=32 +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_SG=y +CONFIG_CFG80211=y +CONFIG_USB_PEGASUS=y +CONFIG_USB_USBNET=y +CONFIG_USB_NET_DM9601=y +CONFIG_USB_NET_MCS7830=y +CONFIG_ATH_COMMON=y +CONFIG_ATH_DEBUG=y +CONFIG_ATH6KL=y +CONFIG_ATH6KL_PLATFORM_DATA=y +CONFIG_ATH6KL_POLL=y +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_GPIO=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_UNIDISPLAY_TS=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_SAMSUNG=y +CONFIG_SERIAL_SAMSUNG_CONSOLE=y +CONFIG_HW_RANDOM=y +CONFIG_I2C=y +CONFIG_I2C_S3C2410=y +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y +CONFIG_SENSORS_EXYNOS4_TMU=y +CONFIG_POWER_SUPPLY=y +CONFIG_MFD_MAX8997=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_MAX8997=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_SAMSUNG_S5P_FIMC=y +CONFIG_VIDEO_S5P_FIMC=y +CONFIG_VIDEO_SAMSUNG_S5P_TV=y +CONFIG_VIDEO_SAMSUNG_S5P_HDMI=y +CONFIG_VIDEO_SAMSUNG_S5P_SDO=y +CONFIG_VIDEO_SAMSUNG_S5P_MIXER=y +CONFIG_V4L_MEM2MEM_DRIVERS=y +CONFIG_VIDEO_SAMSUNG_S5P_G2D=y +CONFIG_VIDEO_SAMSUNG_S5P_JPEG=y +CONFIG_VIDEO_SAMSUNG_S5P_MFC=y +CONFIG_FB=y +CONFIG_FB_S3C=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_PLATFORM=y +CONFIG_LCD_PWRCTRL=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_SAMSUNG=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_S5P=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_EXYNOS=y +CONFIG_USB_STORAGE=y +CONFIG_USB_GADGET=y +CONFIG_USB_S3C_HSOTG=y +CONFIG_USB_ZERO=m +CONFIG_USB_ETH=m +CONFIG_USB_ETH_EEM=y +CONFIG_USB_G_NCM=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_FUNCTIONFS=m +CONFIG_USB_FUNCTIONFS_ETH=y +CONFIG_USB_FUNCTIONFS_RNDIS=y +CONFIG_USB_FUNCTIONFS_GENERIC=y +CONFIG_USB_FILE_STORAGE=m +CONFIG_USB_FILE_STORAGE_TEST=y +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_USB_G_PRINTER=m +CONFIG_USB_CDC_COMPOSITE=m +CONFIG_USB_G_ACM_MS=m +CONFIG_USB_G_MULTI=m +CONFIG_USB_G_MULTI_CDC=y +CONFIG_USB_G_HID=m +CONFIG_USB_G_DBGP=m +CONFIG_USB_G_DBGP_PRINTK=y +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_S3C=y +CONFIG_MMC_SDHCI_S3C_DMA=y +CONFIG_STAGING=y +CONFIG_SND_SOC_ORIGEN_ALC5625=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_S3C=y +CONFIG_DEBUG_S3C_UART2=y diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c index 3a9c1f9475ce..8163f370d04b 100644 --- a/net/netfilter/xt_quota2.c +++ b/net/netfilter/xt_quota2.c @@ -350,14 +350,15 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { static int __init quota_mt2_init(void) { int ret; +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG struct netlink_kernel_cfg cfg = { .groups = 1, }; +#endif pr_debug("xt_quota2: init()"); #ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG - nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, - THIS_MODULE, &cfg); + nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, &cfg); if (!nflognl) return -ENOMEM; #endif diff --git a/scripts/setlocalversion b/scripts/setlocalversion index bd6dca8a0ab2..b65e8a93df04 100755 --- a/scripts/setlocalversion +++ b/scripts/setlocalversion @@ -47,7 +47,7 @@ scm_version() # If we are at a tagged commit (like "v2.6.30-rc6"), we ignore # it, because this version is defined in the top level Makefile. - if [ -z "`git describe --exact-match 2>/dev/null`" ]; then + if [ -z "`git describe --exact-match --match v[2-9].* 2>/dev/null`" ]; then # If only the short version is requested, don't bother # running further git commands @@ -57,7 +57,8 @@ scm_version() fi # If we are past a tagged commit (like # "v2.6.30-rc5-302-g72357d5"), we pretty print it. - if atag="`git describe 2>/dev/null`"; then + # Also match linux tags pattern to discard private tags + if atag="`git describe --match v[2-9].* 2>/dev/null`"; then echo "$atag" | awk -F- '{printf("-%05d-%s", $(NF-1),$(NF))}' # If we don't have a tag at all we print -g{commitish}. diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index b70964ea448c..62a37adf20a8 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c @@ -168,6 +168,7 @@ static int dma_hw_params(struct snd_pcm_substream *substream, req.cap = (samsung_dma_has_circular() ? DMA_CYCLIC : DMA_SLAVE); req.client = prtd->params->client; + req.dt_dmach_prop = prtd->params->dma_prop; config.direction = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM); @@ -432,8 +433,17 @@ static struct snd_soc_platform_driver samsung_asoc_platform = { .pcm_free = dma_free_dma_buffers, }; +static u64 asoc_dma_mask = DMA_BIT_MASK(32); + static int __devinit samsung_asoc_platform_probe(struct platform_device *pdev) { + int ret; + + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &asoc_dma_mask; + if (!pdev->dev.coherent_dma_mask) + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + return snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform); } @@ -443,10 +453,17 @@ static int __devexit samsung_asoc_platform_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id asoc_dma_of_match[] = { + { .compatible = "samsung,audio-dma", }, + { } +}; +MODULE_DEVICE_TABLE(of, asoc_dma_of_match); + static struct platform_driver asoc_dma_driver = { .driver = { .name = "samsung-audio", .owner = THIS_MODULE, + .of_match_table = asoc_dma_of_match, }, .probe = samsung_asoc_platform_probe, diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h index 7d1ead77ef21..2e60415244a3 100644 --- a/sound/soc/samsung/dma.h +++ b/sound/soc/samsung/dma.h @@ -19,6 +19,7 @@ struct s3c_dma_params { int dma_size; /* Size of the DMA transfer */ unsigned ch; struct samsung_dma_ops *ops; + struct property *dma_prop; }; #endif diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 40b00a13dcd1..9af60dc2b5d2 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -15,12 +15,15 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> #include <linux/pm_runtime.h> #include <sound/soc.h> #include <sound/pcm_params.h> #include <linux/platform_data/asoc-s3c.h> +#include <plat/dma-pl330.h> #include "dma.h" #include "idma.h" @@ -49,8 +52,6 @@ struct i2s_dai { struct clk *clk; /* Clock for generating I2S signals */ struct clk *op_clk; - /* Array of clock names for op_clk */ - const char **src_clk; /* Pointer to the Primary_Fifo if this is Sec_Fifo, NULL otherwise */ struct i2s_dai *pri_dai; /* Pointer to the Secondary_Fifo if it has one, NULL otherwise */ @@ -68,6 +69,8 @@ struct i2s_dai { u32 suspend_i2smod; u32 suspend_i2scon; u32 suspend_i2spsr; + unsigned long gpios[7]; /* i2s gpio line numbers */ + int dev_id; /* i2s dev id */ }; /* Lock for cross i/f checks */ @@ -385,6 +388,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, struct i2s_dai *i2s = to_info(dai); struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; u32 mod = readl(i2s->addr + I2SMOD); + char clk_name[16]; switch (clk_id) { case SAMSUNG_I2S_CDCLK: @@ -432,8 +436,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, } } + sprintf(clk_name, "i2s_opclk%d", clk_id); i2s->op_clk = clk_get(&i2s->pdev->dev, - i2s->src_clk[clk_id]); + clk_name); clk_enable(i2s->op_clk); i2s->rclk_srcrate = clk_get_rate(i2s->op_clk); @@ -851,6 +856,8 @@ static int i2s_resume(struct snd_soc_dai *dai) writel(i2s->suspend_i2scon, i2s->addr + I2SCON); writel(i2s->suspend_i2smod, i2s->addr + I2SMOD); writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR); + } else { + writel(CON_RSTCLR, i2s->addr + I2SCON); } return 0; @@ -980,8 +987,9 @@ struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS; } else { /* Create a new platform_device for Secondary */ i2s->pdev = platform_device_register_resndata(NULL, - pdev->name, pdev->id + SAMSUNG_I2S_SECOFF, - NULL, 0, NULL, 0); + "samsung-i2s", + i2s->dev_id + SAMSUNG_I2S_SECOFF, NULL, 0, + NULL, 0); if (IS_ERR(i2s->pdev)) return NULL; } @@ -992,49 +1000,149 @@ struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) return i2s; } +#ifdef CONFIG_OF +static int samsung_i2s_parse_dt_gpio(struct i2s_dai *i2s) +{ + struct device *dev = &i2s->pdev->dev; + int index, gpio, ret; + + for (index = 0; index < 7; index++) { + gpio = of_get_gpio(dev->of_node, index); + if (!gpio_is_valid(gpio)) { + dev_err(dev, "invalid gpio[%d]: %d\n", index, gpio); + goto free_gpio; + } + + ret = gpio_request(gpio, dev_name(dev)); + if (ret) { + dev_err(dev, "gpio [%d] request failed\n", gpio); + goto free_gpio; + } + i2s->gpios[index] = gpio; + } + return 0; + +free_gpio: + while (--index >= 0) + gpio_free(i2s->gpios[index]); + return -EINVAL; +} + +static void samsung_i2s_dt_gpio_free(struct i2s_dai *i2s) +{ + unsigned int index; + for (index = 0; index < 7; index++) + gpio_free(i2s->gpios[index]); +} +#else +static int samsung_i2s_parse_dt_gpio(struct i2s_dai *dai) +{ + return -EINVAL; +} + +static void samsung_i2s_dt_gpio_free(struct i2s_dai *dai) +{ +} + +#endif + static __devinit int samsung_i2s_probe(struct platform_device *pdev) { - u32 dma_pl_chan, dma_cp_chan, dma_pl_sec_chan; + u32 dma_pl_chan, dma_cp_chan; + u32 dma_pl_sec_chan = 0; struct i2s_dai *pri_dai, *sec_dai = NULL; - struct s3c_audio_pdata *i2s_pdata; - struct samsung_i2s *i2s_cfg; + struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data; + struct samsung_i2s *i2s_cfg = NULL; struct resource *res; - u32 regs_base, quirks; - int ret = 0; + u32 regs_base, quirks = 0, idma_addr = 0; + struct property *prop; + struct device_node *np = pdev->dev.of_node; + int ret = 0, id; /* Call during Seconday interface registration */ - if (pdev->id >= SAMSUNG_I2S_SECOFF) { + if (np) { + id = of_alias_get_id(np, "i2s"); + if (id < 0) { + dev_err(&pdev->dev, "failed to get alias id:%d\n", id); + return id; + } + } else { + id = pdev->id; + } + + if (id >= SAMSUNG_I2S_SECOFF) { sec_dai = dev_get_drvdata(&pdev->dev); snd_soc_register_dai(&sec_dai->pdev->dev, &sec_dai->i2s_dai_drv); return 0; } - i2s_pdata = pdev->dev.platform_data; - if (i2s_pdata == NULL) { - dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n"); - return -EINVAL; + pri_dai = i2s_alloc_dai(pdev, false); + if (!pri_dai) { + dev_err(&pdev->dev, "Unable to alloc I2S_pri\n"); + return -ENOMEM; } - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) { - dev_err(&pdev->dev, "Unable to get I2S-TX dma resource\n"); - return -ENXIO; - } - dma_pl_chan = res->start; + if (!np) { + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + dev_err(&pdev->dev, + "Unable to get I2S-TX dma resource\n"); + return -ENXIO; + } + dma_pl_chan = res->start; - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!res) { - dev_err(&pdev->dev, "Unable to get I2S-RX dma resource\n"); - return -ENXIO; - } - dma_cp_chan = res->start; + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!res) { + dev_err(&pdev->dev, + "Unable to get I2S-RX dma resource\n"); + return -ENXIO; + } + dma_cp_chan = res->start; - res = platform_get_resource(pdev, IORESOURCE_DMA, 2); - if (res) - dma_pl_sec_chan = res->start; - else - dma_pl_sec_chan = 0; + res = platform_get_resource(pdev, IORESOURCE_DMA, 2); + if (res) + dma_pl_sec_chan = res->start; + + i2s_cfg = &i2s_pdata->type.i2s; + quirks = i2s_cfg->quirks; + idma_addr = i2s_cfg->idma_addr; + } else { + prop = of_find_property(np, "tx-dma-channel", NULL); + if (!prop) { + dev_err(&pdev->dev, "tx dma channel property not"\ + "specified\n"); + return -ENXIO; + } + dma_pl_chan = DMACH_DT_PROP; + pri_dai->dma_playback.dma_prop = prop; + + prop = of_find_property(np, "rx-dma-channel", NULL); + if (!prop) { + dev_err(&pdev->dev, "tx dma channel property not"\ + "specified\n"); + return -ENXIO; + } + dma_cp_chan = DMACH_DT_PROP; + pri_dai->dma_capture.dma_prop = prop; + + if (of_find_property(np, "supports-6ch", NULL)) + quirks |= QUIRK_PRI_6CHAN; + + if (of_find_property(np, "supports-secdai", NULL)) + quirks |= QUIRK_SEC_DAI; + + if (of_find_property(np, "supports-rstclr", NULL)) + quirks |= QUIRK_NEED_RSTCLR; + + if (of_property_read_u32(np, "idma-addr", &idma_addr)) { + if (quirks & QUIRK_SEC_DAI) { + dev_err(&pdev->dev, "idma address is not"\ + "specified"); + return -EINVAL; + } + } + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -1049,16 +1157,6 @@ static __devinit int samsung_i2s_probe(struct platform_device *pdev) } regs_base = res->start; - i2s_cfg = &i2s_pdata->type.i2s; - quirks = i2s_cfg->quirks; - - pri_dai = i2s_alloc_dai(pdev, false); - if (!pri_dai) { - dev_err(&pdev->dev, "Unable to alloc I2S_pri\n"); - ret = -ENOMEM; - goto err; - } - pri_dai->dma_playback.dma_addr = regs_base + I2STXD; pri_dai->dma_capture.dma_addr = regs_base + I2SRXD; pri_dai->dma_playback.client = @@ -1067,11 +1165,11 @@ static __devinit int samsung_i2s_probe(struct platform_device *pdev) (struct s3c2410_dma_client *)&pri_dai->dma_capture; pri_dai->dma_playback.channel = dma_pl_chan; pri_dai->dma_capture.channel = dma_cp_chan; - pri_dai->src_clk = i2s_cfg->src_clk; pri_dai->dma_playback.dma_size = 4; pri_dai->dma_capture.dma_size = 4; pri_dai->base = regs_base; pri_dai->quirks = quirks; + pri_dai->dev_id = id; if (quirks & QUIRK_PRI_6CHAN) pri_dai->i2s_dai_drv.playback.channels_max = 6; @@ -1086,21 +1184,42 @@ static __devinit int samsung_i2s_probe(struct platform_device *pdev) sec_dai->dma_playback.dma_addr = regs_base + I2STXDS; sec_dai->dma_playback.client = (struct s3c2410_dma_client *)&sec_dai->dma_playback; + + if (np) { + prop = of_find_property(np, "tx-dma-channel-secondary", + NULL); + if (!prop) { + dev_err(&pdev->dev, "tx dma channel property"\ + "not specified\n"); + ret = -ENXIO; + goto err; + } + sec_dai->dma_playback.dma_prop = prop; + } + /* Use iDMA always if SysDMA not provided */ sec_dai->dma_playback.channel = dma_pl_sec_chan ? : -1; - sec_dai->src_clk = i2s_cfg->src_clk; sec_dai->dma_playback.dma_size = 4; sec_dai->base = regs_base; sec_dai->quirks = quirks; - sec_dai->idma_playback.dma_addr = i2s_cfg->idma_addr; + sec_dai->dev_id = id; + sec_dai->idma_playback.dma_addr = idma_addr; sec_dai->pri_dai = pri_dai; pri_dai->sec_dai = sec_dai; } - if (i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { - dev_err(&pdev->dev, "Unable to configure gpio\n"); - ret = -EINVAL; - goto err; + if (np) { + if (samsung_i2s_parse_dt_gpio(pri_dai)) { + dev_err(&pdev->dev, "Unable to configure gpio\n"); + ret = -EINVAL; + goto err; + } + } else { + if (i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { + dev_err(&pdev->dev, "Unable to configure gpio\n"); + ret = -EINVAL; + goto err; + } } snd_soc_register_dai(&pri_dai->pdev->dev, &pri_dai->i2s_dai_drv); @@ -1118,10 +1237,14 @@ static __devexit int samsung_i2s_remove(struct platform_device *pdev) { struct i2s_dai *i2s, *other; struct resource *res; + struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data; i2s = dev_get_drvdata(&pdev->dev); other = i2s->pri_dai ? : i2s->sec_dai; + if (!i2s_pdata->cfg_gpio && pdev->dev.of_node) + samsung_i2s_dt_gpio_free(i2s->pri_dai); + if (other) { other->pri_dai = NULL; other->sec_dai = NULL; @@ -1140,12 +1263,21 @@ static __devexit int samsung_i2s_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id exynos_i2s_match[] = { + { .compatible = "samsung,samsung-i2s" }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_i2s_match); +#endif + static struct platform_driver samsung_i2s_driver = { .probe = samsung_i2s_probe, .remove = __devexit_p(samsung_i2s_remove), .driver = { .name = "samsung-i2s", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(exynos_i2s_match), }, }; |