Skip to content

Commit e6ece74

Browse files
committed
drivers: display: ssd1306: Support connecting SPI and I2C at the same
Support connecting different display for each SPI and I2C at the same time. In a case like DTS below. ``` &spi1 { ssd1306_spi: ssd1306@0 { compatible = "solomon,ssd1306fb"; ... }; }; &i2c0 { ssd1306_i2c: ssd1306@3c { compatible = "solomon,ssd1306fb"; ... }; }; ``` Signed-off-by: TOKITA Hiroshi <[email protected]>
1 parent c7010ab commit e6ece74

File tree

1 file changed

+97
-55
lines changed

1 file changed

+97
-55
lines changed

drivers/display/ssd1306.c

+97-55
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,6 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
#if defined(CONFIG_DT_HAS_SOLOMON_SSD1306FB_ENABLED)
8-
#define DT_DRV_COMPAT solomon_ssd1306fb
9-
#elif defined(CONFIG_DT_HAS_SINOWEALTH_SH1106_ENABLED)
10-
#define DT_DRV_COMPAT sinowealth_sh1106
11-
#endif
12-
137
#include <zephyr/logging/log.h>
148
LOG_MODULE_REGISTER(ssd1306, CONFIG_DISPLAY_LOG_LEVEL);
159

@@ -33,14 +27,23 @@ LOG_MODULE_REGISTER(ssd1306, CONFIG_DISPLAY_LOG_LEVEL);
3327
#define SSD1306_ADDRESSING_MODE (SSD1306_SET_MEM_ADDRESSING_HORIZONTAL)
3428
#endif
3529

30+
union ssd1306_bus {
31+
struct i2c_dt_spec i2c;
32+
struct spi_dt_spec spi;
33+
};
34+
35+
typedef bool (*ssd1306_bus_ready_fn)(const struct device *dev);
36+
typedef int (*ssd1306_write_bus_fn)(const struct device *dev, uint8_t *buf, size_t len,
37+
bool command);
38+
typedef const char *(*ssd1306_bus_name_fn)(const struct device *dev);
39+
3640
struct ssd1306_config {
37-
#if DT_INST_ON_BUS(0, i2c)
38-
struct i2c_dt_spec bus;
39-
#elif DT_INST_ON_BUS(0, spi)
40-
struct spi_dt_spec bus;
41+
union ssd1306_bus bus;
4142
struct gpio_dt_spec data_cmd;
42-
#endif
4343
struct gpio_dt_spec reset;
44+
ssd1306_bus_ready_fn bus_ready;
45+
ssd1306_write_bus_fn write_bus;
46+
ssd1306_bus_name_fn bus_name;
4447
uint16_t height;
4548
uint16_t width;
4649
uint8_t segment_offset;
@@ -61,41 +64,47 @@ struct ssd1306_data {
6164
uint8_t scan_mode;
6265
};
6366

64-
#if DT_INST_ON_BUS(0, i2c)
65-
66-
static inline bool ssd1306_bus_ready(const struct device *dev)
67+
#if (DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(solomon_ssd1306fb, i2c) || \
68+
DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(sinowealth_sh1106, i2c))
69+
static bool ssd1306_bus_ready_i2c(const struct device *dev)
6770
{
6871
const struct ssd1306_config *config = dev->config;
6972

70-
return device_is_ready(config->bus.bus);
73+
return i2c_is_ready_dt(&config->bus.i2c);
7174
}
7275

