mirror of
https://github.com/LibreELEC/LibreELEC.tv.git
synced 2025-07-24 11:16:51 +00:00
RPi: remove slice linux patches
This commit is contained in:
parent
da8df4f85c
commit
987388d0c4
@ -1,12 +0,0 @@
|
||||
--- a/drivers/misc/Kconfig 2016-10-01 08:57:18.219952947 +0100
|
||||
+++ b/drivers/misc/Kconfig 2016-10-01 09:18:14.792293152 +0100
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
menu "Misc devices"
|
||||
|
||||
+config BCM2835_WS2812
|
||||
+ tristate "Support DMA user access to WS2812 LEDs"
|
||||
+
|
||||
config SENSORS_LIS3LV02D
|
||||
tristate
|
||||
depends on INPUT
|
@ -1,10 +0,0 @@
|
||||
--- a/drivers/misc/Makefile 2016-10-01 09:14:16.810942127 +0100
|
||||
+++ b/drivers/misc/Makefile 2016-10-01 09:14:05.458686888 +0100
|
||||
@@ -2,6 +2,7 @@
|
||||
# Makefile for misc devices that really don't fit anywhere else.
|
||||
#
|
||||
|
||||
+obj-$(CONFIG_BCM2835_WS2812) += ws2812.o
|
||||
obj-$(CONFIG_IBM_ASM) += ibmasm/
|
||||
obj-$(CONFIG_AD525X_DPOT) += ad525x_dpot.o
|
||||
obj-$(CONFIG_AD525X_DPOT_I2C) += ad525x_dpot-i2c.o
|
@ -1,539 +0,0 @@
|
||||
--- a/drivers/misc/ws2812.c 2016-08-17 21:00:22.264000134 +0100
|
||||
+++ b/drivers/misc/ws2812.c 2016-10-04 14:12:32.512699063 +0100
|
||||
@@ -0,0 +1,536 @@
|
||||
+/*
|
||||
+ * Raspberry Pi WS2812 PWM driver
|
||||
+ *
|
||||
+ * Written by: Gordon Hollingworth <gordon@fiveninjas.com>
|
||||
+ * Based on DMA PWM driver from Jonathan Bell <jonathan@raspberrypi.org>
|
||||
+ *
|
||||
+ * Copyright (C) 2014 Raspberry Pi Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 as
|
||||
+ * published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * To use this driver you need to make sure that the PWM clock is set to 2.4MHz
|
||||
+ * and the correct PWM0 output is connected. The best way to do this is to
|
||||
+ * create a dt-blob.bin on your RaspberryPi, start by downloading the default
|
||||
+ * dt-blob.dts from
|
||||
+ *
|
||||
+ * Note, this uses the same PWM hardware as the standard audio output on the Pi
|
||||
+ * so you cannot use both simultaneously.
|
||||
+ *
|
||||
+ * http://www.raspberrypi.org/documentation/configuration/pin-configuration.md
|
||||
+ *
|
||||
+ * (Copy the bit from /dts-v1/; through to the end... This will contain the pin
|
||||
+ * configuration for all the Raspberry Pi versions (since they are different.
|
||||
+ * You can get rid of the ones you don't care about. Next alter the PWM0 output
|
||||
+ * you want to use.
|
||||
+ *
|
||||
+ * http://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2835/BCM2835-ARM-Peripherals.pdf
|
||||
+ *
|
||||
+ * The link above will help understand what the GPIOs can do, check out page 102
|
||||
+ * You can use: GPIO12, GPIO18 or GPIO40, so for the Slice board we use GPIO40 so
|
||||
+ * we have the following in the dts file
|
||||
+ *
|
||||
+ * pin@p40 {
|
||||
+ * function = "pwm";
|
||||
+ * termination = "no_pulling";
|
||||
+ * };
|
||||
+ *
|
||||
+ * And at the bottom of the dts file, although still in the 'videocore' block we
|
||||
+ * have:
|
||||
+ *
|
||||
+ * clock_setup {
|
||||
+ * clock@PWM { freq = <2400000>; };
|
||||
+ * };
|
||||
+ *
|
||||
+ * To check whether the changes are correct you can use 'vcgencmd measure_clock 25'
|
||||
+ * This should return the value 2400000
|
||||
+ *
|
||||
+ * Also if you use wiringPi then you can do 'gpio readall' to check that the pin
|
||||
+ * alternate setting is set correctly.
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/fcntl.h>
|
||||
+#include <linux/file.h>
|
||||
+#include <linux/fs.h>
|
||||
+#include <linux/wait.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/ioctl.h>
|
||||
+#include <linux/cdev.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/dma-mapping.h>
|
||||
+#include <linux/dmaengine.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/uaccess.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_address.h>
|
||||
+#include <linux/gpio/consumer.h>
|
||||
+#include <asm-generic/ioctl.h>
|
||||
+
|
||||
+#define DRIVER_NAME "ws2812"
|
||||
+
|
||||
+struct ws2812_state {
|
||||
+ struct device * dev;
|
||||
+ struct cdev cdev;
|
||||
+ struct class * cl;
|
||||
+ struct dma_chan * dma_chan;
|
||||
+ dma_addr_t dma_addr;
|
||||
+
|
||||
+ void __iomem * ioaddr;
|
||||
+ phys_addr_t phys_addr;
|
||||
+
|
||||
+ uint8_t * buffer;
|
||||
+ uint32_t * pixbuf;
|
||||
+
|
||||
+ struct gpio_desc * led_en;
|
||||
+
|
||||
+ unsigned char brightness;
|
||||
+ u32 invert;
|
||||
+ u32 num_leds;
|
||||
+};
|
||||
+
|
||||
+#ifndef BCM2708_PERI_BASE
|
||||
+ #define BCM2708_PERI_BASE 0x20000000
|
||||
+#endif
|
||||
+
|
||||
+#define BCM2835_VCMMU_SHIFT (0x7E000000 - BCM2708_PERI_BASE)
|
||||
+
|
||||
+/* Each LED is controlled with a 24 bit RGB value
|
||||
+ * each bit is created from a nibble of data either
|
||||
+ * 1000 or 1110 so to create 24 bits you need 12 bytes
|
||||
+ * of PWM output
|
||||
+ */
|
||||
+#define BYTES_PER_LED 12
|
||||
+
|
||||
+// Number of 2.4MHz bits in 50us to create a reset condition
|
||||
+#define RESET_BYTES ((50 * 24) / 80)
|
||||
+
|
||||
+#define PWM_CTL 0x0
|
||||
+#define PWM_STA 0x4
|
||||
+#define PWM_DMAC 0x8
|
||||
+#define PWM_RNG1 0x10
|
||||
+#define PWM_DAT1 0x14
|
||||
+#define PWM_FIFO1 0x18
|
||||
+#define PWM_ID 0x50
|
||||
+
|
||||
+#define PWM_DMA_DREQ 5
|
||||
+
|
||||
+static dev_t devid = MKDEV(1337, 0);
|
||||
+
|
||||
+/*
|
||||
+** Functions to access the pwm peripheral
|
||||
+*/
|
||||
+static void pwm_writel(struct ws2812_state * state, uint32_t val, uint32_t reg)
|
||||
+{
|
||||
+ writel(val, state->ioaddr + reg);
|
||||
+}
|
||||
+
|
||||
+#if 0
|
||||
+static uint32_t pwm_readl(struct ws2812_state * state, uint32_t reg)
|
||||
+{
|
||||
+ return readl(state->ioaddr + reg);
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+/* Initialise the PWM module to use serial output
|
||||
+ * mode
|
||||
+ */
|
||||
+static int pwm_init(struct ws2812_state * state)
|
||||
+{
|
||||
+ uint32_t reg;
|
||||
+
|
||||
+ // serial 32 bits per word
|
||||
+ pwm_writel(state, 32, PWM_RNG1);
|
||||
+ // Clear
|
||||
+ pwm_writel(state, 0, PWM_DAT1);
|
||||
+
|
||||
+ reg = (1 << 0) | /* CH1EN */
|
||||
+ (1 << 1) | /* serialiser */
|
||||
+ (0 << 2) | /* don't repeat last word */
|
||||
+ (0 << 3) | /* silence is zero */
|
||||
+ ((state->invert ? 1 : 0) << 4) | /* polarity */
|
||||
+ (1 << 5) | /* use fifo */
|
||||
+ (1 << 6) | /* Clear fifo */
|
||||
+ (1 << 7) | /* MSEN - Mask space enable */
|
||||
+ ((state->invert ? 1 : 0) << 11); /* Silence bit = 1 */
|
||||
+ pwm_writel(state, reg, PWM_CTL);
|
||||
+ reg = (1 << 31) | /* DMA enabled */
|
||||
+ (4 << 8) | /* Threshold for panic */
|
||||
+ (8 << 0); /* Threshold for dreq */
|
||||
+ pwm_writel(state, reg, PWM_DMAC);
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * DMA callback function, release the mapping and the calling function
|
||||
+ */
|
||||
+void ws2812_callback(void * param)
|
||||
+{
|
||||
+ struct ws2812_state * state = (struct ws2812_state *) param;
|
||||
+
|
||||
+ dma_unmap_single(state->dev, state->dma_addr, state->num_leds * BYTES_PER_LED,
|
||||
+ DMA_TO_DEVICE);
|
||||
+
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Issue a DMA to the PWM peripheral from the assigned buffer
|
||||
+ * buffer must be unmapped again before being used
|
||||
+ */
|
||||
+int issue_dma(struct ws2812_state * state, uint8_t *buffer, int length)
|
||||
+{
|
||||
+ struct dma_async_tx_descriptor *desc;
|
||||
+
|
||||
+ state->dma_addr = dma_map_single(state->dev,
|
||||
+ buffer, length,
|
||||
+ DMA_TO_DEVICE);
|
||||
+
|
||||
+ if(state->dma_addr == 0)
|
||||
+ {
|
||||
+ pr_err("Failed to map buffer for DMA\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ desc = dmaengine_prep_slave_single(state->dma_chan, state->dma_addr,
|
||||
+ length, DMA_TO_DEVICE, DMA_PREP_INTERRUPT);
|
||||
+ if(desc == NULL)
|
||||
+ {
|
||||
+ pr_err("Failed to prep the DMA transfer\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ desc->callback = ws2812_callback;
|
||||
+ desc->callback_param = state;
|
||||
+ dmaengine_submit(desc);
|
||||
+ dma_async_issue_pending(state->dma_chan);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+int clear_leds(struct ws2812_state * state)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for(i = 0; i < state->num_leds * BYTES_PER_LED; i++)
|
||||
+ state->buffer[i] = 0x88;
|
||||
+ for(i = 0; i < RESET_BYTES; i++)
|
||||
+ state->buffer[state->num_leds * BYTES_PER_LED + i] = 0;
|
||||
+
|
||||
+ issue_dma(state, state->buffer, state->num_leds * BYTES_PER_LED + RESET_BYTES);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int ws2812_open(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ struct ws2812_state * state;
|
||||
+ state = container_of(inode->i_cdev, struct ws2812_state, cdev);
|
||||
+
|
||||
+ file->private_data = state;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* WS2812B gamma correction
|
||||
+GammaE=255*(res/255).^(1/.45)
|
||||
+From: http://rgb-123.com/ws2812-color-output/
|
||||
+*/
|
||||
+unsigned char gamma_(unsigned char brightness, unsigned char val)
|
||||
+{
|
||||
+ int bright = val;
|
||||
+ unsigned char GammaE[] = {
|
||||
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
+ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
|
||||
+ 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5,
|
||||
+ 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11,
|
||||
+ 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18,
|
||||
+ 19, 19, 20, 21, 21, 22, 22, 23, 23, 24, 25, 25, 26, 27, 27, 28,
|
||||
+ 29, 29, 30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 37, 38, 39, 40,
|
||||
+ 40, 41, 42, 43, 44, 45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 54,
|
||||
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
|
||||
+ 71, 72, 73, 74, 76, 77, 78, 79, 80, 81, 83, 84, 85, 86, 88, 89,
|
||||
+ 90, 91, 93, 94, 95, 96, 98, 99,100,102,103,104,106,107,109,110,
|
||||
+ 111,113,114,116,117,119,120,121,123,124,126,128,129,131,132,134,
|
||||
+ 135,137,138,140,142,143,145,146,148,150,151,153,155,157,158,160,
|
||||
+ 162,163,165,167,169,170,172,174,176,178,179,181,183,185,187,189,
|
||||
+ 191,193,194,196,198,200,202,204,206,208,210,212,214,216,218,220,
|
||||
+ 222,224,227,229,231,233,235,237,239,241,244,246,248,250,252,255};
|
||||
+ bright = (bright * brightness) / 255;
|
||||
+ return GammaE[bright];
|
||||
+}
|
||||
+
|
||||
+// LED serial output
|
||||
+// 4 bits make up a single bit of the output
|
||||
+// 1 1 1 0 -- 1
|
||||
+// 1 0 0 0 -- 0
|
||||
+//
|
||||
+// Plus require a space of 50 microseconds for reset
|
||||
+// 24 bits per led
|
||||
+//
|
||||
+// (24 * 4) / 8 = 12 bytes per led
|
||||
+//
|
||||
+// red = 0xff0000 == 0xeeeeeeee 0x88888888 0x88888888
|
||||
+unsigned char * led_encode(struct ws2812_state * state, int rgb, unsigned char *buf)
|
||||
+{
|
||||
+ int i;
|
||||
+ unsigned char red = gamma_(state->brightness, rgb >> 8);
|
||||
+ unsigned char blu = gamma_(state->brightness, rgb);
|
||||
+ unsigned char grn = gamma_(state->brightness, rgb >> 16);
|
||||
+ int rearrange = red +
|
||||
+ (blu << 8) +
|
||||
+ (grn << 16);
|
||||
+ for(i = 11; i >= 0; i--)
|
||||
+ {
|
||||
+ switch(rearrange & 3)
|
||||
+ {
|
||||
+ case 0: *buf++ = 0x88; break;
|
||||
+ case 1: *buf++ = 0x8e; break;
|
||||
+ case 2: *buf++ = 0xe8; break;
|
||||
+ case 3: *buf++ = 0xee; break;
|
||||
+ }
|
||||
+ rearrange >>= 2;
|
||||
+ }
|
||||
+
|
||||
+ return buf;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+/* Write to the PWM through DMA
|
||||
+ * Function to write the RGB buffer to the WS2812 leds, the input buffer
|
||||
+ * contains a sequence of up to num_leds RGB32 integers, these are then
|
||||
+ * converted into the nibble per bit sequence required to drive the PWM
|
||||
+ */
|
||||
+ssize_t ws2812_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
|
||||
+{
|
||||
+ int32_t *p_rgb;
|
||||
+ int8_t * p_buffer;
|
||||
+ int i, length, num_leds;
|
||||
+ struct ws2812_state * state = (struct ws2812_state *) filp->private_data;
|
||||
+
|
||||
+ num_leds = min(count/4, state->num_leds);
|
||||
+
|
||||
+ if(copy_from_user(state->pixbuf, buf, num_leds * 4))
|
||||
+ return -EFAULT;
|
||||
+
|
||||
+ p_rgb = state->pixbuf;
|
||||
+ p_buffer = state->buffer;
|
||||
+ for(i = 0; i < num_leds; i++)
|
||||
+ p_buffer = led_encode(state, *p_rgb++, p_buffer);
|
||||
+
|
||||
+ /* Fill rest with '0' */
|
||||
+ memset(p_buffer, 0x00, RESET_BYTES);
|
||||
+
|
||||
+ length = (int) p_buffer - (int) state->buffer + RESET_BYTES;
|
||||
+
|
||||
+ /* Setup DMA engine */
|
||||
+ issue_dma(state, state->buffer, length);
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+struct file_operations ws2812_fops = {
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .llseek = NULL,
|
||||
+ .read = NULL,
|
||||
+ .write = ws2812_write,
|
||||
+ .open = ws2812_open,
|
||||
+ .release = NULL,
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * Probe function
|
||||
+ */
|
||||
+static int ws2812_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ int ret;
|
||||
+ struct device *dev = &pdev->dev;
|
||||
+ struct device_node *node = dev->of_node;
|
||||
+ struct ws2812_state * state;
|
||||
+ const __be32 *addr;
|
||||
+ struct resource *res;
|
||||
+ struct dma_slave_config cfg =
|
||||
+ {
|
||||
+ .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
|
||||
+ .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
|
||||
+ .slave_id = PWM_DMA_DREQ,
|
||||
+ .direction = DMA_MEM_TO_DEV,
|
||||
+ .src_addr = 0,
|
||||
+ };
|
||||
+
|
||||
+ if(node == NULL)
|
||||
+ {
|
||||
+ pr_err("Require device tree entry\n");
|
||||
+ goto fail;
|
||||
+ }
|
||||
+
|
||||
+ state = kmalloc(sizeof(struct ws2812_state), GFP_KERNEL);
|
||||
+ if (!state) {
|
||||
+ pr_err("Can't allocate state\n");
|
||||
+ goto fail;
|
||||
+ }
|
||||
+
|
||||
+ state->dev = dev;
|
||||
+ state->brightness = 255;
|
||||
+
|
||||
+ // Create character device interface /dev/ws2812
|
||||
+ if(alloc_chrdev_region(&devid, 0, 1, "ws2812") < 0)
|
||||
+ {
|
||||
+ pr_err("Unable to create chrdev region");
|
||||
+ goto fail_malloc;
|
||||
+ }
|
||||
+ if((state->cl = class_create(THIS_MODULE, "ws2812")) == NULL)
|
||||
+ {
|
||||
+ unregister_chrdev_region(devid, 1);
|
||||
+ pr_err("Unable to create class ws2812");
|
||||
+ goto fail_chrdev;
|
||||
+ }
|
||||
+ if(device_create(state->cl, NULL, devid, NULL, "ws2812") == NULL)
|
||||
+ {
|
||||
+ class_destroy(state->cl);
|
||||
+ unregister_chrdev_region(devid, 1);
|
||||
+ pr_err("Unable to create device ws2812");
|
||||
+ goto fail_class;
|
||||
+ }
|
||||
+
|
||||
+ state->cdev.owner = THIS_MODULE;
|
||||
+ cdev_init(&state->cdev, &ws2812_fops);
|
||||
+
|
||||
+ if(cdev_add(&state->cdev, devid, 1)) {
|
||||
+ pr_err("CDEV failed\n");
|
||||
+ goto fail_device;
|
||||
+ }
|
||||
+
|
||||
+ platform_set_drvdata(pdev, state);
|
||||
+
|
||||
+ /* get parameters from device tree */
|
||||
+ of_property_read_u32(node,
|
||||
+ "rpi,invert",
|
||||
+ &state->invert);
|
||||
+ of_property_read_u32(node,
|
||||
+ "rpi,num_leds",
|
||||
+ &state->num_leds);
|
||||
+
|
||||
+ state->pixbuf = kmalloc(state->num_leds * sizeof(int), GFP_KERNEL);
|
||||
+ if(state->pixbuf == NULL)
|
||||
+ {
|
||||
+ pr_err("Failed to allocate internal buffer\n");
|
||||
+ goto fail_cdev;
|
||||
+ }
|
||||
+
|
||||
+ /* base address in dma-space */
|
||||
+ addr = of_get_address(node, 0, NULL, NULL);
|
||||
+ if (!addr) {
|
||||
+ dev_err(dev, "could not get DMA-register address - not using dma mode\n");
|
||||
+ goto fail_pixbuf;
|
||||
+ }
|
||||
+ state->phys_addr = be32_to_cpup(addr);
|
||||
+ pr_err("bus_addr = 0x%x\n", state->phys_addr);
|
||||
+
|
||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ state->ioaddr = devm_ioremap_resource(&pdev->dev, res);
|
||||
+ if (IS_ERR(state->ioaddr)) {
|
||||
+ pr_err("Failed to get register resource\n");
|
||||
+ goto fail_pixbuf;
|
||||
+ }
|
||||
+
|
||||
+ pr_err("ioaddr = 0x%x\n", (int) state->ioaddr);
|
||||
+
|
||||
+ state->buffer = kmalloc(state->num_leds * BYTES_PER_LED + RESET_BYTES, GFP_KERNEL);
|
||||
+ if(state->buffer == NULL)
|
||||
+ {
|
||||
+ pr_err("Failed to allocate internal buffer\n");
|
||||
+ goto fail_pixbuf;
|
||||
+ }
|
||||
+
|
||||
+ state->dma_chan = dma_request_slave_channel(dev, "pwm_dma");
|
||||
+ if(state->dma_chan == NULL)
|
||||
+ {
|
||||
+ pr_err("Failed to request DMA channel");
|
||||
+ goto fail_buffer;
|
||||
+ }
|
||||
+
|
||||
+ /* request a DMA channel */
|
||||
+ cfg.dst_addr = state->phys_addr + PWM_FIFO1;
|
||||
+ ret = dmaengine_slave_config(state->dma_chan, &cfg);
|
||||
+ if (state->dma_chan < 0) {
|
||||
+ pr_err("Can't allocate DMA channel\n");
|
||||
+ goto fail_dma_init;
|
||||
+ }
|
||||
+ pwm_init(state);
|
||||
+
|
||||
+ // Enable the LED power
|
||||
+ state->led_en = devm_gpiod_get(dev, "led-en", GPIOD_OUT_HIGH);
|
||||
+
|
||||
+ clear_leds(state);
|
||||
+
|
||||
+ return 0;
|
||||
+fail_dma_init:
|
||||
+ dma_release_channel(state->dma_chan);
|
||||
+fail_buffer:
|
||||
+ kfree(state->buffer);
|
||||
+fail_pixbuf:
|
||||
+ kfree(state->pixbuf);
|
||||
+fail_cdev:
|
||||
+ cdev_del(&state->cdev);
|
||||
+fail_device:
|
||||
+ device_destroy(state->cl, devid);
|
||||
+fail_class:
|
||||
+ class_destroy(state->cl);
|
||||
+fail_chrdev:
|
||||
+ unregister_chrdev_region(devid, 1);
|
||||
+fail_malloc:
|
||||
+ kfree(state);
|
||||
+fail:
|
||||
+
|
||||
+ return -1;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static int ws2812_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct ws2812_state *state = platform_get_drvdata(pdev);
|
||||
+
|
||||
+ platform_set_drvdata(pdev, NULL);
|
||||
+
|
||||
+ dma_release_channel(state->dma_chan);
|
||||
+ kfree(state->buffer);
|
||||
+ kfree(state->pixbuf);
|
||||
+ cdev_del(&state->cdev);
|
||||
+ device_destroy(state->cl, devid);
|
||||
+ class_destroy(state->cl);
|
||||
+ unregister_chrdev_region(devid, 1);
|
||||
+ kfree(state);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id ws2812_match[] = {
|
||||
+ { .compatible = "rpi,ws2812" },
|
||||
+ { }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, ws2812_match);
|
||||
+
|
||||
+static struct platform_driver ws2812_driver = {
|
||||
+ .probe = ws2812_probe,
|
||||
+ .remove = ws2812_remove,
|
||||
+ .driver = {
|
||||
+ .name = DRIVER_NAME,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = ws2812_match,
|
||||
+ },
|
||||
+};
|
||||
+module_platform_driver(ws2812_driver);
|
||||
+
|
||||
+MODULE_ALIAS("platform:ws2812");
|
||||
+MODULE_DESCRIPTION("WS2812 PWM driver");
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_AUTHOR("Gordon Hollingworth");
|
@ -1,279 +0,0 @@
|
||||
--- a/sound/soc/bcm/slice.c 1969-12-31 16:00:00.000000000 -0800
|
||||
+++ b/sound/soc/bcm/slice.c 2017-02-15 14:39:20.402591342 -0800
|
||||
@@ -0,0 +1,276 @@
|
||||
+ /*
|
||||
+ * ASoC Driver for Slice on-board sound
|
||||
+ *
|
||||
+ * Author: James Adams <james@fiveninjas.com>
|
||||
+ * Based on the HifiBerry DAC driver by Florian Meier <florian.meier@koalo.de>
|
||||
+ * Copyright 2014
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License
|
||||
+ * version 2 as published by the Free Software Foundation.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful, but
|
||||
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ * General Public License for more details.
|
||||
+ */
|
||||
+
|
||||
+ #include <linux/module.h>
|
||||
+ #include <linux/platform_device.h>
|
||||
+
|
||||
+ #include <linux/io.h>
|
||||
+ #include <linux/clk.h>
|
||||
+
|
||||
+ #include <sound/core.h>
|
||||
+ #include <sound/pcm.h>
|
||||
+ #include <sound/pcm_params.h>
|
||||
+ #include <sound/soc.h>
|
||||
+ #include <sound/jack.h>
|
||||
+
|
||||
+ #include "../codecs/cs4265.h"
|
||||
+
|
||||
+struct clk * gp0_clock;
|
||||
+
|
||||
+ static int snd_slice_init(struct snd_soc_pcm_runtime *rtd)
|
||||
+ {
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ static int snd_slice_hw_params(struct snd_pcm_substream *substream,
|
||||
+ struct snd_pcm_hw_params *params)
|
||||
+ {
|
||||
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
||||
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
+ struct snd_soc_codec *codec = rtd->codec;
|
||||
+ int err;
|
||||
+ int ret;
|
||||
+ unsigned int rate = params_rate(params);
|
||||
+ unsigned int sysclk = 12288000;
|
||||
+
|
||||
+ switch (rate) {
|
||||
+ case 32000:
|
||||
+ sysclk = 12288000;
|
||||
+ break;
|
||||
+ case 44100:
|
||||
+ sysclk = 11289600;
|
||||
+ break;
|
||||
+ case 48000:
|
||||
+ sysclk = 12288000;
|
||||
+ break;
|
||||
+ case 64000:
|
||||
+ sysclk = 12288000;
|
||||
+ break;
|
||||
+ case 88200:
|
||||
+ sysclk = 11289600;
|
||||
+ break;
|
||||
+ case 96000:
|
||||
+ sysclk = 12288000;
|
||||
+ break;
|
||||
+ case 128000:
|
||||
+ dev_err(codec->dev,
|
||||
+ "Failed to set CS4265 SYSCLK, sample rate not supported in ALSA: 128000\n");
|
||||
+ break;
|
||||
+ case 176400:
|
||||
+ sysclk = 11289600;
|
||||
+ break;
|
||||
+ case 192000:
|
||||
+ sysclk = 12288000;
|
||||
+ break;
|
||||
+ default:
|
||||
+ dev_err(codec->dev,
|
||||
+ "Failed to set CS4265 SYSCLK, sample rate not supported\n");
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ // Need two frequencies: 12.288 or 11.2896MHz
|
||||
+ // Source is 1,806,336,000
|
||||
+ // /4 /40 - 1128960
|
||||
+ // /7 /21 - 1228800
|
||||
+ clk_disable_unprepare(gp0_clock);
|
||||
+
|
||||
+ err = clk_set_rate(gp0_clock, sysclk);
|
||||
+ if(err < 0)
|
||||
+ pr_err("Failed to set clock rate for gp0 clock\n");
|
||||
+
|
||||
+ if((ret = clk_prepare_enable(gp0_clock)) < 0)
|
||||
+ pr_err("Failed to enable clock\n");
|
||||
+
|
||||
+ dev_err(codec->dev, "Set sampling frequency %d, using sysclk %d\n", rate, sysclk);
|
||||
+
|
||||
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk,
|
||||
+ SND_SOC_CLOCK_OUT);
|
||||
+
|
||||
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
|
||||
+ SND_SOC_DAIFMT_NB_NF |
|
||||
+ SND_SOC_DAIFMT_CBM_CFM);
|
||||
+
|
||||
+ if (ret) {
|
||||
+ dev_err(cpu_dai->dev,
|
||||
+ "Failed to set the cpu dai format.\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
|
||||
+ SND_SOC_DAIFMT_NB_NF |
|
||||
+ SND_SOC_DAIFMT_CBM_CFM);
|
||||
+ if (ret) {
|
||||
+ dev_err(cpu_dai->dev,
|
||||
+ "Failed to set the codec format.\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ snd_soc_dai_set_bclk_ratio(cpu_dai, 64);
|
||||
+
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ static int snd_slice_params_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
+ struct snd_pcm_hw_params *params)
|
||||
+ {
|
||||
+ printk(KERN_ERR "snd_slice_params_fixup called\n");
|
||||
+ /* force 32 bit */
|
||||
+ params_set_format(params, SNDRV_PCM_FORMAT_S32_LE);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ /* machine stream operations */
|
||||
+ static struct snd_soc_ops snd_slice_ops = {
|
||||
+ .hw_params = snd_slice_hw_params,
|
||||
+ };
|
||||
+
|
||||
+ /* Widgets */
|
||||
+ static const struct snd_soc_dapm_widget snd_slice_dapm_widgets[] = {
|
||||
+ SND_SOC_DAPM_SPK("Speaker 1", NULL),
|
||||
+ SND_SOC_DAPM_SPK("Speaker 2", NULL),
|
||||
+ SND_SOC_DAPM_MIC("Mic 1", NULL),
|
||||
+ SND_SOC_DAPM_MIC("Mic 2", NULL),
|
||||
+ SND_SOC_DAPM_MIC("LineIn 1", NULL),
|
||||
+ SND_SOC_DAPM_MIC("LineIn 2", NULL),
|
||||
+ SND_SOC_DAPM_SPK("Spdif", NULL),
|
||||
+ };
|
||||
+
|
||||
+ /* Audio Map */
|
||||
+ static const struct snd_soc_dapm_route snd_slice_audio_map[] = {
|
||||
+ {"Speaker 1", NULL, "LINEOUTL"},
|
||||
+ {"Speaker 2", NULL, "LINEOUTR"},
|
||||
+ {"MICL", NULL, "Mic 1"},
|
||||
+ {"MICR", NULL, "Mic 2"},
|
||||
+ {"LINEINL", NULL, "LineIn 1"},
|
||||
+ {"LINEINR", NULL, "LineIn 2"},
|
||||
+ {"Spdif", NULL, "SPDIF"},
|
||||
+ };
|
||||
+
|
||||
+ static const struct snd_soc_pcm_stream snd_slice_params = {
|
||||
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
|
||||
+ };
|
||||
+
|
||||
+ static struct snd_soc_dai_link snd_slice_dai[] = {
|
||||
+ {
|
||||
+ .name = "Slice",
|
||||
+ .stream_name = "Slice HiFi",
|
||||
+ .cpu_dai_name = "bcm2708-i2s.0",
|
||||
+ .codec_dai_name = "cs4265-dai1",
|
||||
+ .platform_name = "bcm2708-i2s.0",
|
||||
+ .codec_name = "cs4265.1-004e",
|
||||
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
+ SND_SOC_DAIFMT_CBM_CFM,
|
||||
+ .ops = &snd_slice_ops,
|
||||
+ .init = snd_slice_init,
|
||||
+ .be_hw_params_fixup = snd_slice_params_fixup,
|
||||
+ },
|
||||
+ };
|
||||
+
|
||||
+ /* audio machine driver */
|
||||
+ static struct snd_soc_card snd_slice = {
|
||||
+ .name = "snd_slice",
|
||||
+ .dai_link = snd_slice_dai,
|
||||
+ .num_links = ARRAY_SIZE(snd_slice_dai),
|
||||
+ .fully_routed = 1,
|
||||
+ .dapm_widgets = snd_slice_dapm_widgets,
|
||||
+ .num_dapm_widgets = ARRAY_SIZE(snd_slice_dapm_widgets),
|
||||
+ .dapm_routes = snd_slice_audio_map,
|
||||
+ .num_dapm_routes = ARRAY_SIZE(snd_slice_audio_map),
|
||||
+ };
|
||||
+
|
||||
+ static int snd_slice_probe(struct platform_device *pdev)
|
||||
+ {
|
||||
+ int ret = 0;
|
||||
+ snd_slice.dev = &pdev->dev;
|
||||
+
|
||||
+ if (pdev->dev.of_node) {
|
||||
+ struct device_node *i2s_node;
|
||||
+ struct snd_soc_dai_link *dai = &snd_slice_dai[0];
|
||||
+ i2s_node = of_parse_phandle(pdev->dev.of_node,
|
||||
+ "i2s-controller", 0);
|
||||
+
|
||||
+ if (i2s_node) {
|
||||
+ dai->cpu_dai_name = NULL;
|
||||
+ dai->cpu_of_node = i2s_node;
|
||||
+ dai->platform_name = NULL;
|
||||
+ dai->platform_of_node = i2s_node;
|
||||
+ }
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ printk(KERN_ERR "SLICEAUDIO - ERROR no Device Tree!\n");
|
||||
+ }
|
||||
+
|
||||
+ ret = snd_soc_register_card(&snd_slice);
|
||||
+ if (ret) {
|
||||
+ dev_err(&pdev->dev,
|
||||
+ "snd_soc_register_card() failed: %d\n", ret);
|
||||
+ goto snd_soc_register_card_failed;
|
||||
+ }
|
||||
+
|
||||
+ gp0_clock = devm_clk_get(&pdev->dev, "gp0");
|
||||
+ if (IS_ERR(gp0_clock)) {
|
||||
+ pr_err("Failed to get gp0 clock\n");
|
||||
+ return PTR_ERR(gp0_clock);
|
||||
+ }
|
||||
+
|
||||
+ ret = clk_set_rate(gp0_clock, 12288000);
|
||||
+ if (ret) {
|
||||
+ pr_err("Failed to set the GP0 clock rate\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ret = clk_prepare_enable(gp0_clock);
|
||||
+ if (ret) {
|
||||
+ pr_err("Failed to turn on gp0 clock: %d\n", ret);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+ snd_soc_register_card_failed:
|
||||
+
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ static int snd_slice_remove(struct platform_device *pdev)
|
||||
+ {
|
||||
+ return snd_soc_unregister_card(&snd_slice);
|
||||
+ }
|
||||
+
|
||||
+ static const struct of_device_id slice_of_match[] = {
|
||||
+ { .compatible = "fiveninjas,slice", },
|
||||
+ {},
|
||||
+ };
|
||||
+ MODULE_DEVICE_TABLE(of, slice_of_match);
|
||||
+
|
||||
+ static struct platform_driver snd_slice_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "snd-slice",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = slice_of_match,
|
||||
+ },
|
||||
+ .probe = snd_slice_probe,
|
||||
+ .remove = snd_slice_remove,
|
||||
+ };
|
||||
+
|
||||
+ module_platform_driver(snd_slice_driver);
|
||||
+
|
||||
+ MODULE_AUTHOR("James Adams <james@fiveninjas.com>");
|
||||
+ MODULE_DESCRIPTION("ASoC Driver for Slice on-board audio");
|
||||
+ MODULE_LICENSE("GPL v2");
|
@ -1,17 +0,0 @@
|
||||
--- a/sound/soc/bcm/Kconfig 2016-10-24 08:46:19.034757161 +0100
|
||||
+++ b/sound/soc/bcm/Kconfig 2016-10-24 08:52:36.735342726 +0100
|
||||
@@ -131,6 +131,13 @@ config SND_BCM2708_SOC_ALLO_PIANO_DAC
|
||||
help
|
||||
Say Y or M if you want to add support for Allo Piano DAC.
|
||||
|
||||
+config SND_BCM2708_SOC_SLICE
|
||||
+ tristate "Support for Slice on-board sound"
|
||||
+ depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
|
||||
+ select SND_SOC_CS4265
|
||||
+ help
|
||||
+ Say Y or M if you want to add support for Slice on-board sound.
|
||||
+
|
||||
config SND_PISOUND
|
||||
tristate "Support for Blokas Labs pisound"
|
||||
depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
|
||||
|
@ -1,15 +0,0 @@
|
||||
--- a/sound/soc/bcm/Makefile 2016-10-24 08:57:16.805712924 +0100
|
||||
+++ b/sound/soc/bcm/Makefile 2016-10-24 08:59:51.541235893 +0100
|
||||
@@ -26,6 +26,7 @@ snd-soc-digidac1-soundcard-objs := digid
|
||||
snd-soc-allo-piano-dac-objs := allo-piano-dac.o
|
||||
snd-soc-pisound-objs := pisound.o
|
||||
snd-soc-fe-pi-audio-objs := fe-pi-audio.o
|
||||
+snd-soc-slice-objs := slice.o
|
||||
|
||||
obj-$(CONFIG_SND_BCM2708_SOC_ADAU1977_ADC) += snd-soc-adau1977-adc.o
|
||||
obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o
|
||||
@@ -44,3 +45,4 @@ obj-$(CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO_V2) +=
|
||||
obj-$(CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC) += snd-soc-allo-piano-dac.o
|
||||
obj-$(CONFIG_SND_PISOUND) += snd-soc-pisound.o
|
||||
obj-$(CONFIG_SND_BCM2708_SOC_FE_PI_AUDIO) += snd-soc-fe-pi-audio.o
|
||||
+obj-$(CONFIG_SND_BCM2708_SOC_SLICE) += snd-soc-slice.o
|
Loading…
x
Reference in New Issue
Block a user