[msm8x60/pmic]: Add LCD brightness control API for PM8058
Adding LCD display brightness control API by configuring PWM in PMIC.
Change-Id: I4d4103eba315cd3d4986032f7355cc9a08d176ac
CRs-Fixed: 287902
diff --git a/platform/msm8x60/include/platform/pmic.h b/platform/msm8x60/include/platform/pmic.h
index 813f290..258b368 100755
--- a/platform/msm8x60/include/platform/pmic.h
+++ b/platform/msm8x60/include/platform/pmic.h
@@ -71,15 +71,11 @@
#define LDO_TEST_XO_EN_ALL_MASK (0x1F)
/* PMIC 8058 defines */
-#define LPG_CTL_0 (0x13C)
-#define LPG_CTL_1 (0x13D)
-#define LPG_CTL_2 (0x13E)
-#define LPG_CTL_3 (0x13F)
-#define LPG_CTL_4 (0x140)
-#define LPG_CTL_5 (0x141)
-#define LPG_CTL_6 (0x142)
-#define LPG_BANK_SEL (0x143)
-#define LPG_BANK_ENABLE (0x144)
+#define PM8058_LPG_CTL_BASE (0x13C)
+#define PM8058_LPG_CTL(n) (PM8058_LPG_CTL_BASE + (n))
+#define PM8058_LPG_BANK_SEL (0x143)
+#define PM8058_LPG_BANK_ENABLE (0x144)
+
#define GPIO24_GPIO_CNTRL (0x167)
#define GPIO25_GPIO_CNTRL (0x168)
diff --git a/platform/msm8x60/include/platform/pmic_pwm.h b/platform/msm8x60/include/platform/pmic_pwm.h
new file mode 100644
index 0000000..810790b
--- /dev/null
+++ b/platform/msm8x60/include/platform/pmic_pwm.h
@@ -0,0 +1,152 @@
+/*
+ * * 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
+
+#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 3
+
+#define NUM_LPG_CTL_REGS 7
+
+#define PRE_DIVIDE_0 2
+#define PRE_DIVIDE_1 3
+#define PRE_DIVIDE_2 5
+
+#define PRE_DIVIDE_MIN PRE_DIVIDE_0
+#define PRE_DIVIDE_MAX PRE_DIVIDE_2
+
+#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 pm_pwm_config {
+ int pwm_size; /* round up to 6 or 9 for 6/9-bit PWM SIZE */
+ int clk;
+ int pre_div;
+ int pre_div_exp;
+ int pwm_value;
+ int bypass_lut;
+ uint8_t pwm_ctl[NUM_LPG_CTL_REGS];
+};
+
+#endif
diff --git a/platform/msm8x60/panel.c b/platform/msm8x60/panel.c
index 9139030..0809661 100644
--- a/platform/msm8x60/panel.c
+++ b/platform/msm8x60/panel.c
@@ -1,5 +1,5 @@
/*
- * * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ * * Copyright (c) 2010-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
@@ -34,6 +34,7 @@
#include <platform/gpio_hw.h>
#include <platform/clock.h>
#include <platform/pmic.h>
+#include <platform/pmic_pwm.h>
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
@@ -228,84 +229,84 @@
gpio_tlmm_config(27, func, dir, pull, drv, enable); /* lcdc_blu0 */
}
-/* Backlight duty cycle init is used to configure the PMIC8058 for
- * PWM output and drive those pins.
- */
-static void bl_duty_cycle_init(void)
+/* API to set backlight level configuring PWM in PM8058 */
+
+int panel_set_backlight(uint8_t bt_level)
{
- /* Disable backlight LPG channels before configuring them and dedicated
- PMIC GPIOs */
- pm8058_write_one(0x00, LPG_BANK_ENABLE);
+ int rc = -1;
+ uint32_t duty_us, period_us;
- /* Configure PM8058 GPIO24 as a PWM driver (LPG ch0) for chain 1 of 6 LEDs */
- pm8058_write_one(0x81, GPIO24_GPIO_CNTRL); /* Write, Bank0, VIN0, Mode
+ if((bt_level <= 0) || (bt_level > 15))
+ {
+ dprintf(CRITICAL, "Error in brightness level (1-15 allowed)\n");
+ goto bail_out;
+ }
+
+ duty_us = bt_level*PWM_DUTY_LEVEL;
+ period_us = PWM_PERIOD_USEC;
+ rc = pm_pwm_config(0, duty_us, period_us);
+ if(rc)
+ {
+ dprintf(CRITICAL, "Error in pwm_config0\n");
+ goto bail_out;
+ }
+
+ duty_us = PWM_PERIOD_USEC - (bt_level*PWM_DUTY_LEVEL);
+ period_us = PWM_PERIOD_USEC;
+ rc = pm_pwm_config(1, duty_us, period_us);
+ if(rc)
+ {
+ dprintf(CRITICAL, "Error in pwm_config1\n");
+ goto bail_out;
+ }
+
+ rc = pm_pwm_enable(0);
+ if(rc)
+ {
+ dprintf(CRITICAL, "Error in pwm_enable0\n");
+ goto bail_out;
+ }
+
+ rc = pm_pwm_enable(1);
+ if(rc)
+ dprintf(CRITICAL, "Error in pwm_enable1\n");
+
+bail_out:
+ return rc;
+}
+
+void bl_gpio_init(void)
+{
+ /* Configure PM8058 GPIO24 as a PWM driver (LPG ch0) for chain 1 of 6 LEDs */
+ pm8058_write_one(0x81, GPIO24_GPIO_CNTRL); /* Write, Bank0, VIN0, Mode
selection enabled */
- pm8058_write_one(0x98, GPIO24_GPIO_CNTRL); /* Write, Bank1, OutOn/InOff,
+ pm8058_write_one(0x98, GPIO24_GPIO_CNTRL); /* Write, Bank1, OutOn/InOff,
CMOS, Don't Invert Output */
- pm8058_write_one(0xAA, GPIO24_GPIO_CNTRL); /* Write, Bank2, GPIO no pull */
- pm8058_write_one(0xB4, GPIO24_GPIO_CNTRL); /* Write, Bank3, high drv
+ pm8058_write_one(0xAA, GPIO24_GPIO_CNTRL); /* Write, Bank2, GPIO no pull */
+ pm8058_write_one(0xB4, GPIO24_GPIO_CNTRL); /* Write, Bank3, high drv
strength */
- pm8058_write_one(0xC6, GPIO24_GPIO_CNTRL); /* Write, Bank4, Src: LPG_DRV1
+ pm8058_write_one(0xC6, GPIO24_GPIO_CNTRL); /* Write, Bank4, Src: LPG_DRV1
(Spec. Fnc 2) */
- pm8058_write_one(0xD8, GPIO24_GPIO_CNTRL); /* Write, Bank5, Interrupt
+ pm8058_write_one(0xD8, GPIO24_GPIO_CNTRL); /* Write, Bank5, Interrupt
polarity noninversion */
- /* Configure PM8058 GPIO25 as a PWM driver (LPG ch1) for chain 2 of 5 LEDs */
- pm8058_write_one(0x81, GPIO25_GPIO_CNTRL); /* Write, Bank0, VIN0, Mode
+ /* Configure PM8058 GPIO25 as a PWM driver (LPG ch1) for chain 2 of 5 LEDs */
+ pm8058_write_one(0x81, GPIO25_GPIO_CNTRL); /* Write, Bank0, VIN0, Mode
selection enabled */
- pm8058_write_one(0x98, GPIO25_GPIO_CNTRL); /* Write, Bank1, OutOn/InOff,
+ pm8058_write_one(0x98, GPIO25_GPIO_CNTRL); /* Write, Bank1, OutOn/InOff,
CMOS, Don't Invert Output */
- pm8058_write_one(0xAA, GPIO25_GPIO_CNTRL); /* Write, Bank2, GPIO no pull */
- pm8058_write_one(0xB4, GPIO25_GPIO_CNTRL); /* Write, Bank3, high drv
+ pm8058_write_one(0xAA, GPIO25_GPIO_CNTRL); /* Write, Bank2, GPIO no pull */
+ pm8058_write_one(0xB4, GPIO25_GPIO_CNTRL); /* Write, Bank3, high drv
strength */
- pm8058_write_one(0xC6, GPIO25_GPIO_CNTRL); /* Write, Bank4, Src: LPG_DRV2
+ pm8058_write_one(0xC6, GPIO25_GPIO_CNTRL); /* Write, Bank4, Src: LPG_DRV2
(Spec. Fnc 2) */
- pm8058_write_one(0xD8, GPIO25_GPIO_CNTRL); /* Write, Bank5, Interrupt
+ pm8058_write_one(0xD8, GPIO25_GPIO_CNTRL); /* Write, Bank5, Interrupt
polarity noninversion */
-
- /* Configure PM8058 LPG channel 0 as non-LUT PWM for PM8058 GPIO24 */
- pm8058_write_one(0x0, LPG_BANK_SEL); /* Select LPG ch0 slice of control
- regs */
- pm8058_write_one(0x00, LPG_CTL_0); /* Disable PWM, PWM output, and LPG
- ramp generator */
- pm8058_write_one(0x40, LPG_CTL_1); /* Dont Toggle, Enable user PWM value,
- no LUT high value idx */
- pm8058_write_one(0x00, LPG_CTL_2); /* Dont Loop, no LUT low value index */
- pm8058_write_one(0xDE, LPG_CTL_3); /* LS 8 bits of 9-bit PWM user value */
- pm8058_write_one(0x7F, LPG_CTL_4); /* MSbit of 9-bit PWM user value,
- 19.2MHz, Dev 6, Expo M = 7 */
- pm8058_write_one(0x01, LPG_CTL_5); /* PWM = 9bit, disable pause at high
- value LUT index */
- pm8058_write_one(0x00, LPG_CTL_6); /* Disable pause at low value LUT index
- */
- pm8058_write_one(0x0C, LPG_CTL_0); /* Enable PWM and PWM output, LPG ramp
- generator remains disabled */
-
- /* Configure PM8058 LPG chan 1 as PWM for PM8058 GPIO25 */
- pm8058_write_one(0x1, LPG_BANK_SEL); /* Select LPG ch1 slice of control
- regs */
- pm8058_write_one(0x00, LPG_CTL_0); /* Disable PWM, PWM output, and LPG
- ramp generator */
- pm8058_write_one(0x40, LPG_CTL_1); /* Dont Toggle, Enable user PWM value,
- no LUT high value idx */
- pm8058_write_one(0x00, LPG_CTL_2); /* Dont Loop, no LUT low value index */
- pm8058_write_one(0x00, LPG_CTL_3); /* LS 8 bits of 9-bit PWM user value */
- pm8058_write_one(0x7F, LPG_CTL_4); /* MSbit of 9-bit PWM user value,
- 19.2MHz, Dev 6, Expo M = 7 */
- pm8058_write_one(0x01, LPG_CTL_5); /* PWM = 9bit, disable pause at high
- value LUT index */
- pm8058_write_one(0x00, LPG_CTL_6); /* Disable pause at low value LUT index
- */
- pm8058_write_one(0x0C, LPG_CTL_0); /* Enable PWM and PWM output, LPG ramp
- generator remains disabled */
-
- /* Enable both LPG channels to enable backlight driver */
- pm8058_write_one(0x03, LPG_BANK_ENABLE); /* Enable LPG ch0 (GPIO24) &
- ch1 (GPIO25) */
}
void board_lcd_enable(void)
{
+ int rc = -1;
dev = qup_i2c_init(GSBI8_BASE, 100000, 24000000);
/* Make sure dev is created and initialized properly */
@@ -344,8 +345,12 @@
/* Arbitrary delay */
udelay(20000);
- /* Set the backlight duty cycle via the PM8058 LPG_DRV1 and LPG_DRV2 */
- bl_duty_cycle_init();
+ /* Set the GPIOs needed for backlight */
+ bl_gpio_init();
+ /* Set backlight level with API (to 8 by default) */
+ rc = panel_set_backlight(8);
+ if(rc)
+ dprintf(CRITICAL,"Error in setting panel backlight\n");
dprintf(INFO, "Enable BACKLIGHT_EN line for output.\n");
expander_write(GPIO_EXPANDER_REG_DIR_B, ~0x10 & dir_b);
diff --git a/platform/msm8x60/pmic.c b/platform/msm8x60/pmic.c
index 8c71600..6ae9302 100755
--- a/platform/msm8x60/pmic.c
+++ b/platform/msm8x60/pmic.c
@@ -42,8 +42,24 @@
unsigned short);
extern int pa1_ssbi2_write_bytes(unsigned char *buffer, unsigned short length,
unsigned short slave_addr);
+extern int pa1_ssbi2_read_bytes(unsigned char *buffer, unsigned short length,
+ unsigned short slave_addr);
+extern int pa2_ssbi2_write_bytes(unsigned char *buffer, unsigned short length,
+ unsigned short slave_addr);
+extern int pa2_ssbi2_read_bytes(unsigned char *buffer, unsigned short length,
+ unsigned short slave_addr);
-/*PM8058*/
+/* PM8058 APIs */
+int pm8058_write(uint16_t addr, uint8_t *data, uint16_t length)
+{
+ return pa1_ssbi2_write_bytes(data, length, addr);
+}
+
+int pm8058_read(uint16_t addr, uint8_t *data, uint16_t length)
+{
+ return pa1_ssbi2_read_bytes(data, length, addr);
+}
+
void pm8058_write_one(unsigned data, unsigned address)
{
pm8058_write_func wr_function = &pa1_ssbi2_write_bytes;
@@ -106,11 +122,8 @@
return status;
}
-/*PM8901*/
-extern int pa2_ssbi2_write_bytes(unsigned char *buffer, unsigned short length,
- unsigned short slave_addr);
-extern int pa2_ssbi2_read_bytes(unsigned char *buffer, unsigned short length,
- unsigned short slave_addr);
+/* PM8901 APIs */
+
/*
* Write to the control registers on PMIC via the SSBI2 interface.
* Returns : (0) on success and (-1) on error.
diff --git a/platform/msm8x60/pmic_pwm.c b/platform/msm8x60/pmic_pwm.c
new file mode 100644
index 0000000..4847612
--- /dev/null
+++ b/platform/msm8x60/pmic_pwm.c
@@ -0,0 +1,281 @@
+/*
+ * * 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 <platform/pmic.h>
+#include <platform/pmic_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
+};
+
+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,
+ },
+};
+
+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 pm_pwm_calc_period(uint32_t period_us,
+ struct pm_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 pm_pwm_configure(uint8_t pwm_id, struct pm_pwm_config *pwm_conf)
+{
+ 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 = pm8058_write(PM8058_LPG_BANK_SEL, &pwm_id, 1);
+ if(rc)
+ goto bail_out;
+
+ for (i = 0; i < len; i++) {
+ rc = pm8058_write(PM8058_LPG_CTL(i),&pwm_conf->pwm_ctl[i], 1);
+ if (rc) {
+ dprintf(CRITICAL,"pm8058_write() failed in pwm_configure %d\n", rc);
+ break;
+ }
+ }
+
+bail_out:
+ if(rc)
+ dprintf(CRITICAL,"Error in pm_pwm_configure()\n");
+ return rc;
+}
+
+/* Top level function for configuring PWM */
+
+int pm_pwm_config(uint8_t pwm_id, uint32_t duty_us, uint32_t period_us)
+{
+ struct pm_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;
+ }
+
+ pm_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 = pm_pwm_configure(pwm_id, &pwm_conf);
+ if(rc)
+ dprintf(CRITICAL,"Error in pwm_config()\n");
+
+ return rc;
+}
+
+/* Top level function to enable PWM with specified id */
+
+int pm_pwm_enable(uint8_t pwm_id)
+{
+ int rc = -1;
+ uint8_t reg;
+
+ /* Read it before enabling other bank */
+ rc = pm8058_read(PM8058_LPG_BANK_ENABLE, ®, 1);
+ if(rc)
+ goto bail_out;
+
+ reg |= (1 << pwm_id);
+
+ rc = pm8058_write(PM8058_LPG_BANK_ENABLE,®,1);
+ if(rc)
+ goto bail_out;
+
+ /* Selecting the bank */
+ rc = pm8058_write(PM8058_LPG_BANK_SEL, &pwm_id, 1);
+ if(rc)
+ goto bail_out;
+
+ /* Read it before setting PWM start */
+ rc = pm8058_read(PM8058_LPG_CTL(0), ®, 1);
+ if(rc)
+ goto bail_out;
+
+ reg |= PM_PWM_PWM_START;
+ reg &= ~PM_PWM_RAMP_GEN_START;
+ rc = pm8058_write(PM8058_LPG_CTL(0),®, 1);
+
+bail_out:
+ if(rc)
+ dprintf(CRITICAL, "Error in pwm_enable()\n");
+ return rc;
+}
diff --git a/platform/msm8x60/rules.mk b/platform/msm8x60/rules.mk
index f258110..7076ee8 100644
--- a/platform/msm8x60/rules.mk
+++ b/platform/msm8x60/rules.mk
@@ -27,6 +27,7 @@
$(LOCAL_DIR)/gpio.o \
$(LOCAL_DIR)/panel.o \
$(LOCAL_DIR)/pmic.o \
+ $(LOCAL_DIR)/pmic_pwm.o \
$(LOCAL_DIR)/scm-io.o
LINKER_SCRIPT += $(BUILDDIR)/system-onesegment.ld