Files

284 lines
6.5 KiB
C

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <common.h>
#include <asm/io.h>
#include <dm.h>
#include <errno.h>
#include <led.h>
#include <asm/gpio.h>
#include <dm/lists.h>
#include <malloc.h>
#include <errno.h>
#define DEFAULT_T0H (200)
#define DEFAULT_T0L (800)
#define DEFAULT_T1H (800)
#define DEFAULT_T1L (200)
#define DEFAULT_TRES (8000)
/* 4 led by defalt */
#define DEFAULT_LED_COUNTS (4)
struct unipolar_led_timing {
/*timing count in ns */
unsigned int t0l;
unsigned int t0h;
unsigned int t1l;
unsigned int t1h;
unsigned int tres;
};
struct led_unipolar_gpio_priv {
struct gpio_desc gpio;
/* led counts*/
unsigned int counts;
/* timming setting */
struct unipolar_led_timing timing;
/* data frame with counts x 3(r,g,b) */
unsigned char *frame;
const unsigned char *defcolor;
unsigned int effect;
};
static void unipolar_rz_0(struct led_unipolar_gpio_priv *priv)
{
struct gpio_desc *gpio = &priv->gpio;
/* TOH min 0.2us */
dm_gpio_set_value(gpio, 1);
/* TOL min 0.8us */
dm_gpio_set_value(gpio, 0);
dm_gpio_set_value(gpio, 0);
}
static void unipolar_rz_1(struct led_unipolar_gpio_priv *priv)
{
struct gpio_desc *gpio = &priv->gpio;
/* T1H min 0.6us */
dm_gpio_set_value(gpio, 1);
dm_gpio_set_value(gpio, 1);
/* T1L min 0.2us */
dm_gpio_set_value(gpio, 0);
}
static void unipolar_rz_res(struct led_unipolar_gpio_priv *priv)
{
struct gpio_desc *gpio = &priv->gpio;
struct unipolar_led_timing *timing = &priv->timing;
/* Trst, min 80us */
dm_gpio_set_value(gpio, 0);
udelay(timing->tres / 1000);
}
static void byte_to_unipolar_rz(struct led_unipolar_gpio_priv *priv, unsigned char d)
{
int i = 0;
for (i = 0; i < 8; ++i) {
if ((d >> i) & 0x1)
unipolar_rz_1(priv);
else
unipolar_rz_0(priv);
}
}
static void set_led_rgb(struct led_unipolar_gpio_priv *priv,
unsigned char r, unsigned char g, unsigned char b)
{
byte_to_unipolar_rz(priv, r);
byte_to_unipolar_rz(priv, g);
byte_to_unipolar_rz(priv, b);
}
static int unipolar_gpio_leds_onoff(struct led_unipolar_gpio_priv *priv, enum led_state_t state)
{
int ret = 0, rgb;
unsigned int i, counts = priv->counts;
rgb = (state == LEDST_OFF) ? 0 : 0xff;
for (i = 0; i < counts; ++i)
set_led_rgb(priv, rgb, rgb, rgb);
unipolar_rz_res(priv);
priv->effect = state;
return ret;
}
static int unipolar_gpio_leds_effect(struct led_unipolar_gpio_priv *priv, unsigned int effect)
{
int ret = 0;
unsigned int i, counts = priv->counts;
unsigned char *rgb = (unsigned char *)priv->defcolor;
/* send frame rest */
unipolar_rz_res(priv);
if (effect < 14) {
for (i = 0; i < effect; ++i)
set_led_rgb(priv, 255, 0, 255);
} else {
for (i = 0; i < counts; i++)
set_led_rgb(priv, rgb[i * 3 + (effect % 3 + 0) % 3],
rgb[i * 3 + (effect % 3 + 1) % 3],
rgb[i * 3 + (effect % 3 + 2) % 3]);
}
unipolar_rz_res(priv);
priv->effect = effect;
return ret;
}
static int unipolar_gpio_led_set_state(struct udevice *dev, enum led_state_t state)
{
struct led_unipolar_gpio_priv *priv = dev_get_priv(dev);
int ret;
if (!dm_gpio_is_valid(&priv->gpio))
return -EREMOTEIO;
switch (state) {
case LEDST_OFF:
case LEDST_ON:
ret = unipolar_gpio_leds_onoff(priv, state);
break;
case LEDST_TOGGLE:
printf("%s: effect %d\n", __func__, priv->effect);
ret = unipolar_gpio_leds_effect(priv, ++priv->effect);
break;
/*
* To satisfy the coding style check
* WARNING: ENOSYS means 'invalid syscall nr' and nothing else
* using error number directly.
*/
default:
ret = -38;
}
return ret;
}
static enum led_state_t unipolar_gpio_led_get_state(struct udevice *dev)
{
struct led_unipolar_gpio_priv *priv = dev_get_priv(dev);
int ret;
ret = priv->effect;
return ret ? LEDST_ON : LEDST_OFF;
}
static int led_unipolar_gpio_probe(struct udevice *dev)
{
struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev);
struct led_unipolar_gpio_priv *priv = dev_get_priv(dev);
struct unipolar_led_timing *timing = &priv->timing;
int ret = 0;
size_t sz;
priv->frame = NULL;
priv->effect = 0;
/* Ignore the top-level LED node */
if (!uc_plat->label)
goto _out;
/* gpio */
ret = gpio_request_by_name(dev, "gpios", 0, &priv->gpio, GPIOD_IS_OUT);
if (ret)
goto _out;
/* timing */
timing->t0h = dev_read_u32_default(dev, "t0h", DEFAULT_T0H);
timing->t0l = dev_read_u32_default(dev, "t0l", DEFAULT_T0L);
timing->t1h = dev_read_u32_default(dev, "t1h", DEFAULT_T1H);
timing->t1l = dev_read_u32_default(dev, "t1l", DEFAULT_T1L);
timing->tres = dev_read_u32_default(dev, "tres", DEFAULT_TRES);
/* led and frame */
priv->counts = dev_read_u32_default(dev, "counts", DEFAULT_LED_COUNTS);
/* rgb */
sz = priv->counts * 3;
priv->frame = malloc(sz);
if (!priv->frame) {
ret = -ENOSPC;
goto _out;
}
/* default all white */
memset(priv->frame, 0xff, sz);
priv->defcolor = dev_read_u8_array_ptr(dev, "color-defaults", sz);
if (!priv->defcolor) {
ret = -ENODATA;
goto _out;
}
_out:
if (priv->frame)
free(priv->frame);
return ret;
}
static int led_unipolar_gpio_remove(struct udevice *dev)
{
/*
* The GPIO driver may have already been removed. We will need to
* address this more generally.
*/
#ifndef CONFIG_SANDBOX
struct led_unipolar_gpio_priv *priv = dev_get_priv(dev);
if (dm_gpio_is_valid(&priv->gpio))
dm_gpio_free(dev, &priv->gpio);
#endif
if (priv->frame)
free(priv->frame);
return 0;
}
static int led_unipolar_gpio_bind(struct udevice *parent)
{
struct udevice *dev;
ofnode node;
int ret;
dev_for_each_subnode(node, parent) {
struct led_uc_plat *uc_plat;
const char *label;
label = ofnode_read_string(node, "label");
if (!label) {
debug("%s: node %s has no label\n", __func__,
ofnode_get_name(node));
return -EINVAL;
}
ret = device_bind_driver_to_node(parent, "unipolar_gpio_led",
ofnode_get_name(node),
node, &dev);
if (ret)
return ret;
uc_plat = dev_get_uclass_platdata(dev);
uc_plat->label = label;
}
return 0;
}
static const struct led_ops unipolar_gpio_led_ops = {
.set_state = unipolar_gpio_led_set_state,
.get_state = unipolar_gpio_led_get_state,
};
static const struct udevice_id led_unipolar_gpio_ids[] = {
{ .compatible = "unipolar-gpio-leds" },
{ }
};
U_BOOT_DRIVER(unipolar_led_gpio) = {
.name = "unipolar_gpio_led",
.id = UCLASS_LED,
.of_match = led_unipolar_gpio_ids,
.ops = &unipolar_gpio_led_ops,
.priv_auto_alloc_size = sizeof(struct led_unipolar_gpio_priv),
.bind = led_unipolar_gpio_bind,
.probe = led_unipolar_gpio_probe,
.remove = led_unipolar_gpio_remove,
};