174373977SJavier Martinez Canillas // SPDX-License-Identifier: GPL-2.0-only 274373977SJavier Martinez Canillas /* 374373977SJavier Martinez Canillas * DRM driver for Solomon SSD130X OLED displays (SPI bus) 474373977SJavier Martinez Canillas * 574373977SJavier Martinez Canillas * Copyright 2022 Red Hat Inc. 674373977SJavier Martinez Canillas * Authors: Javier Martinez Canillas <javierm@redhat.com> 774373977SJavier Martinez Canillas */ 874373977SJavier Martinez Canillas #include <linux/spi/spi.h> 974373977SJavier Martinez Canillas #include <linux/module.h> 1074373977SJavier Martinez Canillas 1174373977SJavier Martinez Canillas #include "ssd130x.h" 1274373977SJavier Martinez Canillas 1374373977SJavier Martinez Canillas #define DRIVER_NAME "ssd130x-spi" 1474373977SJavier Martinez Canillas #define DRIVER_DESC "DRM driver for Solomon SSD130X OLED displays (SPI)" 1574373977SJavier Martinez Canillas 1674373977SJavier Martinez Canillas struct ssd130x_spi_transport { 1774373977SJavier Martinez Canillas struct spi_device *spi; 1874373977SJavier Martinez Canillas struct gpio_desc *dc; 1974373977SJavier Martinez Canillas }; 2074373977SJavier Martinez Canillas 2174373977SJavier Martinez Canillas static const struct regmap_config ssd130x_spi_regmap_config = { 2274373977SJavier Martinez Canillas .reg_bits = 8, 2374373977SJavier Martinez Canillas .val_bits = 8, 2474373977SJavier Martinez Canillas }; 2574373977SJavier Martinez Canillas 2674373977SJavier Martinez Canillas /* 2774373977SJavier Martinez Canillas * The regmap bus .write handler, it is just a wrapper around spi_write() 2874373977SJavier Martinez Canillas * but toggling the Data/Command control pin (D/C#). Since for 4-wire SPI 2974373977SJavier Martinez Canillas * a D/C# pin is used, in contrast with I2C where a control byte is sent, 3074373977SJavier Martinez Canillas * prior to every data byte, that contains a bit with the D/C# value. 3174373977SJavier Martinez Canillas * 3274373977SJavier Martinez Canillas * These control bytes are considered registers by the ssd130x core driver 3374373977SJavier Martinez Canillas * and can be used by the ssd130x SPI driver to determine if the data sent 3474373977SJavier Martinez Canillas * is for a command register or for the Graphic Display Data RAM (GDDRAM). 3574373977SJavier Martinez Canillas */ 3674373977SJavier Martinez Canillas static int ssd130x_spi_write(void *context, const void *data, size_t count) 3774373977SJavier Martinez Canillas { 3874373977SJavier Martinez Canillas struct ssd130x_spi_transport *t = context; 3974373977SJavier Martinez Canillas struct spi_device *spi = t->spi; 4074373977SJavier Martinez Canillas const u8 *reg = data; 4174373977SJavier Martinez Canillas 4274373977SJavier Martinez Canillas if (*reg == SSD130X_COMMAND) 4374373977SJavier Martinez Canillas gpiod_set_value_cansleep(t->dc, 0); 4474373977SJavier Martinez Canillas 4574373977SJavier Martinez Canillas if (*reg == SSD130X_DATA) 4674373977SJavier Martinez Canillas gpiod_set_value_cansleep(t->dc, 1); 4774373977SJavier Martinez Canillas 4874373977SJavier Martinez Canillas /* Remove control byte since is not used in a 4-wire SPI interface */ 4974373977SJavier Martinez Canillas return spi_write(spi, reg + 1, count - 1); 5074373977SJavier Martinez Canillas } 5174373977SJavier Martinez Canillas 5274373977SJavier Martinez Canillas /* The ssd130x driver does not read registers but regmap expects a .read */ 5374373977SJavier Martinez Canillas static int ssd130x_spi_read(void *context, const void *reg, size_t reg_size, 5474373977SJavier Martinez Canillas void *val, size_t val_size) 5574373977SJavier Martinez Canillas { 5674373977SJavier Martinez Canillas return -EOPNOTSUPP; 5774373977SJavier Martinez Canillas } 5874373977SJavier Martinez Canillas 5974373977SJavier Martinez Canillas /* 6074373977SJavier Martinez Canillas * A custom bus is needed due the special write that toggles a D/C# pin, 6174373977SJavier Martinez Canillas * another option could be to just have a .reg_write() callback but that 6274373977SJavier Martinez Canillas * will prevent to do data writes in bulk. 6374373977SJavier Martinez Canillas * 6474373977SJavier Martinez Canillas * Once the regmap API is extended to support defining a bulk write handler 6574373977SJavier Martinez Canillas * in the struct regmap_config, this can be simplified and the bus dropped. 6674373977SJavier Martinez Canillas */ 6774373977SJavier Martinez Canillas static struct regmap_bus regmap_ssd130x_spi_bus = { 6874373977SJavier Martinez Canillas .write = ssd130x_spi_write, 6974373977SJavier Martinez Canillas .read = ssd130x_spi_read, 7074373977SJavier Martinez Canillas }; 7174373977SJavier Martinez Canillas 7274373977SJavier Martinez Canillas static int ssd130x_spi_probe(struct spi_device *spi) 7374373977SJavier Martinez Canillas { 7474373977SJavier Martinez Canillas struct ssd130x_spi_transport *t; 7574373977SJavier Martinez Canillas struct ssd130x_device *ssd130x; 7674373977SJavier Martinez Canillas struct regmap *regmap; 7774373977SJavier Martinez Canillas struct gpio_desc *dc; 7874373977SJavier Martinez Canillas struct device *dev = &spi->dev; 7974373977SJavier Martinez Canillas 8074373977SJavier Martinez Canillas dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW); 8174373977SJavier Martinez Canillas if (IS_ERR(dc)) 8274373977SJavier Martinez Canillas return dev_err_probe(dev, PTR_ERR(dc), 8374373977SJavier Martinez Canillas "Failed to get dc gpio\n"); 8474373977SJavier Martinez Canillas 8574373977SJavier Martinez Canillas t = devm_kzalloc(dev, sizeof(*t), GFP_KERNEL); 8674373977SJavier Martinez Canillas if (!t) 8774373977SJavier Martinez Canillas return dev_err_probe(dev, -ENOMEM, 8874373977SJavier Martinez Canillas "Failed to allocate SPI transport data\n"); 8974373977SJavier Martinez Canillas 9074373977SJavier Martinez Canillas t->spi = spi; 9174373977SJavier Martinez Canillas t->dc = dc; 9274373977SJavier Martinez Canillas 9374373977SJavier Martinez Canillas regmap = devm_regmap_init(dev, ®map_ssd130x_spi_bus, t, 9474373977SJavier Martinez Canillas &ssd130x_spi_regmap_config); 9574373977SJavier Martinez Canillas if (IS_ERR(regmap)) 9674373977SJavier Martinez Canillas return PTR_ERR(regmap); 9774373977SJavier Martinez Canillas 9874373977SJavier Martinez Canillas ssd130x = ssd130x_probe(dev, regmap); 9974373977SJavier Martinez Canillas if (IS_ERR(ssd130x)) 10074373977SJavier Martinez Canillas return PTR_ERR(ssd130x); 10174373977SJavier Martinez Canillas 10274373977SJavier Martinez Canillas spi_set_drvdata(spi, ssd130x); 10374373977SJavier Martinez Canillas 10474373977SJavier Martinez Canillas return 0; 10574373977SJavier Martinez Canillas } 10674373977SJavier Martinez Canillas 10774373977SJavier Martinez Canillas static void ssd130x_spi_remove(struct spi_device *spi) 10874373977SJavier Martinez Canillas { 10974373977SJavier Martinez Canillas struct ssd130x_device *ssd130x = spi_get_drvdata(spi); 11074373977SJavier Martinez Canillas 11174373977SJavier Martinez Canillas ssd130x_remove(ssd130x); 11274373977SJavier Martinez Canillas } 11374373977SJavier Martinez Canillas 11474373977SJavier Martinez Canillas static void ssd130x_spi_shutdown(struct spi_device *spi) 11574373977SJavier Martinez Canillas { 11674373977SJavier Martinez Canillas struct ssd130x_device *ssd130x = spi_get_drvdata(spi); 11774373977SJavier Martinez Canillas 11874373977SJavier Martinez Canillas ssd130x_shutdown(ssd130x); 11974373977SJavier Martinez Canillas } 12074373977SJavier Martinez Canillas 12174373977SJavier Martinez Canillas static const struct of_device_id ssd130x_of_match[] = { 12274373977SJavier Martinez Canillas { 12374373977SJavier Martinez Canillas .compatible = "sinowealth,sh1106", 12474373977SJavier Martinez Canillas .data = &ssd130x_variants[SH1106_ID], 12574373977SJavier Martinez Canillas }, 12674373977SJavier Martinez Canillas { 12774373977SJavier Martinez Canillas .compatible = "solomon,ssd1305", 12874373977SJavier Martinez Canillas .data = &ssd130x_variants[SSD1305_ID], 12974373977SJavier Martinez Canillas }, 13074373977SJavier Martinez Canillas { 13174373977SJavier Martinez Canillas .compatible = "solomon,ssd1306", 13274373977SJavier Martinez Canillas .data = &ssd130x_variants[SSD1306_ID], 13374373977SJavier Martinez Canillas }, 13474373977SJavier Martinez Canillas { 13574373977SJavier Martinez Canillas .compatible = "solomon,ssd1307", 13674373977SJavier Martinez Canillas .data = &ssd130x_variants[SSD1307_ID], 13774373977SJavier Martinez Canillas }, 13874373977SJavier Martinez Canillas { 13974373977SJavier Martinez Canillas .compatible = "solomon,ssd1309", 14074373977SJavier Martinez Canillas .data = &ssd130x_variants[SSD1309_ID], 14174373977SJavier Martinez Canillas }, 14274373977SJavier Martinez Canillas { /* sentinel */ } 14374373977SJavier Martinez Canillas }; 14474373977SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, ssd130x_of_match); 14574373977SJavier Martinez Canillas 146*01ece651SJavier Martinez Canillas #if IS_MODULE(CONFIG_DRM_SSD130X_SPI) 14774373977SJavier Martinez Canillas /* 14874373977SJavier Martinez Canillas * The SPI core always reports a MODALIAS uevent of the form "spi:<dev>", even 14974373977SJavier Martinez Canillas * if the device was registered via OF. This means that the module will not be 15074373977SJavier Martinez Canillas * auto loaded, unless it contains an alias that matches the MODALIAS reported. 15174373977SJavier Martinez Canillas * 15274373977SJavier Martinez Canillas * To workaround this issue, add a SPI device ID table. Even when this should 15374373977SJavier Martinez Canillas * not be needed for this driver to match the registered SPI devices. 15474373977SJavier Martinez Canillas */ 15574373977SJavier Martinez Canillas static const struct spi_device_id ssd130x_spi_table[] = { 15674373977SJavier Martinez Canillas { "sh1106", SH1106_ID }, 15774373977SJavier Martinez Canillas { "ssd1305", SSD1305_ID }, 15874373977SJavier Martinez Canillas { "ssd1306", SSD1306_ID }, 15974373977SJavier Martinez Canillas { "ssd1307", SSD1307_ID }, 16074373977SJavier Martinez Canillas { "ssd1309", SSD1309_ID }, 16174373977SJavier Martinez Canillas { /* sentinel */ } 16274373977SJavier Martinez Canillas }; 16374373977SJavier Martinez Canillas MODULE_DEVICE_TABLE(spi, ssd130x_spi_table); 164*01ece651SJavier Martinez Canillas #endif 16574373977SJavier Martinez Canillas 16674373977SJavier Martinez Canillas static struct spi_driver ssd130x_spi_driver = { 16774373977SJavier Martinez Canillas .driver = { 16874373977SJavier Martinez Canillas .name = DRIVER_NAME, 16974373977SJavier Martinez Canillas .of_match_table = ssd130x_of_match, 17074373977SJavier Martinez Canillas }, 17174373977SJavier Martinez Canillas .probe = ssd130x_spi_probe, 17274373977SJavier Martinez Canillas .remove = ssd130x_spi_remove, 17374373977SJavier Martinez Canillas .shutdown = ssd130x_spi_shutdown, 17474373977SJavier Martinez Canillas }; 17574373977SJavier Martinez Canillas module_spi_driver(ssd130x_spi_driver); 17674373977SJavier Martinez Canillas 17774373977SJavier Martinez Canillas MODULE_DESCRIPTION(DRIVER_DESC); 17874373977SJavier Martinez Canillas MODULE_AUTHOR("Javier Martinez Canillas <javierm@redhat.com>"); 17974373977SJavier Martinez Canillas MODULE_LICENSE("GPL"); 18074373977SJavier Martinez Canillas MODULE_IMPORT_NS(DRM_SSD130X); 181