Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2018 Google LLC.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <errno.h>
#include <led_strip.h>
#include <spi.h>

struct apa102_data {
	struct spi_config cfg;
};

static int apa102_update(struct device *dev, void *buf, size_t size)
{
	struct apa102_data *data = dev->driver_data;
	static const u8_t zeros[] = {0, 0, 0, 0};
	static const u8_t ones[] = {0xFF, 0xFF, 0xFF, 0xFF};
	struct spi_buf tx[] = {
		{
			/* Start frame: at least 32 zeros */
			.buf = (u8_t *)zeros,
			.len = sizeof(zeros),
		},
		{
			/* LED data itself */
			.buf = buf,
			.len = size,
		},
		{
			/* End frame: at least 32 ones to clock the
			 * remaining bits to the LEDs at the end of
			 * the strip.
			 */
			.buf = (u8_t *)ones,
			.len = sizeof(ones),
		},
	};

	return spi_write(&data->cfg, tx, ARRAY_SIZE(tx));
}

static int apa102_update_rgb(struct device *dev, struct led_rgb *pixels,
			     size_t count)
{
	u8_t *p = (u8_t *)pixels;
	size_t i;
	/* SOF (3 bits) followed by the 0 to 31 global dimming level */
	u8_t prefix = 0xE0 | 31;

	/* Rewrite to the on-wire format */
	for (i = 0; i < count; i++) {
		u8_t r = pixels[i].r;
		u8_t g = pixels[i].g;
		u8_t b = pixels[i].b;

		*p++ = prefix;
		*p++ = b;
		*p++ = g;
		*p++ = r;
	}

	BUILD_ASSERT(sizeof(struct led_rgb) == 4);
	return apa102_update(dev, pixels, sizeof(struct led_rgb) * count);
}

static int apa102_update_channels(struct device *dev, u8_t *channels,
				  size_t num_channels)
{
	/* Not implemented */
	return -EINVAL;
}

static int apa102_init(struct device *dev)
{
	struct apa102_data *data = dev->driver_data;
	struct device *spi;

	spi = device_get_binding(CONFIG_APA102_STRIP_BUS_NAME);
	if (!spi) {
		return -ENODEV;
	}

	data->cfg.dev = spi;
	data->cfg.frequency = CONFIG_APA102_STRIP_FREQUENCY;
	data->cfg.operation =
		SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8);

	return 0;
}

static struct apa102_data apa102_data_0;

static const struct led_strip_driver_api apa102_api = {
	.update_rgb = apa102_update_rgb,
	.update_channels = apa102_update_channels,
};

DEVICE_AND_API_INIT(apa102_0, CONFIG_APA102_STRIP_NAME, apa102_init,
		    &apa102_data_0, NULL, POST_KERNEL,
		    CONFIG_LED_STRIP_INIT_PRIORITY, &apa102_api);