blob: b70c8e0f61ecf116251de9b05da967b2dcec6a27 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* Copyright (c) 2018 PHYTEC Messtechnik GmbH
*
*/
#include <stdint.h>
#include "os/mynewt.h"
#include "hal/hal_gpio.h"
#include "hal/hal_spi.h"
#include "modlog/modlog.h"
#include "display/display.h"
#include "display/cfb.h"
#include "ssd1673_regs.h"
#define EPD_PANEL_WIDTH 250
#define EPD_PANEL_HEIGHT 120
#define EPD_PANEL_NUMOF_COLUMS 250
#define EPD_PANEL_NUMOF_ROWS_PER_PAGE 8
#define EPD_PANEL_NUMOF_PAGES (EPD_PANEL_HEIGHT / \
EPD_PANEL_NUMOF_ROWS_PER_PAGE)
#define SSD1673_PANEL_FIRST_PAGE 0
#define SSD1673_PANEL_LAST_PAGE (EPD_PANEL_NUMOF_PAGES - 1)
#define SSD1673_PANEL_FIRST_GATE 0
#define SSD1673_PANEL_LAST_GATE 249
struct ssd1673_data {
struct display_driver_api driver_api;
struct hal_spi_settings spi_config;
uint8_t contrast;
uint8_t scan_mode;
uint8_t last_lut;
uint8_t numof_part_cycles;
};
static struct ssd1673_data ssd1673_driver;
static struct os_dev ssd1673;
#define SSD1673_LAST_LUT_INITIAL 0
#define SSD1673_LAST_LUT_DEFAULT 255
#define SSD1673_LUT_SIZE 29
static uint8_t ssd1673_lut_initial[SSD1673_LUT_SIZE] = {
0x22, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x11,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E,
0x01, 0x00, 0x00, 0x00, 0x00
};
static uint8_t ssd1673_lut_default[SSD1673_LUT_SIZE] = {
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00
};
#define SSD1673_BUSY_DELAY_TICKS os_time_ms_to_ticks32(SSD1673_BUSY_DELAY)
#define SSD1673_RESET_DELAY_TICKS os_time_ms_to_ticks32(SSD1673_RESET_DELAY)
#define CONFIG_SSD1673_OS_DEV_NAME MYNEWT_VAL(SSD1673_OS_DEV_NAME)
#define CONFIG_SSD1673_BUSY_PIN MYNEWT_VAL(SSD1673_BUSY_PIN)
#define CONFIG_SSD1673_RESET_PIN MYNEWT_VAL(SSD1673_RESET_PIN)
#define CONFIG_SSD1673_DC_PIN MYNEWT_VAL(SSD1673_DC_PIN)
#define CONFIG_SSD1673_CS_PIN MYNEWT_VAL(SSD1673_CS_PIN)
#define CONFIG_SSD1673_SPI_FREQ MYNEWT_VAL(SSD1673_SPI_FREQ)
#define CONFIG_SSD1673_SPI_DEV MYNEWT_VAL(SSD1673_SPI_DEV)
static inline int ssd1673_write_cmd(struct ssd1673_data *driver,
uint8_t cmd, uint8_t *data, size_t len)
{
hal_gpio_write(CONFIG_SSD1673_DC_PIN, 0);
hal_gpio_write(CONFIG_SSD1673_CS_PIN, 0);
if (hal_spi_txrx(CONFIG_SSD1673_SPI_DEV,
&cmd, NULL, sizeof(cmd))) {
hal_gpio_write(CONFIG_SSD1673_CS_PIN, 1);
return -1;
}
if (data != NULL) {
hal_gpio_write(CONFIG_SSD1673_DC_PIN, 1);
if (hal_spi_txrx(CONFIG_SSD1673_SPI_DEV,
data, NULL, len)) {
hal_gpio_write(CONFIG_SSD1673_CS_PIN, 1);
return -1;
}
}
hal_gpio_write(CONFIG_SSD1673_CS_PIN, 1);
return 0;
}
static inline void ssd1673_busy_wait(struct ssd1673_data *driver)
{
int val = 0;
val = hal_gpio_read(CONFIG_SSD1673_BUSY_PIN);
while (val) {
os_cputime_delay_ticks(SSD1673_BUSY_DELAY_TICKS);
val = hal_gpio_read(CONFIG_SSD1673_BUSY_PIN);
};
}
static inline int ssd1673_set_ram_param(struct ssd1673_data *driver,
uint8_t sx, uint8_t ex,
uint8_t sy, uint8_t ey)
{
uint8_t tmp[2];
tmp[0] = sx; tmp[1] = ex;
if (ssd1673_write_cmd(driver, SSD1673_CMD_RAM_XPOS_CTRL,
tmp, sizeof(tmp))) {
return -1;
}
tmp[0] = sy; tmp[1] = ey;
if (ssd1673_write_cmd(driver, SSD1673_CMD_RAM_YPOS_CTRL,
tmp, sizeof(tmp))) {
return -1;
}
return 0;
}
static inline int ssd1673_set_ram_ptr(struct ssd1673_data *driver,
uint8_t x, uint8_t y)
{
if (ssd1673_write_cmd(driver, SSD1673_CMD_RAM_XPOS_CNTR,
&x, sizeof(x))) {
return -1;
}
if (ssd1673_write_cmd(driver, SSD1673_CMD_RAM_YPOS_CNTR,
&y, sizeof(y))) {
return -1;
}
return 0;
}
static inline void ssd1673_set_orientation(struct ssd1673_data *driver)
{
#if CONFIG_SSD1673_ORIENTATION_FLIPPED == 1
driver->scan_mode = SSD1673_DATA_ENTRY_XIYDY;
#else
driver->scan_mode = SSD1673_DATA_ENTRY_XDYIY;
#endif
}
int ssd1673_resume(const struct os_dev *dev)
{
struct ssd1673_data *driver = dev->od_init_arg;
uint8_t tmp;
/*
* Uncomment for voltage measurement
* tmp = SSD1673_CTRL2_ENABLE_CLK;
* ssd1673_write_cmd(SSD1673_CMD_UPDATE_CTRL2, &tmp, sizeof(tmp));
* ssd1673_write_cmd(SSD1673_CMD_MASTER_ACTIVATION, NULL, 0);
*/
tmp = SSD1673_SLEEP_MODE_PON;
return ssd1673_write_cmd(driver, SSD1673_CMD_SLEEP_MODE,
&tmp, sizeof(tmp));
}
static int ssd1673_suspend(const struct os_dev *dev)
{
struct ssd1673_data *driver = dev->od_init_arg;
uint8_t tmp = SSD1673_SLEEP_MODE_DSM;
return ssd1673_write_cmd(driver, SSD1673_CMD_SLEEP_MODE,
&tmp, sizeof(tmp));
}
static int ssd1673_update_display(const struct os_dev *dev, bool initial)
{
struct ssd1673_data *driver = dev->od_init_arg;
uint8_t tmp;
tmp = SSD1673_CTRL1_INITIAL_UPDATE_LH;
if (ssd1673_write_cmd(driver, SSD1673_CMD_UPDATE_CTRL1,
&tmp, sizeof(tmp))) {
return -1;
}
if (initial) {
driver->numof_part_cycles = 0;
driver->last_lut = SSD1673_LAST_LUT_INITIAL;
if (ssd1673_write_cmd(driver, SSD1673_CMD_UPDATE_LUT,
ssd1673_lut_initial,
sizeof(ssd1673_lut_initial))) {
return -1;
}
} else {
driver->numof_part_cycles++;
if (driver->last_lut != SSD1673_LAST_LUT_DEFAULT) {
driver->last_lut = SSD1673_LAST_LUT_DEFAULT;
if (ssd1673_write_cmd(driver, SSD1673_CMD_UPDATE_LUT,
ssd1673_lut_default,
sizeof(ssd1673_lut_default))) {
return -1;
}
}
}
tmp = (SSD1673_CTRL2_ENABLE_CLK |
SSD1673_CTRL2_ENABLE_ANALOG |
SSD1673_CTRL2_TO_PATTERN |
SSD1673_CTRL2_DISABLE_ANALOG |
SSD1673_CTRL2_DISABLE_CLK);
if (ssd1673_write_cmd(driver, SSD1673_CMD_UPDATE_CTRL2,
&tmp, sizeof(tmp))) {
return -1;
}
if (ssd1673_write_cmd(driver, SSD1673_CMD_MASTER_ACTIVATION,
NULL, 0)) {
return -1;
}
return 0;
}
static int ssd1673_write(const struct os_dev *dev, const uint16_t x,
const uint16_t y,
const struct display_buffer_descriptor *desc,
const void *buf)
{
struct ssd1673_data *driver = dev->od_init_arg;
uint8_t cmd = SSD1673_CMD_WRITE_RAM;
uint8_t dummy_page[SSD1673_RAM_YRES];
bool update = true;
if (desc->pitch < desc->width) {
DFLT_LOG_ERROR("Pitch is smaller then width");
return -1;
}
if (buf == NULL || desc->buf_size == 0) {
DFLT_LOG_ERROR("Display buffer is not available");
return -1;
}
if (desc->pitch > desc->width) {
DFLT_LOG_ERROR("Unsupported mode");
return -1;
}
if (x != 0 && y != 0) {
DFLT_LOG_ERROR("Unsupported origin");
return -1;
}
ssd1673_busy_wait(driver);
memset(dummy_page, 0xff, sizeof(dummy_page));
switch (driver->scan_mode) {
case SSD1673_DATA_ENTRY_XIYDY:
if (ssd1673_set_ram_param(driver,
SSD1673_PANEL_FIRST_PAGE,
SSD1673_PANEL_LAST_PAGE + 1,
SSD1673_PANEL_LAST_GATE,
SSD1673_PANEL_FIRST_GATE)) {
return -1;
}
if (ssd1673_set_ram_ptr(driver,
SSD1673_PANEL_FIRST_PAGE,
SSD1673_PANEL_LAST_GATE)) {
return -1;
}
break;
case SSD1673_DATA_ENTRY_XDYIY:
if (ssd1673_set_ram_param(driver,
SSD1673_PANEL_LAST_PAGE + 1,
SSD1673_PANEL_FIRST_PAGE,
SSD1673_PANEL_FIRST_GATE,
SSD1673_PANEL_LAST_GATE)) {
return -1;
}
if (ssd1673_set_ram_ptr(driver,
SSD1673_PANEL_LAST_PAGE + 1,
SSD1673_PANEL_FIRST_GATE)) {
return -1;
}
break;
default:
return -1;
}
if (ssd1673_write_cmd(driver, SSD1673_CMD_ENTRY_MODE,
&driver->scan_mode, sizeof(driver->scan_mode))) {
return -1;
}
hal_gpio_write(CONFIG_SSD1673_DC_PIN, 0);
hal_gpio_write(CONFIG_SSD1673_CS_PIN, 0);
if (hal_spi_txrx(CONFIG_SSD1673_SPI_DEV, &cmd, NULL, sizeof(cmd))) {
hal_gpio_write(CONFIG_SSD1673_CS_PIN, 1);
return -1;
}
hal_gpio_write(CONFIG_SSD1673_DC_PIN, 1);
/* clear unusable page */
if (driver->scan_mode == SSD1673_DATA_ENTRY_XDYIY) {
if (hal_spi_txrx(CONFIG_SSD1673_SPI_DEV, dummy_page,
NULL, sizeof(dummy_page))) {
hal_gpio_write(CONFIG_SSD1673_CS_PIN, 1);
return -1;
}
}
if (hal_spi_txrx(CONFIG_SSD1673_SPI_DEV, (uint8_t *)buf,
NULL, desc->buf_size)) {
hal_gpio_write(CONFIG_SSD1673_CS_PIN, 1);
return -1;
}
/* clear unusable page */
if (driver->scan_mode == SSD1673_DATA_ENTRY_XIYDY) {
if (hal_spi_txrx(CONFIG_SSD1673_SPI_DEV, dummy_page,
NULL, sizeof(dummy_page))) {
hal_gpio_write(CONFIG_SSD1673_CS_PIN, 1);
return -1;
}
}
hal_gpio_write(CONFIG_SSD1673_CS_PIN, 1);
if (update) {
if (driver->contrast) {
return ssd1673_update_display(dev, true);
}
return ssd1673_update_display(dev, false);
}
return 0;
}
static int ssd1673_read(const struct os_dev *dev, const uint16_t x,
const uint16_t y,
const struct display_buffer_descriptor *desc,
void *buf)
{
DFLT_LOG_ERROR("not supported");
return -1;
}
static void *ssd1673_get_framebuffer(const struct os_dev *dev)
{
DFLT_LOG_ERROR("not supported");
return NULL;
}
static int ssd1673_set_brightness(const struct os_dev *dev,
const uint8_t brightness)
{
DFLT_LOG_WARN("not supported");
return -1;
}
static int ssd1673_set_contrast(const struct os_dev *dev, uint8_t contrast)
{
struct ssd1673_data *driver = dev->od_init_arg;
driver->contrast = contrast;
return 0;
}
static void ssd1673_get_capabilities(const struct os_dev *dev,
struct display_capabilities *caps)
{
memset(caps, 0, sizeof(struct display_capabilities));
caps->x_resolution = EPD_PANEL_WIDTH;
caps->y_resolution = EPD_PANEL_HEIGHT;
caps->supported_pixel_formats = PIXEL_FORMAT_MONO10;
caps->current_pixel_format = PIXEL_FORMAT_MONO10;
caps->screen_info = SCREEN_INFO_MONO_VTILED |
SCREEN_INFO_MONO_MSB_FIRST |
SCREEN_INFO_EPD;
}
static int ssd1673_set_pixel_format(const struct os_dev *dev,
const enum display_pixel_format pf)
{
DFLT_LOG_ERROR("not supported");
return -1;
}
static int ssd1673_controller_init(struct os_dev *dev)
{
struct ssd1673_data *driver = dev->od_init_arg;
uint8_t tmp[3];
DFLT_LOG_DEBUG("");
hal_gpio_write(CONFIG_SSD1673_RESET_PIN, 0);
os_time_delay(SSD1673_RESET_DELAY_TICKS);
hal_gpio_write(CONFIG_SSD1673_RESET_PIN, 1);
os_time_delay(SSD1673_RESET_DELAY_TICKS);
ssd1673_busy_wait(driver);
if (ssd1673_write_cmd(driver, SSD1673_CMD_SW_RESET, NULL, 0)) {
return -1;
}
ssd1673_busy_wait(driver);
tmp[0] = (SSD1673_RAM_YRES - 1);
tmp[1] = 0;
if (ssd1673_write_cmd(driver, SSD1673_CMD_GDO_CTRL, tmp, 2)) {
return -1;
}
tmp[0] = SSD1673_VAL_GDV_CTRL_A;
tmp[1] = SSD1673_VAL_GDV_CTRL_B;
if (ssd1673_write_cmd(driver, SSD1673_CMD_GDV_CTRL, tmp, 2)) {
return -1;
}
tmp[0] = SSD1673_VAL_SDV_CTRL;
if (ssd1673_write_cmd(driver, SSD1673_CMD_SDV_CTRL, tmp, 1)) {
return -1;
}
tmp[0] = SSD1673_VAL_VCOM_VOLTAGE;
if (ssd1673_write_cmd(driver, SSD1673_CMD_VCOM_VOLTAGE, tmp, 1)) {
return -1;
}
tmp[0] = SSD1673_VAL_DUMMY_LINE;
if (ssd1673_write_cmd(driver, SSD1673_CMD_DUMMY_LINE, tmp, 1)) {
return -1;
}
tmp[0] = SSD1673_VAL_GATE_LWIDTH;
if (ssd1673_write_cmd(driver, SSD1673_CMD_GATE_LINE_WIDTH, tmp, 1)) {
return -1;
}
ssd1673_set_orientation(driver);
driver->numof_part_cycles = 0;
driver->last_lut = SSD1673_LAST_LUT_INITIAL;
driver->contrast = 0;
return 0;
}
static int ssd1673_init(struct os_dev *dev, void *arg)
{
struct ssd1673_data *driver = dev->od_init_arg;
int rc;
DFLT_LOG_DEBUG("");
driver->spi_config.baudrate = CONFIG_SSD1673_SPI_FREQ;
driver->spi_config.data_mode = HAL_SPI_MODE0;
driver->spi_config.data_order = HAL_SPI_MSB_FIRST;
driver->spi_config.word_size = HAL_SPI_WORD_SIZE_8BIT;
rc = hal_spi_config(0, &driver->spi_config);
assert(rc == 0);
driver->driver_api.blanking_on = ssd1673_resume,
driver->driver_api.blanking_off = ssd1673_suspend,
driver->driver_api.write = ssd1673_write,
driver->driver_api.read = ssd1673_read,
driver->driver_api.get_framebuffer = ssd1673_get_framebuffer,
driver->driver_api.set_brightness = ssd1673_set_brightness,
driver->driver_api.set_contrast = ssd1673_set_contrast,
driver->driver_api.get_capabilities = ssd1673_get_capabilities,
driver->driver_api.set_pixel_format = ssd1673_set_pixel_format,
driver->driver_api.set_orientation = NULL,
rc = hal_gpio_init_out(CONFIG_SSD1673_RESET_PIN, 1);
assert(rc == 0);
rc = hal_gpio_init_out(CONFIG_SSD1673_DC_PIN, 1);
assert(rc == 0);
rc = hal_gpio_init_out(CONFIG_SSD1673_CS_PIN, 1);
assert(rc == 0);
rc = hal_gpio_init_in(CONFIG_SSD1673_BUSY_PIN, HAL_GPIO_PULL_NONE);
assert(rc == 0);
ssd1673_controller_init(dev);
return 0;
}
int ssd1673_pkg_init(void)
{
int rc;
/* Ensure this function only gets called by sysinit. */
SYSINIT_ASSERT_ACTIVE();
rc = os_dev_create(&ssd1673, CONFIG_SSD1673_OS_DEV_NAME,
OS_DEV_INIT_SECONDARY, OS_DEV_INIT_PRIO_DEFAULT,
ssd1673_init, &ssd1673_driver);
SYSINIT_PANIC_ASSERT(rc == 0);
return 0;
}