73-
static inline int ssd1306_write_bus(const struct device *dev,
74-
uint8_t *buf, size_t len, bool command)
76+
static int ssd1306_write_bus_i2c(const struct device *dev, uint8_t *buf, size_t len, bool command)
7577
{
7678
const struct ssd1306_config *config = dev->config;
7779

78-
return i2c_burst_write_dt(&config->bus,
80+
return i2c_burst_write_dt(&config->bus.i2c,
7981
command ? SSD1306_CONTROL_ALL_BYTES_CMD :
8082
SSD1306_CONTROL_ALL_BYTES_DATA,
8183
buf, len);
8284
}
8385

84-
#elif DT_INST_ON_BUS(0, spi)
86+
static const char *ssd1306_bus_name_i2c(const struct device *dev)
87+
{
88+
const struct ssd1306_config *config = dev->config;
8589

86-
static inline bool ssd1306_bus_ready(const struct device *dev)
90+
return config->bus.i2c.bus->name;
91+
}
92+
#endif
93+
94+
#if (DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(solomon_ssd1306fb, spi) || \
95+
DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(sinowealth_sh1106, spi))
96+
static bool ssd1306_bus_ready_spi(const struct device *dev)
8797
{
8898
const struct ssd1306_config *config = dev->config;
8999

90100
if (gpio_pin_configure_dt(&config->data_cmd, GPIO_OUTPUT_INACTIVE) < 0) {
91101
return false;
92102
}
93103

94-
return spi_is_ready_dt(&config->bus);
104+
return spi_is_ready_dt(&config->bus.spi);
95105
}
96106

97-
static inline int ssd1306_write_bus(const struct device *dev,
98-
uint8_t *buf, size_t len, bool command)
107+
static int ssd1306_write_bus_spi(const struct device *dev, uint8_t *buf, size_t len, bool command)
99108
{
100109
const struct ssd1306_config *config = dev->config;
101110
int errno;
@@ -111,12 +120,34 @@ static inline int ssd1306_write_bus(const struct device *dev,
111120
.count = 1
112121
};
113122

114-
errno = spi_write_dt(&config->bus, &tx_bufs);
123+
errno = spi_write_dt(&config->bus.spi, &tx_bufs);
115124

116125
return errno;
117126
}
127+
128+
static const char *ssd1306_bus_name_spi(const struct device *dev)
129+
{
130+
const struct ssd1306_config *config = dev->config;
131+
132+
return config->bus.spi.bus->name;
133+
}
118134
#endif
119135

136+
static inline bool ssd1306_bus_ready(const struct device *dev)
137+
{
138+
const struct ssd1306_config *config = dev->config;
139+
140+
return config->bus_ready(dev);
141+
}
142+
143+
static inline int ssd1306_write_bus(const struct device *dev, uint8_t *buf, size_t len,
144+
bool command)
145+
{
146+
const struct ssd1306_config *config = dev->config;
147+
148+
return config->write_bus(dev, buf, len, command);
149+
}
150+
120151
static inline int ssd1306_set_panel_orientation(const struct device *dev)
121152
{
122153
const struct ssd1306_config *config = dev->config;
@@ -410,7 +441,7 @@ static int ssd1306_init(const struct device *dev)
410441
k_sleep(K_TIMEOUT_ABS_MS(config->ready_time_ms));
411442

412443
if (!ssd1306_bus_ready(dev)) {
413-
LOG_ERR("Bus device %s not ready!", config->bus.bus->name);
444+
LOG_ERR("Bus device %s not ready!", config->bus_name(dev));
414445
return -EINVAL;
415446
}
416447

@@ -432,32 +463,6 @@ static int ssd1306_init(const struct device *dev)
432463
return 0;
433464
}
434465

