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 /*
2274373977SJavier Martinez Canillas * The regmap bus .write handler, it is just a wrapper around spi_write()
2374373977SJavier Martinez Canillas * but toggling the Data/Command control pin (D/C#). Since for 4-wire SPI
2474373977SJavier Martinez Canillas * a D/C# pin is used, in contrast with I2C where a control byte is sent,
2574373977SJavier Martinez Canillas * prior to every data byte, that contains a bit with the D/C# value.
2674373977SJavier Martinez Canillas *
2774373977SJavier Martinez Canillas * These control bytes are considered registers by the ssd130x core driver
2874373977SJavier Martinez Canillas * and can be used by the ssd130x SPI driver to determine if the data sent
2974373977SJavier Martinez Canillas * is for a command register or for the Graphic Display Data RAM (GDDRAM).
3074373977SJavier Martinez Canillas */
ssd130x_spi_write(void * context,const void * data,size_t count)3174373977SJavier Martinez Canillas static int ssd130x_spi_write(void *context, const void *data, size_t count)
3274373977SJavier Martinez Canillas {
3374373977SJavier Martinez Canillas struct ssd130x_spi_transport *t = context;
3474373977SJavier Martinez Canillas struct spi_device *spi = t->spi;
3574373977SJavier Martinez Canillas const u8 *reg = data;
3674373977SJavier Martinez Canillas
3774373977SJavier Martinez Canillas if (*reg == SSD130X_COMMAND)
3874373977SJavier Martinez Canillas gpiod_set_value_cansleep(t->dc, 0);
3974373977SJavier Martinez Canillas
4074373977SJavier Martinez Canillas if (*reg == SSD130X_DATA)
4174373977SJavier Martinez Canillas gpiod_set_value_cansleep(t->dc, 1);
4274373977SJavier Martinez Canillas
4374373977SJavier Martinez Canillas /* Remove control byte since is not used in a 4-wire SPI interface */
4474373977SJavier Martinez Canillas return spi_write(spi, reg + 1, count - 1);
4574373977SJavier Martinez Canillas }
4674373977SJavier Martinez Canillas
4774373977SJavier Martinez Canillas /* The ssd130x driver does not read registers but regmap expects a .read */
ssd130x_spi_read(void * context,const void * reg,size_t reg_size,void * val,size_t val_size)4874373977SJavier Martinez Canillas static int ssd130x_spi_read(void *context, const void *reg, size_t reg_size,
4974373977SJavier Martinez Canillas void *val, size_t val_size)
5074373977SJavier Martinez Canillas {
5174373977SJavier Martinez Canillas return -EOPNOTSUPP;
5274373977SJavier Martinez Canillas }
5374373977SJavier Martinez Canillas
54*6fc20a85SJavier Martinez Canillas static const struct regmap_config ssd130x_spi_regmap_config = {
55*6fc20a85SJavier Martinez Canillas .reg_bits = 8,
56*6fc20a85SJavier Martinez Canillas .val_bits = 8,
5774373977SJavier Martinez Canillas .write = ssd130x_spi_write,
5874373977SJavier Martinez Canillas .read = ssd130x_spi_read,
59*6fc20a85SJavier Martinez Canillas .can_multi_write = true,
6074373977SJavier Martinez Canillas };
6174373977SJavier Martinez Canillas
ssd130x_spi_probe(struct spi_device * spi)6274373977SJavier Martinez Canillas static int ssd130x_spi_probe(struct spi_device *spi)
6374373977SJavier Martinez Canillas {
6474373977SJavier Martinez Canillas struct ssd130x_spi_transport *t;
6574373977SJavier Martinez Canillas struct ssd130x_device *ssd130x;
6674373977SJavier Martinez Canillas struct regmap *regmap;
6774373977SJavier Martinez Canillas struct gpio_desc *dc;
6874373977SJavier Martinez Canillas struct device *dev = &spi->dev;
6974373977SJavier Martinez Canillas
7074373977SJavier Martinez Canillas dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW);
7174373977SJavier Martinez Canillas if (IS_ERR(dc))
7274373977SJavier Martinez Canillas return dev_err_probe(dev, PTR_ERR(dc),
7374373977SJavier Martinez Canillas "Failed to get dc gpio\n");
7474373977SJavier Martinez Canillas
7574373977SJavier Martinez Canillas t = devm_kzalloc(dev, sizeof(*t), GFP_KERNEL);
7674373977SJavier Martinez Canillas if (!t)
7774373977SJavier Martinez Canillas return dev_err_probe(dev, -ENOMEM,
7874373977SJavier Martinez Canillas "Failed to allocate SPI transport data\n");
7974373977SJavier Martinez Canillas
8074373977SJavier Martinez Canillas t->spi = spi;
8174373977SJavier Martinez Canillas t->dc = dc;
8274373977SJavier Martinez Canillas
83*6fc20a85SJavier Martinez Canillas regmap = devm_regmap_init(dev, NULL, t, &ssd130x_spi_regmap_config);
8474373977SJavier Martinez Canillas if (IS_ERR(regmap))
8574373977SJavier Martinez Canillas return PTR_ERR(regmap);
8674373977SJavier Martinez Canillas
8774373977SJavier Martinez Canillas ssd130x = ssd130x_probe(dev, regmap);
8874373977SJavier Martinez Canillas if (IS_ERR(ssd130x))
8974373977SJavier Martinez Canillas return PTR_ERR(ssd130x);
9074373977SJavier Martinez Canillas
9174373977SJavier Martinez Canillas spi_set_drvdata(spi, ssd130x);
9274373977SJavier Martinez Canillas
9374373977SJavier Martinez Canillas return 0;
9474373977SJavier Martinez Canillas }
9574373977SJavier Martinez Canillas
ssd130x_spi_remove(struct spi_device * spi)9674373977SJavier Martinez Canillas static void ssd130x_spi_remove(struct spi_device *spi)
9774373977SJavier Martinez Canillas {
9874373977SJavier Martinez Canillas struct ssd130x_device *ssd130x = spi_get_drvdata(spi);
9974373977SJavier Martinez Canillas
10074373977SJavier Martinez Canillas ssd130x_remove(ssd130x);
10174373977SJavier Martinez Canillas }
10274373977SJavier Martinez Canillas
ssd130x_spi_shutdown(struct spi_device * spi)10374373977SJavier Martinez Canillas static void ssd130x_spi_shutdown(struct spi_device *spi)
10474373977SJavier Martinez Canillas {
10574373977SJavier Martinez Canillas struct ssd130x_device *ssd130x = spi_get_drvdata(spi);
10674373977SJavier Martinez Canillas
10774373977SJavier Martinez Canillas ssd130x_shutdown(ssd130x);
10874373977SJavier Martinez Canillas }
10974373977SJavier Martinez Canillas
11074373977SJavier Martinez Canillas static const struct of_device_id ssd130x_of_match[] = {
11174373977SJavier Martinez Canillas {
11274373977SJavier Martinez Canillas .compatible = "sinowealth,sh1106",
11374373977SJavier Martinez Canillas .data = &ssd130x_variants[SH1106_ID],
11474373977SJavier Martinez Canillas },
11574373977SJavier Martinez Canillas {
11674373977SJavier Martinez Canillas .compatible = "solomon,ssd1305",
11774373977SJavier Martinez Canillas .data = &ssd130x_variants[SSD1305_ID],
11874373977SJavier Martinez Canillas },
11974373977SJavier Martinez Canillas {
12074373977SJavier Martinez Canillas .compatible = "solomon,ssd1306",
12174373977SJavier Martinez Canillas .data = &ssd130x_variants[SSD1306_ID],
12274373977SJavier Martinez Canillas },
12374373977SJavier Martinez Canillas {
12474373977SJavier Martinez Canillas .compatible = "solomon,ssd1307",
12574373977SJavier Martinez Canillas .data = &ssd130x_variants[SSD1307_ID],
12674373977SJavier Martinez Canillas },
12774373977SJavier Martinez Canillas {
12874373977SJavier Martinez Canillas .compatible = "solomon,ssd1309",
12974373977SJavier Martinez Canillas .data = &ssd130x_variants[SSD1309_ID],
13074373977SJavier Martinez Canillas },
13174373977SJavier Martinez Canillas { /* sentinel */ }
13274373977SJavier Martinez Canillas };
13374373977SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, ssd130x_of_match);
13474373977SJavier Martinez Canillas
13501ece651SJavier Martinez Canillas #if IS_MODULE(CONFIG_DRM_SSD130X_SPI)
13674373977SJavier Martinez Canillas /*
13774373977SJavier Martinez Canillas * The SPI core always reports a MODALIAS uevent of the form "spi:<dev>", even
13874373977SJavier Martinez Canillas * if the device was registered via OF. This means that the module will not be
13974373977SJavier Martinez Canillas * auto loaded, unless it contains an alias that matches the MODALIAS reported.
14074373977SJavier Martinez Canillas *
14174373977SJavier Martinez Canillas * To workaround this issue, add a SPI device ID table. Even when this should
14274373977SJavier Martinez Canillas * not be needed for this driver to match the registered SPI devices.
14374373977SJavier Martinez Canillas */
14474373977SJavier Martinez Canillas static const struct spi_device_id ssd130x_spi_table[] = {
14574373977SJavier Martinez Canillas { "sh1106", SH1106_ID },
14674373977SJavier Martinez Canillas { "ssd1305", SSD1305_ID },
14774373977SJavier Martinez Canillas { "ssd1306", SSD1306_ID },
14874373977SJavier Martinez Canillas { "ssd1307", SSD1307_ID },
14974373977SJavier Martinez Canillas { "ssd1309", SSD1309_ID },
15074373977SJavier Martinez Canillas { /* sentinel */ }
15174373977SJavier Martinez Canillas };
15274373977SJavier Martinez Canillas MODULE_DEVICE_TABLE(spi, ssd130x_spi_table);
15301ece651SJavier Martinez Canillas #endif
15474373977SJavier Martinez Canillas
15574373977SJavier Martinez Canillas static struct spi_driver ssd130x_spi_driver = {
15674373977SJavier Martinez Canillas .driver = {
15774373977SJavier Martinez Canillas .name = DRIVER_NAME,
15874373977SJavier Martinez Canillas .of_match_table = ssd130x_of_match,
15974373977SJavier Martinez Canillas },
16074373977SJavier Martinez Canillas .probe = ssd130x_spi_probe,
16174373977SJavier Martinez Canillas .remove = ssd130x_spi_remove,
16274373977SJavier Martinez Canillas .shutdown = ssd130x_spi_shutdown,
16374373977SJavier Martinez Canillas };
16474373977SJavier Martinez Canillas module_spi_driver(ssd130x_spi_driver);
16574373977SJavier Martinez Canillas
16674373977SJavier Martinez Canillas MODULE_DESCRIPTION(DRIVER_DESC);
16774373977SJavier Martinez Canillas MODULE_AUTHOR("Javier Martinez Canillas <javierm@redhat.com>");
16874373977SJavier Martinez Canillas MODULE_LICENSE("GPL");
16974373977SJavier Martinez Canillas MODULE_IMPORT_NS(DRM_SSD130X);
170