dect
/
linux-2.6
Archived
13
0
Fork 0

ARM: arm-soc board updates, take 2

This branch contains board updates for shmobile that had dependencies
 on earlier branches past the first driver branch, and thus are merged
 separately.
 
 Most of these are to enable audio and USB on shmobile. They contain a
 dependent ASoC branch that has been coordinated with Mark Brown.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iQIcBAABAgAGBQJQySyyAAoJEIwa5zzehBx3PmkP/3hvmOJz/WVvsfsU2eomAiD/
 QA8NRWCLV4rLdJfe5FUztP0S3UtrA34pdjehwW1drPtX0J4c+o2aap7UKInNPzih
 kUj9W+D7tLyzITzHReD5kvP5HbyI+VnNN07N+okYNfpYw+dc9/sYQuQ11SdrgRHA
 IUZr0G7uF0RaZQ3tb+WjAgOr09AwDf7lxQenS+TN9wND14jGq91XINfgKuVkD9fF
 WhvUj/l/2O+R9t3HkklJQvdxIEbxHd0eaDc4mNJpnTjN3+4yTsaWsFkgfNkGY113
 +e5RPx5IkRH6d+IBzYMSGjZtRi4JOQKDT9yce4tLmQtjL03EF6yHtwV1PiYx8FPI
 QpY0i9kEGrTA6l7DSBK/4/pUSZHBAeMqaM+/9/9v26JZSGNp8rJzJ2wLaGkADv/v
 MUPL0iu86pRpOOur6ArHwu3uwOf7UCalXWTHB/3fFcpspR1x+FtA7qMTvKh/wQSp
 0DhOVD5MbEq/MByqp0VKLLzzbXnPgkWeFNKsRXdMJOVfPpUygC1QIJcT2zC38Tc0
 8yJM0q/omAzUVO4LytcQQwiqCVLce6BRJa+cbv8olET4kZ4bG3sVsrbXyTk8H/N5
 VO2lu9/cXexHJCg5HNOmmF7SrG56iuLXtNwRriy6j1DSd1A12p8fvMJt28562KLw
 voCOaNlGm5uYcbLn0M4b
 =1a/W
 -----END PGP SIGNATURE-----

Merge tag 'boards2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc

Pull ARM SoC board updates, take 2 from Olof Johansson:
 "This branch contains board updates for shmobile that had dependencies
  on earlier branches past the first driver branch, and thus are merged
  separately.

  Most of these are to enable audio and USB on shmobile.  They contain a
  dependent ASoC branch that has been coordinated with Mark Brown."

* tag 'boards2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc:
  ARM: shmobile: mackerel: Add FLCTL IRQ resource
  ARM: shmobile: use FSI driver's audio clock on ap4evb
  ARM: shmobile: use FSI driver's audio clock on mackerel
  ARM: shmobile: use FSI driver's audio clock on armadillo800eva
  ARM: shmobile: mackerel: enable DMAEngine on USB Host
  ARM: shmobile: marzen: add USB OHCI driver support
  ARM: shmobile: marzen: add USB EHCI driver support
  ARM: shmobile: marzen: add USB phy support
  ASoC: fsi: add master clock control functions
  ASoC: fsi: care fsi_hw_start/stop() return value
  ASoC: fsi: fsi_set_master_clk() was called from fsi_hw_xxx() only
  ASoC: fsi: use devm_request_irq()
  ASoC: fsi: fixup channels_min/max
This commit is contained in:
Linus Torvalds 2012-12-13 11:00:00 -08:00
commit fe504c5c74
8 changed files with 617 additions and 289 deletions

View File

@ -47,6 +47,8 @@ CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_STANDALONE is not set
# CONFIG_PREVENT_FIRMWARE_BUILD is not set
# CONFIG_FW_LOADER is not set
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_NETDEVICES=y
# CONFIG_NET_VENDOR_BROADCOM is not set
# CONFIG_NET_VENDOR_FARADAY is not set
@ -59,9 +61,8 @@ CONFIG_SMSC911X=y
# CONFIG_NET_VENDOR_STMICRO is not set
# CONFIG_WLAN is not set
# CONFIG_INPUT_MOUSEDEV is not set
# CONFIG_INPUT_KEYBOARD is not set
CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
# CONFIG_DEVKMEM is not set
@ -78,9 +79,16 @@ CONFIG_GPIO_SYSFS=y
CONFIG_THERMAL=y
CONFIG_RCAR_THERMAL=y
CONFIG_SSB=y
# CONFIG_USB_SUPPORT is not set
CONFIG_USB=y
CONFIG_USB_RCAR_PHY=y
CONFIG_MMC=y
CONFIG_MMC_SDHI=y
CONFIG_USB=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_STORAGE=y
CONFIG_UIO=y
CONFIG_UIO_PDRV_GENIRQ=y
# CONFIG_IOMMU_SUPPORT is not set

