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