drivers: display: uc81xx: add support for uc8175
Add support for uc8175 display driver. uc8175 has a slightly different command/data length requirements for certain registers, namely TRES and PTL, compared to uc8176/uc8179 This commit refactors the driver code and such that setting TRES and PTL registers are now done by function pointers provided by config->quirks, by the same token as how it is done for setting CDI register Signed-off-by: Xiao Qin <xiaoq@google.com>
This commit is contained in:
parent
2b20c0e3e6
commit
7c46b0b898
4 changed files with 126 additions and 28 deletions
|
|
@ -6,7 +6,7 @@
|
||||||
config UC81XX
|
config UC81XX
|
||||||
bool "UltraChip UC81xx compatible display controller driver"
|
bool "UltraChip UC81xx compatible display controller driver"
|
||||||
default y
|
default y
|
||||||
depends on DT_HAS_ULTRACHIP_UC8176_ENABLED || DT_HAS_ULTRACHIP_UC8179_ENABLED
|
depends on DT_HAS_ULTRACHIP_UC8175_ENABLED || DT_HAS_ULTRACHIP_UC8176_ENABLED || DT_HAS_ULTRACHIP_UC8179_ENABLED
|
||||||
select SPI
|
select SPI
|
||||||
help
|
help
|
||||||
Enable driver for UC81xx compatible controller.
|
Enable driver for UC81xx compatible controller.
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,10 @@ struct uc81xx_quirks {
|
||||||
bool auto_copy;
|
bool auto_copy;
|
||||||
|
|
||||||
int (*set_cdi)(const struct device *dev, bool border);
|
int (*set_cdi)(const struct device *dev, bool border);
|
||||||
|
int (*set_tres)(const struct device *dev);
|
||||||
|
int (*set_ptl)(const struct device *dev, uint16_t x, uint16_t y,
|
||||||
|
uint16_t x_end_idx, uint16_t y_end_idx,
|
||||||
|
const struct display_buffer_descriptor *desc);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct uc81xx_config {
|
struct uc81xx_config {
|
||||||
|
|
@ -224,10 +228,6 @@ static int uc81xx_set_profile(const struct device *dev,
|
||||||
UC81XX_PSR_SHL |
|
UC81XX_PSR_SHL |
|
||||||
UC81XX_PSR_SHD |
|
UC81XX_PSR_SHD |
|
||||||
UC81XX_PSR_RST;
|
UC81XX_PSR_RST;
|
||||||
const struct uc81xx_tres tres = {
|
|
||||||
.hres = sys_cpu_to_be16(config->width),
|
|
||||||
.vres = sys_cpu_to_be16(config->height),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (type >= UC81XX_NUM_PROFILES) {
|
if (type >= UC81XX_NUM_PROFILES) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
@ -272,9 +272,7 @@ static int uc81xx_set_profile(const struct device *dev,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set panel resolution */
|
/* Set panel resolution */
|
||||||
LOG_HEXDUMP_DBG(&tres, sizeof(tres), "TRES");
|
if (config->quirks->set_tres(dev)) {
|
||||||
if (uc81xx_write_cmd(dev, UC81XX_CMD_TRES,
|
|
||||||
(const void *)&tres, sizeof(tres))) {
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -403,13 +401,6 @@ static int uc81xx_write(const struct device *dev, const uint16_t x, const uint16
|
||||||
|
|
||||||
uint16_t x_end_idx = x + desc->width - 1;
|
uint16_t x_end_idx = x + desc->width - 1;
|
||||||
uint16_t y_end_idx = y + desc->height - 1;
|
uint16_t y_end_idx = y + desc->height - 1;
|
||||||
const struct uc81xx_ptl ptl = {
|
|
||||||
.hrst = sys_cpu_to_be16(x),
|
|
||||||
.hred = sys_cpu_to_be16(x_end_idx),
|
|
||||||
.vrst = sys_cpu_to_be16(y),
|
|
||||||
.vred = sys_cpu_to_be16(y_end_idx),
|
|
||||||
.flags = UC81XX_PTL_FLAG_PT_SCAN,
|
|
||||||
};
|
|
||||||
size_t buf_len;
|
size_t buf_len;
|
||||||
const uint8_t back_buffer = data->blanking_on ?
|
const uint8_t back_buffer = data->blanking_on ?
|
||||||
UC81XX_CMD_DTM1 : UC81XX_CMD_DTM2;
|
UC81XX_CMD_DTM1 : UC81XX_CMD_DTM2;
|
||||||
|
|
@ -448,15 +439,11 @@ static int uc81xx_write(const struct device *dev, const uint16_t x, const uint16
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Setup Partial Window and enable Partial Mode */
|
|
||||||
LOG_HEXDUMP_DBG(&ptl, sizeof(ptl), "ptl");
|
|
||||||
|
|
||||||
if (uc81xx_write_cmd(dev, UC81XX_CMD_PTIN, NULL, 0)) {
|
if (uc81xx_write_cmd(dev, UC81XX_CMD_PTIN, NULL, 0)) {
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uc81xx_write_cmd(dev, UC81XX_CMD_PTL,
|
if (config->quirks->set_ptl(dev, x, y, x_end_idx, y_end_idx, desc)) {
|
||||||
(const void *)&ptl, sizeof(ptl))) {
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -487,8 +474,7 @@ static int uc81xx_write(const struct device *dev, const uint16_t x, const uint16
|
||||||
* needed.
|
* needed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (uc81xx_write_cmd(dev, UC81XX_CMD_PTL,
|
if (config->quirks->set_ptl(dev, x, y, x_end_idx, y_end_idx, desc)) {
|
||||||
(const void *)&ptl, sizeof(ptl))) {
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -654,7 +640,73 @@ static int uc81xx_init(const struct device *dev)
|
||||||
return uc81xx_controller_init(dev);
|
return uc81xx_controller_init(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DT_HAS_COMPAT_STATUS_OKAY(ultrachip_uc8176)
|
#if DT_HAS_COMPAT_STATUS_OKAY(ultrachip_uc8175)
|
||||||
|
static int uc81xx_set_tres_8(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct uc81xx_config *config = dev->config;
|
||||||
|
const struct uc81xx_tres8 tres = {
|
||||||
|
.hres = config->width,
|
||||||
|
.vres = config->height,
|
||||||
|
};
|
||||||
|
|
||||||
|
LOG_HEXDUMP_DBG(&tres, sizeof(tres), "TRES");
|
||||||
|
|
||||||
|
return uc81xx_write_cmd(dev, UC81XX_CMD_TRES, (const void *)&tres, sizeof(tres));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int uc81xx_set_ptl_8(const struct device *dev, uint16_t x, uint16_t y,
|
||||||
|
uint16_t x_end_idx, uint16_t y_end_idx,
|
||||||
|
const struct display_buffer_descriptor *desc)
|
||||||
|
{
|
||||||
|
const struct uc81xx_ptl8 ptl = {
|
||||||
|
.hrst = x,
|
||||||
|
.hred = x_end_idx,
|
||||||
|
.vrst = y,
|
||||||
|
.vred = y_end_idx,
|
||||||
|
.flags = UC81XX_PTL_FLAG_PT_SCAN,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Setup Partial Window and enable Partial Mode */
|
||||||
|
LOG_HEXDUMP_DBG(&ptl, sizeof(ptl), "ptl");
|
||||||
|
|
||||||
|
return uc81xx_write_cmd(dev, UC81XX_CMD_PTL, (const void *)&ptl, sizeof(ptl));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DT_HAS_COMPAT_STATUS_OKAY(ultrachip_uc8176) || DT_HAS_COMPAT_STATUS_OKAY(ultrachip_uc8179)
|
||||||
|
static int uc81xx_set_tres_16(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct uc81xx_config *config = dev->config;
|
||||||
|
const struct uc81xx_tres8 tres = {
|
||||||
|
.hres = sys_cpu_to_be16(config->width),
|
||||||
|
.vres = sys_cpu_to_be16(config->height),
|
||||||
|
};
|
||||||
|
|
||||||
|
LOG_HEXDUMP_DBG(&tres, sizeof(tres), "TRES");
|
||||||
|
|
||||||
|
return uc81xx_write_cmd(dev, UC81XX_CMD_TRES, (const void *)&tres, sizeof(tres));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int uc81xx_set_ptl_16(const struct device *dev, uint16_t x, uint16_t y,
|
||||||
|
uint16_t x_end_idx, uint16_t y_end_idx,
|
||||||
|
const struct display_buffer_descriptor *desc)
|
||||||
|
{
|
||||||
|
const struct uc81xx_ptl16 ptl = {
|
||||||
|
.hrst = sys_cpu_to_be16(x),
|
||||||
|
.hred = sys_cpu_to_be16(x_end_idx),
|
||||||
|
.vrst = sys_cpu_to_be16(y),
|
||||||
|
.vred = sys_cpu_to_be16(y_end_idx),
|
||||||
|
.flags = UC81XX_PTL_FLAG_PT_SCAN,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Setup Partial Window and enable Partial Mode */
|
||||||
|
LOG_HEXDUMP_DBG(&ptl, sizeof(ptl), "ptl");
|
||||||
|
|
||||||
|
return uc81xx_write_cmd(dev, UC81XX_CMD_PTL, (const void *)&ptl, sizeof(ptl));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DT_HAS_COMPAT_STATUS_OKAY(ultrachip_uc8175) || DT_HAS_COMPAT_STATUS_OKAY(ultrachip_uc8176)
|
||||||
static int uc8176_set_cdi(const struct device *dev, bool border)
|
static int uc8176_set_cdi(const struct device *dev, bool border)
|
||||||
{
|
{
|
||||||
const struct uc81xx_config *config = dev->config;
|
const struct uc81xx_config *config = dev->config;
|
||||||
|
|
@ -675,7 +727,22 @@ static int uc8176_set_cdi(const struct device *dev, bool border)
|
||||||
LOG_DBG("CDI: %#hhx", cdi);
|
LOG_DBG("CDI: %#hhx", cdi);
|
||||||
return uc81xx_write_cmd_uint8(dev, UC81XX_CMD_CDI, cdi);
|
return uc81xx_write_cmd_uint8(dev, UC81XX_CMD_CDI, cdi);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DT_HAS_COMPAT_STATUS_OKAY(ultrachip_uc8175)
|
||||||
|
static const struct uc81xx_quirks uc8175_quirks = {
|
||||||
|
.max_width = 80,
|
||||||
|
.max_height = 160,
|
||||||
|
|
||||||
|
.auto_copy = false,
|
||||||
|
|
||||||
|
.set_cdi = uc8176_set_cdi,
|
||||||
|
.set_tres = uc81xx_set_tres_8,
|
||||||
|
.set_ptl = uc81xx_set_ptl_8,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DT_HAS_COMPAT_STATUS_OKAY(ultrachip_uc8176)
|
||||||
static const struct uc81xx_quirks uc8176_quirks = {
|
static const struct uc81xx_quirks uc8176_quirks = {
|
||||||
.max_width = 400,
|
.max_width = 400,
|
||||||
.max_height = 300,
|
.max_height = 300,
|
||||||
|
|
@ -683,6 +750,8 @@ static const struct uc81xx_quirks uc8176_quirks = {
|
||||||
.auto_copy = false,
|
.auto_copy = false,
|
||||||
|
|
||||||
.set_cdi = uc8176_set_cdi,
|
.set_cdi = uc8176_set_cdi,
|
||||||
|
.set_tres = uc81xx_set_tres_16,
|
||||||
|
.set_ptl = uc81xx_set_ptl_16,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -714,6 +783,8 @@ static const struct uc81xx_quirks uc8179_quirks = {
|
||||||
.auto_copy = true,
|
.auto_copy = true,
|
||||||
|
|
||||||
.set_cdi = uc8179_set_cdi,
|
.set_cdi = uc8179_set_cdi,
|
||||||
|
.set_tres = uc81xx_set_tres_16,
|
||||||
|
.set_ptl = uc81xx_set_ptl_16,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -814,6 +885,9 @@ static struct display_driver_api uc81xx_driver_api = {
|
||||||
CONFIG_DISPLAY_INIT_PRIORITY, \
|
CONFIG_DISPLAY_INIT_PRIORITY, \
|
||||||
&uc81xx_driver_api);
|
&uc81xx_driver_api);
|
||||||
|
|
||||||
|
DT_FOREACH_STATUS_OKAY_VARGS(ultrachip_uc8175, UC81XX_DEFINE,
|
||||||
|
&uc8175_quirks);
|
||||||
|
|
||||||
DT_FOREACH_STATUS_OKAY_VARGS(ultrachip_uc8176, UC81XX_DEFINE,
|
DT_FOREACH_STATUS_OKAY_VARGS(ultrachip_uc8176, UC81XX_DEFINE,
|
||||||
&uc8176_quirks);
|
&uc8176_quirks);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,14 +106,31 @@
|
||||||
#define UC8179_CDI_DDX1 BIT(1)
|
#define UC8179_CDI_DDX1 BIT(1)
|
||||||
#define UC8179_CDI_DDX0 BIT(0)
|
#define UC8179_CDI_DDX0 BIT(0)
|
||||||
|
|
||||||
struct uc81xx_tres {
|
struct uc81xx_tres8 {
|
||||||
|
uint8_t hres;
|
||||||
|
uint8_t vres;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
BUILD_ASSERT(sizeof(struct uc81xx_tres8) == 2);
|
||||||
|
|
||||||
|
struct uc81xx_ptl8 {
|
||||||
|
uint8_t hrst;
|
||||||
|
uint8_t hred;
|
||||||
|
uint8_t vrst;
|
||||||
|
uint8_t vred;
|
||||||
|
uint8_t flags;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
BUILD_ASSERT(sizeof(struct uc81xx_ptl8) == 5);
|
||||||
|
|
||||||
|
struct uc81xx_tres16 {
|
||||||
uint16_t hres;
|
uint16_t hres;
|
||||||
uint16_t vres;
|
uint16_t vres;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
BUILD_ASSERT(sizeof(struct uc81xx_tres) == 4);
|
BUILD_ASSERT(sizeof(struct uc81xx_tres16) == 4);
|
||||||
|
|
||||||
struct uc81xx_ptl {
|
struct uc81xx_ptl16 {
|
||||||
uint16_t hrst;
|
uint16_t hrst;
|
||||||
uint16_t hred;
|
uint16_t hred;
|
||||||
uint16_t vrst;
|
uint16_t vrst;
|
||||||
|
|
@ -121,11 +138,10 @@ struct uc81xx_ptl {
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
BUILD_ASSERT(sizeof(struct uc81xx_ptl) == 9);
|
BUILD_ASSERT(sizeof(struct uc81xx_ptl16) == 9);
|
||||||
|
|
||||||
#define UC81XX_PTL_FLAG_PT_SCAN BIT(0)
|
#define UC81XX_PTL_FLAG_PT_SCAN BIT(0)
|
||||||
|
|
||||||
|
|
||||||
/* Time constants in ms */
|
/* Time constants in ms */
|
||||||
#define UC81XX_RESET_DELAY 10U
|
#define UC81XX_RESET_DELAY 10U
|
||||||
#define UC81XX_PON_DELAY 100U
|
#define UC81XX_PON_DELAY 100U
|
||||||
|
|
|
||||||
8
dts/bindings/display/ultrachip,uc8175.yaml
Normal file
8
dts/bindings/display/ultrachip,uc8175.yaml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Copyright (c) 2022 Andreas Sandberg
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
description: UltraChip UC8175 EPD controller
|
||||||
|
|
||||||
|
compatible: "ultrachip,uc8175"
|
||||||
|
|
||||||
|
include: ultrachip,uc81xx-common.yaml
|
||||||
Loading…
Reference in a new issue