435-
static const struct ssd1306_config ssd1306_config = {
436-
#if DT_INST_ON_BUS(0, i2c)
437-
.bus = I2C_DT_SPEC_INST_GET(0),
438-
#elif DT_INST_ON_BUS(0, spi)
439-
.bus = SPI_DT_SPEC_INST_GET(
440-
0, SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8), 0),
441-
.data_cmd = GPIO_DT_SPEC_INST_GET(0, data_cmd_gpios),
442-
#endif
443-
.reset = GPIO_DT_SPEC_INST_GET_OR(0, reset_gpios, { 0 }),
444-
.height = DT_INST_PROP(0, height),
445-
.width = DT_INST_PROP(0, width),
446-
.segment_offset = DT_INST_PROP(0, segment_offset),
447-
.page_offset = DT_INST_PROP(0, page_offset),
448-
.display_offset = DT_INST_PROP(0, display_offset),
449-
.multiplex_ratio = DT_INST_PROP(0, multiplex_ratio),
450-
.segment_remap = DT_INST_PROP(0, segment_remap),
451-
.com_invdir = DT_INST_PROP(0, com_invdir),
452-
.com_sequential = DT_INST_PROP(0, com_sequential),
453-
.prechargep = DT_INST_PROP(0, prechargep),
454-
.color_inversion = DT_INST_PROP(0, inversion_on),
455-
.sh1106_compatible = DT_NODE_HAS_COMPAT(0, sinowealth_sh1106),
456-
.ready_time_ms = DT_INST_PROP(0, ready_time_ms),
457-
};
458-
459-
static struct ssd1306_data ssd1306_driver;
460-
461466
static struct display_driver_api ssd1306_driver_api = {
462467
.blanking_on = ssd1306_suspend,
463468
.blanking_off = ssd1306_resume,
@@ -471,7 +476,44 @@ static struct display_driver_api ssd1306_driver_api = {
471476
.set_orientation = ssd1306_set_orientation,
472477
};
473478

474-
DEVICE_DT_INST_DEFINE(0, ssd1306_init, NULL,
475-
&ssd1306_driver, &ssd1306_config,
476-
POST_KERNEL, CONFIG_DISPLAY_INIT_PRIORITY,
477-
&ssd1306_driver_api);
479+
#define SSD1306_CONFIG_SPI(node_id) \
480+
.bus = {.spi = SPI_DT_SPEC_GET( \
481+
node_id, SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8), 0)}, \
482+
.bus_ready = ssd1306_bus_ready_spi, \
483+
.write_bus = ssd1306_write_bus_spi, \
484+
.bus_name = ssd1306_bus_name_spi, \
485+
.data_cmd = GPIO_DT_SPEC_GET(node_id, data_cmd_gpios),
486+
487+
#define SSD1306_CONFIG_I2C(node_id) \
488+
.bus = {.i2c = I2C_DT_SPEC_GET(node_id)}, \
489+
.bus_ready = ssd1306_bus_ready_i2c, \
490+
.write_bus = ssd1306_write_bus_i2c, \
491+
.bus_name = ssd1306_bus_name_i2c, \
492+
.data_cmd = {0},
493+
494+
#define SSD1306_DEFINE(node_id) \
495+
static struct ssd1306_data data##node_id; \
496+
static const struct ssd1306_config config##node_id = { \
497+
.reset = GPIO_DT_SPEC_GET_OR(node_id, reset_gpios, {0}), \
498+
.height = DT_PROP(node_id, height), \
499+
.width = DT_PROP(node_id, width), \
500+
.segment_offset = DT_PROP(node_id, segment_offset), \
501+
.page_offset = DT_PROP(node_id, page_offset), \
502+
.display_offset = DT_PROP(node_id, display_offset), \
503+
.multiplex_ratio = DT_PROP(node_id, multiplex_ratio), \
504+
.segment_remap = DT_PROP(node_id, segment_remap), \
505+
.com_invdir = DT_PROP(node_id, com_invdir), \
506+
.com_sequential = DT_PROP(node_id, com_sequential), \
507+
.prechargep = DT_PROP(node_id, prechargep), \
508+
.color_inversion = DT_PROP(node_id, inversion_on), \
509+
.sh1106_compatible = DT_NODE_HAS_COMPAT(node_id, sinowealth_sh1106), \
510+
.ready_time_ms = DT_PROP(node_id, ready_time_ms), \
511+
COND_CODE_1(DT_ON_BUS(node_id, spi), (SSD1306_CONFIG_SPI(node_id)), \
512+
(SSD1306_CONFIG_I2C(node_id))) \
513+
}; \
514+
\
515+
DEVICE_DT_DEFINE(node_id, ssd1306_init, NULL, &data##node_id, &config##node_id, \
516+
POST_KERNEL, CONFIG_DISPLAY_INIT_PRIORITY, &ssd1306_driver_api);
517+
518+
DT_FOREACH_STATUS_OKAY(solomon_ssd1306fb, SSD1306_DEFINE)
519+
DT_FOREACH_STATUS_OKAY(sinowealth_sh1106, SSD1306_DEFINE)

0 commit comments

Comments
 (0)