/* * * Copyright (c) 2010-2013, The Linux Foundation. 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 The Linux Foundation 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 #include #include #include #include #include #include #include #include #include #include #include #include #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) static struct qup_i2c_dev *dev = NULL; void gpio_tlmm_config(uint32_t gpio, uint8_t func, uint8_t dir, uint8_t pull, uint8_t drvstr, uint32_t enable); uint8_t expander_read(uint8_t addr) { uint8_t ret = 0; /* Create a i2c_msg buffer, that is used to put the controller into read mode and then to read some data. */ struct i2c_msg msg_buf[] = { {CORE_GPIO_EXPANDER_I2C_ADDRESS, I2C_M_WR, 1, &addr}, {CORE_GPIO_EXPANDER_I2C_ADDRESS, I2C_M_RD, 1, &ret} }; qup_i2c_xfer(dev, msg_buf, 2); return ret; } uint8_t expander_write(uint8_t addr, uint8_t val) { uint8_t data_buf[] = { addr, val }; /* Create a i2c_msg buffer, that is used to put the controller into write mode and then to write some data. */ struct i2c_msg msg_buf[] = { {CORE_GPIO_EXPANDER_I2C_ADDRESS, I2C_M_WR, 2, data_buf} }; qup_i2c_xfer(dev, msg_buf, 1); /* Double check that the write worked. */ if (val != expander_read(addr)) { return -1; } return 0; } void panel_backlight(int on) { } static int display_common_power(int on) { } static int lcd_power_on() { uint8_t buffer = 0x0, mask = 0x0, prev_val = 0x0; int ret = 0; /* Configure LDO L2 TEST Bank 2, to Range Select 0 */ /* Not updating reference voltage */ buffer = (0x80); /* Write mode */ buffer |= (PM8901_LDO_TEST_BANK(2)); /* Test Bank 2 */ mask = buffer | LDO_TEST_RANGE_SELECT_MASK; if ((ret = pm8901_test_bank_read(&prev_val, PM8901_LDO_TEST_BANK(2), PM8901_LDO_L2_TEST_BANK))) { return ret; } if ((ret = pm8901_vreg_write(&buffer, mask, PM8901_LDO_L2_TEST_BANK, prev_val))) { return ret; } /* Enable LDO L2 at Max Voltage (should be around 3.3v) */ buffer = (0x0 << PM8901_LDO_CTL_ENABLE__S); /* Disable Pull Down */ buffer |= (0x1 << PM8901_LDO_CTL_PULL_DOWN__S); /* Put LDO into normal mode instead of low power mode */ buffer |= (0x0 << PM8901_LDO_CTL_MODE__S); /* Set voltage programming to 3.3V or 2.85V(8660 fluid) */ if (board_machtype() == LINUX_MACHTYPE_8660_FLUID) buffer |= (0xB); else buffer |= (0xF); mask = buffer | LDO_CTL_ENABLE_MASK | LDO_CTL_PULL_DOWN_MASK | LDO_CTL_NORMAL_POWER_MODE_MASK | LDO_CTL_VOLTAGE_SET_MASK; /* Do a normal read here, as to not destroy the value in LDO control */ if ((ret = pm8901_read(&prev_val, 1, PM8901_LDO_L2))) { return ret; } /* Configure the LDO2 for 3.3V or 2.85V(8660 fluid) */ ret = pm8901_vreg_write(&buffer, mask, PM8901_LDO_L2, prev_val); /* Configure LDO L2 TEST Bank 4, for High Range Mode */ buffer = (0x80); /* Write mode */ buffer |= (PM8901_LDO_TEST_BANK(4)); /* Test Bank 4 */ buffer |= (0x01); /* Put into High Range Mode */ mask = buffer | LDO_TEST_OUTPUT_RANGE_MASK; if ((ret = pm8901_test_bank_read(&prev_val, PM8901_LDO_TEST_BANK(4), PM8901_LDO_L2_TEST_BANK))) { return ret; } if ((ret = pm8901_vreg_write(&buffer, mask, PM8901_LDO_L2_TEST_BANK, prev_val))) { return ret; } /* Configure LDO L2 TEST Bank 2, to Range Select 0 */ buffer = (0x80); /* Write mode */ buffer |= (PM8901_LDO_TEST_BANK(2)); /* Test Bank 2 */ buffer |= (1 << 1); /* For fine step 50 mV */ buffer |= (1 << 3); /* to update reference voltage */ mask = buffer | LDO_TEST_RANGE_SELECT_MASK; mask |= (1 << 2); /* Setting mask to make ref voltage as 1.25 V */ if ((ret = pm8901_test_bank_read(&prev_val, PM8901_LDO_TEST_BANK(2), PM8901_LDO_L2_TEST_BANK))) { return ret; } if ((ret = pm8901_vreg_write(&buffer, mask, PM8901_LDO_L2_TEST_BANK, prev_val))) { return ret; } /* Enable PMR for LDO L2 */ buffer = 0x7F; mask = 0x7F; if ((ret = pm8901_read(&prev_val, 1, PM8901_PMR_REG(LDO_L2)))) { return ret; } ret = pm8901_vreg_write(&buffer, mask, PM8901_PMR_REG(LDO_L2), prev_val); return ret; } /* Configures the GPIO that are needed to enable LCD. * This function also configures the PMIC for PWM control of the LCD backlight. */ static void lcd_gpio_cfg(uint8_t on) { uint32_t func; uint32_t pull; uint32_t dir; uint32_t enable = 0; /* not used in gpio_tlmm_config */ uint32_t drv; if (on) { func = 1; /* Configure GPIO for LCDC function */ pull = GPIO_NO_PULL; dir = 1; /* doesn't matter since it is not configured as GPIO */ drv = GPIO_16MA; } else { /* As discussed in the MSM8660 FFA HW SW Control Doc configure these GPIO as input and pull down. */ func = 0; /* GPIO */ pull = GPIO_PULL_DOWN; dir = 0; /* Input */ drv = 0; /* does not matter configured as input */ } gpio_tlmm_config(0, func, dir, pull, drv, enable); /* lcdc_pclk */ gpio_tlmm_config(1, func, dir, pull, drv, enable); /* lcdc_hsync */ gpio_tlmm_config(2, func, dir, pull, drv, enable); /* lcdc_vsync */ gpio_tlmm_config(3, func, dir, pull, drv, enable); /* lcdc_den */ gpio_tlmm_config(4, func, dir, pull, drv, enable); /* lcdc_red7 */ gpio_tlmm_config(5, func, dir, pull, drv, enable); /* lcdc_red6 */ gpio_tlmm_config(6, func, dir, pull, drv, enable); /* lcdc_red5 */ gpio_tlmm_config(7, func, dir, pull, drv, enable); /* lcdc_red4 */ gpio_tlmm_config(8, func, dir, pull, drv, enable); /* lcdc_red3 */ gpio_tlmm_config(9, func, dir, pull, drv, enable); /* lcdc_red2 */ gpio_tlmm_config(10, func, dir, pull, drv, enable); /* lcdc_red1 */ gpio_tlmm_config(11, func, dir, pull, drv, enable); /* lcdc_red0 */ gpio_tlmm_config(12, func, dir, pull, drv, enable); /* lcdc_rgn7 */ gpio_tlmm_config(13, func, dir, pull, drv, enable); /* lcdc_rgn6 */ gpio_tlmm_config(14, func, dir, pull, drv, enable); /* lcdc_rgn5 */ gpio_tlmm_config(15, func, dir, pull, drv, enable); /* lcdc_rgn4 */ gpio_tlmm_config(16, func, dir, pull, drv, enable); /* lcdc_rgn3 */ gpio_tlmm_config(17, func, dir, pull, drv, enable); /* lcdc_rgn2 */ gpio_tlmm_config(18, func, dir, pull, drv, enable); /* lcdc_rgn1 */ gpio_tlmm_config(19, func, dir, pull, drv, enable); /* lcdc_rgn0 */ gpio_tlmm_config(20, func, dir, pull, drv, enable); /* lcdc_blu7 */ gpio_tlmm_config(21, func, dir, pull, drv, enable); /* lcdc_blu6 */ gpio_tlmm_config(22, func, dir, pull, drv, enable); /* lcdc_blu5 */ gpio_tlmm_config(23, func, dir, pull, drv, enable); /* lcdc_blu4 */ gpio_tlmm_config(24, func, dir, pull, drv, enable); /* lcdc_blu3 */ gpio_tlmm_config(25, func, dir, pull, drv, enable); /* lcdc_blu2 */ gpio_tlmm_config(26, func, dir, pull, drv, enable); /* lcdc_blu1 */ gpio_tlmm_config(27, func, dir, pull, drv, enable); /* lcdc_blu0 */ } /* API to set backlight level configuring PWM in PM8058 */ int panel_set_backlight(uint8_t bt_level) { int rc = -1; uint32_t duty_us, period_us; 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, 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 strength */ 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 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 selection enabled */ 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 strength */ 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 polarity noninversion */ } void board_lcd_enable(void) { int rc = -1; dev = qup_i2c_init(GSBI_ID_8, 100000, 24000000); /* Make sure dev is created and initialized properly */ if (!dev) { while (1) ; return; } /* Store current value of these registers as to not destroy their previous state. */ uint8_t open_drain_a = expander_read(GPIO_EXPANDER_REG_OPEN_DRAIN_A); uint8_t dir_b = expander_read(GPIO_EXPANDER_REG_DIR_B); uint8_t dir_a = expander_read(GPIO_EXPANDER_REG_DIR_A); uint8_t data_b = expander_read(GPIO_EXPANDER_REG_DATA_B); uint8_t data_a = expander_read(GPIO_EXPANDER_REG_DATA_A); /* Set the LVDS_SHUTDOWN_N to open drain and output low. */ dprintf(INFO, "Enable lvds_shutdown_n line for Open Drain.\n"); expander_write(GPIO_EXPANDER_REG_OPEN_DRAIN_A, 0x04 | open_drain_a); dprintf(INFO, "Enable lvds_shutdown_n line for output.\n"); expander_write(GPIO_EXPANDER_REG_DIR_A, ~0x04 & dir_a); dprintf(INFO, "Drive the LVDS_SHUTDOWN_N pin high here.\n"); expander_write(GPIO_EXPANDER_REG_DATA_A, 0x04 | data_a); /* Turn on the VREG_L2B to 3.3V. */ /* Power on the appropiate PMIC LDO power rails */ if (lcd_power_on()) return; /* Enable the GPIO as LCDC mode LCD. */ lcd_gpio_cfg(1); /* Arbitrary delay */ udelay(20000); /* 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); dprintf(INFO, "Drive BACKLIGHT_EN to high\n"); expander_write(GPIO_EXPANDER_REG_DATA_B, 0x10 | data_b); } void lcdc_on(void) { board_lcd_enable(); } void auo_board_lcd_enable(void) { /* Make sure dev is created and initialized properly */ dev = qup_i2c_init(GSBI_ID_8, 100000, 24000000); if (!dev) { dprintf(CRITICAL, "Error in qup_i2c_init\n"); while (1) ; } /* Setup RESX_N */ uint8_t open_drain_a = expander_read(GPIO_EXPANDER_REG_OPEN_DRAIN_A); uint8_t dir_a = expander_read(GPIO_EXPANDER_REG_DIR_A); uint8_t data_a = expander_read(GPIO_EXPANDER_REG_DATA_A); expander_write(GPIO_EXPANDER_REG_DIR_A, ~0x04 & dir_a); expander_write(GPIO_EXPANDER_REG_DATA_A, ~0x04 & data_a); /* Power on the appropiate PMIC LDO power rails */ if (lcd_power_on()) return; /* Toggle RESX_N */ mdelay(20); expander_write(GPIO_EXPANDER_REG_DATA_A, 0x04 | data_a); mdelay(1); expander_write(GPIO_EXPANDER_REG_DATA_A, ~0x04 & data_a); mdelay(1); expander_write(GPIO_EXPANDER_REG_DATA_A, 0x04 | data_a); mdelay(25); /* Enable the gpios for LCD */ lcd_gpio_cfg(1); auo_lcdc_init(); } void panel_poweron(void) { if (board_machtype() == LINUX_MACHTYPE_8660_FLUID) { auo_board_lcd_enable(); } else { panel_backlight(1); lcdc_on(); } } struct lcdc_timing_parameters *get_lcd_timing(void) { if (board_machtype() == LINUX_MACHTYPE_8660_FLUID) return auo_timing_param(); else return DEFAULT_LCD_TIMING; }