mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-08-25 00:55:22 +00:00
tx_bufs/tx_count and rx_bufs/rx_count can be hold in another dedicated structure, thus reducing the number of parameters to transceive. This permits to avoid using the stack when calling transceive. Since we saved parameters, we can expose back the struct device pointer, to stay consistent with other device driver APIs. Fixes #5839 Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
153 lines
3.5 KiB
C
153 lines
3.5 KiB
C
/*
|
|
* Copyright (c) 2017 Linaro Limited
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <led_strip.h>
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_LED_STRIP_LEVEL
|
|
#include <logging/sys_log.h>
|
|
|
|
#include <zephyr.h>
|
|
#include <device.h>
|
|
#include <spi.h>
|
|
#include <misc/util.h>
|
|
|
|
/*
|
|
* LPD880X SPI master configuration:
|
|
*
|
|
* - mode 0 (the default), 8 bit, MSB first, one-line SPI
|
|
* - no shenanigans (no CS hold, release device lock, not an EEPROM)
|
|
*/
|
|
#define LPD880X_SPI_OPERATION (SPI_OP_MODE_MASTER | \
|
|
SPI_TRANSFER_MSB | \
|
|
SPI_WORD_SET(8) | \
|
|
SPI_LINES_SINGLE)
|
|
|
|
struct lpd880x_data {
|
|
struct device *spi;
|
|
struct spi_config config;
|
|
};
|
|
|
|
static int lpd880x_update(struct device *dev, void *data, size_t size)
|
|
{
|
|
struct lpd880x_data *drv_data = dev->driver_data;
|
|
/*
|
|
* Per the AdaFruit reverse engineering notes on the protocol,
|
|
* a zero byte propagates through at most 32 LED driver ICs.
|
|
* The LPD8803 is the worst case, at 3 output channels per IC.
|
|
*/
|
|
u8_t reset_size = ceiling_fraction(ceiling_fraction(size, 3), 32);
|
|
u8_t reset_buf[reset_size];
|
|
u8_t last = 0x00;
|
|
const struct spi_buf bufs[3] = {
|
|
{
|
|
/* Prepares the strip to shift in new data values. */
|
|
.buf = reset_buf,
|
|
.len = reset_size
|
|
},
|
|
{
|
|
/* Displays the serialized pixel data. */
|
|
.buf = data,
|
|
.len = size
|
|
},
|
|
{
|
|
/* Ensures the last byte of pixel data is displayed. */
|
|
.buf = &last,
|
|
.len = sizeof(last)
|
|
}
|
|
|
|
};
|
|
const struct spi_buf_set tx = {
|
|
.buffers = bufs,
|
|
.count = 3
|
|
};
|
|
size_t rc;
|
|
|
|
memset(reset_buf, 0x00, reset_size);
|
|
|
|
rc = spi_write(drv_data->spi, &drv_data->config, &tx);
|
|
if (rc) {
|
|
SYS_LOG_ERR("can't update strip: %d", rc);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int lpd880x_strip_update_rgb(struct device *dev,
|
|
struct led_rgb *pixels,
|
|
size_t num_pixels)
|
|
{
|
|
u8_t *px = (u8_t *)pixels;
|
|
u8_t r, g, b;
|
|
size_t i;
|
|
|
|
/*
|
|
* Overwrite a prefix of the pixels array with its on-wire
|
|
* representation, eliminating padding/scratch garbage, if any.
|
|
*/
|
|
for (i = 0; i < num_pixels; i++) {
|
|
r = 0x80 | (pixels[i].r >> 1);
|
|
g = 0x80 | (pixels[i].g >> 1);
|
|
b = 0x80 | (pixels[i].b >> 1);
|
|
|
|
/*
|
|
* GRB is the ordering used by commonly available
|
|
* LPD880x strips.
|
|
*/
|
|
*px++ = g;
|
|
*px++ = r;
|
|
*px++ = b;
|
|
}
|
|
|
|
return lpd880x_update(dev, pixels, 3 * num_pixels);
|
|
}
|
|
|
|
static int lpd880x_strip_update_channels(struct device *dev, u8_t *channels,
|
|
size_t num_channels)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < num_channels; i++) {
|
|
channels[i] = 0x80 | (channels[i] >> 1);
|
|
}
|
|
|
|
return lpd880x_update(dev, channels, num_channels);
|
|
}
|
|
|
|
static int lpd880x_strip_init(struct device *dev)
|
|
{
|
|
struct lpd880x_data *data = dev->driver_data;
|
|
struct spi_config *config = &data->config;
|
|
|
|
data->spi = device_get_binding(CONFIG_LPD880X_STRIP_SPI_DEV_NAME);
|
|
if (!data->spi) {
|
|
SYS_LOG_ERR("SPI device %s not found",
|
|
CONFIG_LPD880X_STRIP_SPI_DEV_NAME);
|
|
return -ENODEV;
|
|
}
|
|
|
|
config->frequency = CONFIG_LPD880X_STRIP_SPI_BAUD_RATE;
|
|
config->operation = LPD880X_SPI_OPERATION;
|
|
config->slave = 0; /* MOSI/CLK only; CS is not supported. */
|
|
config->cs = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct lpd880x_data lpd880x_strip_data;
|
|
|
|
static const struct led_strip_driver_api lpd880x_strip_api = {
|
|
.update_rgb = lpd880x_strip_update_rgb,
|
|
.update_channels = lpd880x_strip_update_channels,
|
|
};
|
|
|
|
DEVICE_AND_API_INIT(lpd880x_strip, CONFIG_LPD880X_STRIP_NAME,
|
|
lpd880x_strip_init, &lpd880x_strip_data,
|
|
NULL, POST_KERNEL, CONFIG_LED_STRIP_INIT_PRIORITY,
|
|
&lpd880x_strip_api);
|