View File

@ -29,6 +29,8 @@ config ARCH_R8A7779
select ARM_GIC
select CPU_V7
select SH_CLK_CPG
select USB_ARCH_HAS_EHCI
select USB_ARCH_HAS_OHCI
config ARCH_EMEV2
bool "Emma Mobile EV2"

View File

@ -658,133 +658,16 @@ static struct platform_device lcdc_device = {
/* FSI */
#define IRQ_FSI evt2irq(0x1840)
static int __fsi_set_rate(struct clk *clk, long rate, int enable)
{
int ret = 0;
if (rate <= 0)
return ret;
if (enable) {
ret = clk_set_rate(clk, rate);
if (0 == ret)
ret = clk_enable(clk);
} else {
clk_disable(clk);
}
return ret;
}
static int __fsi_set_round_rate(struct clk *clk, long rate, int enable)
{
return __fsi_set_rate(clk, clk_round_rate(clk, rate), enable);
}
static int fsi_ak4642_set_rate(struct device *dev, int rate, int enable)
{
struct clk *fsia_ick;
struct clk *fsiack;
int ret = -EIO;
fsia_ick = clk_get(dev, "icka");
if (IS_ERR(fsia_ick))
return PTR_ERR(fsia_ick);
/*
* FSIACK is connected to AK4642,
* and use external clock pin from it.
* it is parent of fsia_ick now.
*/
fsiack = clk_get_parent(fsia_ick);
if (!fsiack)
goto fsia_ick_out;
/*
* we get 1/1 divided clock by setting same rate to fsiack and fsia_ick
*
** FIXME **
* Because the freq_table of external clk (fsiack) are all 0,
* the return value of clk_round_rate became 0.
* So, it use __fsi_set_rate here.
*/
ret = __fsi_set_rate(fsiack, rate, enable);
if (ret < 0)
goto fsiack_out;
ret = __fsi_set_round_rate(fsia_ick, rate, enable);
if ((ret < 0) && enable)
__fsi_set_round_rate(fsiack, rate, 0); /* disable FSI ACK */
fsiack_out:
clk_put(fsiack);
fsia_ick_out:
clk_put(fsia_ick);
return 0;
}
static int fsi_hdmi_set_rate(struct device *dev, int rate, int enable)
{
struct clk *fsib_clk;
struct clk *fdiv_clk = clk_get(NULL, "fsidivb");
long fsib_rate = 0;
long fdiv_rate = 0;
int ackmd_bpfmd;
int ret;
switch (rate) {
case 44100:
fsib_rate = rate * 256;
ackmd_bpfmd = SH_FSI_ACKMD_256 | SH_FSI_BPFMD_64;
break;
case 48000:
fsib_rate = 85428000; /* around 48kHz x 256 x 7 */
fdiv_rate = rate * 256;
ackmd_bpfmd = SH_FSI_ACKMD_256 | SH_FSI_BPFMD_64;
break;
default:
pr_err("unsupported rate in FSI2 port B\n");
return -EINVAL;
}
/* FSI B setting */
fsib_clk = clk_get(dev, "ickb");
if (IS_ERR(fsib_clk))
return -EIO;
ret = __fsi_set_round_rate(fsib_clk, fsib_rate, enable);
if (ret < 0)
goto fsi_set_rate_end;
/* FSI DIV setting */
ret = __fsi_set_round_rate(fdiv_clk, fdiv_rate, enable);
if (ret < 0) {
/* disable FSI B */
if (enable)
__fsi_set_round_rate(fsib_clk, fsib_rate, 0);
goto fsi_set_rate_end;
}
ret = ackmd_bpfmd;
fsi_set_rate_end:
clk_put(fsib_clk);
return ret;
}
static struct sh_fsi_platform_info fsi_info = {
.port_a = {
.flags = SH_FSI_BRS_INV,
.set_rate = fsi_ak4642_set_rate,
},
.port_b = {
.flags = SH_FSI_BRS_INV |
SH_FSI_BRM_INV |
SH_FSI_LRS_INV |
SH_FSI_CLK_CPG |
SH_FSI_FMT_SPDIF,
.set_rate = fsi_hdmi_set_rate,
},
};
@ -1144,25 +1027,6 @@ out:
clk_put(hdmi_ick);
}
static void __init fsi_init_pm_clock(void)
{
struct clk *fsia_ick;
int ret;
fsia_ick = clk_get(&fsi_device.dev, "icka");
if (IS_ERR(fsia_ick)) {
ret = PTR_ERR(fsia_ick);
pr_err("Cannot get FSI ICK: %d\n", ret);
return;
}
ret = clk_set_parent(fsia_ick, &sh7372_fsiack_clk);
if (ret < 0)
pr_err("Cannot set FSI-A parent: %d\n", ret);
clk_put(fsia_ick);
}
/* TouchScreen */
#ifdef CONFIG_AP4EVB_QHD
# define GPIO_TSC_IRQ GPIO_FN_IRQ28_123
@ -1476,7 +1340,6 @@ static void __init ap4evb_init(void)
ARRAY_SIZE(domain_devices));
hdmi_init_pm_clock();
fsi_init_pm_clock();
sh7372_pm_init();
pm_clk_add(&fsi_device.dev, "spu2");
pm_clk_add(&lcdc1_device.dev, "hdmi");

