Merge "msm7627a: Add support for evaluation board platform"
diff --git a/app/aboot/aboot.c b/app/aboot/aboot.c
index 9f796c0..6cf6264 100644
--- a/app/aboot/aboot.c
+++ b/app/aboot/aboot.c
@@ -842,19 +842,20 @@
void cmd_erase_mmc(const char *arg, void *data, unsigned sz)
{
unsigned long long ptn = 0;
+ unsigned int out[512] = {0};
int index = INVALID_PTN;
- unsigned long long size;
index = partition_get_index(arg);
ptn = partition_get_offset(index);
- size = partition_get_size(index);
if(ptn == 0) {
fastboot_fail("Partition table doesn't exist\n");
return;
}
- if (mmc_erase_card(ptn, size)) {
- fastboot_fail("Failed to erase partition\n");
+ /* Simple inefficient version of erase. Just writing
+ 0 in first block */
+ if (mmc_write(ptn , 512, (unsigned int *)out)) {
+ fastboot_fail("failed to erase partition");
return;
}
fastboot_okay("");
@@ -1275,6 +1276,7 @@
fastboot_register("oem device-info", cmd_oem_devinfo);
fastboot_publish("product", TARGET(BOARD));
fastboot_publish("kernel", "lk");
+ fastboot_publish("serialno", sn_buf);
partition_dump();
sz = target_get_max_flash_size();
fastboot_init(target_get_scratch_address(), sz);
diff --git a/app/aboot/fastboot.c b/app/aboot/fastboot.c
index 0b3d166..2b699b3 100644
--- a/app/aboot/fastboot.c
+++ b/app/aboot/fastboot.c
@@ -376,6 +376,10 @@
fastboot_publish("version", "0.5");
thr = thread_create("fastboot", fastboot_handler, 0, DEFAULT_PRIORITY, 4096);
+ if (!thr)
+ {
+ goto fail_alloc_in;
+ }
thread_resume(thr);
return 0;
diff --git a/app/app.c b/app/app.c
index 2aad5f4..547659a 100644
--- a/app/app.c
+++ b/app/app.c
@@ -59,8 +59,14 @@
static void start_app(const struct app_descriptor *app)
{
+ thread_t *thr;
printf("starting app %s\n", app->name);
- thread_resume(thread_create(app->name, &app_thread_entry, (void *)app, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
+ thr = thread_create(app->name, &app_thread_entry, (void *)app, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
+ if(!thr)
+ {
+ return;
+ }
+ thread_resume(thr);
}
diff --git a/app/tests/led_tests.c b/app/tests/led_tests.c
new file mode 100644
index 0000000..97c6039
--- /dev/null
+++ b/app/tests/led_tests.c
@@ -0,0 +1,43 @@
+#include<dev/pm8921_leds.h>
+#include<dev/pm8921.h>
+
+void led_tests()
+{
+uint32_t duty_us, period_us;
+
+ /* 50% Duty cycle */
+ duty_us = 500000;
+ period_us = 1000000;
+
+ /* Configure PM8921_ID_LED_0 from PWM2*/
+ pm8921_config_led_current(PM8921_ID_LED_0, 2, PWM2, 1);
+
+ /* PWM2 for PM8921_ID_LED_0 is LPG 5
+ * Configure and enable lpg5
+ */
+ pm_set_pwm_config(5, duty_us, period_us, &pmic);
+ pm_pwm_channel_enable(5, &pmic);
+
+ /* Configure and enable lpg0 for panel backlight*/
+ pm_set_pwm_config(0, duty_us, period_us, &pmic);
+ pm_pwm_channel_enable(0, &pmic);
+
+ mdelay(10000);
+
+ /* Configure PM8921_ID_LED_1 also from PWM2*/
+ pm8921_config_led_current(PM8921_ID_LED_1, 2, PWM2, 1);
+ mdelay(10000);
+
+ /* Disable PM8921_ID_LED_0 */
+ pm8921_config_led_current(PM8921_ID_LED_0, 2, 2, 0);
+
+ /* Turn on GPIO 24 through LPG 0
+ * Will be reconfigured during display_init
+ */
+ panel_backlight_on_pwm();
+
+ mdelay(10000);
+
+ /* Disable PM8921_ID_LED_1 */
+ pm8921_config_led_current(PM8921_ID_LED_1, 2, 2, 0);
+}
diff --git a/arch/arm/include/arch/arm/mmu.h b/arch/arm/include/arch/arm/mmu.h
index 1b777a3..d5067eb 100644
--- a/arch/arm/include/arch/arm/mmu.h
+++ b/arch/arm/include/arch/arm/mmu.h
@@ -47,6 +47,8 @@
#define MMU_MEMORY_AP_READ_ONLY (0x7 << 10)
#define MMU_MEMORY_AP_READ_WRITE (0x3 << 10)
+#define MMU_MEMORY_XN (0x1 << 4)
+
#else
#error "MMU implementation needs to be updated for this ARM architecture"
diff --git a/dev/pmic/pm8921/include/dev/pm8921.h b/dev/pmic/pm8921/include/dev/pm8921.h
index 7c2b271..e2c185b 100644
--- a/dev/pmic/pm8921/include/dev/pm8921.h
+++ b/dev/pmic/pm8921/include/dev/pm8921.h
@@ -30,7 +30,7 @@
#define __DEV_PM8921_H
#include <sys/types.h>
-
+#include <dev/pm8921_leds.h>
#define PM_GPIO_DIR_OUT 0x01
#define PM_GPIO_DIR_IN 0x02
@@ -109,5 +109,12 @@
int pm8921_ldo_set_voltage(uint32_t ldo_id, uint32_t voltage);
int pm8921_config_reset_pwr_off(unsigned reset);
int pm8921_gpio_get(uint8_t gpio, uint8_t *status);
+int pm8921_config_led_current(enum pm8921_leds led_num,
+ uint8_t current,
+ enum led_mode sink,
+ int enable);
+int pm8921_config_drv_keypad(unsigned int drv_flash_sel,
+ unsigned int flash_logic,
+ unsigned int flash_ensel);
#endif
diff --git a/dev/pmic/pm8921/include/dev/pm8921_leds.h b/dev/pmic/pm8921/include/dev/pm8921_leds.h
new file mode 100644
index 0000000..faad2f5
--- /dev/null
+++ b/dev/pmic/pm8921/include/dev/pm8921_leds.h
@@ -0,0 +1,99 @@
+/*
+ * * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __PMIC_LED_H
+#define __PMIC_LED_H
+
+/* LED CNTL register */
+#define LED_CNTL_BASE 0x131
+#define PM8921_LED_CNTL_REG(n) (LED_CNTL_BASE + n)
+
+#define LED_DRV0_CNTL PM8921_LED_CNTL_REG(0)
+#define LED_DRV1_CNTL PM8921_LED_CNTL_REG(1)
+#define LED_DRV2_CNTL PM8921_LED_CNTL_REG(2)
+
+/* LED CNTL setting */
+
+/* Current settings:
+ * [00000, 10100]: Iout = x * 2 mA
+ * [10101, 11111]: invalid settings
+ */
+
+#define LED_CURRENT_SET(x) ((x) << 3)
+#define LED_SIGNAL_SELECT(x) ((x) << 0)
+
+#define PM8921_DRV_KEYPAD_CNTL_REG 0x48
+
+/* Keypad DRV CNTL Settings */
+
+#define DRV_FLASH_SEL(x) ((x) << 4)
+#define FLASH_LOGIC_SEL(x) ((x) << 2)
+#define FLASH_ENSEL(x) ((x) << 0)
+
+#define MAX_LC_LED_BRIGHTNESS 20
+#define MAX_FLASH_BRIGHTNESS 15
+#define MAX_KB_LED_BRIGHTNESS 15
+
+/*
+ * led ids
+ * @PM8921_ID_LED_0 - First low current led
+ * @PM8921_ID_LED_1 - Second low current led
+ * @PM8921_ID_LED_2 - Third low current led
+ */
+
+enum pm8921_leds {
+ PM8921_ID_LED_0,
+ PM8921_ID_LED_1,
+ PM8921_ID_LED_2,
+};
+
+enum led_mode{
+ MANUAL,
+ PWM1,
+ PWM2,
+ PWM3,
+ DBUS1,
+ DBUS2,
+ DBUS3,
+ DBUS4,
+};
+
+enum kp_backlight_mode{
+ MANUAL_MODE,
+ DBUS1_MODE,
+ DBUS2_MODE,
+ LPG_MODE,
+};
+
+enum kp_backlight_flash_logic{
+ FLASH_ON_WITH_DTEST_HIGH,
+ FLASH_ON_WITH_DTEST_LOW,
+};
+
+#endif
diff --git a/dev/pmic/pm8921/include/dev/pm8921_pwm.h b/dev/pmic/pm8921/include/dev/pm8921_pwm.h
new file mode 100644
index 0000000..23b0930
--- /dev/null
+++ b/dev/pmic/pm8921/include/dev/pm8921_pwm.h
@@ -0,0 +1,166 @@
+/*
+ * * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __PMIC_PWM_H
+#define __PMIC_PWM_H
+
+/* PMIC 8921 LPG defines */
+
+#define PM8921_LPG_CTL_BASE (0x13C)
+
+#define PM8921_LPG_CTL(n) (PM8921_LPG_CTL_BASE + (n))
+#define PM8921_LPG_BANK_SEL (0x143)
+#define PM8921_LPG_BANK_ENABLE (0x144)
+
+#define USEC_PER_SEC 1000000L
+#define NSEC_PER_SEC 1000000000L
+#define NSEC_PER_USEC 1000
+
+#define PWM_FREQ_HZ 300
+#define PWM_LEVEL 15
+
+#define NUM_CLOCKS 3
+#define NUM_PRE_DIVIDE 4
+
+#define NUM_LPG_CTL_REGS 7
+
+#define PRE_DIVIDE_0 2
+#define PRE_DIVIDE_1 3
+#define PRE_DIVIDE_2 5
+#define PRE_DIVIDE_3 6
+
+#define PRE_DIVIDE_MIN PRE_DIVIDE_0
+#define PRE_DIVIDE_MAX PRE_DIVIDE_3
+
+#define PM_PWM_M_MIN 0
+#define PM_PWM_M_MAX 7
+
+#define NSEC_1000HZ (NSEC_PER_SEC / 1000)
+#define NSEC_32768HZ (NSEC_PER_SEC / 32768)
+#define NSEC_19P2MHZ (NSEC_PER_SEC / 19200000)
+
+#define CLK_PERIOD_MIN NSEC_19P2MHZ
+#define CLK_PERIOD_MAX NSEC_1000HZ
+
+#define MIN_MPT ((PRE_DIVIDE_MIN * CLK_PERIOD_MIN) << PM_PWM_M_MIN)
+#define MAX_MPT ((PRE_DIVIDE_MAX * CLK_PERIOD_MAX) << PM_PWM_M_MAX)
+
+/* The MAX value is computation limit. Hardware limit is 393 seconds. */
+#define PM_PWM_PERIOD_MAX (274 * USEC_PER_SEC)
+/* The MIN value is hardware limit. */
+#define PM_PWM_PERIOD_MIN 7 /* micro seconds */
+
+#define PWM_PERIOD_USEC (USEC_PER_SEC / PWM_FREQ_HZ)
+#define PWM_DUTY_LEVEL (PWM_PERIOD_USEC / PWM_LEVEL)
+
+/* Control 0 */
+#define PM_PWM_1KHZ_COUNT_MASK 0xF0
+#define PM_PWM_1KHZ_COUNT_SHIFT 4
+
+#define PM_PWM_1KHZ_COUNT_MAX 15
+
+#define PM_PWM_OUTPUT_EN 0x08
+#define PM_PWM_PWM_EN 0x04
+#define PM_PWM_RAMP_GEN_EN 0x02
+#define PM_PWM_RAMP_START 0x01
+
+#define PM_PWM_PWM_START (PM_PWM_OUTPUT_EN | PM_PWM_PWM_EN)
+#define PM_PWM_RAMP_GEN_START (PM_PWM_RAMP_GEN_EN | PM_PWM_RAMP_START)
+
+/* Control 1 */
+#define PM_PWM_REVERSE_EN 0x80
+#define PM_PWM_BYPASS_LUT 0x40
+#define PM_PWM_HIGH_INDEX_MASK 0x3F
+
+/* Control 2 */
+#define PM_PWM_LOOP_EN 0x80
+#define PM_PWM_RAMP_UP 0x40
+#define PM_PWM_LOW_INDEX_MASK 0x3F
+
+/* Control 3 */
+#define PM_PWM_VALUE_BIT7_0 0xFF
+#define PM_PWM_VALUE_BIT5_0 0x3F
+
+/* Control 4 */
+#define PM_PWM_VALUE_BIT8 0x80
+
+#define PM_PWM_CLK_SEL_MASK 0x60
+#define PM_PWM_CLK_SEL_SHIFT 5
+
+#define PM_PWM_CLK_SEL_NO 0
+#define PM_PWM_CLK_SEL_1KHZ 1
+#define PM_PWM_CLK_SEL_32KHZ 2
+#define PM_PWM_CLK_SEL_19P2MHZ 3
+
+#define PM_PWM_PREDIVIDE_MASK 0x18
+#define PM_PWM_PREDIVIDE_SHIFT 3
+
+#define PM_PWM_PREDIVIDE_2 0
+#define PM_PWM_PREDIVIDE_3 1
+#define PM_PWM_PREDIVIDE_5 2
+#define PM_PWM_PREDIVIDE_6 3
+
+#define PM_PWM_M_MASK 0x07
+#define PM_PWM_M_MIN 0
+#define PM_PWM_M_MAX 7
+
+/* Control 5 */
+#define PM_PWM_PAUSE_COUNT_HI_MASK 0xFC
+#define PM_PWM_PAUSE_COUNT_HI_SHIFT 2
+
+#define PM_PWM_PAUSE_ENABLE_HIGH 0x02
+#define PM_PWM_SIZE_9_BIT 0x01
+
+/* Control 6 */
+#define PM_PWM_PAUSE_COUNT_LO_MASK 0xFC
+#define PM_PWM_PAUSE_COUNT_LO_SHIFT 2
+
+#define PM_PWM_PAUSE_ENABLE_LOW 0x02
+#define PM_PWM_RESERVED 0x01
+
+#define PM_PWM_PAUSE_COUNT_MAX 56 /* < 2^6 = 64*/
+
+struct pm8921_pwm_config {
+ uint8_t pwm_size; /* round up to 6 or 9 for 6/9-bit PWM SIZE */
+ uint8_t clk;
+ uint8_t pre_div;
+ uint8_t pre_div_exp;
+ uint8_t pwm_value;
+ uint8_t bypass_lut;
+ uint8_t pwm_ctl[NUM_LPG_CTL_REGS];
+};
+
+/* External PWM functions */
+int pm8921_pwm_enable(uint8_t pwm_id, pm8921_dev_t *dev);
+int pm8921_pwm_config(uint8_t pwm_id,
+ uint32_t duty_us,
+ uint32_t period_us,
+ pm8921_dev_t *dev);
+
+#endif
diff --git a/dev/pmic/pm8921/pm8921.c b/dev/pmic/pm8921/pm8921.c
index 4ff05fa..c6ee438 100644
--- a/dev/pmic/pm8921/pm8921.c
+++ b/dev/pmic/pm8921/pm8921.c
@@ -31,7 +31,6 @@
#include <dev/pm8921.h>
#include "pm8921_hw.h"
-
static pm8921_dev_t *dev;
static uint8_t ldo_n_voltage_mult[LDO_VOLTAGE_ENTRIES] = {
@@ -292,3 +291,149 @@
return rc;
}
+/* A wrapper function to configure PMIC PWM
+ * pwm_id : Channel number to configure
+ * duty_us : duty cycle for output waveform in micro seconds
+ * period_us : period for output waveform in micro seconds
+ */
+int pm8921_set_pwm_config(uint8_t pwm_id, uint32_t duty_us, uint32_t period_us)
+{
+ int rc;
+
+ rc = pm8921_pwm_config(pwm_id, duty_us, period_us, dev);
+
+ return rc;
+}
+
+/* A wrapper function to enable PMIC PWM
+ * pwm_id : Channel number to enable
+ */
+int pm8921_pwm_channel_enable(uint8_t pwm_id)
+{
+ int rc;
+
+ rc = pm8921_pwm_enable(pwm_id, dev);
+
+ return rc;
+}
+
+/* Configure LED's for current sinks
+ * enable = 1: Configure external signal detection
+ * for the sink with the current level
+ * enable = 0: Turn off external signal detection
+ *
+ * Values for sink are defined as follows:
+ * 0 = MANUAL, turn on LED when curent [00000, 10100]
+ * 1 = PWM1
+ * 2 = PWM2
+ * 3 = PWM3
+ * 4 = DBUS1
+ * 5 = DBUS2
+ * 6 = DBUS3
+ * 7 = DBUS4
+ *
+ * Current settings are calculated as per the equation:
+ * [00000, 10100]: Iout = current * 2 mA
+ * [10101, 11111]: invalid settings
+ */
+
+int pm8921_config_led_current(enum pm8921_leds led_num,
+ uint8_t current,
+ enum led_mode sink,
+ int enable)
+{
+ uint8_t val;
+ int ret;
+
+ /* Program the CTRL reg */
+ val = 0x0;
+
+ if (enable != 0)
+ {
+
+ if (current > 0x15)
+ {
+ dprintf(CRITICAL, "Invalid current settings for PM8921 LED Ctrl Reg \
+ current=%d.\n", current);
+ return -1;
+ }
+
+ if (sink > 0x7)
+ {
+ dprintf(CRITICAL, "Invalid signal selection for PM8921 LED Ctrl Reg \
+ sink=%d.\n", sink);
+ return -1;
+ }
+
+ val |= LED_CURRENT_SET(current);
+ val |= LED_SIGNAL_SELECT(sink);
+ }
+
+ ret = dev->write(&val, 1, PM8921_LED_CNTL_REG(led_num));
+
+ if (ret)
+ dprintf(CRITICAL, "Failed to write to PM8921 LED Ctrl Reg ret=%d.\n", ret);
+
+ return ret;
+
+}
+
+/* Configure DRV_KEYPAD
+ *drv_flash_sel:
+ * 0000 = off
+ * Iout = drv_flash_sel * 20 mA (300 mA driver)
+ * Iout = drv_flash_sel * 40 mA (600 mA driver)
+ *
+ * flash_logic = 0 : flash is on when DTEST is high
+ * flash_logic = 0 : flash is off when DTEST is high
+ *
+ * flash_ensel = 0 : manual mode, turn on flash when drv_flash_sel > 0
+ * flash_ensel = 1 : DBUS1
+ * flash_ensel = 2 : DBUS2
+ * flash_ensel = 3 : enable flash from LPG
+ */
+
+int pm8921_config_drv_keypad(unsigned int drv_flash_sel, unsigned int flash_logic, unsigned int flash_ensel)
+{
+ uint8_t val;
+ int ret;
+
+ /* Program the CTRL reg */
+ val = 0x0;
+
+ if (drv_flash_sel != 0)
+ {
+ if (drv_flash_sel > 0x0F)
+ {
+ dprintf(CRITICAL, "Invalid current settings for PM8921 \
+ KEYPAD_DRV Ctrl Reg drv_flash_sel=%d.\n", drv_flash_sel);
+ return -1;
+ }
+
+ if (flash_logic > 1)
+ {
+ dprintf(CRITICAL, "Invalid signal selection for PM8921 \
+ KEYPAD_DRV Ctrl Reg flash_logic=%d.\n", flash_logic);
+ return -1;
+ }
+
+ if (flash_ensel > 3)
+ {
+ dprintf(CRITICAL, "Invalid signal selection for PM8921 \
+ KEYPAD_DRV Ctrl Reg flash_ensel=%d.\n", flash_ensel);
+ return -1;
+ }
+
+ val |= DRV_FLASH_SEL(drv_flash_sel);
+ val |= FLASH_LOGIC_SEL(flash_logic);
+ val |= FLASH_ENSEL(flash_ensel);
+ }
+
+ ret = dev->write(&val, 1, PM8921_DRV_KEYPAD_CNTL_REG);
+
+ if (ret)
+ dprintf(CRITICAL, "Failed to write to PM8921 KEYPAD_DRV Ctrl Reg ret=%d.\n", ret);
+
+ return ret;
+
+}
diff --git a/dev/pmic/pm8921/pm8921_pwm.c b/dev/pmic/pm8921/pm8921_pwm.c
new file mode 100644
index 0000000..de89778
--- /dev/null
+++ b/dev/pmic/pm8921/pm8921_pwm.c
@@ -0,0 +1,327 @@
+/*
+ * * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <debug.h>
+#include <dev/pm8921.h>
+#include <dev/pm8921_pwm.h>
+
+
+static char *clks[NUM_CLOCKS] = {
+ "1K", "32768", "19.2M"
+};
+
+static unsigned pre_div[NUM_PRE_DIVIDE] = {
+ PRE_DIVIDE_0, PRE_DIVIDE_1, PRE_DIVIDE_2, PRE_DIVIDE_3
+};
+
+static unsigned int pt_t[NUM_PRE_DIVIDE][NUM_CLOCKS] = {
+ { PRE_DIVIDE_0 * NSEC_1000HZ,
+ PRE_DIVIDE_0 * NSEC_32768HZ,
+ PRE_DIVIDE_0 * NSEC_19P2MHZ,
+ },
+ { PRE_DIVIDE_1 * NSEC_1000HZ,
+ PRE_DIVIDE_1 * NSEC_32768HZ,
+ PRE_DIVIDE_1 * NSEC_19P2MHZ,
+ },
+ { PRE_DIVIDE_2 * NSEC_1000HZ,
+ PRE_DIVIDE_2 * NSEC_32768HZ,
+ PRE_DIVIDE_2 * NSEC_19P2MHZ,
+ },
+
+ { PRE_DIVIDE_2 * NSEC_1000HZ,
+ PRE_DIVIDE_2 * NSEC_32768HZ,
+ PRE_DIVIDE_2 * NSEC_19P2MHZ,
+ },
+};
+
+static uint16_t duty_msec[PM_PWM_1KHZ_COUNT_MAX + 1] = {
+ 0, 1, 2, 3, 4, 6, 8, 16, 18, 24, 32, 36, 64, 128, 256, 512
+};
+
+static uint16_t pause_count[PM_PWM_PAUSE_COUNT_MAX + 1] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 23, 28, 31, 42, 47, 56, 63, 83, 94, 111, 125, 167, 188, 222, 250, 333,
+ 375, 500, 667, 750, 800, 900, 1000, 1100,
+ 1200, 1300, 1400, 1500, 1600, 1800, 2000, 2500,
+ 3000, 3500, 4000, 4500, 5000, 5500, 6000, 6500,
+ 7000
+};
+
+/* Function to get the PWM size, divider, clock for the given period */
+
+static void pm8921_pwm_calc_period(uint32_t period_us,
+ struct pm8921_pwm_config *pwm_conf)
+{
+ int n, m, clk, div;
+ int best_m, best_div, best_clk;
+ int last_err, cur_err, better_err, better_m;
+ uint32_t tmp_p, last_p, min_err, period_n;
+
+ /* PWM Period / N : handle underflow or overflow */
+ if (period_us < (PM_PWM_PERIOD_MAX / NSEC_PER_USEC))
+ period_n = (period_us * NSEC_PER_USEC) >> 6;
+ else
+ period_n = (period_us >> 6) * NSEC_PER_USEC;
+
+ if (period_n >= MAX_MPT)
+ {
+ n = 9;
+ period_n >>= 3;
+ }
+ else
+ n = 6;
+
+ min_err = MAX_MPT;
+ best_m = 0;
+ best_clk = 0;
+ best_div = 0;
+
+ for (clk = 0; clk < NUM_CLOCKS; clk++)
+ {
+ for (div = 0; div < NUM_PRE_DIVIDE; div++)
+ {
+ tmp_p = period_n;
+ last_p = tmp_p;
+ for (m = 0; m <= PM_PWM_M_MAX; m++)
+ {
+ if (tmp_p <= pt_t[div][clk])
+ {
+ /* Found local best */
+ if (!m)
+ {
+ better_err = pt_t[div][clk] - tmp_p;
+ better_m = m;
+ }
+ else
+ {
+ last_err = last_p - pt_t[div][clk];
+ cur_err = pt_t[div][clk] - tmp_p;
+
+ if (cur_err < last_err)
+ {
+ better_err = cur_err;
+ better_m = m;
+ }
+ else
+ {
+ better_err = last_err;
+ better_m = m - 1;
+ }
+ }
+
+ if (better_err < min_err)
+ {
+ min_err = better_err;
+ best_m = better_m;
+ best_clk = clk;
+ best_div = div;
+ }
+ break;
+ }
+ else
+ {
+ last_p = tmp_p;
+ tmp_p >>= 1;
+ }
+ }
+ }
+ }
+
+ pwm_conf->pwm_size = n;
+ pwm_conf->clk = best_clk;
+ pwm_conf->pre_div = best_div;
+ pwm_conf->pre_div_exp = best_m;
+}
+
+/* Function to configure PWM control registers with clock, divider values */
+
+static int pm8921_pwm_configure(uint8_t pwm_id,
+ struct pm8921_pwm_config *pwm_conf,
+ pm8921_dev_t *dev)
+{
+ int i, len, rc = -1;
+ uint8_t reg;
+
+ reg = (pwm_conf->pwm_size > 6) ? PM_PWM_SIZE_9_BIT : 0;
+ pwm_conf->pwm_ctl[5] = reg;
+
+ reg = ((pwm_conf->clk + 1) << PM_PWM_CLK_SEL_SHIFT)
+ & PM_PWM_CLK_SEL_MASK;
+ reg |= (pwm_conf->pre_div << PM_PWM_PREDIVIDE_SHIFT)
+ & PM_PWM_PREDIVIDE_MASK;
+ reg |= pwm_conf->pre_div_exp & PM_PWM_M_MASK;
+ pwm_conf->pwm_ctl[4] = reg;
+
+ /* Just to let know we bypass LUT */
+ if (pwm_conf->bypass_lut)
+ {
+ /* CTL0 is set in pwm_enable() */
+ pwm_conf->pwm_ctl[0] &= PM_PWM_PWM_START;
+ pwm_conf->pwm_ctl[1] = PM_PWM_BYPASS_LUT;
+ pwm_conf->pwm_ctl[2] = 0;
+
+ if (pwm_conf->pwm_size > 6)
+ {
+ pwm_conf->pwm_ctl[3] = pwm_conf->pwm_value
+ & PM_PWM_VALUE_BIT7_0;
+ pwm_conf->pwm_ctl[4] |= (pwm_conf->pwm_value >> 1)
+ & PM_PWM_VALUE_BIT8;
+ }
+ else
+ {
+ pwm_conf->pwm_ctl[3] = pwm_conf->pwm_value
+ & PM_PWM_VALUE_BIT5_0;
+ }
+
+ len = 6;
+ }
+ else
+ {
+ /* Right now, we are not using LUT */
+ goto bail_out;
+ }
+
+ /* Selecting the bank */
+ rc = dev->write(&pwm_id, 1, PM8921_LPG_BANK_SEL);
+ if (rc)
+ goto bail_out;
+
+ for (i = 0; i < len; i++)
+ {
+ rc = dev->write(&pwm_conf->pwm_ctl[i], 1, PM8921_LPG_CTL(i));
+ if (rc)
+ {
+ dprintf(CRITICAL, "pm8921_write() failed in \
+ pwm_configure %d\n", rc);
+ break;
+ }
+ }
+
+bail_out:
+ if (rc)
+ dprintf(CRITICAL, "Error in pm8921_pwm_configure()\n");
+ return rc;
+}
+
+/* Top level function for configuring PWM
+ * Always called from the main pm8921.c file
+ */
+
+int pm8921_pwm_config(uint8_t pwm_id,
+ uint32_t duty_us,
+ uint32_t period_us,
+ pm8921_dev_t *dev)
+{
+ struct pm8921_pwm_config pwm_conf;
+ uint32_t max_pwm_value, tmp;
+ int rc = -1;
+
+ if ((duty_us > period_us) || (period_us > PM_PWM_PERIOD_MAX) ||
+ (period_us < PM_PWM_PERIOD_MIN))
+ {
+ dprintf(CRITICAL, "Error in duty cycle and period\n");
+ return -1;
+ }
+
+ pm8921_pwm_calc_period(period_us, &pwm_conf);
+
+ /* Figure out pwm_value with overflow handling */
+ if (period_us > (1 << pwm_conf.pwm_size))
+ {
+ tmp = period_us;
+ tmp >>= pwm_conf.pwm_size;
+ pwm_conf.pwm_value = duty_us / tmp;
+ }
+ else
+ {
+ tmp = duty_us;
+ tmp <<= pwm_conf.pwm_size;
+ pwm_conf.pwm_value = tmp / period_us;
+ }
+
+ max_pwm_value = (1 << pwm_conf.pwm_size) - 1;
+
+ if (pwm_conf.pwm_value > max_pwm_value)
+ pwm_conf.pwm_value = max_pwm_value;
+
+ /* Bypassing LUT */
+ pwm_conf.bypass_lut = 1;
+
+ dprintf(SPEW, "duty/period=%u/%u usec: pwm_value=%d (of %d)\n",
+ duty_us, period_us, pwm_conf.pwm_value,
+ 1 << pwm_conf.pwm_size);
+
+ rc = pm8921_pwm_configure(pwm_id, &pwm_conf, dev);
+
+ if (rc)
+ dprintf(CRITICAL, "Error in pwm_config()\n");
+
+ return rc;
+}
+
+/* Top level function to enable PWM with specified id
+ * Always called from the main pm8921.c file
+ */
+
+
+int pm8921_pwm_enable(uint8_t pwm_id, pm8921_dev_t *dev)
+{
+ int rc = -1;
+ uint8_t reg;
+
+ /* Read it before enabling other bank */
+ rc = dev->read(®, 1, PM8921_LPG_BANK_ENABLE);
+ if (rc)
+ goto bail_out;
+
+ reg |= (1 << pwm_id);
+
+ rc = dev->write(®, 1, PM8921_LPG_BANK_ENABLE);
+ if (rc)
+ goto bail_out;
+
+ /* Selecting the bank */
+ rc = dev->write(&pwm_id, 1, PM8921_LPG_BANK_SEL);
+ if (rc)
+ goto bail_out;
+
+ /* Read it before setting PWM start */
+ rc = dev->read(®, 1, PM8921_LPG_CTL(0));
+ if (rc)
+ goto bail_out;
+
+ reg |= PM_PWM_PWM_START;
+ reg &= ~PM_PWM_RAMP_GEN_START;
+ rc = dev->write(®, 1, PM8921_LPG_CTL(0));
+
+bail_out:
+ if (rc)
+ dprintf(CRITICAL, "Error in pwm_enable()\n");
+ return rc;
+}
diff --git a/dev/pmic/pm8921/rules.mk b/dev/pmic/pm8921/rules.mk
index e377786..1ab80b1 100644
--- a/dev/pmic/pm8921/rules.mk
+++ b/dev/pmic/pm8921/rules.mk
@@ -3,5 +3,6 @@
INCLUDES += -I$(LOCAL_DIR)/include
OBJS += \
- $(LOCAL_DIR)/pm8921.o
+ $(LOCAL_DIR)/pm8921.o \
+ $(LOCAL_DIR)/pm8921_pwm.o \
diff --git a/platform/msm8960/include/platform/iomap.h b/platform/msm8960/include/platform/iomap.h
index 7494995..f653437 100644
--- a/platform/msm8960/include/platform/iomap.h
+++ b/platform/msm8960/include/platform/iomap.h
@@ -36,9 +36,10 @@
#define MSM_IOMAP_BASE 0x00100000
#define MSM_IOMAP_END 0x28000000
+#define MSM_IMEM_BASE 0x2A000000
+
#define MSM_SHARED_IMEM_BASE 0x2A03F000
#define RESTART_REASON_ADDR (MSM_SHARED_IMEM_BASE + 0x65C)
-#define MSM_SHARED_BASE 0x80000000
#define MSM_SHARED_BASE 0x80000000
diff --git a/platform/msm8960/platform.c b/platform/msm8960/platform.c
old mode 100755
new mode 100644
index 8589d78..1233ce7
--- a/platform/msm8960/platform.c
+++ b/platform/msm8960/platform.c
@@ -36,6 +36,7 @@
#include <dev/fbcon.h>
#include <mmu.h>
#include <arch/arm/mmu.h>
+#include <partition_parser.h>
extern void platform_init_timer(void);
extern void platform_panel_backlight_on(void);
@@ -59,15 +60,19 @@
/* Kernel region - cacheable, write through */
#define KERNEL_MEMORY (MMU_MEMORY_TYPE_NORMAL_WRITE_THROUGH | \
- MMU_MEMORY_AP_READ_WRITE)
+ MMU_MEMORY_AP_READ_WRITE | MMU_MEMORY_XN)
/* Scratch region - cacheable, write through */
#define SCRATCH_MEMORY (MMU_MEMORY_TYPE_NORMAL_WRITE_THROUGH | \
- MMU_MEMORY_AP_READ_WRITE)
+ MMU_MEMORY_AP_READ_WRITE | MMU_MEMORY_XN)
/* Peripherals - non-shared device */
#define IOMAP_MEMORY (MMU_MEMORY_TYPE_DEVICE_NON_SHARED | \
- MMU_MEMORY_AP_READ_WRITE)
+ MMU_MEMORY_AP_READ_WRITE | MMU_MEMORY_XN)
+
+/* IMEM: Must set execute never bit to avoid instruction prefetch from TZ */
+#define IMEM_MEMORY (MMU_MEMORY_TYPE_STRONGLY_ORDERED | \
+ MMU_MEMORY_AP_READ_WRITE | MMU_MEMORY_XN)
mmu_section_t mmu_section_table[] = {
/* Physical addr, Virtual addr, Size (in MB), Flags */
@@ -75,6 +80,7 @@
{BASE_ADDR, BASE_ADDR, 44, KERNEL_MEMORY},
{SCRATCH_ADDR, SCRATCH_ADDR, 128, SCRATCH_MEMORY},
{MSM_IOMAP_BASE, MSM_IOMAP_BASE, MSM_IOMAP_SIZE, IOMAP_MEMORY},
+ {MSM_IMEM_BASE, MSM_IMEM_BASE, 1, IMEM_MEMORY},
};
void platform_early_init(void)
@@ -109,12 +115,31 @@
mipi_dsi_shutdown();
}
+/*
+ * Write-protect partition list.
+ *
+ * Partition added in this list should have (size + padding) in multiple of
+ * mmc write protect group size. Otherwise this can end up write protecting
+ * some blocks from next partition.
+ */
+char *wp_list[] = {"fsg", NULL};
+
+void platform_wp_paritition(void)
+{
+ int count = 0;
+ while(wp_list[count] != NULL)
+ {
+ paritition_wp_by_name(wp_list[count]);
+ count++;
+ }
+}
+
void platform_uninit(void)
{
#if DISPLAY_SPLASH_SCREEN
display_shutdown();
#endif
-
+ platform_wp_paritition();
platform_uninit_timer();
}
@@ -157,3 +182,4 @@
{
return ticks_per_sec;
}
+
diff --git a/platform/msm_shared/include/mmc.h b/platform/msm_shared/include/mmc.h
index a07d169..6c754ff 100644
--- a/platform/msm_shared/include/mmc.h
+++ b/platform/msm_shared/include/mmc.h
@@ -374,6 +374,7 @@
#define MMC_BOOT_E_DATA_ADM_ERR 21
/* EXT_CSD */
+#define MMC_BOOT_ACCESS_BIT_SET 0x1
#define MMC_BOOT_ACCESS_WRITE 0x3
#define MMC_BOOT_EXT_USER_WP 171
@@ -593,6 +594,10 @@
unsigned int mmc_erase_card(unsigned long long data_addr,
unsigned long long data_len);
+unsigned int mmc_wp(unsigned int sector, unsigned int size,
+ unsigned char set_clear_wp);
+
struct mmc_boot_host *get_mmc_host(void);
struct mmc_boot_card *get_mmc_card(void);
+
#endif
diff --git a/platform/msm_shared/include/partition_parser.h b/platform/msm_shared/include/partition_parser.h
index 8e0f2ee..48fd982 100644
--- a/platform/msm_shared/include/partition_parser.h
+++ b/platform/msm_shared/include/partition_parser.h
@@ -176,6 +176,7 @@
struct mmc_boot_host *mmc_host,
struct mmc_boot_card *mmc_card);
unsigned int write_partition(unsigned size, unsigned char *partition);
+unsigned int paritition_wp_by_name(const char *name);
/* For Debugging */
void partition_dump(void);
diff --git a/platform/msm_shared/mmc.c b/platform/msm_shared/mmc.c
index b1fc977..2d86ba1 100644
--- a/platform/msm_shared/mmc.c
+++ b/platform/msm_shared/mmc.c
@@ -85,8 +85,6 @@
struct mmc_boot_host mmc_host;
struct mmc_boot_card mmc_card;
-static unsigned int mmc_wp(unsigned int addr, unsigned int size,
- unsigned char set_clear_wp);
static unsigned int mmc_boot_send_ext_cmd(struct mmc_boot_card *card,
unsigned char *buf);
static unsigned int mmc_boot_read_reg(struct mmc_boot_card *card,
@@ -2284,7 +2282,7 @@
sizeof(struct mmc_boot_command));
/* Disabling PERM_WP for USER AREA (CMD6) */
- mmc_ret = mmc_boot_switch_cmd(card, MMC_BOOT_ACCESS_WRITE,
+ mmc_ret = mmc_boot_switch_cmd(card, MMC_BOOT_ACCESS_BIT_SET,
MMC_BOOT_EXT_USER_WP,
MMC_BOOT_US_PERM_WP_DIS);
@@ -2338,14 +2336,14 @@
(card->csd.erase_grp_mult + 1) * (card->csd.wp_grp_size +
1);
}
-
+ dprintf(SPEW, "Write protect size: %d bytes\n", (wp_group_size * MMC_BOOT_WR_BLOCK_LEN));
if (wp_group_size == 0) {
return MMC_BOOT_E_FAILURE;
}
/* Setting POWER_ON_WP for USER AREA (CMD6) */
- mmc_ret = mmc_boot_switch_cmd(card, MMC_BOOT_ACCESS_WRITE,
+ mmc_ret = mmc_boot_switch_cmd(card, MMC_BOOT_ACCESS_BIT_SET,
MMC_BOOT_EXT_USER_WP,
MMC_BOOT_US_PWR_WP_EN);
@@ -2373,6 +2371,9 @@
if (size % wp_group_size) {
loop_count = (size / wp_group_size) + 1;
+ dprintf(CRITICAL, "WARNING: Size passed to write protect is not multiple of wp_group_size!\n");
+ dprintf(CRITICAL, "WARNING: Write protecting %d extra bytes.\n",
+ ((loop_count * wp_group_size) - size) * MMC_BOOT_WR_BLOCK_LEN);
} else {
loop_count = (size / wp_group_size);
}
@@ -2433,9 +2434,9 @@
}
/*
- * Test Function for setting Write protect for given sector
+ * Function for setting Write protect for given sector
*/
-static unsigned int
+unsigned int
mmc_wp(unsigned int sector, unsigned int size, unsigned char set_clear_wp)
{
unsigned int rc = MMC_BOOT_E_SUCCESS;
diff --git a/platform/msm_shared/partition_parser.c b/platform/msm_shared/partition_parser.c
index 68e54f1..2becaed 100644
--- a/platform/msm_shared/partition_parser.c
+++ b/platform/msm_shared/partition_parser.c
@@ -251,6 +251,7 @@
/* Read GPT Entries */
for (i = 0; i < (max_partition_count / 4); i++) {
+ ASSERT(partition_count < NUM_PARTITIONS);
ret = mmc_boot_read_from_card(mmc_host, mmc_card,
(partition_0 * BLOCK_SIZE) +
(i * BLOCK_SIZE),
@@ -800,6 +801,10 @@
unsigned int input_string_length = strlen(name);
unsigned n;
+ if( partition_count >= NUM_PARTITIONS)
+ {
+ return INVALID_PTN;
+ }
for (n = 0; n < partition_count; n++) {
if (!memcmp
(name, &partition_entries[n].name, input_string_length)
@@ -941,3 +946,42 @@
return 0;
}
+
+/*
+ * Power on write protect partition by name.
+ *
+ * Partition passed to this function should have (size + padding) in multiple
+ * of mmc write protect group size. Otherwise this can end up write protecting
+ * some blocks from next partition.
+ */
+
+unsigned int paritition_wp_by_name(const char *name)
+{
+ unsigned long long ptn = 0;
+ unsigned long long size = 0;
+ int index = INVALID_PTN;
+ unsigned int ret = MMC_BOOT_E_SUCCESS;
+
+ index = partition_get_index(name);
+ ptn = partition_get_offset(index);
+ if(ptn == 0) {
+ dprintf(CRITICAL, "%s partition not found.\n", name);
+ return 1;
+ }
+
+ size = partition_get_size(index);
+
+ /* Offset in sectors */
+ ptn = ptn / MMC_BOOT_RD_BLOCK_LEN;
+
+ /* Size in sectors */
+ size = size / MMC_BOOT_RD_BLOCK_LEN;
+
+ ret = mmc_wp((unsigned)ptn, (unsigned)size, 1);
+ if(ret)
+ {
+ dprintf(CRITICAL, "Failed to write protect: %s\n", name);
+ return 1;
+ }
+ return 0;
+}
diff --git a/target/msm8960/keypad.c b/target/msm8960/keypad.c
index 4015170..806f07f 100644
--- a/target/msm8960/keypad.c
+++ b/target/msm8960/keypad.c
@@ -28,9 +28,12 @@
*/
#include <string.h>
+#include <debug.h>
#include <dev/keys.h>
#include <dev/ssbi.h>
#include <dev/gpio_keypad.h>
+#include <dev/pm8921.h>
+
#define NUM_OF_ROWS 1
#define NUM_OF_COLS 5
@@ -67,3 +70,37 @@
ssbi_keypad_init(&qwerty_keypad);
}
+
+/* Configure keypad_drv through pwm or DBUS inputs or manually */
+int led_kp_set( int current,
+ enum kp_backlight_mode mode,
+ enum kp_backlight_flash_logic flash_logic)
+{
+ int rc = pm8921_config_drv_keypad(current, flash_logic, mode);
+
+ if (rc)
+ {
+ dprintf(CRITICAL, "FAIL pm8921_config_drv_keypad(): rc=%d.\n", rc);
+ }
+}
+
+/* Configure gpio 26 through lpg2 */
+void keypad_led_drv_on_pwm(void)
+{
+ struct pm8921_gpio keypad_pwm = {
+ .direction = PM_GPIO_DIR_OUT,
+ .output_buffer = 0,
+ .output_value = 0,
+ .pull = PM_GPIO_PULL_NO,
+ .vin_sel = 2,
+ .out_strength = PM_GPIO_STRENGTH_HIGH,
+ .function = PM_GPIO_FUNC_2,
+ .inv_int_pol = 0,
+ };
+
+ int rc = pm8921_gpio_config(PM_GPIO(26), &keypad_pwm);
+ if (rc)
+ {
+ dprintf(CRITICAL, "FAIL pm8921_gpio_config(): rc=%d.\n", rc);
+ }
+}
diff --git a/target/msm8960/panel.c b/target/msm8960/panel.c
index 924b310..caeda14 100644
--- a/target/msm8960/panel.c
+++ b/target/msm8960/panel.c
@@ -54,6 +54,49 @@
}
}
+/* Configure gpio 24 through lpg0 */
+void panel_backlight_on_pwm(void)
+{
+ struct pm8921_gpio backlight_pwm = {
+ .direction = PM_GPIO_DIR_OUT,
+ .output_buffer = 0,
+ .output_value = 0,
+ .pull = PM_GPIO_PULL_NO,
+ .vin_sel = 2,
+ .out_strength = PM_GPIO_STRENGTH_HIGH,
+ .function = PM_GPIO_FUNC_2,
+ .inv_int_pol = 0,
+ };
+
+ int rc = pm8921_gpio_config(PM_GPIO(24), &backlight_pwm);
+ if (rc)
+ {
+ dprintf(CRITICAL, "FAIL pm8921_gpio_config(): rc=%d.\n", rc);
+ }
+}
+
+/* Configure gpio 25 through lpg1 */
+void panel_sec_backlight_on_pwm(void)
+{
+ struct pm8921_gpio backlight_pwm = {
+ .direction = PM_GPIO_DIR_OUT,
+ .output_buffer = 0,
+ .output_value = 0,
+ .pull = PM_GPIO_PULL_NO,
+ .vin_sel = 2,
+ .out_strength = PM_GPIO_STRENGTH_HIGH,
+ .function = PM_GPIO_FUNC_2,
+ .inv_int_pol = 0,
+ };
+
+ int rc = pm8921_gpio_config(PM_GPIO(25), &backlight_pwm);
+ if (rc)
+ {
+ dprintf(CRITICAL, "FAIL pm8921_gpio_config(): rc=%d.\n", rc);
+ }
+}
+
+
/* Pull DISP_RST_N high to get panel out of reset */
void mipi_panel_reset(void)
{