| /* |
| * Copyright (C) 2015 Marvell International Ltd. |
| * Fenghang Yin <yinfh@marvell.com> |
| * |
| * SPDX-License-Identifier: GPL-2.0+ |
| */ |
| |
| #include <errno.h> |
| #include <common.h> |
| #include <command.h> |
| #include <asm/io.h> |
| #include <asm/gpio.h> |
| #include <linux/types.h> |
| #include <i2c.h> |
| #include <asm/gpio.h> |
| #include <power/oled_ssd1306.h> |
| |
| /* extern const u8 char611v_I2COLED[96][7][2]; */ |
| /* extern const u8 USB_Status[1][48][3]; */ |
| /* extern const u8 External_Power_Status[1][16][4]; */ |
| /* extern const u8 download_Status[2][32][4]; */ |
| extern const u8 BatteryState[6][64][4]; |
| extern const u8 logo[1][64][4]; |
| extern const u8 Battery_Status[1][32][4]; |
| |
| /* static u8 ssd1306_DataBuf[DATABUF_SIZE_I2COLED] = {0}; */ |
| static u8 ssd1306_battery_base_percent = 0xff; |
| static u8 ssd1306_battery_display_stage = 0; |
| static u8 is_ssd1306_present = 0; |
| |
| static u8 get_display_percent(u8 percent); |
| static void battery_charging_display(u8 battery_stage); |
| /* |
| * |
| * Control byte |
| * Bit7 6 5 4 3 2 1 0 |
| * Co D/C 0 0 0 0 0 0 |
| * |
| * Co - Continuation bit |
| * If the Co is set as logic "0", the transmission of the following information |
| * will contain data bytes only. |
| * |
| * D/C - Data / Command selection bit |
| * The D/C# bit determines the next data byte is acted as a command or a data. |
| * If the D/C# bit is set to logic "0", it defines the following data byte as a command. |
| * If the D/C# bit is set to logic "1", it defines the following data byte as a data |
| * which will be stored at the GDDRAM. The GDDRAM column address pointer will be increased |
| * by one automatically after each data write. |
| * |
| */ |
| #define Control_Byte_Command 0x00 // Co - 0, D/C - 0 |
| #define Control_Byte_Data 0x40 // Co - 0, D/C - 1 |
| |
| int ssd1306_i2c_write_cmd(u8 value) |
| { |
| int ret; |
| |
| if (!is_ssd1306_present) |
| return 0; |
| |
| i2c_set_bus_num(1); |
| ret = i2c_write(SSD1306_SLAVE_ADDR, Control_Byte_Command, 1, &value, 1); |
| if (ret) { |
| printf("[%s]: i2c write error\n", __func__); |
| } |
| |
| return 0; |
| } |
| |
| int ssd1306_i2c_write_data(u8 value) |
| { |
| int ret; |
| |
| if (!is_ssd1306_present) |
| return 0; |
| |
| i2c_set_bus_num(1); |
| ret = i2c_write(SSD1306_SLAVE_ADDR, Control_Byte_Data, 1, &value, 1); |
| if (ret) { |
| printf("[%s]: i2c write error\n", __func__); |
| } |
| |
| return 0; |
| } |
| |
| static void oled_detect(void) |
| { |
| u8 data; |
| int ret; |
| |
| i2c_set_bus_num(1); |
| |
| ret = i2c_read(SSD1306_SLAVE_ADDR, 0x0, 1, &data, 1); |
| if (!ret) |
| is_ssd1306_present = 1; |
| else |
| is_ssd1306_present = 0; |
| } |
| |
| void ssd1306_set_column_addr(u8 addr_start, u8 addr_end) |
| { |
| if (is_ssd1306_present == 0) |
| return; |
| |
| ssd1306_i2c_write_cmd(0x21); |
| ssd1306_i2c_write_cmd(addr_start); |
| ssd1306_i2c_write_cmd(addr_end); |
| } |
| |
| void clear_screen(void) |
| { |
| u32 i, bytes_number = 128 * 8; |
| |
| if (is_ssd1306_present == 0) |
| return; |
| |
| ssd1306_set_column_addr(0x00, 0x7f); |
| for (i = 0; i < bytes_number; i++) |
| { |
| ssd1306_i2c_write_data(0x00); |
| } |
| |
| ssd1306_i2c_write_cmd(I2COLED_Display_ON); /* set display on */ |
| } |
| |
| #if 0 |
| void ssd1306_dis_defaultline(u32 page_start, u32 page_num, u32 p_coloum, u8 *str) |
| { |
| u32 i; |
| u32 index,size; |
| u32 char_index; |
| |
| const u8 * char_buf; |
| |
| if (is_ssd1306_present == 0) |
| return; |
| |
| /**** (2)Map lib to Color Data ****/ |
| index = 0; |
| while((index < DATABUF_SIZE_I2COLED) && (*str != 0)) |
| { |
| char_index = *str -' '; |
| char_buf = (const u8 *)&char611v_I2COLED[char_index]; |
| for (i = 0; i < char611v_CharSize; i++) |
| { |
| ssd1306_DataBuf[index++] = (*char_buf++); |
| } |
| |
| //ssd1306_DataBuf[index++] =0x00; //add space |
| //ssd1306_DataBuf[index++] =0x00; //add space |
| str++; |
| } |
| |
| /**** (3) write Data ****/ |
| |
| size = index; |
| |
| //(1)set page limit |
| ssd1306_i2c_write_cmd(0x22); |
| ssd1306_i2c_write_cmd(page_start); |
| ssd1306_i2c_write_cmd((page_start + page_num-1)); |
| |
| //(2)set column limit |
| ssd1306_i2c_write_cmd(0x21); |
| ssd1306_i2c_write_cmd(p_coloum); |
| ssd1306_i2c_write_cmd((p_coloum + (size>>1)-1)); |
| |
| //(3)send data |
| for (i = 0; i < size; i++) |
| { |
| ssd1306_i2c_write_data(ssd1306_DataBuf[i]); |
| } |
| } |
| #endif |
| |
| void ssd1306_fill_area(u32 page_start, |
| u32 page_num, |
| u32 p_coloum, |
| u32 p_width, |
| u8 fill_data, |
| u32 byte_num) |
| { |
| int i; |
| |
| if (is_ssd1306_present == 0) |
| return; |
| |
| /* set page limit */ |
| ssd1306_i2c_write_cmd(0x22); |
| ssd1306_i2c_write_cmd(page_start); |
| ssd1306_i2c_write_cmd((page_start+page_num-1)); |
| |
| /* set column limit */ |
| ssd1306_i2c_write_cmd(0x21); |
| ssd1306_i2c_write_cmd(p_coloum); |
| ssd1306_i2c_write_cmd((p_coloum+p_width-1)); |
| |
| /* send data */ |
| for (i = 0; i < byte_num; i++) |
| { |
| ssd1306_i2c_write_data(fill_data); |
| } |
| } |
| |
| void ssd1306_pic_display(u32 page_start, |
| u32 page_num, |
| u32 p_coloum, |
| u32 p_width, |
| const u8 *buf, |
| u32 byte_num) |
| { |
| u32 i; |
| |
| if (is_ssd1306_present == 0) |
| return; |
| |
| /* (1)set page limit */ |
| ssd1306_i2c_write_cmd(0x22); |
| ssd1306_i2c_write_cmd(page_start); |
| ssd1306_i2c_write_cmd((page_start+page_num-1)); |
| |
| /* (2)set column limit */ |
| ssd1306_i2c_write_cmd(0x21); |
| ssd1306_i2c_write_cmd(p_coloum); |
| ssd1306_i2c_write_cmd((p_coloum+p_width-1)); |
| |
| /* (3)send data */ |
| for (i = 0; i < byte_num; i++) |
| { |
| ssd1306_i2c_write_data((*buf++)); |
| } |
| } |
| |
| static void battery_charging_display(u8 battery_stage) |
| { |
| if (is_ssd1306_present == 0) |
| return; |
| |
| ssd1306_pic_display( ssd136_Battery_Charging_Page_Start, |
| ssd136_Battery_Charging_Page_Num, |
| ssd136_Battery_Charging_Coloum, |
| ssd136_Battery_Charging_Width, |
| (const u8 *)&BatteryState[battery_stage], |
| ssd136_Battery_Charging_Bytes_Num |
| ); |
| } |
| |
| static void show_battery_status(u8 choice) |
| { |
| if (is_ssd1306_present == 0) |
| return; |
| |
| ssd1306_fill_area(0x2, 0x4, 0x20, 0x40, 0x00, (0x4*0x40)); |
| ssd1306_pic_display( ssd136_Battery_status0_Page_Start, |
| ssd136_Battery_status0_Page_Num, |
| ssd136_Battery_status0_Coloum, |
| ssd136_Battery_status0_Width, |
| (const u8 *)&Battery_Status[choice], |
| ssd136_Battery_status0_Bytes_Num); |
| } |
| |
| static u8 get_display_percent(u8 percent) |
| { |
| u8 stage; |
| #if 0 |
| if (percent < 5) { |
| stage = 0; |
| } else if (percent < 26) { |
| stage = 1; |
| } else if (percent < 46) { |
| stage = 2; |
| } else if (percent < 66) { |
| stage = 3; |
| } else if (percent < 95) { |
| stage = 4; |
| } else { |
| stage = 5; |
| } |
| #else |
| if (percent < 20) { |
| stage = 0; |
| } else if (percent < 40) { |
| stage = 1; |
| } else if (percent < 60) { |
| stage = 2; |
| } else if (percent < 80) { |
| stage = 3; |
| } else if (percent < 100) { |
| stage = 4; |
| } else { |
| stage = 5; |
| } |
| #endif |
| if (ssd1306_battery_base_percent == 0xff) |
| { |
| ssd1306_battery_base_percent = stage; |
| ssd1306_battery_display_stage = ssd1306_battery_base_percent; |
| } |
| |
| return stage; |
| } |
| |
| void oled_show_bat_soc(u8 percent) |
| { |
| if (is_ssd1306_present == 0) |
| return; |
| |
| get_display_percent(percent); |
| printf("percent: %d/100\n", percent); |
| |
| battery_charging_display(ssd1306_battery_base_percent); |
| } |
| |
| void oled_show_no_battery(void) |
| { |
| if (is_ssd1306_present == 0) |
| return; |
| |
| show_battery_status(0); |
| } |
| |
| void oled_show_mrvl_logo(u8 choice) |
| { |
| if (is_ssd1306_present == 0) |
| return; |
| |
| ssd1306_pic_display( ssd136_LOGO0_Page_Start, |
| ssd136_LOGO0_Page_Num, |
| ssd136_LOGO0_Coloum, |
| ssd136_LOGO0_Width, |
| (const u8 *)&logo[choice], |
| ssd136_LOGO0_Bytes_Num); |
| } |
| |
| #if 0 |
| void display_bat_stat(u8 batpercent, u32 vbat_mv) |
| { |
| if (is_ssd1306_present == 0) |
| return; |
| |
| get_display_percent(batpercent); |
| battery_charging_display(ssd1306_battery_base_percent); |
| } |
| |
| void external_power_display(u8 choice) |
| { |
| if (is_ssd1306_present == 0) |
| return; |
| |
| ssd1306_pic_display( ssd136_External_Power_status0_Page_Start, |
| ssd136_External_Power_status0_Page_Num, |
| ssd136_External_Power_status0_Coloum, |
| ssd136_External_Power_status0_Width, |
| (const u8 *)&External_Power_Status[choice], |
| ssd136_External_Power_status0_Bytes_Num); |
| } |
| |
| void show_usb_status(u8 choice) |
| { |
| if (is_ssd1306_present == 0) |
| return; |
| |
| ssd1306_pic_display( ssd136_USB_status0_Page_Start, |
| ssd136_USB_status0_Page_Num, |
| ssd136_USB_status0_Coloum, |
| ssd136_USB_status0_Width, |
| (const u8 *)&USB_Status[choice], |
| ssd136_USB_status0_Bytes_Num); |
| } |
| |
| void show_external_power(void) |
| { |
| if (is_ssd1306_present == 0) |
| return; |
| |
| external_power_display(0); |
| } |
| |
| void show_onkey_bootup(void) |
| { |
| if (is_ssd1306_present == 0) |
| return; |
| |
| ssd1306_pic_display(ssd136_LOGO0_Page_Start, |
| ssd136_LOGO0_Page_Num, |
| ssd136_LOGO0_Coloum, |
| ssd136_LOGO0_Width, |
| (const u8 *)&logo[0], |
| ssd136_LOGO0_Bytes_Num); |
| } |
| |
| void show_battery_full(void) |
| { |
| if (is_ssd1306_present == 0) |
| return; |
| |
| battery_charging_display(5); |
| } |
| #endif |
| |
| void oled_show_bat_charging(u16 percent) |
| { |
| u8 update_stage; |
| |
| if (is_ssd1306_present == 0) |
| return; |
| |
| if (percent >= 100) { |
| battery_charging_display(5); |
| return; |
| } |
| |
| update_stage = get_display_percent(percent); |
| |
| if (ssd1306_battery_display_stage == 5) |
| ssd1306_battery_display_stage = ssd1306_battery_base_percent; |
| else |
| ssd1306_battery_display_stage++; |
| |
| battery_charging_display(ssd1306_battery_display_stage); |
| |
| if (update_stage > ssd1306_battery_base_percent) |
| ssd1306_battery_base_percent = update_stage; |
| } |
| |
| int oled_i2c_init(void) |
| { |
| oled_detect(); |
| if (!is_ssd1306_present) { |
| printf("SSD1306 OLED doesn't exsit\n"); |
| return -ENODEV; |
| } |
| |
| ssd1306_i2c_write_cmd(I2COLED_Display_OFF); |
| |
| ssd1306_i2c_write_cmd(0x00);/* set lower column start address */ |
| ssd1306_i2c_write_cmd(0x10);/* set higher column start address */ |
| |
| ssd1306_i2c_write_cmd(0x40);/* set display start line : 0x40+x_line*/ |
| ssd1306_i2c_write_cmd(0x81);/* set contrast control register */ |
| ssd1306_i2c_write_cmd(0x7f); |
| ssd1306_i2c_write_cmd(0xa1);/* set column remap */ |
| ssd1306_i2c_write_cmd(0xa6);/* set normal control */ |
| ssd1306_i2c_write_cmd(0xa8);/* set multiplex ratio */ |
| ssd1306_i2c_write_cmd(63); |
| |
| ssd1306_i2c_write_cmd(0xd3);/* set display offset */ |
| ssd1306_i2c_write_cmd(0x00); |
| ssd1306_i2c_write_cmd(0xd5);/* set display clock divide ratio/oscillator frequancy */ |
| ssd1306_i2c_write_cmd(0x80); |
| |
| ssd1306_i2c_write_cmd(0xda);/* set com configuration */ |
| ssd1306_i2c_write_cmd(0x12);//0x12 is nomarl |
| |
| ssd1306_i2c_write_cmd(0x8d);/* interal dc-dc */ |
| ssd1306_i2c_write_cmd(0x14); |
| |
| SSD1306_Cmd_1Para(ScanModeSet, SCAN_In_vertical); |
| |
| return 0; |
| } |
| |
| |
| void oled_display_off(void) |
| { |
| ssd1306_i2c_write_cmd(I2COLED_Display_OFF); |
| } |
| |
| void oled_power_up(void) |
| { |
| gpio_direction_output(OLED_RST, 1); |
| udelay(3); |
| gpio_direction_output(OLED_RST, 0); |
| udelay(3); |
| gpio_direction_output(OLED_RST, 1); |
| mdelay(100); |
| } |
| |
| void oled_power_off(void) |
| { |
| //step1. turn off vcc and delay 100ms |
| //step2. turn off vdd |
| } |
| |
| void oled_init(void) |
| { |
| int ret; |
| |
| oled_power_up(); |
| ret = oled_i2c_init(); |
| if (ret != 0) |
| return; |
| |
| clear_screen(); |
| } |
| |
| void test_i2c_oled(void) |
| { |
| oled_power_up(); |
| oled_i2c_init(); |
| clear_screen(); |
| ssd1306_i2c_write_cmd(I2COLED_Display_ON);/* set display on */ |
| #if 0 |
| ssd1306_dis_defaultline(0, 2, 0, (u8 *)"ABCDEFGHIJKLMNO"); |
| ssd1306_dis_defaultline(2, 2, 0, (u8 *)"abcdefghijklmnopq"); |
| ssd1306_dis_defaultline(4, 2, 0, (u8 *)"BCDEFGHIJKLMNO"); |
| ssd1306_dis_defaultline(6, 2, 0, (u8 *)"UVWXYZxyz!%"); |
| |
| printf("show logo\n"); |
| oled_show_logo(0); |
| mdelay(1000); |
| |
| printf("show no battery\n"); |
| show_no_battery(); |
| mdelay(1000); |
| |
| printf("external_power_display\n"); |
| show_external_power(); |
| mdelay(1000); |
| |
| printf("onkey_bootup\n"); |
| show_onkey_bootup(); |
| mdelay(1000); |
| |
| printf("onkey_bootup\n"); |
| show_onkey_bootup(); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 5\n"); |
| oled_show_bat_charging(5); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 5\n"); |
| oled_show_bat_charging(5); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 5\n"); |
| oled_show_bat_charging(5); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 5\n"); |
| oled_show_bat_charging(5); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 5\n"); |
| oled_show_bat_charging(5); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 15\n"); |
| oled_show_bat_charging(15); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 25\n"); |
| oled_show_bat_charging(25); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 35\n"); |
| oled_show_bat_charging(35); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 45\n"); |
| oled_show_bat_charging(45); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 55\n"); |
| oled_show_bat_charging(55); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 65\n"); |
| oled_show_bat_charging(65); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 75\n"); |
| oled_show_bat_charging(75); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 85\n"); |
| oled_show_bat_charging(85); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 95\n"); |
| oled_show_bat_charging(95); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 99\n"); |
| oled_show_bat_charging(99); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 5\n"); |
| oled_show_bat_charging(5); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 5\n"); |
| oled_show_bat_charging(5); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 5\n"); |
| oled_show_bat_charging(5); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 5\n"); |
| oled_show_bat_charging(5); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 5\n"); |
| oled_show_bat_charging(5); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 15\n"); |
| oled_show_bat_charging(15); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 25\n"); |
| oled_show_bat_charging(25); |
| mdelay(1000); |
| |
| printf("oled_show_bat_charging 35\n"); |
| oled_show_bat_charging(35); |
| mdelay(1000); |
| #endif |
| } |
| |
| static int do_oled_test(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
| { |
| test_i2c_oled(); |
| return 0; |
| } |
| |
| U_BOOT_CMD( |
| ioled, 1, 1, do_oled_test, |
| "turn on oled and test oled\n", |
| "" |
| ); |