View File

@ -768,32 +768,6 @@ static struct platform_device ceu0_device = {
};
/* FSI */
static int fsi_hdmi_set_rate(struct device *dev, int rate, int enable)
{
struct clk *fsib;
int ret;
/* it support 48KHz only */
if (48000 != rate)
return -EINVAL;
fsib = clk_get(dev, "ickb");
if (IS_ERR(fsib))
return -EINVAL;
if (enable) {
ret = SH_FSI_ACKMD_256 | SH_FSI_BPFMD_64;
clk_enable(fsib);
} else {
ret = 0;
clk_disable(fsib);
}
clk_put(fsib);
return ret;
}
static struct sh_fsi_platform_info fsi_info = {
/* FSI-WM8978 */
.port_a = {
@ -802,8 +776,8 @@ static struct sh_fsi_platform_info fsi_info = {
/* FSI-HDMI */
.port_b = {
.flags = SH_FSI_FMT_SPDIF |
SH_FSI_ENABLE_STREAM_MODE,
.set_rate = fsi_hdmi_set_rate,
SH_FSI_ENABLE_STREAM_MODE |
SH_FSI_CLK_CPG,
.tx_id = SHDMA_SLAVE_FSIB_TX,
}
};
@ -938,13 +912,11 @@ static void __init eva_clock_init(void)
struct clk *xtal1 = clk_get(NULL, "extal1");
struct clk *usb24s = clk_get(NULL, "usb24s");
struct clk *fsibck = clk_get(NULL, "fsibck");
struct clk *fsib = clk_get(&fsi_device.dev, "ickb");
if (IS_ERR(system) ||
IS_ERR(xtal1) ||
IS_ERR(usb24s) ||
IS_ERR(fsibck) ||
IS_ERR(fsib)) {
IS_ERR(fsibck)) {
pr_err("armadillo800eva board clock init failed\n");
goto clock_error;
}
@ -956,9 +928,7 @@ static void __init eva_clock_init(void)
clk_set_parent(usb24s, system);
/* FSIBCK is 12.288MHz, and it is parent of FSI-B */
clk_set_parent(fsib, fsibck);
clk_set_rate(fsibck, 12288000);
clk_set_rate(fsib, 12288000);
clock_error:
if (!IS_ERR(system))
@ -969,8 +939,6 @@ clock_error:
clk_put(usb24s);
if (!IS_ERR(fsibck))
clk_put(fsibck);
if (!IS_ERR(fsib))
clk_put(fsib);
}
/*

View File

@ -816,6 +816,8 @@ static struct platform_device usbhs1_device = {
.id = 1,
.dev = {
.platform_data = &usbhs1_private.info,
.dma_mask = &usbhs1_device.dev.coherent_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
.num_resources = ARRAY_SIZE(usbhs1_resources),
.resource = usbhs1_resources,
@ -860,76 +862,6 @@ static struct platform_device leds_device = {
/* FSI */
#define IRQ_FSI evt2irq(0x1840)
static int __fsi_set_round_rate(struct clk *clk, long rate, int enable)
{
int ret;
if (rate <= 0)
return 0;
if (!enable) {
clk_disable(clk);
return 0;
}
ret = clk_set_rate(clk, clk_round_rate(clk, rate));
if (ret < 0)
return ret;
return clk_enable(clk);
}
static int fsi_b_set_rate(struct device *dev, int rate, int enable)
{
struct clk *fsib_clk;
struct clk *fdiv_clk = clk_get(NULL, "fsidivb");
long fsib_rate = 0;
long fdiv_rate = 0;
int ackmd_bpfmd;
int ret;
/* clock start */
switch (rate) {
case 44100:
fsib_rate = rate * 256;
ackmd_bpfmd = SH_FSI_ACKMD_256 | SH_FSI_BPFMD_64;
break;
case 48000:
fsib_rate = 85428000; /* around 48kHz x 256 x 7 */
fdiv_rate = rate * 256;
ackmd_bpfmd = SH_FSI_ACKMD_256 | SH_FSI_BPFMD_64;
break;
default:
pr_err("unsupported rate in FSI2 port B\n");
return -EINVAL;
}
/* FSI B setting */
fsib_clk = clk_get(dev, "ickb");
if (IS_ERR(fsib_clk))
return -EIO;
/* fsib */
ret = __fsi_set_round_rate(fsib_clk, fsib_rate, enable);
if (ret < 0)
goto fsi_set_rate_end;
/* FSI DIV */
ret = __fsi_set_round_rate(fdiv_clk, fdiv_rate, enable);
if (ret < 0) {
/* disable FSI B */
if (enable)
__fsi_set_round_rate(fsib_clk, fsib_rate, 0);
goto fsi_set_rate_end;
}
ret = ackmd_bpfmd;
fsi_set_rate_end:
clk_put(fsib_clk);
return ret;
}
static struct sh_fsi_platform_info fsi_info = {
.port_a = {
.flags = SH_FSI_BRS_INV,
@ -940,8 +872,8 @@ static struct sh_fsi_platform_info fsi_info = {
.flags = SH_FSI_BRS_INV |
SH_FSI_BRM_INV |
SH_FSI_LRS_INV |
SH_FSI_CLK_CPG |
SH_FSI_FMT_SPDIF,
.set_rate = fsi_b_set_rate,
}
};
@ -1018,7 +950,11 @@ static struct resource nand_flash_resources[] = {
.start = 0xe6a30000,
.end = 0xe6a3009b,
.flags = IORESOURCE_MEM,
}
},
[1] = {
.start = evt2irq(0x0d80), /* flstei: status error irq */
.flags = IORESOURCE_IRQ,
},
};
static struct sh_flctl_platform_data nand_flash_data = {

View File

@ -34,6 +34,10 @@
#include <linux/spi/sh_hspi.h>
#include <linux/mmc/sh_mobile_sdhi.h>
#include <linux/mfd/tmio.h>
#include <linux/usb/otg.h>
#include <linux/usb/ehci_pdriver.h>
#include <linux/usb/ohci_pdriver.h>
#include <linux/pm_runtime.h>
#include <mach/hardware.h>
#include <mach/r8a7779.h>
#include <mach/common.h>
@ -144,13 +148,185 @@ static struct platform_device hspi_device = {
.num_resources = ARRAY_SIZE(hspi_resources),
};
/* USB PHY */
static struct resource usb_phy_resources[] = {
[0] = {
.start = 0xffe70000,
.end = 0xffe70900 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 0xfff70000,
.end = 0xfff70900 - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device usb_phy_device = {
.name = "rcar_usb_phy",
.resource = usb_phy_resources,
.num_resources = ARRAY_SIZE(usb_phy_resources),
};
static struct platform_device *marzen_devices[] __initdata = {
&eth_device,
&sdhi0_device,
&thermal_device,
&hspi_device,
&usb_phy_device,
};
/* USB */
static struct usb_phy *phy;
static int usb_power_on(struct platform_device *pdev)
{
if (!phy)
return -EIO;
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
usb_phy_init(phy);
return 0;
}
static void usb_power_off(struct platform_device *pdev)
{
if (!phy)
return;
usb_phy_shutdown(phy);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}
static struct usb_ehci_pdata ehcix_pdata = {
.power_on = usb_power_on,
.power_off = usb_power_off,
.power_suspend = usb_power_off,
};
static struct resource ehci0_resources[] = {
[0] = {
.start = 0xffe70000,
.end = 0xffe70400 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = gic_spi(44),
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device ehci0_device = {
.name = "ehci-platform",
.id = 0,
.dev = {
.dma_mask = &ehci0_device.dev.coherent_dma_mask,
.coherent_dma_mask = 0xffffffff,
.platform_data = &ehcix_pdata,
},
.num_resources = ARRAY_SIZE(ehci0_resources),
.resource = ehci0_resources,
};
static struct resource ehci1_resources[] = {
[0] = {
.start = 0xfff70000,
.end = 0xfff70400 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = gic_spi(45),
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device ehci1_device = {
.name = "ehci-platform",
.id = 1,
.dev = {
.dma_mask = &ehci1_device.dev.coherent_dma_mask,
.coherent_dma_mask = 0xffffffff,
.platform_data = &ehcix_pdata,
},
.num_resources = ARRAY_SIZE(ehci1_resources),
.resource = ehci1_resources,
};
static struct usb_ohci_pdata ohcix_pdata = {
.power_on = usb_power_on,
.power_off = usb_power_off,
.power_suspend = usb_power_off,
};
static struct resource ohci0_resources[] = {
[0] = {
.start = 0xffe70400,
.end = 0xffe70800 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = gic_spi(44),
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device ohci0_device = {
.name = "ohci-platform",
.id = 0,
.dev = {
.dma_mask = &ohci0_device.dev.coherent_dma_mask,
.coherent_dma_mask = 0xffffffff,
.platform_data = &ohcix_pdata,
},
.num_resources = ARRAY_SIZE(ohci0_resources),
.resource = ohci0_resources,
};
static struct resource ohci1_resources[] = {
[0] = {
.start = 0xfff70400,
.end = 0xfff70800 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = gic_spi(45),
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device ohci1_device = {
.name = "ohci-platform",
.id = 1,
.dev = {
.dma_mask = &ohci1_device.dev.coherent_dma_mask,
.coherent_dma_mask = 0xffffffff,
.platform_data = &ohcix_pdata,
},
.num_resources = ARRAY_SIZE(ohci1_resources),
.resource = ohci1_resources,
};
static struct platform_device *marzen_late_devices[] __initdata = {
&ehci0_device,
&ehci1_device,
&ohci0_device,
&ohci1_device,
};
void __init marzen_init_late(void)
{
/* get usb phy */
phy = usb_get_phy(USB_PHY_TYPE_USB2);
shmobile_init_late();
platform_add_devices(marzen_late_devices,
ARRAY_SIZE(marzen_late_devices));
}
static void __init marzen_init(void)
{
regulator_register_always_on(0, "fixed-3.3V", fixed3v3_power_consumers,
@ -188,6 +364,14 @@ static void __init marzen_init(void)
gpio_request(GPIO_FN_HSPI_TX0, NULL);
gpio_request(GPIO_FN_HSPI_RX0, NULL);
/* USB (CN21) */
gpio_request(GPIO_FN_USB_OVC0, NULL);
gpio_request(GPIO_FN_USB_OVC1, NULL);
gpio_request(GPIO_FN_USB_OVC2, NULL);
/* USB (CN22) */
gpio_request(GPIO_FN_USB_PENC2, NULL);
r8a7779_add_standard_devices();
platform_add_devices(marzen_devices, ARRAY_SIZE(marzen_devices));
}
@ -200,6 +384,6 @@ MACHINE_START(MARZEN, "marzen")
.init_irq = r8a7779_init_irq,
.handle_irq = gic_handle_irq,
.init_machine = marzen_init,
.init_late = shmobile_init_late,
.init_late = marzen_init_late,
.timer = &shmobile_timer,
MACHINE_END

View File

@ -26,6 +26,7 @@
* A: inversion
* B: format mode
* C: chip specific
* D: clock selecter if master mode
*/
/* A: clock inversion */
@ -44,6 +45,11 @@
#define SH_FSI_OPTION_MASK 0x00000F00
#define SH_FSI_ENABLE_STREAM_MODE (1 << 8) /* for 16bit data */
/* D: clock selecter if master mode */
#define SH_FSI_CLK_MASK 0x0000F000
#define SH_FSI_CLK_EXTERNAL (1 << 12)
#define SH_FSI_CLK_CPG (2 << 12) /* FSIxCK + FSI-DIV */
/*
* set_rate return value
*

View File

@ -22,6 +22,7 @@
#include <linux/module.h>
#include <linux/workqueue.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include <sound/sh_fsi.h>
/* PortA/PortB register */
@ -188,6 +189,14 @@ typedef int (*set_rate_func)(struct device *dev, int rate, int enable);
* --> go to codecs
*/
/*
* FSI clock
*
* FSIxCLK [CPG] (ick) -------> |
* |-> FSI_DIV (div)-> FSI2
* FSIxCK [external] (xck) ---> |
*/
/*
* struct
*/
@ -228,6 +237,20 @@ struct fsi_stream {
dma_addr_t dma;
};
struct fsi_clk {
/* see [FSI clock] */
struct clk *own;
struct clk *xck;
struct clk *ick;
struct clk *div;
int (*set_rate)(struct device *dev,
struct fsi_priv *fsi,
unsigned long rate);
unsigned long rate;
unsigned int count;
};
struct fsi_priv {
void __iomem *base;
struct fsi_master *master;
@ -236,6 +259,8 @@ struct fsi_priv {
struct fsi_stream playback;
struct fsi_stream capture;
struct fsi_clk clock;
u32 fmt;
int chan_num:16;
@ -717,14 +742,335 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable)
/*
* clock function
*/
static int fsi_clk_init(struct device *dev,
struct fsi_priv *fsi,
int xck,
int ick,
int div,
int (*set_rate)(struct device *dev,
struct fsi_priv *fsi,
unsigned long rate))
{
struct fsi_clk *clock = &fsi->clock;
int is_porta = fsi_is_port_a(fsi);
clock->xck = NULL;
clock->ick = NULL;
clock->div = NULL;
clock->rate = 0;
clock->count = 0;
clock->set_rate = set_rate;
clock->own = devm_clk_get(dev, NULL);
if (IS_ERR(clock->own))
return -EINVAL;
/* external clock */
if (xck) {
clock->xck = devm_clk_get(dev, is_porta ? "xcka" : "xckb");
if (IS_ERR(clock->xck)) {
dev_err(dev, "can't get xck clock\n");
return -EINVAL;
}
if (clock->xck == clock->own) {
dev_err(dev, "cpu doesn't support xck clock\n");
return -EINVAL;
}
}
/* FSIACLK/FSIBCLK */
if (ick) {
clock->ick = devm_clk_get(dev, is_porta ? "icka" : "ickb");
if (IS_ERR(clock->ick)) {
dev_err(dev, "can't get ick clock\n");
return -EINVAL;
}
if (clock->ick == clock->own) {
dev_err(dev, "cpu doesn't support ick clock\n");
return -EINVAL;
}
}
/* FSI-DIV */
if (div) {
clock->div = devm_clk_get(dev, is_porta ? "diva" : "divb");
if (IS_ERR(clock->div)) {
dev_err(dev, "can't get div clock\n");
return -EINVAL;
}
if (clock->div == clock->own) {
dev_err(dev, "cpu doens't support div clock\n");
return -EINVAL;
}
}
return 0;
}
#define fsi_clk_invalid(fsi) fsi_clk_valid(fsi, 0)
static void fsi_clk_valid(struct fsi_priv *fsi, unsigned long rate)
{
fsi->clock.rate = rate;
}
static int fsi_clk_is_valid(struct fsi_priv *fsi)
{
return fsi->clock.set_rate &&
fsi->clock.rate;
}
static int fsi_clk_enable(struct device *dev,
struct fsi_priv *fsi,
unsigned long rate)
{
struct fsi_clk *clock = &fsi->clock;
int ret = -EINVAL;
if (!fsi_clk_is_valid(fsi))
return ret;
if (0 == clock->count) {
ret = clock->set_rate(dev, fsi, rate);
if (ret < 0) {
fsi_clk_invalid(fsi);
return ret;
}
if (clock->xck)
clk_enable(clock->xck);
if (clock->ick)
clk_enable(clock->ick);
if (clock->div)
clk_enable(clock->div);
clock->count++;
}
return ret;
}
static int fsi_clk_disable(struct device *dev,
struct fsi_priv *fsi)
{
struct fsi_clk *clock = &fsi->clock;
if (!fsi_clk_is_valid(fsi))
return -EINVAL;
if (1 == clock->count--) {
if (clock->xck)
clk_disable(clock->xck);
if (clock->ick)
clk_disable(clock->ick);
if (clock->div)
clk_disable(clock->div);
}
return 0;
}
static int fsi_clk_set_ackbpf(struct device *dev,
struct fsi_priv *fsi,
int ackmd, int bpfmd)
{
u32 data = 0;
/* check ackmd/bpfmd relationship */
if (bpfmd > ackmd) {
dev_err(dev, "unsupported rate (%d/%d)\n", ackmd, bpfmd);
return -EINVAL;
}
/* ACKMD */
switch (ackmd) {
case 512:
data |= (0x0 << 12);
break;
case 256:
data |= (0x1 << 12);
break;
case 128:
data |= (0x2 << 12);
break;
case 64:
data |= (0x3 << 12);
break;
case 32:
data |= (0x4 << 12);
break;
default:
dev_err(dev, "unsupported ackmd (%d)\n", ackmd);
return -EINVAL;
}
/* BPFMD */
switch (bpfmd) {
case 32:
data |= (0x0 << 8);
break;
case 64:
data |= (0x1 << 8);
break;
case 128:
data |= (0x2 << 8);
break;
case 256:
data |= (0x3 << 8);
break;
case 512:
data |= (0x4 << 8);
break;
case 16:
data |= (0x7 << 8);
break;
default:
dev_err(dev, "unsupported bpfmd (%d)\n", bpfmd);
return -EINVAL;
}
dev_dbg(dev, "ACKMD/BPFMD = %d/%d\n", ackmd, bpfmd);
fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
udelay(10);
return 0;
}
static int fsi_clk_set_rate_external(struct device *dev,
struct fsi_priv *fsi,
unsigned long rate)
{
struct clk *xck = fsi->clock.xck;
struct clk *ick = fsi->clock.ick;
unsigned long xrate;
int ackmd, bpfmd;
int ret = 0;
/* check clock rate */
xrate = clk_get_rate(xck);
if (xrate % rate) {
dev_err(dev, "unsupported clock rate\n");
return -EINVAL;
}
clk_set_parent(ick, xck);
clk_set_rate(ick, xrate);
bpfmd = fsi->chan_num * 32;
ackmd = xrate / rate;
dev_dbg(dev, "external/rate = %ld/%ld\n", xrate, rate);
ret = fsi_clk_set_ackbpf(dev, fsi, ackmd, bpfmd);
if (ret < 0)
dev_err(dev, "%s failed", __func__);
return ret;
}
static int fsi_clk_set_rate_cpg(struct device *dev,
struct fsi_priv *fsi,
unsigned long rate)
{
struct clk *ick = fsi->clock.ick;
struct clk *div = fsi->clock.div;
unsigned long target = 0; /* 12288000 or 11289600 */
unsigned long actual, cout;
unsigned long diff, min;
unsigned long best_cout, best_act;
int adj;
int ackmd, bpfmd;
int ret = -EINVAL;
if (!(12288000 % rate))
target = 12288000;
if (!(11289600 % rate))
target = 11289600;
if (!target) {
dev_err(dev, "unsupported rate\n");
return ret;
}
bpfmd = fsi->chan_num * 32;
ackmd = target / rate;
ret = fsi_clk_set_ackbpf(dev, fsi, ackmd, bpfmd);
if (ret < 0) {
dev_err(dev, "%s failed", __func__);
return ret;
}
/*
* The clock flow is
*
* [CPG] = cout => [FSI_DIV] = audio => [FSI] => [codec]
*
* But, it needs to find best match of CPG and FSI_DIV
* combination, since it is difficult to generate correct
* frequency of audio clock from ick clock only.
* Because ick is created from its parent clock.
*
* target = rate x [512/256/128/64]fs
* cout = round(target x adjustment)
* actual = cout / adjustment (by FSI-DIV) ~= target
* audio = actual
*/
min = ~0;
best_cout = 0;
best_act = 0;
for (adj = 1; adj < 0xffff; adj++) {
cout = target * adj;
if (cout > 100000000) /* max clock = 100MHz */
break;
/* cout/actual audio clock */
cout = clk_round_rate(ick, cout);
actual = cout / adj;
/* find best frequency */
diff = abs(actual - target);
if (diff < min) {
min = diff;
best_cout = cout;
best_act = actual;
}
}
ret = clk_set_rate(ick, best_cout);
if (ret < 0) {
dev_err(dev, "ick clock failed\n");
return -EIO;
}
ret = clk_set_rate(div, clk_round_rate(div, best_act));
if (ret < 0) {
dev_err(dev, "div clock failed\n");
return -EIO;
}
dev_dbg(dev, "ick/div = %ld/%ld\n",
clk_get_rate(ick), clk_get_rate(div));
return ret;
}
static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi,
long rate, int enable)
{
set_rate_func set_rate = fsi_get_info_set_rate(fsi);
int ret;
if (!set_rate)
return 0;
/*
* CAUTION
*
* set_rate will be deleted
*/
if (!set_rate) {
if (enable)
return fsi_clk_enable(dev, fsi, rate);
else
return fsi_clk_disable(dev, fsi);
}
ret = set_rate(dev, rate, enable);
if (ret < 0) /* error */
@ -1334,14 +1680,21 @@ static int fsi_hw_startup(struct fsi_priv *fsi,
/* fifo init */
fsi_fifo_init(fsi, io, dev);
/* start master clock */
if (fsi_is_clk_master(fsi))
return fsi_set_master_clk(dev, fsi, fsi->rate, 1);
return 0;
}
static void fsi_hw_shutdown(struct fsi_priv *fsi,
static int fsi_hw_shutdown(struct fsi_priv *fsi,
struct device *dev)
{
/* stop master clock */
if (fsi_is_clk_master(fsi))
fsi_set_master_clk(dev, fsi, fsi->rate, 0);
return fsi_set_master_clk(dev, fsi, fsi->rate, 0);
return 0;
}
static int fsi_dai_startup(struct snd_pcm_substream *substream,
@ -1349,6 +1702,7 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
{
struct fsi_priv *fsi = fsi_get_priv(substream);
fsi_clk_invalid(fsi);
fsi->rate = 0;
return 0;
@ -1359,6 +1713,7 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
{
struct fsi_priv *fsi = fsi_get_priv(substream);
fsi_clk_invalid(fsi);
fsi->rate = 0;
}
@ -1372,13 +1727,16 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
fsi_stream_init(fsi, io, substream);
fsi_hw_startup(fsi, io, dai->dev);
ret = fsi_stream_transfer(io);
if (0 == ret)
if (!ret)
ret = fsi_hw_startup(fsi, io, dai->dev);
if (!ret)
ret = fsi_stream_transfer(io);
if (!ret)
fsi_stream_start(fsi, io);
break;
case SNDRV_PCM_TRIGGER_STOP:
fsi_hw_shutdown(fsi, dai->dev);
if (!ret)
ret = fsi_hw_shutdown(fsi, dai->dev);
fsi_stream_stop(fsi, io);
fsi_stream_quit(fsi, io);
break;
@ -1437,9 +1795,25 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
if (fsi_is_clk_master(fsi) && !set_rate) {
dev_err(dai->dev, "platform doesn't have set_rate\n");
return -EINVAL;
if (fsi_is_clk_master(fsi)) {
/*
* CAUTION
*
* set_rate will be deleted
*/
if (set_rate)
dev_warn(dai->dev, "set_rate will be removed soon\n");
switch (flags & SH_FSI_CLK_MASK) {
case SH_FSI_CLK_EXTERNAL:
fsi_clk_init(dai->dev, fsi, 1, 1, 0,
fsi_clk_set_rate_external);
break;
case SH_FSI_CLK_CPG:
fsi_clk_init(dai->dev, fsi, 0, 1, 1,
fsi_clk_set_rate_cpg);
break;
}
}
/* set format */
@ -1462,19 +1836,13 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsi_priv *fsi = fsi_get_priv(substream);
long rate = params_rate(params);
int ret;
if (!fsi_is_clk_master(fsi))
return 0;
if (fsi_is_clk_master(fsi)) {
fsi->rate = params_rate(params);
fsi_clk_valid(fsi, fsi->rate);
}
ret = fsi_set_master_clk(dai->dev, fsi, rate, 1);
if (ret < 0)
return ret;
fsi->rate = rate;
return ret;
return 0;
}
static const struct snd_soc_dai_ops fsi_dai_ops = {
@ -1498,7 +1866,7 @@ static struct snd_pcm_hardware fsi_pcm_hardware = {
.rates = FSI_RATES,
.rate_min = 8000,
.rate_max = 192000,
.channels_min = 1,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 32,
@ -1586,14 +1954,14 @@ static struct snd_soc_dai_driver fsi_soc_dai[] = {
.playback = {
.rates = FSI_RATES,
.formats = FSI_FMTS,
.channels_min = 1,
.channels_max = 8,
.channels_min = 2,
.channels_max = 2,
},
.capture = {
.rates = FSI_RATES,
.formats = FSI_FMTS,
.channels_min = 1,
.channels_max = 8,
.channels_min = 2,
.channels_max = 2,
},
.ops = &fsi_dai_ops,
},
@ -1602,14 +1970,14 @@ static struct snd_soc_dai_driver fsi_soc_dai[] = {
.playback = {
.rates = FSI_RATES,
.formats = FSI_FMTS,
.channels_min = 1,
.channels_max = 8,
.channels_min = 2,
.channels_max = 2,
},
.capture = {
.rates = FSI_RATES,
.formats = FSI_FMTS,
.channels_min = 1,
.channels_max = 8,
.channels_min = 2,
.channels_max = 2,
},
.ops = &fsi_dai_ops,
},
@ -1702,7 +2070,7 @@ static int fsi_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
dev_set_drvdata(&pdev->dev, master);
ret = request_irq(irq, &fsi_interrupt, 0,
ret = devm_request_irq(&pdev->dev, irq, &fsi_interrupt, 0,
id_entry->name, master);
if (ret) {
dev_err(&pdev->dev, "irq request err\n");
@ -1712,7 +2080,7 @@ static int fsi_probe(struct platform_device *pdev)
ret = snd_soc_register_platform(&pdev->dev, &fsi_soc_platform);
if (ret < 0) {
dev_err(&pdev->dev, "cannot snd soc register\n");
goto exit_free_irq;
goto exit_fsib;
}
ret = snd_soc_register_dais(&pdev->dev, fsi_soc_dai,
@ -1726,8 +2094,6 @@ static int fsi_probe(struct platform_device *pdev)
exit_snd_soc:
snd_soc_unregister_platform(&pdev->dev);
exit_free_irq:
free_irq(irq, master);
exit_fsib:
pm_runtime_disable(&pdev->dev);
fsi_stream_remove(&master->fsib);
@ -1743,7 +2109,6 @@ static int fsi_remove(struct platform_device *pdev)
master = dev_get_drvdata(&pdev->dev);
free_irq(master->irq, master);
pm_runtime_disable(&pdev->dev);
snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(fsi_soc_dai));
@ -1774,10 +2139,6 @@ static void __fsi_resume(struct fsi_priv *fsi,
return;
fsi_hw_startup(fsi, io, dev);
if (fsi_is_clk_master(fsi) && fsi->rate)
fsi_set_master_clk(dev, fsi, fsi->rate, 1);
fsi_stream_start(fsi, io);
}