diff --git a/projects/RPi/patches/linux/linux-01-misc-Kconfig.patch b/projects/RPi/patches/linux/linux-01-misc-Kconfig.patch new file mode 100644 index 0000000000..e30677018d --- /dev/null +++ b/projects/RPi/patches/linux/linux-01-misc-Kconfig.patch @@ -0,0 +1,12 @@ +--- 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 diff --git a/projects/RPi/patches/linux/linux-02-misc-Makefile.patch b/projects/RPi/patches/linux/linux-02-misc-Makefile.patch new file mode 100644 index 0000000000..f4ad6286bd --- /dev/null +++ b/projects/RPi/patches/linux/linux-02-misc-Makefile.patch @@ -0,0 +1,10 @@ +--- 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 diff --git a/projects/RPi/patches/linux/linux-03-ws2807-c.patch b/projects/RPi/patches/linux/linux-03-ws2807-c.patch new file mode 100644 index 0000000000..24eedd81ae --- /dev/null +++ b/projects/RPi/patches/linux/linux-03-ws2807-c.patch @@ -0,0 +1,539 @@ +--- 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 ++ * Based on DMA PWM driver from Jonathan Bell ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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"); diff --git a/projects/RPi/patches/linux/linux-04-rtc-pcf8523-c.patch b/projects/RPi/patches/linux/linux-04-rtc-pcf8523-c.patch new file mode 100644 index 0000000000..da7593ed7e --- /dev/null +++ b/projects/RPi/patches/linux/linux-04-rtc-pcf8523-c.patch @@ -0,0 +1,17 @@ +--- a/drivers/rtc/rtc-pcf8523.c 2016-10-01 10:16:30.259771931 +0100 ++++ b/drivers/rtc/rtc-pcf8523.c 2016-10-01 10:21:29.762638800 +0100 +@@ -291,7 +291,13 @@ static int pcf8523_probe(struct i2c_clie + if (!pcf) + return -ENOMEM; + +- err = pcf8523_select_capacitance(client, true); ++ if (of_property_read_bool(client->dev.of_node, "nxp,xtalcap-7pf")) { ++ printk(KERN_ERR "PCF8523 - set 7pF crystal load"); ++ err = pcf8523_select_capacitance(client, false); ++ } else { ++ printk(KERN_ERR "PCF8523 - set 12pF crystal load"); ++ err = pcf8523_select_capacitance(client, true); ++ } + if (err < 0) + return err; + diff --git a/projects/RPi/patches/linux/linux-05-cs4265-c.patch b/projects/RPi/patches/linux/linux-05-cs4265-c.patch new file mode 100644 index 0000000000..51939528a9 --- /dev/null +++ b/projects/RPi/patches/linux/linux-05-cs4265-c.patch @@ -0,0 +1,20 @@ +--- linux-4.4-rc7-old/sound/soc/codecs/cs4265.c 2016-01-13 20:56:05.637652775 +0000 ++++ linux-4.4-rc7/sound/soc/codecs/cs4265.c 2016-01-17 11:21:16.977652775 +0000 +@@ -157,7 +157,7 @@ + SOC_SINGLE("Validity Bit Control Switch", CS4265_SPDIF_CTL2, + 3, 1, 0), + SOC_ENUM("SPDIF Mono/Stereo", spdif_mono_stereo_enum), +- SOC_SINGLE("MMTLR Data Switch", 0, ++ SOC_SINGLE("MMTLR Data Switch", CS4265_SPDIF_CTL2, + 1, 1, 0), + SOC_ENUM("Mono Channel Select", spdif_mono_select_enum), + SND_SOC_BYTES("C Data Buffer", CS4265_C_DATA_BUFF, 24), +@@ -199,8 +199,6 @@ + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DIN2", NULL, 0, + SND_SOC_NOPM, 0, 0), +- SND_SOC_DAPM_AIF_IN("TXIN", NULL, 0, +- CS4265_SPDIF_CTL2, 5, 1), + + SND_SOC_DAPM_OUTPUT("LINEOUTL"), + SND_SOC_DAPM_OUTPUT("LINEOUTR"), diff --git a/projects/RPi/patches/linux/linux-06-slice-c.patch b/projects/RPi/patches/linux/linux-06-slice-c.patch new file mode 100644 index 0000000000..d2263c61bc --- /dev/null +++ b/projects/RPi/patches/linux/linux-06-slice-c.patch @@ -0,0 +1,279 @@ +--- 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 ++ * Based on the HifiBerry DAC driver by Florian Meier ++ * 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 ++ #include ++ ++ #include ++ #include ++ ++ #include ++ #include ++ #include ++ #include ++ #include ++ ++ #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 "); ++ MODULE_DESCRIPTION("ASoC Driver for Slice on-board audio"); ++ MODULE_LICENSE("GPL v2"); diff --git a/projects/RPi/patches/linux/linux-07-slice-Kconfig.patch b/projects/RPi/patches/linux/linux-07-slice-Kconfig.patch new file mode 100644 index 0000000000..a51dfde8ca --- /dev/null +++ b/projects/RPi/patches/linux/linux-07-slice-Kconfig.patch @@ -0,0 +1,17 @@ +--- 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 + diff --git a/projects/RPi/patches/linux/linux-08-slice-Makefile.patch b/projects/RPi/patches/linux/linux-08-slice-Makefile.patch new file mode 100644 index 0000000000..b1395c95a6 --- /dev/null +++ b/projects/RPi/patches/linux/linux-08-slice-Makefile.patch @@ -0,0 +1,15 @@ +--- 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-dionaudio-loco-objs := dionaudio_loco.o + snd-soc-allo-piano-dac-objs := allo-piano-dac.o + snd-soc-pisound-objs := pisound.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_DIGIDAC1_SOUNDCARD) += + obj-$(CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO) += snd-soc-dionaudio-loco.o + 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_SLICE) += snd-soc-slice.o