cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ssd130x-spi.c (4911B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * DRM driver for Solomon SSD130X OLED displays (SPI bus)
      4 *
      5 * Copyright 2022 Red Hat Inc.
      6 * Authors: Javier Martinez Canillas <javierm@redhat.com>
      7 */
      8#include <linux/spi/spi.h>
      9#include <linux/module.h>
     10
     11#include "ssd130x.h"
     12
     13#define DRIVER_NAME	"ssd130x-spi"
     14#define DRIVER_DESC	"DRM driver for Solomon SSD130X OLED displays (SPI)"
     15
     16struct ssd130x_spi_transport {
     17	struct spi_device *spi;
     18	struct gpio_desc *dc;
     19};
     20
     21static const struct regmap_config ssd130x_spi_regmap_config = {
     22	.reg_bits = 8,
     23	.val_bits = 8,
     24};
     25
     26/*
     27 * The regmap bus .write handler, it is just a wrapper around spi_write()
     28 * but toggling the Data/Command control pin (D/C#). Since for 4-wire SPI
     29 * a D/C# pin is used, in contrast with I2C where a control byte is sent,
     30 * prior to every data byte, that contains a bit with the D/C# value.
     31 *
     32 * These control bytes are considered registers by the ssd130x core driver
     33 * and can be used by the ssd130x SPI driver to determine if the data sent
     34 * is for a command register or for the Graphic Display Data RAM (GDDRAM).
     35 */
     36static int ssd130x_spi_write(void *context, const void *data, size_t count)
     37{
     38	struct ssd130x_spi_transport *t = context;
     39	struct spi_device *spi = t->spi;
     40	const u8 *reg = data;
     41
     42	if (*reg == SSD130X_COMMAND)
     43		gpiod_set_value_cansleep(t->dc, 0);
     44
     45	if (*reg == SSD130X_DATA)
     46		gpiod_set_value_cansleep(t->dc, 1);
     47
     48	/* Remove control byte since is not used in a 4-wire SPI interface */
     49	return spi_write(spi, reg + 1, count - 1);
     50}
     51
     52/* The ssd130x driver does not read registers but regmap expects a .read */
     53static int ssd130x_spi_read(void *context, const void *reg, size_t reg_size,
     54			    void *val, size_t val_size)
     55{
     56	return -EOPNOTSUPP;
     57}
     58
     59/*
     60 * A custom bus is needed due the special write that toggles a D/C# pin,
     61 * another option could be to just have a .reg_write() callback but that
     62 * will prevent to do data writes in bulk.
     63 *
     64 * Once the regmap API is extended to support defining a bulk write handler
     65 * in the struct regmap_config, this can be simplified and the bus dropped.
     66 */
     67static struct regmap_bus regmap_ssd130x_spi_bus = {
     68	.write = ssd130x_spi_write,
     69	.read = ssd130x_spi_read,
     70};
     71
     72static int ssd130x_spi_probe(struct spi_device *spi)
     73{
     74	struct ssd130x_spi_transport *t;
     75	struct ssd130x_device *ssd130x;
     76	struct regmap *regmap;
     77	struct gpio_desc *dc;
     78	struct device *dev = &spi->dev;
     79
     80	dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW);
     81	if (IS_ERR(dc))
     82		return dev_err_probe(dev, PTR_ERR(dc),
     83				     "Failed to get dc gpio\n");
     84
     85	t = devm_kzalloc(dev, sizeof(*t), GFP_KERNEL);
     86	if (!t)
     87		return dev_err_probe(dev, -ENOMEM,
     88				     "Failed to allocate SPI transport data\n");
     89
     90	t->spi = spi;
     91	t->dc = dc;
     92
     93	regmap = devm_regmap_init(dev, &regmap_ssd130x_spi_bus, t,
     94				  &ssd130x_spi_regmap_config);
     95	if (IS_ERR(regmap))
     96		return PTR_ERR(regmap);
     97
     98	ssd130x = ssd130x_probe(dev, regmap);
     99	if (IS_ERR(ssd130x))
    100		return PTR_ERR(ssd130x);
    101
    102	spi_set_drvdata(spi, ssd130x);
    103
    104	return 0;
    105}
    106
    107static void ssd130x_spi_remove(struct spi_device *spi)
    108{
    109	struct ssd130x_device *ssd130x = spi_get_drvdata(spi);
    110
    111	ssd130x_remove(ssd130x);
    112}
    113
    114static void ssd130x_spi_shutdown(struct spi_device *spi)
    115{
    116	struct ssd130x_device *ssd130x = spi_get_drvdata(spi);
    117
    118	ssd130x_shutdown(ssd130x);
    119}
    120
    121static const struct of_device_id ssd130x_of_match[] = {
    122	{
    123		.compatible = "sinowealth,sh1106",
    124		.data = &ssd130x_variants[SH1106_ID],
    125	},
    126	{
    127		.compatible = "solomon,ssd1305",
    128		.data = &ssd130x_variants[SSD1305_ID],
    129	},
    130	{
    131		.compatible = "solomon,ssd1306",
    132		.data = &ssd130x_variants[SSD1306_ID],
    133	},
    134	{
    135		.compatible = "solomon,ssd1307",
    136		.data = &ssd130x_variants[SSD1307_ID],
    137	},
    138	{
    139		.compatible = "solomon,ssd1309",
    140		.data = &ssd130x_variants[SSD1309_ID],
    141	},
    142	{ /* sentinel */ }
    143};
    144MODULE_DEVICE_TABLE(of, ssd130x_of_match);
    145
    146/*
    147 * The SPI core always reports a MODALIAS uevent of the form "spi:<dev>", even
    148 * if the device was registered via OF. This means that the module will not be
    149 * auto loaded, unless it contains an alias that matches the MODALIAS reported.
    150 *
    151 * To workaround this issue, add a SPI device ID table. Even when this should
    152 * not be needed for this driver to match the registered SPI devices.
    153 */
    154static const struct spi_device_id ssd130x_spi_table[] = {
    155	{ "sh1106",  SH1106_ID },
    156	{ "ssd1305", SSD1305_ID },
    157	{ "ssd1306", SSD1306_ID },
    158	{ "ssd1307", SSD1307_ID },
    159	{ "ssd1309", SSD1309_ID },
    160	{ /* sentinel */ }
    161};
    162MODULE_DEVICE_TABLE(spi, ssd130x_spi_table);
    163
    164static struct spi_driver ssd130x_spi_driver = {
    165	.driver = {
    166		.name = DRIVER_NAME,
    167		.of_match_table = ssd130x_of_match,
    168	},
    169	.probe = ssd130x_spi_probe,
    170	.remove = ssd130x_spi_remove,
    171	.shutdown = ssd130x_spi_shutdown,
    172};
    173module_spi_driver(ssd130x_spi_driver);
    174
    175MODULE_DESCRIPTION(DRIVER_DESC);
    176MODULE_AUTHOR("Javier Martinez Canillas <javierm@redhat.com>");
    177MODULE_LICENSE("GPL");
    178MODULE_IMPORT_NS(DRM_SSD130X);