Lines Matching +full:burst +full:- +full:length

1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2021-2023 Samuel Holland <samuel@sholland.org>
5 * Partly based on drivers/leds/leds-turris-omnia.c, which is:
12 #include <linux/dma-mapping.h>
16 #include <linux/led-class-multicolor.h>
98 static int sun50i_a100_ledc_dma_xfer(struct sun50i_a100_ledc *priv, unsigned int length) in sun50i_a100_ledc_dma_xfer() argument
103 desc = dmaengine_prep_slave_single(priv->dma_chan, priv->dma_handle, in sun50i_a100_ledc_dma_xfer()
104 LEDS_TO_BYTES(length), DMA_MEM_TO_DEV, 0); in sun50i_a100_ledc_dma_xfer()
106 return -ENOMEM; in sun50i_a100_ledc_dma_xfer()
110 return -EIO; in sun50i_a100_ledc_dma_xfer()
112 dma_async_issue_pending(priv->dma_chan); in sun50i_a100_ledc_dma_xfer()
119 unsigned int burst, length, offset; in sun50i_a100_ledc_pio_xfer() local
122 length = priv->pio_length; in sun50i_a100_ledc_pio_xfer()
123 offset = priv->pio_offset; in sun50i_a100_ledc_pio_xfer()
124 burst = min(length, LEDC_FIFO_DEPTH - fifo_used); in sun50i_a100_ledc_pio_xfer()
126 iowrite32_rep(priv->base + LEDC_DATA_REG, priv->buffer + offset, burst); in sun50i_a100_ledc_pio_xfer()
128 if (burst < length) { in sun50i_a100_ledc_pio_xfer()
129 priv->pio_length = length - burst; in sun50i_a100_ledc_pio_xfer()
130 priv->pio_offset = offset + burst; in sun50i_a100_ledc_pio_xfer()
133 control = readl(priv->base + LEDC_INT_CTRL_REG); in sun50i_a100_ledc_pio_xfer()
135 writel(control, priv->base + LEDC_INT_CTRL_REG); in sun50i_a100_ledc_pio_xfer()
139 control = readl(priv->base + LEDC_INT_CTRL_REG); in sun50i_a100_ledc_pio_xfer()
141 writel(control, priv->base + LEDC_INT_CTRL_REG); in sun50i_a100_ledc_pio_xfer()
145 static void sun50i_a100_ledc_start_xfer(struct sun50i_a100_ledc *priv, unsigned int length) in sun50i_a100_ledc_start_xfer() argument
150 if (priv->dma_chan && length > LEDC_FIFO_DEPTH) { in sun50i_a100_ledc_start_xfer()
153 ret = sun50i_a100_ledc_dma_xfer(priv, length); in sun50i_a100_ledc_start_xfer()
155 dev_warn(priv->dev, "Failed to set up DMA (%d), using PIO\n", ret); in sun50i_a100_ledc_start_xfer()
160 /* The DMA trigger level must be at least the burst length. */ in sun50i_a100_ledc_start_xfer()
163 writel(control, priv->base + LEDC_DMA_CTRL_REG); in sun50i_a100_ledc_start_xfer()
165 control = readl(priv->base + LEDC_CTRL_REG); in sun50i_a100_ledc_start_xfer()
167 control |= FIELD_PREP(LEDC_CTRL_REG_DATA_LENGTH, length) | LEDC_CTRL_REG_LEDC_EN; in sun50i_a100_ledc_start_xfer()
168 writel(control, priv->base + LEDC_CTRL_REG); in sun50i_a100_ledc_start_xfer()
174 priv->pio_length = length; in sun50i_a100_ledc_start_xfer()
175 priv->pio_offset = 0; in sun50i_a100_ledc_start_xfer()
186 status = readl(priv->base + LEDC_INT_STS_REG); in sun50i_a100_ledc_irq()
191 spin_lock(&priv->lock); in sun50i_a100_ledc_irq()
194 next_length = priv->next_length; in sun50i_a100_ledc_irq()
196 priv->next_length = 0; in sun50i_a100_ledc_irq()
198 priv->xfer_active = false; in sun50i_a100_ledc_irq()
200 spin_unlock(&priv->lock); in sun50i_a100_ledc_irq()
210 writel(status, priv->base + LEDC_INT_STS_REG); in sun50i_a100_ledc_irq()
218 struct sun50i_a100_ledc *priv = dev_get_drvdata(cdev->dev->parent); in sun50i_a100_ledc_brightness_set()
227 priv->buffer[led->addr] = led->subled_info[0].brightness << 16 | in sun50i_a100_ledc_brightness_set()
228 led->subled_info[1].brightness << 8 | in sun50i_a100_ledc_brightness_set()
229 led->subled_info[2].brightness; in sun50i_a100_ledc_brightness_set()
231 spin_lock_irqsave(&priv->lock, flags); in sun50i_a100_ledc_brightness_set()
234 next_length = max(priv->next_length, led->addr + 1); in sun50i_a100_ledc_brightness_set()
235 xfer_active = priv->xfer_active; in sun50i_a100_ledc_brightness_set()
237 priv->next_length = next_length; in sun50i_a100_ledc_brightness_set()
239 priv->xfer_active = true; in sun50i_a100_ledc_brightness_set()
241 spin_unlock_irqrestore(&priv->lock, flags); in sun50i_a100_ledc_brightness_set()
257 device_property_read_string(dev, "allwinner,pixel-format", &format); in sun50i_a100_ledc_parse_format()
263 priv->format = i; in sun50i_a100_ledc_parse_format()
271 control = readl(priv->base + LEDC_CTRL_REG); in sun50i_a100_ledc_set_format()
273 control |= FIELD_PREP(LEDC_CTRL_REG_RGB_MODE, priv->format); in sun50i_a100_ledc_set_format()
274 writel(control, priv->base + LEDC_CTRL_REG); in sun50i_a100_ledc_set_format()
288 struct sun50i_a100_ledc_timing *timing = &priv->timing; in sun50i_a100_ledc_parse_timing()
292 device_property_read_u32(dev, "allwinner,t0h-ns", &timing->t0h_ns); in sun50i_a100_ledc_parse_timing()
293 device_property_read_u32(dev, "allwinner,t0l-ns", &timing->t0l_ns); in sun50i_a100_ledc_parse_timing()
294 device_property_read_u32(dev, "allwinner,t1h-ns", &timing->t1h_ns); in sun50i_a100_ledc_parse_timing()
295 device_property_read_u32(dev, "allwinner,t1l-ns", &timing->t1l_ns); in sun50i_a100_ledc_parse_timing()
296 device_property_read_u32(dev, "allwinner,treset-ns", &timing->treset_ns); in sun50i_a100_ledc_parse_timing()
303 const struct sun50i_a100_ledc_timing *timing = &priv->timing; in sun50i_a100_ledc_set_timing()
304 unsigned long mod_freq = clk_get_rate(priv->mod_clk); in sun50i_a100_ledc_set_timing()
312 control = FIELD_PREP(LEDC_T01_TIMING_CTRL_REG_T1H, timing->t1h_ns / cycle_ns) | in sun50i_a100_ledc_set_timing()
313 FIELD_PREP(LEDC_T01_TIMING_CTRL_REG_T1L, timing->t1l_ns / cycle_ns) | in sun50i_a100_ledc_set_timing()
314 FIELD_PREP(LEDC_T01_TIMING_CTRL_REG_T0H, timing->t0h_ns / cycle_ns) | in sun50i_a100_ledc_set_timing()
315 FIELD_PREP(LEDC_T01_TIMING_CTRL_REG_T0L, timing->t0l_ns / cycle_ns); in sun50i_a100_ledc_set_timing()
316 writel(control, priv->base + LEDC_T01_TIMING_CTRL_REG); in sun50i_a100_ledc_set_timing()
318 control = FIELD_PREP(LEDC_RESET_TIMING_CTRL_REG_TR, timing->treset_ns / cycle_ns) | in sun50i_a100_ledc_set_timing()
319 FIELD_PREP(LEDC_RESET_TIMING_CTRL_REG_LED_NUM, priv->max_addr); in sun50i_a100_ledc_set_timing()
320 writel(control, priv->base + LEDC_RESET_TIMING_CTRL_REG); in sun50i_a100_ledc_set_timing()
328 ret = reset_control_deassert(priv->reset); in sun50i_a100_ledc_resume()
332 ret = clk_prepare_enable(priv->bus_clk); in sun50i_a100_ledc_resume()
336 ret = clk_prepare_enable(priv->mod_clk); in sun50i_a100_ledc_resume()
344 priv->base + LEDC_INT_CTRL_REG); in sun50i_a100_ledc_resume()
349 clk_disable_unprepare(priv->bus_clk); in sun50i_a100_ledc_resume()
351 reset_control_assert(priv->reset); in sun50i_a100_ledc_resume()
365 spin_lock_irqsave(&priv->lock, flags); in sun50i_a100_ledc_suspend()
366 xfer_active = priv->xfer_active; in sun50i_a100_ledc_suspend()
367 spin_unlock_irqrestore(&priv->lock, flags); in sun50i_a100_ledc_suspend()
374 clk_disable_unprepare(priv->mod_clk); in sun50i_a100_ledc_suspend()
375 clk_disable_unprepare(priv->bus_clk); in sun50i_a100_ledc_suspend()
376 reset_control_assert(priv->reset); in sun50i_a100_ledc_suspend()
385 dma_release_channel(priv->dma_chan); in sun50i_a100_ledc_dma_cleanup()
393 struct device *dev = &pdev->dev; in sun50i_a100_ledc_probe()
409 return dev_err_probe(dev, -EINVAL, "'reg' must be between 0 and %d\n", in sun50i_a100_ledc_probe()
410 LEDC_MAX_LEDS - 1); in sun50i_a100_ledc_probe()
414 return dev_err_probe(dev, -EINVAL, "'color' must be LED_COLOR_ID_RGB\n"); in sun50i_a100_ledc_probe()
421 return -ENODEV; in sun50i_a100_ledc_probe()
425 return -ENOMEM; in sun50i_a100_ledc_probe()
427 priv->dev = dev; in sun50i_a100_ledc_probe()
428 priv->max_addr = max_addr; in sun50i_a100_ledc_probe()
429 priv->num_leds = num_leds; in sun50i_a100_ledc_probe()
430 spin_lock_init(&priv->lock); in sun50i_a100_ledc_probe()
441 priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); in sun50i_a100_ledc_probe()
442 if (IS_ERR(priv->base)) in sun50i_a100_ledc_probe()
443 return PTR_ERR(priv->base); in sun50i_a100_ledc_probe()
445 priv->bus_clk = devm_clk_get(dev, "bus"); in sun50i_a100_ledc_probe()
446 if (IS_ERR(priv->bus_clk)) in sun50i_a100_ledc_probe()
447 return PTR_ERR(priv->bus_clk); in sun50i_a100_ledc_probe()
449 priv->mod_clk = devm_clk_get(dev, "mod"); in sun50i_a100_ledc_probe()
450 if (IS_ERR(priv->mod_clk)) in sun50i_a100_ledc_probe()
451 return PTR_ERR(priv->mod_clk); in sun50i_a100_ledc_probe()
453 priv->reset = devm_reset_control_get_exclusive(dev, NULL); in sun50i_a100_ledc_probe()
454 if (IS_ERR(priv->reset)) in sun50i_a100_ledc_probe()
455 return PTR_ERR(priv->reset); in sun50i_a100_ledc_probe()
457 priv->dma_chan = dma_request_chan(dev, "tx"); in sun50i_a100_ledc_probe()
458 if (IS_ERR(priv->dma_chan)) { in sun50i_a100_ledc_probe()
459 if (PTR_ERR(priv->dma_chan) != -ENODEV) in sun50i_a100_ledc_probe()
460 return PTR_ERR(priv->dma_chan); in sun50i_a100_ledc_probe()
462 priv->dma_chan = NULL; in sun50i_a100_ledc_probe()
464 priv->buffer = devm_kzalloc(dev, LEDS_TO_BYTES(LEDC_MAX_LEDS), GFP_KERNEL); in sun50i_a100_ledc_probe()
465 if (!priv->buffer) in sun50i_a100_ledc_probe()
466 return -ENOMEM; in sun50i_a100_ledc_probe()
472 dma_cfg.dst_addr = mem->start + LEDC_DATA_REG; in sun50i_a100_ledc_probe()
476 ret = dmaengine_slave_config(priv->dma_chan, &dma_cfg); in sun50i_a100_ledc_probe()
480 priv->buffer = dmam_alloc_attrs(dmaengine_get_dma_device(priv->dma_chan), in sun50i_a100_ledc_probe()
481 LEDS_TO_BYTES(LEDC_MAX_LEDS), &priv->dma_handle, in sun50i_a100_ledc_probe()
483 if (!priv->buffer) in sun50i_a100_ledc_probe()
484 return -ENOMEM; in sun50i_a100_ledc_probe()
499 led = priv->leds; in sun50i_a100_ledc_probe()
504 fwnode_property_read_u32(child, "reg", &led->addr); in sun50i_a100_ledc_probe()
506 led->subled_info[0].color_index = LED_COLOR_ID_RED; in sun50i_a100_ledc_probe()
507 led->subled_info[0].channel = 0; in sun50i_a100_ledc_probe()
508 led->subled_info[1].color_index = LED_COLOR_ID_GREEN; in sun50i_a100_ledc_probe()
509 led->subled_info[1].channel = 1; in sun50i_a100_ledc_probe()
510 led->subled_info[2].color_index = LED_COLOR_ID_BLUE; in sun50i_a100_ledc_probe()
511 led->subled_info[2].channel = 2; in sun50i_a100_ledc_probe()
513 led->mc_cdev.num_colors = ARRAY_SIZE(led->subled_info); in sun50i_a100_ledc_probe()
514 led->mc_cdev.subled_info = led->subled_info; in sun50i_a100_ledc_probe()
516 cdev = &led->mc_cdev.led_cdev; in sun50i_a100_ledc_probe()
517 cdev->max_brightness = U8_MAX; in sun50i_a100_ledc_probe()
518 cdev->brightness_set = sun50i_a100_ledc_brightness_set; in sun50i_a100_ledc_probe()
522 ret = led_classdev_multicolor_register_ext(dev, &led->mc_cdev, &init_data); in sun50i_a100_ledc_probe()
524 dev_err_probe(dev, ret, "Failed to register multicolor LED %u", led->addr); in sun50i_a100_ledc_probe()
525 while (led-- > priv->leds) in sun50i_a100_ledc_probe()
526 led_classdev_multicolor_unregister(&led->mc_cdev); in sun50i_a100_ledc_probe()
527 sun50i_a100_ledc_suspend(&pdev->dev); in sun50i_a100_ledc_probe()
544 for (u32 i = 0; i < priv->num_leds; i++) in sun50i_a100_ledc_remove()
545 led_classdev_multicolor_unregister(&priv->leds[i].mc_cdev); in sun50i_a100_ledc_remove()
546 sun50i_a100_ledc_suspend(&pdev->dev); in sun50i_a100_ledc_remove()
550 { .compatible = "allwinner,sun50i-a100-ledc" },
564 .name = "sun50i-a100-ledc",